* Implement the feature for the offset operation.
* Improve searching of coincidences in context of B-spline curve
* Decline offset if duplicated entities are selected.
#include <Geom_BSplineCurve.hxx>
+#include <GeomConvert.hxx>
+
#define MY_BSPLINE (*(implPtr<Handle_Geom_BSplineCurve>()))
-GeomAPI_BSpline::GeomAPI_BSpline(const GeomCurvePtr& theCurve)
+GeomAPI_BSpline::GeomAPI_BSpline (const GeomCurvePtr& theCurve)
{
GeomCurvePtr anUntrimmedCurve = theCurve->basisCurve();
Handle(Geom_Curve) aCurve = anUntrimmedCurve->impl<Handle(Geom_Curve)>();
{
return MY_BSPLINE->IsPeriodic();
}
+
+GeomBSplinePtr GeomAPI_BSpline::convertToBSpline (const GeomCurvePtr& theCurve)
+{
+ GeomCurvePtr anUntrimmedCurve = theCurve->basisCurve();
+ Handle(Geom_Curve) aCurve = anUntrimmedCurve->impl<Handle(Geom_Curve)>();
+ Handle(Geom_BSplineCurve) aBSpl = Handle(Geom_BSplineCurve)::DownCast(aCurve);
+ if (aBSpl.IsNull()) {
+ // convert to b-spline
+ aBSpl = GeomConvert::CurveToBSplineCurve(aCurve);
+ }
+ if (aBSpl.IsNull())
+ throw Standard_ConstructionError("GeomAPI_BSpline: Conversion to B-spline failed");
+ GeomCurvePtr aResCurve (new GeomAPI_Curve());
+ aResCurve->setImpl(new Handle_Geom_BSplineCurve(aBSpl));
+ GeomBSplinePtr aResult (new GeomAPI_BSpline(aResCurve));
+ return aResult;
+}
#include <memory>
class GeomAPI_Pnt;
+class GeomAPI_BSpline;
+
+//! Pointer on the object
+typedef std::shared_ptr<GeomAPI_BSpline> GeomBSplinePtr;
/**\class GeomAPI_BSpline
* \ingroup DataModel
{
public:
/// Creation of B-spline defined by a curve
- GEOMAPI_EXPORT GeomAPI_BSpline(const GeomCurvePtr& theCurve);
+ GEOMAPI_EXPORT GeomAPI_BSpline (const GeomCurvePtr& theCurve);
/// Degree of B-spline curve
GEOMAPI_EXPORT int degree() const;
/// Return \c true if the curve is periodic
GEOMAPI_EXPORT bool isPeriodic() const;
-};
-//! Pointer on the object
-typedef std::shared_ptr<GeomAPI_BSpline> GeomBSplinePtr;
+ /// Convert any curve into a B-spline curve
+ GEOMAPI_EXPORT static GeomBSplinePtr convertToBSpline (const GeomCurvePtr& theCurve);
+};
#endif
Handle(Geom_Curve) aCurve = BRep_Tool::Curve((const TopoDS_Edge&)aShape, aFirst, aLast);
if (aCurve.IsNull()) // degenerative edge
return false;
- if (aCurve->IsKind(STANDARD_TYPE(Geom_Ellipse)))
- return true;
- return false;
+ while (aCurve->IsKind(STANDARD_TYPE(Geom_TrimmedCurve)))
+ aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve();
+ return aCurve->IsKind(STANDARD_TYPE(Geom_Ellipse));
}
bool GeomAPI_Edge::isBSpline() const
double aFirst, aLast;
Handle(Geom_Curve) aCurve = BRep_Tool::Curve((const TopoDS_Edge&)aShape, aFirst, aLast);
if (!aCurve.IsNull()) {
+ while (aCurve->IsKind(STANDARD_TYPE(Geom_TrimmedCurve)))
+ aCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve)->BasisCurve();
Handle(Geom_Ellipse) aElips = Handle(Geom_Ellipse)::DownCast(aCurve);
if (!aElips.IsNull()) {
gp_Elips aGpElips = aElips->Elips();
%shared_ptr(GeomAlgoAPI_Copy)
%shared_ptr(GeomAlgoAPI_Symmetry)
%shared_ptr(GeomAlgoAPI_MapShapesAndAncestors)
+%shared_ptr(GeomAlgoAPI_WireBuilder)
// all supported interfaces
%include "GeomAlgoAPI_MakeShape.h"
#include "GeomAlgoAPI_Offset.h"
+#include <GeomAPI_Pln.h>
+
#include <BRepOffsetAPI_MakeOffsetShape.hxx>
+#include <BRepOffsetAPI_MakeOffset.hxx>
+
+#include <BRepBuilderAPI_MakeWire.hxx>
+#include <BRepBuilderAPI_MakeFace.hxx>
+#include <TopoDS.hxx>
+#include <TopoDS_Wire.hxx>
GeomAlgoAPI_Offset::GeomAlgoAPI_Offset(const GeomShapePtr& theShape,
const double theOffsetValue)
// nothing is generated
}
}
+
+GeomAlgoAPI_Offset::GeomAlgoAPI_Offset(const GeomPlanePtr& thePlane,
+ const GeomShapePtr& theEdgeOrWire,
+ const double theOffsetValue)
+{
+ // 1. Make wire from edge, if need
+ TopoDS_Wire aWire;
+ TopoDS_Shape anEdgeOrWire = theEdgeOrWire->impl<TopoDS_Shape>();
+ if (anEdgeOrWire.ShapeType() == TopAbs_WIRE) {
+ aWire = TopoDS::Wire(anEdgeOrWire);
+ } else {
+ if (anEdgeOrWire.ShapeType() == TopAbs_EDGE) {
+ BRepBuilderAPI_MakeWire aWireBuilder;
+ aWireBuilder.Add(TopoDS::Edge(anEdgeOrWire));
+ if (aWireBuilder.IsDone()) {
+ aWire = aWireBuilder.Wire();
+ }
+ }
+ }
+ if (aWire.IsNull())
+ return;
+
+ // 2. Make invalid face to pass it in Offset algorithm
+ BRepBuilderAPI_MakeFace aFaceBuilder (thePlane->impl<gp_Pln>(), aWire);
+ const TopoDS_Face& aFace = aFaceBuilder.Face();
+
+ // 3. Make Offset
+ BRepOffsetAPI_MakeOffset* aParal = new BRepOffsetAPI_MakeOffset;
+ setImpl(aParal);
+ setBuilderType(OCCT_BRepBuilderAPI_MakeShape);
+
+ Standard_Boolean isOpenResult = !aWire.Closed();
+ aParal->Init(aFace, GeomAbs_Arc, isOpenResult);
+ aParal->Perform(theOffsetValue, 0.);
+ if (aParal->IsDone()) {
+ TopoDS_Shape anOffset = aParal->Shape();
+ GeomShapePtr aResult(new GeomAPI_Shape());
+ aResult->setImpl(new TopoDS_Shape(anOffset));
+ setShape(aResult);
+ setDone(true);
+ }
+}
#include <GeomAlgoAPI.h>
#include <GeomAlgoAPI_MakeShape.h>
+class GeomAPI_Pln;
+
/// \class GeomAlgoAPI_Offset
/// \ingroup DataAlgo
/// \brief Perform 3D offset for the shape
GEOMALGOAPI_EXPORT GeomAlgoAPI_Offset(const GeomShapePtr& theShape,
const double theOffsetValue);
+ /// \brief Perform the offset algorithm on the plane
+ /// \param[in] thePlane base plane for all offsets
+ /// \param[in] theEdgesOrWire base shapes
+ /// \param[in] theOffsetValue offset distance, it can be negative
+ GEOMALGOAPI_EXPORT GeomAlgoAPI_Offset(const std::shared_ptr<GeomAPI_Pln>& thePlane,
+ const GeomShapePtr& theEdgeOrWire,
+ const double theOffsetValue);
+
/// \return the list of shapes generated from the shape \a theShape.
/// \param[in] theOldShape base shape.
/// \param[out] theNewShapes shapes generated from \a theShape. Does not cleared!
GEOMALGOAPI_EXPORT virtual void generated(const GeomShapePtr theOldShape,
ListOfShape& theNewShapes);
+
private:
/// \brief Perform offset operation
void build(const GeomShapePtr& theShape, const double theOffsetValue);
#include <GeomAPI_Vertex.h>
#include <GeomAPI_ShapeExplorer.h>
+#include <BRep_Builder.hxx>
#include <BRep_Tool.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
+#include <BRepTools_ReShape.hxx>
#include <Geom_Curve.hxx>
#include <Precision.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Wire.hxx>
+#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>
#include <cmath>
}
};
-//=================================================================================================
-GeomShapePtr GeomAlgoAPI_WireBuilder::wire(const ListOfShape& theShapes)
+static GeomShapePtr fromTopoDS(const TopoDS_Shape& theShape)
+{
+ GeomShapePtr aResultShape(new GeomAPI_Shape());
+ aResultShape->setImpl(new TopoDS_Shape(theShape));
+ return aResultShape;
+}
+
+GeomAlgoAPI_WireBuilder::GeomAlgoAPI_WireBuilder(const ListOfShape& theShapes,
+ const bool theForceOpenWire)
{
TopTools_ListOfShape aListOfEdges;
SetOfEdges aProcessedEdges;
ListOfShape::const_iterator anIt = theShapes.cbegin();
- for(; anIt != theShapes.cend(); ++anIt) {
+ for (; anIt != theShapes.cend(); ++anIt) {
TopoDS_Shape aShape = (*anIt)->impl<TopoDS_Shape>();
switch(aShape.ShapeType()) {
case TopAbs_EDGE: {
break;
}
case TopAbs_WIRE: {
- for(TopExp_Explorer anExp(aShape, TopAbs_EDGE); anExp.More(); anExp.Next()) {
+ for (TopExp_Explorer anExp(aShape, TopAbs_EDGE); anExp.More(); anExp.Next()) {
TopoDS_Shape anEdge = anExp.Current();
anEdge.Orientation(TopAbs_FORWARD);
// if the edge was already processed, remove it to keep original order of the current wire
}
break;
}
- default: {
- return GeomShapePtr();
- }
+ default:
+ break;
}
}
- BRepBuilderAPI_MakeWire aWireBuilder;
- aWireBuilder.Add(aListOfEdges);
- if(aWireBuilder.Error() != BRepBuilderAPI_WireDone) {
- return GeomShapePtr();
+ bool isSplitWire = false;
+ gp_Pnt aSplitPoint;
+ if (theForceOpenWire && aListOfEdges.Size() > 1) {
+ // find a vertex to split the wire
+ TopoDS_Vertex V1[2];
+ TopExp::Vertices(TopoDS::Edge(aListOfEdges.First()), V1[0], V1[1]);
+ TopoDS_Vertex V2[2];
+ TopExp::Vertices(TopoDS::Edge(aListOfEdges.Last()), V2[0], V2[1]);
+ gp_Pnt P1[2] = { BRep_Tool::Pnt(V1[0]), BRep_Tool::Pnt(V1[1]) };
+ gp_Pnt P2[2] = { BRep_Tool::Pnt(V2[0]), BRep_Tool::Pnt(V2[1]) };
+ double Tol1[2] = { BRep_Tool::Tolerance(V1[0]), BRep_Tool::Tolerance(V1[1]) };
+ double Tol2[2] = { BRep_Tool::Tolerance(V2[0]), BRep_Tool::Tolerance(V2[1]) };
+ for (int i = 0; i < 2 && !isSplitWire; ++i)
+ for (int j = 0; j < 2 && !isSplitWire; ++j)
+ if (P1[i].Distance(P2[j]) < Max(Tol1[i], Tol2[j])) {
+ aSplitPoint = P1[i];
+ isSplitWire = true;
+ }
}
- GeomShapePtr aResultShape(new GeomAPI_Shape());
- aResultShape->setImpl(new TopoDS_Shape(aWireBuilder.Wire()));
- return aResultShape;
+ BRepBuilderAPI_MakeWire* aWireBuilder = new BRepBuilderAPI_MakeWire;
+ aWireBuilder->Add(aListOfEdges);
+ if (aWireBuilder->Error() == BRepBuilderAPI_WireDone) {
+ setImpl(aWireBuilder);
+ setBuilderType(OCCT_BRepBuilderAPI_MakeShape);
+
+ // split the result wire
+ TopoDS_Wire aWire = aWireBuilder->Wire();
+ if (isSplitWire && BRep_Tool::IsClosed(aWire)) {
+ TopoDS_Wire aNewWire;
+ BRep_Builder aBuilder;
+ aBuilder.MakeWire(aNewWire);
+ for (TopExp_Explorer anExp(aWire, TopAbs_EDGE); anExp.More(); anExp.Next()) {
+ TopoDS_Edge aNewCurrent = TopoDS::Edge(anExp.Current());
+ if (isSplitWire) {
+ bool isToReshape = false;
+ BRepTools_ReShape aReshape;
+ TopoDS_Vertex aVF, aVL;
+ TopExp::Vertices(aNewCurrent, aVF, aVL);
+ gp_Pnt aPF = BRep_Tool::Pnt(aVF);
+ double aTolF = BRep_Tool::Tolerance(aVF);
+ gp_Pnt aPL = BRep_Tool::Pnt(aVL);
+ double aTolL = BRep_Tool::Tolerance(aVL);
+ if (aSplitPoint.SquareDistance(aPF) < aTolF * aTolF) {
+ aReshape.Replace(aVF, aReshape.CopyVertex(aVF));
+ isToReshape = true;
+ }
+ else if (aSplitPoint.SquareDistance(aPL) < aTolL * aTolL) {
+ aReshape.Replace(aVL, aReshape.CopyVertex(aVL));
+ isToReshape = true;
+ }
+ if (isToReshape) {
+ aNewCurrent = TopoDS::Edge(aReshape.Apply(aNewCurrent));
+ isSplitWire = false; // no need to continue splitting
+ }
+ }
+ aBuilder.Add(aNewWire, aNewCurrent);
+ }
+ aWire = aNewWire;
+ }
+
+ // store generated/modified shapes
+ for (TopTools_ListOfShape::Iterator aBaseIt(aListOfEdges); aBaseIt.More(); aBaseIt.Next()) {
+ TopoDS_Edge aBaseCurrent = TopoDS::Edge(aBaseIt.Value());
+ Standard_Real aFirst, aLast;
+ Handle(Geom_Curve) aBaseCurve = BRep_Tool::Curve(aBaseCurrent, aFirst, aLast);
+
+ for (TopExp_Explorer anExp(aWire, TopAbs_EDGE); anExp.More(); anExp.Next()) {
+ TopoDS_Edge aNewCurrent = TopoDS::Edge(anExp.Current());
+ Handle(Geom_Curve) aNewCurve = BRep_Tool::Curve(aNewCurrent, aFirst, aLast);
+ if (aBaseCurve == aNewCurve) {
+ GeomShapePtr aBaseShape = fromTopoDS(aBaseCurrent);
+ GeomShapePtr aNewShape = fromTopoDS(aNewCurrent);
+ addGenerated(aBaseShape, aNewShape);
+ addModified(aBaseShape, aNewShape);
+ }
+ }
+ }
+
+ setShape(fromTopoDS(aWire));
+ setDone(true);
+ }
+}
+
+//=================================================================================================
+GeomShapePtr GeomAlgoAPI_WireBuilder::wire(const ListOfShape& theShapes)
+{
+ return GeomAlgoAPI_WireBuilder(theShapes).shape();
}
//=================================================================================================
#define GeomAlgoAPI_WireBuilder_H_
#include "GeomAlgoAPI.h"
+#include "GeomAlgoAPI_MakeShapeCustom.h"
#include <GeomAPI_Shape.h>
/// \class GeomAlgoAPI_WireBuilder
/// \ingroup DataAlgo
/// \brief Allows to create wire-shapes by different parameters.
-class GeomAlgoAPI_WireBuilder
+class GeomAlgoAPI_WireBuilder : public GeomAlgoAPI_MakeShapeCustom
{
public:
+ /// \brief Creates a wire from edges and wires.
+ /// \param[in] theShapes list of shapes. Only edges and wires allowed.
+ /// \param[in] theForceOpenWire indicates the necessity to split wire
+ /// in the first vertex if it becomes closed.
+ /// The edges are not to be consecutive.
+ /// But they are to be all connected geometrically or topologically.
+ GEOMALGOAPI_EXPORT GeomAlgoAPI_WireBuilder(const ListOfShape& theShapes,
+ const bool theForceOpenWire = false);
+
/// \brief Creates a wire from edges and wires.
/// \param[in] theShapes list of shapes. Only edges and wires allowed.
/// The edges are not to be consecutive.
const int theValue,
bool sendUpdated)
{
- if (myArray->Value(theIndex) != theValue) {
+ if (!isInitialized() || myArray->Value(theIndex) != theValue) {
setInitialized();
myArray->SetValue(theIndex, theValue);
if (sendUpdated)
SketchAPI_MacroEllipse.h
SketchAPI_MacroEllipticArc.h
SketchAPI_Mirror.h
+ SketchAPI_Offset.h
SketchAPI_Point.h
SketchAPI_Projection.h
SketchAPI_Rectangle.h
SketchAPI_MacroEllipse.cpp
SketchAPI_MacroEllipticArc.cpp
SketchAPI_Mirror.cpp
+ SketchAPI_Offset.cpp
SketchAPI_Point.cpp
SketchAPI_Projection.cpp
SketchAPI_Rectangle.cpp
%shared_ptr(SketchAPI_IntersectionPoint)
%shared_ptr(SketchAPI_Line)
%shared_ptr(SketchAPI_Mirror)
+%shared_ptr(SketchAPI_Offset)
%shared_ptr(SketchAPI_Sketch)
%shared_ptr(SketchAPI_SketchEntity)
%shared_ptr(SketchAPI_Point)
%include "SketchAPI_BSpline.h"
%include "SketchAPI_Projection.h"
%include "SketchAPI_Mirror.h"
+%include "SketchAPI_Offset.h"
%include "SketchAPI_Translation.h"
%include "SketchAPI_Rectangle.h"
%include "SketchAPI_Rotation.h"
--- /dev/null
+// Copyright (C) 2014-2019 CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include "SketchAPI_Offset.h"
+#include <SketchAPI_SketchEntity.h>
+//--------------------------------------------------------------------------------------
+#include <ModelHighAPI_Dumper.h>
+#include <ModelHighAPI_Selection.h>
+#include <ModelHighAPI_Tools.h>
+
+//--------------------------------------------------------------------------------------
+SketchAPI_Offset::SketchAPI_Offset (const std::shared_ptr<ModelAPI_Feature> & theFeature)
+ : ModelHighAPI_Interface(theFeature)
+{
+ initialize();
+}
+
+SketchAPI_Offset::SketchAPI_Offset (const std::shared_ptr<ModelAPI_Feature> & theFeature,
+ const std::list<std::shared_ptr<ModelAPI_Object> > & theObjects,
+ const ModelHighAPI_Double & theOffsetValue,
+ bool theIsReversed)
+ : ModelHighAPI_Interface(theFeature)
+{
+ if (initialize()) {
+ fillAttribute(theObjects, edgesList());
+ fillAttribute(theOffsetValue, value());
+ fillAttribute(theIsReversed, reversed());
+
+ execute();
+ }
+}
+
+SketchAPI_Offset::~SketchAPI_Offset()
+{
+}
+
+std::list<std::shared_ptr<SketchAPI_SketchEntity> > SketchAPI_Offset::offset() const
+{
+ std::list<ObjectPtr> aList = feature()->reflist(SketchPlugin_Constraint::ENTITY_B())->list();
+ std::list<FeaturePtr> anIntermediate;
+ std::list<ObjectPtr>::const_iterator anIt = aList.begin();
+ for (; anIt != aList.end(); ++anIt) {
+ FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
+ anIntermediate.push_back(aFeature);
+ }
+ return SketchAPI_SketchEntity::wrap(anIntermediate);
+}
+
+//--------------------------------------------------------------------------------------
+
+void SketchAPI_Offset::dump (ModelHighAPI_Dumper& theDumper) const
+{
+ FeaturePtr aBase = feature();
+ const std::string& aSketchName = theDumper.parentName(aBase);
+
+ AttributeRefListPtr aOffsetObjects = edgesList();
+ AttributeDoublePtr aValue = value();
+ AttributeBooleanPtr aReversed = reversed();
+
+ // Check all attributes are already dumped. If not, store the feature as postponed.
+ if (!theDumper.isDumped(aOffsetObjects)) {
+ theDumper.postpone(aBase);
+ return;
+ }
+
+ theDumper << aBase << " = " << aSketchName << ".addOffset(" << aOffsetObjects << ", "
+ << aValue << ", " << aReversed << ")" << std::endl;
+
+ // Dump variables for a list of created features
+ theDumper << "[";
+ std::list<std::shared_ptr<SketchAPI_SketchEntity> > aList = offset();
+ std::list<std::shared_ptr<SketchAPI_SketchEntity> >::const_iterator anIt = aList.begin();
+ for (; anIt != aList.end(); ++anIt) {
+ if (anIt != aList.begin())
+ theDumper << ", ";
+ theDumper << (*anIt)->feature();
+ }
+ theDumper << "] = " << theDumper.name(aBase) << ".offset()" << std::endl;
+
+//// // Set necessary "auxiliary" flag for created features
+//// // (flag is set if it differs to anAux)
+//// for (anIt = aList.begin(); anIt != aList.end(); ++anIt) {
+//// FeaturePtr aFeature = (*anIt)->feature();
+//// bool aFeatAux = aFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value();
+//// if (aFeatAux != anAux->value())
+//// theDumper << theDumper.name((*anIt)->feature(), false)
+//// << ".setAuxiliary(" << aFeatAux << ")" <<std::endl;
+//// }
+}
--- /dev/null
+// Copyright (C) 2014-2019 CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef SRC_SKETCHAPI_SKETCHAPI_OFFSET_H_
+#define SRC_SKETCHAPI_SKETCHAPI_OFFSET_H_
+
+//--------------------------------------------------------------------------------------
+#include "SketchAPI.h"
+#include "SketchAPI_SketchEntity.h"
+
+#include <list>
+
+#include <SketchPlugin_Offset.h>
+
+#include <ModelHighAPI_Interface.h>
+#include <ModelHighAPI_Macro.h>
+//--------------------------------------------------------------------------------------
+class ModelAPI_Object;
+class ModelHighAPI_RefAttr;
+class ModelHighAPI_Double;
+//--------------------------------------------------------------------------------------
+/**\class SketchAPI_Offset
+ * \ingroup CPPHighAPI
+ * \brief Interface for Offset feature
+ */
+class SketchAPI_Offset : public ModelHighAPI_Interface
+{
+public:
+ /// Constructor without values
+ SKETCHAPI_EXPORT
+ explicit SketchAPI_Offset(const std::shared_ptr<ModelAPI_Feature> & theFeature);
+ /// Constructor with values
+ SKETCHAPI_EXPORT
+ SketchAPI_Offset(const std::shared_ptr<ModelAPI_Feature> & theFeature,
+ const std::list<std::shared_ptr<ModelAPI_Object> > & theObjects,
+ const ModelHighAPI_Double & theOffsetValue,
+ bool theIsReversed);
+ /// Destructor
+ SKETCHAPI_EXPORT
+ virtual ~SketchAPI_Offset();
+
+ INTERFACE_3(SketchPlugin_Offset::ID(),
+
+ edgesList, SketchPlugin_Offset::EDGES_ID(),
+ ModelAPI_AttributeRefList, /** Offset edges list */,
+
+ value, SketchPlugin_Offset::VALUE_ID(),
+ ModelAPI_AttributeDouble, /** Value */,
+
+ reversed, SketchPlugin_Offset::REVERSED_ID(),
+ ModelAPI_AttributeBoolean, /** Negative value */
+ )
+
+ /// List of created objects
+ SKETCHAPI_EXPORT
+ std::list<std::shared_ptr<SketchAPI_SketchEntity> > offset() const;
+
+ /// Dump wrapped feature
+ virtual void dump(ModelHighAPI_Dumper& theDumper) const;
+};
+
+//! Pointer on Offset object
+typedef std::shared_ptr<SketchAPI_Offset> OffsetPtr;
+
+//--------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------
+#endif /* SRC_SKETCHAPI_SKETCHAPI_OFFSET_H_ */
#include <SketchPlugin_ConstraintVertical.h>
#include <SketchPlugin_MacroBSpline.h>
#include <SketchPlugin_SketchCopy.h>
+#include <SketchPlugin_Offset.h>
#include <SketcherPrs_Tools.h>
//--------------------------------------------------------------------------------------
#include <ModelAPI_Events.h>
#include "SketchAPI_MacroEllipse.h"
#include "SketchAPI_MacroEllipticArc.h"
#include "SketchAPI_Mirror.h"
+#include "SketchAPI_Offset.h"
#include "SketchAPI_Point.h"
#include "SketchAPI_Projection.h"
#include "SketchAPI_Rectangle.h"
return MirrorPtr(new SketchAPI_Mirror(aFeature, theMirrorLine, theObjects));
}
+//--------------------------------------------------------------------------------------
+std::shared_ptr<SketchAPI_Offset> SketchAPI_Sketch::addOffset(
+ const std::list<std::shared_ptr<ModelAPI_Object> > & theObjects,
+ const ModelHighAPI_Double & theValue,
+ const bool theReversed)
+{
+ std::shared_ptr<ModelAPI_Feature> aFeature =
+ compositeFeature()->addFeature(SketchPlugin_Offset::ID());
+ return OffsetPtr(new SketchAPI_Offset(aFeature, theObjects, theValue, theReversed));
+}
+
//--------------------------------------------------------------------------------------
std::shared_ptr<SketchAPI_Translation> SketchAPI_Sketch::addTranslation(
const std::list<std::shared_ptr<ModelAPI_Object> > & theObjects,
class SketchAPI_IntersectionPoint;
class SketchAPI_Line;
class SketchAPI_Mirror;
+class SketchAPI_Offset;
class SketchAPI_Point;
class SketchAPI_Projection;
class SketchAPI_Rectangle;
const ModelHighAPI_RefAttr & theMirrorLine,
const std::list<std::shared_ptr<ModelAPI_Object> > & theObjects);
+ /// Add offset
+ SKETCHAPI_EXPORT
+ std::shared_ptr<SketchAPI_Offset> addOffset(
+ const std::list<std::shared_ptr<ModelAPI_Object> > & theObjects,
+ const ModelHighAPI_Double & theValue,
+ const bool theReversed);
+
/// Add translation
SKETCHAPI_EXPORT
std::shared_ptr<SketchAPI_Translation> addTranslation(
#include "SketchAPI_IntersectionPoint.h"
#include "SketchAPI_Line.h"
#include "SketchAPI_Mirror.h"
+ #include "SketchAPI_Offset.h"
#include "SketchAPI_Sketch.h"
#include "SketchAPI_SketchEntity.h"
#include "SketchAPI_Point.h"
SketchPlugin_MacroEllipticArc.h
SketchPlugin_MultiRotation.h
SketchPlugin_MultiTranslation.h
+ SketchPlugin_Offset.h
SketchPlugin_Plugin.h
SketchPlugin_Point.h
SketchPlugin_Projection.h
SketchPlugin_MacroEllipticArc.cpp
SketchPlugin_MultiRotation.cpp
SketchPlugin_MultiTranslation.cpp
+ SketchPlugin_Offset.cpp
SketchPlugin_Plugin.cpp
SketchPlugin_Point.cpp
SketchPlugin_Projection.cpp
TestMultiRotation05.py
TestMultiRotationWithParameter.py
TestMultiTranslation.py
+ TestOffset1.py
+ TestOffset2.py
TestPresentation.py
TestProjection.py
TestProjectionBSpline.py
--- /dev/null
+// Copyright (C) 2020 CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <SketchPlugin_Offset.h>
+
+#include <SketchPlugin_Sketch.h>
+#include <SketchPlugin_Line.h>
+#include <SketchPlugin_Point.h>
+#include <SketchPlugin_Arc.h>
+#include <SketchPlugin_Circle.h>
+#include <SketchPlugin_Ellipse.h>
+#include <SketchPlugin_EllipticArc.h>
+#include <SketchPlugin_BSpline.h>
+#include <SketchPlugin_BSplinePeriodic.h>
+#include <SketchPlugin_Tools.h>
+
+#include <SketcherPrs_Factory.h>
+
+#include <Events_InfoMessage.h>
+
+#include <ModelAPI_AttributeBoolean.h>
+#include <ModelAPI_AttributeDouble.h>
+#include <ModelAPI_AttributeDoubleArray.h>
+#include <ModelAPI_AttributeInteger.h>
+#include <ModelAPI_AttributeIntArray.h>
+#include <ModelAPI_AttributeRefList.h>
+#include <ModelAPI_Events.h>
+#include <ModelAPI_ResultConstruction.h>
+#include <ModelAPI_Tools.h>
+#include <ModelAPI_Validator.h>
+
+#include <GeomAlgoAPI_MakeShapeList.h>
+#include <GeomAlgoAPI_Offset.h>
+#include <GeomAlgoAPI_ShapeTools.h>
+#include <GeomAlgoAPI_WireBuilder.h>
+
+#include <GeomAPI_BSpline.h>
+#include <GeomAPI_Circ.h>
+#include <GeomAPI_Edge.h>
+#include <GeomAPI_Ellipse.h>
+#include <GeomAPI_ShapeExplorer.h>
+#include <GeomAPI_Wire.h>
+#include <GeomAPI_WireExplorer.h>
+
+#include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
+
+static const double tolerance = 1.e-7;
+
+SketchPlugin_Offset::SketchPlugin_Offset()
+{
+}
+
+void SketchPlugin_Offset::initAttributes()
+{
+ data()->addAttribute(EDGES_ID(), ModelAPI_AttributeRefList::typeId());
+ data()->addAttribute(VALUE_ID(), ModelAPI_AttributeDouble::typeId());
+ data()->addAttribute(REVERSED_ID(), ModelAPI_AttributeBoolean::typeId());
+
+ // store original entities
+ data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefList::typeId());
+ // store offset entities
+ data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefList::typeId());
+ // store mapping between original entity and index of the corresponding offset entity
+ data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeIntArray::typeId());
+
+ ModelAPI_Session::get()->validators()->
+ registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_A());
+ ModelAPI_Session::get()->validators()->
+ registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_B());
+ ModelAPI_Session::get()->validators()->
+ registerNotObligatory(getKind(), SketchPlugin_Constraint::ENTITY_C());
+}
+
+void SketchPlugin_Offset::execute()
+{
+ SketchPlugin_Sketch* aSketch = sketch();
+ if (!aSketch) return;
+
+ // 1. Sketch plane
+ std::shared_ptr<GeomAPI_Pln> aPlane = aSketch->plane();
+
+ // 2. Offset value
+ AttributeDoublePtr aValueAttr = real(VALUE_ID());
+ if (!aValueAttr->isInitialized()) return;
+ double aValue = aValueAttr->value();
+ if (aValue < tolerance) return;
+
+ // 2.a. Reversed?
+ AttributeBooleanPtr aReversedAttr = boolean(REVERSED_ID());
+ if (!aReversedAttr->isInitialized()) return;
+ if (aReversedAttr->value()) aValue = -aValue; // reverse offset direction
+
+ // 3. List of all selected edges
+ AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
+ std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
+
+ // 4. Put all selected edges in a set to pass them into findWireOneWay() below
+ std::set<FeaturePtr> anEdgesSet;
+ std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
+ for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
+ FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
+ if (aFeature) {
+ anEdgesSet.insert(aFeature);
+ }
+ }
+
+ // Wait all objects being created, then send update events
+ static Events_ID anUpdateEvent = Events_Loop::eventByName(EVENT_OBJECT_UPDATED);
+ bool isUpdateFlushed = Events_Loop::loop()->isFlushed(anUpdateEvent);
+ if (isUpdateFlushed)
+ Events_Loop::loop()->setFlushed(anUpdateEvent, false);
+
+ // 5. Gather wires and make offset for each wire
+ ListOfMakeShape anOffsetAlgos;
+ std::set<FeaturePtr> aProcessedEdgesSet;
+ for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
+ FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
+ if (aFeature.get()) {
+ if (aProcessedEdgesSet.find(aFeature) != aProcessedEdgesSet.end())
+ continue;
+
+ // 5.a. End points (if any)
+ std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
+ SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
+
+ // 5.b. Find a chain of edges
+ std::list<FeaturePtr> aChain;
+ aChain.push_back(aFeature);
+ bool isClosed = !(aStartPoint && anEndPoint); // not closed edge
+ if (!isClosed) {
+ isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet,
+ aProcessedEdgesSet, aChain, true);
+ if (!isClosed) {
+ isClosed = findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet,
+ aProcessedEdgesSet, aChain, false);
+ }
+ }
+ aProcessedEdgesSet.insert(aFeature);
+
+ // 5.c. Make wire
+ ListOfShape aTopoChain;
+ std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
+ for (; aChainIt != aChain.end(); ++aChainIt) {
+ FeaturePtr aChainFeature = (*aChainIt);
+ GeomShapePtr aTopoEdge = aChainFeature->lastResult()->shape();
+ if (aTopoEdge->shapeType() == GeomAPI_Shape::EDGE) {
+ aTopoChain.push_back(aTopoEdge);
+ }
+ }
+ std::shared_ptr<GeomAlgoAPI_WireBuilder> aWireBuilder(
+ new GeomAlgoAPI_WireBuilder(aTopoChain, !isClosed));
+
+ GeomShapePtr aWireShape = aWireBuilder->shape();
+ GeomWirePtr aWire (new GeomAPI_Wire (aWireShape));
+
+ // Fix for a problem of offset side change with selection change.
+ // Wire direction is defined by the first selected edge of this wire.
+ double aSign = 1.;
+ if (!aWire->isClosed()) {
+ ListOfShape aModified;
+ // First selected edge of current chain
+ GeomShapePtr aFirstSel = aFeature->lastResult()->shape();
+ aWireBuilder->modified(aFirstSel, aModified);
+ GeomShapePtr aModFS = aModified.front();
+ if (aModFS->orientation() != aFirstSel->orientation())
+ aSign = -1.;
+ }
+
+ // 5.d. Make offset for the wire
+ std::shared_ptr<GeomAlgoAPI_Offset> anOffsetShape(
+ new GeomAlgoAPI_Offset(aPlane, aWireShape, aValue*aSign));
+
+ std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeList(new GeomAlgoAPI_MakeShapeList);
+ aMakeList->appendAlgo(aWireBuilder);
+ aMakeList->appendAlgo(anOffsetShape);
+ anOffsetAlgos.push_back(aMakeList);
+ }
+ }
+
+ // 6. Store offset results.
+ // Create sketch feature for each edge of anOffsetShape, and also store
+ // created features in CREATED_ID() to remove them on next execute()
+ addToSketch(anOffsetAlgos);
+
+ // send events to update the sub-features by the solver
+ if (isUpdateFlushed)
+ Events_Loop::loop()->setFlushed(anUpdateEvent, true);
+}
+
+bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge,
+ const FeaturePtr& theEdge,
+ const std::shared_ptr<GeomDataAPI_Point2D>& theEndPoint,
+ std::set<FeaturePtr>& theEdgesSet,
+ std::set<FeaturePtr>& theProcessedEdgesSet,
+ std::list<FeaturePtr>& theChain,
+ const bool isPrepend)
+{
+ // 1. Find a single edge, coincident to theEndPoint by one of its ends
+ if (!theEndPoint) return false;
+
+ FeaturePtr aNextEdgeFeature;
+ int nbFound = 0;
+
+ std::set<AttributePoint2DPtr> aCoincPoints =
+ SketchPlugin_Tools::findPointsCoincidentToPoint(theEndPoint);
+
+ std::set<AttributePoint2DPtr>::iterator aPointsIt = aCoincPoints.begin();
+ for (; aPointsIt != aCoincPoints.end(); aPointsIt++) {
+ AttributePoint2DPtr aP = (*aPointsIt);
+ FeaturePtr aCoincFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(aP->owner());
+ bool isInSet = (theEdgesSet.find(aCoincFeature) != theEdgesSet.end());
+
+ // Condition 0: not auxiliary
+ if (!isInSet && aCoincFeature->boolean(SketchPlugin_SketchEntity::AUXILIARY_ID())->value())
+ continue;
+
+ // Condition 1: not a point feature
+ if (aCoincFeature->getKind() != SketchPlugin_Point::ID()) {
+ // Condition 2: it is not the current edge
+ if (aCoincFeature != theEdge) {
+ // Condition 3: it is in the set of interest.
+ // Empty set means all sketch edges.
+ if (isInSet || theEdgesSet.empty()) {
+ // Condition 4: consider only features with two end points
+ std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
+ SketchPlugin_SegmentationTools::getFeaturePoints(aCoincFeature, aP1, aP2);
+ if (aP1 && aP2) {
+ // Condition 5: consider only features, that have aP as one of they ends.
+ // For example, we do not need an arc, coincident to aP by its center.
+ if (theEndPoint->pnt()->isEqual(aP1->pnt()) ||
+ theEndPoint->pnt()->isEqual(aP2->pnt())) {
+ // Condition 6: only one edge can prolongate the chain. If several, we stop here.
+ nbFound++;
+ if (nbFound > 1)
+ return false;
+
+ // One found
+ aNextEdgeFeature = aCoincFeature;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Only one edge can prolongate the chain. If several or none, we stop here.
+ if (nbFound != 1)
+ return false;
+
+ // 2. So, we have the single edge, that prolongate the chain
+
+ // Condition 7: if we reached the very first edge of the chain
+ if (aNextEdgeFeature == theFirstEdge)
+ // Closed chain found
+ return true;
+
+ // 3. Add the found edge to the chain
+ if (isPrepend)
+ theChain.push_front(aNextEdgeFeature);
+ else
+ theChain.push_back(aNextEdgeFeature);
+ theProcessedEdgesSet.insert(aNextEdgeFeature);
+
+ // 4. Which end of aNextEdgeFeature we need to proceed
+ std::shared_ptr<GeomDataAPI_Point2D> aP1, aP2;
+ SketchPlugin_SegmentationTools::getFeaturePoints(aNextEdgeFeature, aP1, aP2);
+ if (aP2->pnt()->isEqual(theEndPoint->pnt())) {
+ // reversed
+ aP2 = aP1;
+ }
+
+ // 5. Continue gathering the chain (recursive)
+ return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet,
+ theProcessedEdgesSet, theChain, isPrepend);
+}
+
+static void setRefListValue(AttributeRefListPtr theList, int theListSize,
+ ObjectPtr theValue, int theIndex)
+{
+ if (theIndex < theListSize) {
+ ObjectPtr aCur = theList->object(theIndex);
+ if (aCur != theValue)
+ theList->substitute(aCur, theValue);
+ }
+ else
+ theList->append(theValue);
+}
+
+// Reorder shapes according to the wire's order
+static void reorderShapes(ListOfShape& theShapes, GeomShapePtr theWire)
+{
+ std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aShapes;
+ aShapes.insert(theShapes.begin(), theShapes.end());
+ theShapes.clear();
+
+ GeomWirePtr aWire(new GeomAPI_Wire(theWire));
+ GeomAPI_WireExplorer anExp(aWire);
+ for (; anExp.more(); anExp.next()) {
+ GeomShapePtr aCurEdge = anExp.current();
+ auto aFound = aShapes.find(aCurEdge);
+ if (aFound != aShapes.end()) {
+ theShapes.push_back(aCurEdge);
+ aShapes.erase(aFound);
+ }
+ }
+}
+
+static void removeLastFromIndex(AttributeRefListPtr theList, int theListSize, int& theLastIndex)
+{
+ if (theLastIndex < theListSize) {
+ std::set<int> anIndicesToRemove;
+ for (; theLastIndex < theListSize; ++theLastIndex)
+ anIndicesToRemove.insert(theLastIndex);
+ theList->remove(anIndicesToRemove);
+ }
+}
+
+void SketchPlugin_Offset::addToSketch(const ListOfMakeShape& theOffsetAlgos)
+{
+ AttributeRefListPtr aSelectedRefList = reflist(EDGES_ID());
+ AttributeRefListPtr aBaseRefList = reflist(ENTITY_A());
+ AttributeRefListPtr anOffsetRefList = reflist(ENTITY_B());
+ AttributeIntArrayPtr anOffsetToBaseMap = intArray(ENTITY_C());
+
+ // compare the list of selected edges and the previously stored,
+ // and store maping between them
+ std::map<ObjectPtr, std::list<ObjectPtr> > aMapExistent;
+ std::list<ObjectPtr> anObjectsToRemove;
+ std::list<ObjectPtr> aSelectedList = aSelectedRefList->list();
+ for (std::list<ObjectPtr>::iterator aSIt = aSelectedList.begin();
+ aSIt != aSelectedList.end(); ++aSIt) {
+ aMapExistent[*aSIt] = std::list<ObjectPtr>();
+ }
+ for (int anIndex = 0, aSize = anOffsetRefList->size(); anIndex < aSize; ++anIndex) {
+ ObjectPtr aCurrent = anOffsetRefList->object(anIndex);
+ int aBaseIndex = anOffsetToBaseMap->value(anIndex);
+ if (aBaseIndex >= 0) {
+ ObjectPtr aBaseObj = aBaseRefList->object(aBaseIndex);
+ std::map<ObjectPtr, std::list<ObjectPtr> >::iterator aFound = aMapExistent.find(aBaseObj);
+ if (aFound != aMapExistent.end())
+ aFound->second.push_back(aCurrent);
+ else
+ anObjectsToRemove.push_back(aCurrent);
+ }
+ else
+ anObjectsToRemove.push_back(aCurrent);
+ }
+
+ // update lists of base shapes and of offset shapes
+ int aBaseListSize = aBaseRefList->size();
+ int anOffsetListSize = anOffsetRefList->size();
+ int aBaseListIndex = 0, anOffsetListIndex = 0;
+ std::list<int> anOffsetBaseBackRefs;
+ std::set<GeomShapePtr, GeomAPI_Shape::ComparatorWithOri> aProcessedOffsets;
+ for (std::list<ObjectPtr>::iterator aSIt = aSelectedList.begin();
+ aSIt != aSelectedList.end(); ++aSIt) {
+ // find an offseted edge
+ FeaturePtr aBaseFeature = ModelAPI_Feature::feature(*aSIt);
+ GeomShapePtr aBaseShape = aBaseFeature->lastResult()->shape();
+ ListOfShape aNewShapes;
+ for (ListOfMakeShape::const_iterator anAlgoIt = theOffsetAlgos.begin();
+ anAlgoIt != theOffsetAlgos.end(); ++anAlgoIt) {
+ (*anAlgoIt)->generated(aBaseShape, aNewShapes);
+ if (!aNewShapes.empty()) {
+ reorderShapes(aNewShapes, (*anAlgoIt)->shape());
+ break;
+ }
+ }
+
+ // store base feature
+ setRefListValue(aBaseRefList, aBaseListSize, *aSIt, aBaseListIndex);
+
+ // create or update an offseted feature
+ const std::list<ObjectPtr>& anImages = aMapExistent[*aSIt];
+ std::list<ObjectPtr>::const_iterator anImgIt = anImages.begin();
+ for (ListOfShape::iterator aNewIt = aNewShapes.begin(); aNewIt != aNewShapes.end(); ++aNewIt) {
+ FeaturePtr aNewFeature;
+ if (anImgIt != anImages.end())
+ aNewFeature = ModelAPI_Feature::feature(*anImgIt++);
+ updateExistentOrCreateNew(*aNewIt, aNewFeature, anObjectsToRemove);
+ aProcessedOffsets.insert(*aNewIt);
+
+ // store an offseted feature
+ setRefListValue(anOffsetRefList, anOffsetListSize, aNewFeature, anOffsetListIndex);
+
+ anOffsetBaseBackRefs.push_back(aBaseListIndex);
+ ++anOffsetListIndex;
+ }
+ ++aBaseListIndex;
+ anObjectsToRemove.insert(anObjectsToRemove.end(), anImgIt, anImages.end());
+ }
+ // create arcs generated from vertices
+ for (ListOfMakeShape::const_iterator anAlgoIt = theOffsetAlgos.begin();
+ anAlgoIt != theOffsetAlgos.end(); ++anAlgoIt) {
+ GeomShapePtr aCurWire = (*anAlgoIt)->shape();
+ GeomAPI_ShapeExplorer anExp(aCurWire, GeomAPI_Shape::EDGE);
+ for (; anExp.more(); anExp.next()) {
+ GeomShapePtr aCurEdge = anExp.current();
+ if (aProcessedOffsets.find(aCurEdge) == aProcessedOffsets.end()) {
+ FeaturePtr aNewFeature;
+ updateExistentOrCreateNew(aCurEdge, aNewFeature, anObjectsToRemove);
+ aProcessedOffsets.insert(aCurEdge);
+
+ // store an offseted feature
+ setRefListValue(anOffsetRefList, anOffsetListSize, aNewFeature, anOffsetListIndex);
+
+ anOffsetBaseBackRefs.push_back(-1);
+ ++anOffsetListIndex;
+ }
+ }
+ }
+
+ removeLastFromIndex(aBaseRefList, aBaseListSize, aBaseListIndex);
+ removeLastFromIndex(anOffsetRefList, anOffsetListSize, anOffsetListIndex);
+
+ anOffsetToBaseMap->setSize((int)anOffsetBaseBackRefs.size(), false);
+ int anIndex = 0;
+ for (std::list<int>::iterator anIt = anOffsetBaseBackRefs.begin();
+ anIt != anOffsetBaseBackRefs.end(); ++anIt) {
+ anOffsetToBaseMap->setValue(anIndex++, *anIt, false);
+ }
+
+ // remove unused objects
+ std::set<FeaturePtr> aSet;
+ for (std::list<ObjectPtr>::iterator anIt = anObjectsToRemove.begin();
+ anIt != anObjectsToRemove.end(); ++anIt) {
+ FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
+ if (aFeature)
+ aSet.insert(aFeature);
+ }
+ ModelAPI_Tools::removeFeaturesAndReferences(aSet);
+}
+
+static void findOrCreateFeatureByKind(SketchPlugin_Sketch* theSketch,
+ const std::string& theFeatureKind,
+ FeaturePtr& theFeature,
+ std::list<ObjectPtr>& thePoolOfFeatures)
+{
+ if (theFeature) {
+ // check the feature type is the same as required
+ if (theFeature->getKind() != theFeatureKind) {
+ // return feature to the pool and try to find the most appropriate
+ thePoolOfFeatures.push_back(theFeature);
+ theFeature = FeaturePtr();
+ }
+ }
+ if (!theFeature) {
+ // try to find appropriate feature in the pool
+ for (std::list<ObjectPtr>::iterator it = thePoolOfFeatures.begin();
+ it != thePoolOfFeatures.end(); ++it) {
+ FeaturePtr aCurFeature = ModelAPI_Feature::feature(*it);
+ if (aCurFeature->getKind() == theFeatureKind) {
+ theFeature = aCurFeature;
+ thePoolOfFeatures.erase(it);
+ break;
+ }
+ }
+ // feature not found, create new
+ if (!theFeature)
+ theFeature = theSketch->addFeature(theFeatureKind);
+ }
+}
+
+void SketchPlugin_Offset::updateExistentOrCreateNew(const GeomShapePtr& theShape,
+ FeaturePtr& theFeature,
+ std::list<ObjectPtr>& thePoolOfFeatures)
+{
+ if (theShape->shapeType() != GeomAPI_Shape::EDGE)
+ return;
+
+ std::shared_ptr<GeomAPI_Edge> aResEdge(new GeomAPI_Edge(theShape));
+
+ std::shared_ptr<GeomAPI_Pnt2d> aFP, aLP;
+ std::shared_ptr<GeomAPI_Pnt> aFP3d = aResEdge->firstPoint();
+ std::shared_ptr<GeomAPI_Pnt> aLP3d = aResEdge->lastPoint();
+ if (aFP3d && aLP3d) {
+ aFP = sketch()->to2D(aFP3d);
+ aLP = sketch()->to2D(aLP3d);
+ }
+
+ if (aResEdge->isLine()) {
+ findOrCreateFeatureByKind(sketch(), SketchPlugin_Line::ID(), theFeature, thePoolOfFeatures);
+
+ std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+ (theFeature->attribute(SketchPlugin_Line::START_ID()))->setValue(aFP);
+ std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+ (theFeature->attribute(SketchPlugin_Line::END_ID()))->setValue(aLP);
+ }
+ else if (aResEdge->isArc()) {
+ std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
+ std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
+ std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
+
+ findOrCreateFeatureByKind(sketch(), SketchPlugin_Arc::ID(), theFeature, thePoolOfFeatures);
+
+ GeomDirPtr aCircNormal = aCircEdge->normal();
+ GeomDirPtr aSketchNormal = sketch()->coordinatePlane()->normal();
+ if (aSketchNormal->dot(aCircNormal) < -tolerance)
+ std::swap(aFP, aLP);
+
+ bool aWasBlocked = theFeature->data()->blockSendAttributeUpdated(true);
+ std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+ (theFeature->attribute(SketchPlugin_Arc::END_ID()))->setValue(aLP);
+ std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+ (theFeature->attribute(SketchPlugin_Arc::START_ID()))->setValue(aFP);
+ std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+ (theFeature->attribute(SketchPlugin_Arc::CENTER_ID()))->setValue(aCP);
+ theFeature->data()->blockSendAttributeUpdated(aWasBlocked);
+ }
+ else if (aResEdge->isCircle()) {
+ std::shared_ptr<GeomAPI_Circ> aCircEdge = aResEdge->circle();
+ std::shared_ptr<GeomAPI_Pnt> aCP3d = aCircEdge->center();
+ std::shared_ptr<GeomAPI_Pnt2d> aCP = sketch()->to2D(aCP3d);
+
+ findOrCreateFeatureByKind(sketch(), SketchPlugin_Circle::ID(), theFeature, thePoolOfFeatures);
+
+ std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+ (theFeature->attribute(SketchPlugin_Circle::CENTER_ID()))->setValue(aCP);
+ theFeature->real(SketchPlugin_Circle::RADIUS_ID())->setValue(aCircEdge->radius());
+ }
+ else if (aResEdge->isEllipse()) {
+ std::shared_ptr<GeomAPI_Ellipse> anEllipseEdge = aResEdge->ellipse();
+
+ GeomPointPtr aCP3d = anEllipseEdge->center();
+ GeomPnt2dPtr aCP = sketch()->to2D(aCP3d);
+
+ GeomPointPtr aFocus3d = anEllipseEdge->firstFocus();
+ GeomPnt2dPtr aFocus = sketch()->to2D(aFocus3d);
+
+ if (aFP3d && aLP3d) {
+ // Elliptic arc
+ findOrCreateFeatureByKind(sketch(), SketchPlugin_EllipticArc::ID(),
+ theFeature, thePoolOfFeatures);
+
+ bool aWasBlocked = theFeature->data()->blockSendAttributeUpdated(true);
+ std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+ (theFeature->attribute(SketchPlugin_EllipticArc::CENTER_ID()))->setValue(aCP);
+ std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+ (theFeature->attribute(SketchPlugin_EllipticArc::FIRST_FOCUS_ID()))->setValue(aFocus);
+ std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+ (theFeature->attribute(SketchPlugin_EllipticArc::START_POINT_ID()))->setValue(aFP);
+ std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+ (theFeature->attribute(SketchPlugin_EllipticArc::END_POINT_ID()))->setValue(aLP);
+ theFeature->data()->blockSendAttributeUpdated(aWasBlocked);
+ }
+ else {
+ // Ellipse
+ findOrCreateFeatureByKind(sketch(), SketchPlugin_Ellipse::ID(),
+ theFeature, thePoolOfFeatures);
+
+ std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+ (theFeature->attribute(SketchPlugin_Ellipse::CENTER_ID()))->setValue(aCP);
+ std::dynamic_pointer_cast<GeomDataAPI_Point2D>
+ (theFeature->attribute(SketchPlugin_Ellipse::FIRST_FOCUS_ID()))->setValue(aFocus);
+ theFeature->real(SketchPlugin_Ellipse::MINOR_RADIUS_ID())->setValue(
+ anEllipseEdge->minorRadius());
+ }
+ }
+ else {
+ // convert to b-spline
+ mkBSpline(theFeature, aResEdge, thePoolOfFeatures);
+ }
+
+ if (theFeature.get()) {
+ theFeature->boolean(SketchPlugin_SketchEntity::COPY_ID())->setValue(true);
+ theFeature->execute();
+
+ static Events_ID aRedisplayEvent = Events_Loop::eventByName(EVENT_OBJECT_TO_REDISPLAY);
+ ModelAPI_EventCreator::get()->sendUpdated(theFeature, aRedisplayEvent);
+ const std::list<ResultPtr>& aResults = theFeature->results();
+ for (std::list<ResultPtr>::const_iterator anIt = aResults.begin();
+ anIt != aResults.end(); ++anIt)
+ ModelAPI_EventCreator::get()->sendUpdated(*anIt, aRedisplayEvent);
+ }
+}
+
+void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult,
+ const GeomEdgePtr& theEdge,
+ std::list<ObjectPtr>& thePoolOfFeatures)
+{
+ GeomCurvePtr aCurve (new GeomAPI_Curve (theEdge));
+ // Forced conversion to b-spline, if aCurve is not b-spline
+ GeomBSplinePtr aBSpline = GeomAPI_BSpline::convertToBSpline(aCurve);
+
+ const std::string& aBSplineKind = aBSpline->isPeriodic() ? SketchPlugin_BSplinePeriodic::ID()
+ : SketchPlugin_BSpline::ID();
+ findOrCreateFeatureByKind(sketch(), aBSplineKind, theResult, thePoolOfFeatures);
+
+ theResult->integer(SketchPlugin_BSpline::DEGREE_ID())->setValue(aBSpline->degree());
+
+ AttributePoint2DArrayPtr aPolesAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>
+ (theResult->attribute(SketchPlugin_BSpline::POLES_ID()));
+ std::list<GeomPointPtr> aPoles = aBSpline->poles();
+ aPolesAttr->setSize((int)aPoles.size());
+ std::list<GeomPointPtr>::iterator anIt = aPoles.begin();
+ for (int anIndex = 0; anIt != aPoles.end(); ++anIt, ++anIndex) {
+ GeomPnt2dPtr aPoleInSketch = sketch()->to2D(*anIt);
+ aPolesAttr->setPnt(anIndex, aPoleInSketch);
+ }
+
+ AttributeDoubleArrayPtr aWeightsAttr =
+ theResult->data()->realArray(SketchPlugin_BSpline::WEIGHTS_ID());
+ std::list<double> aWeights = aBSpline->weights();
+ if (aWeights.empty()) { // rational B-spline
+ int aSize = (int)aPoles.size();
+ aWeightsAttr->setSize(aSize);
+ for (int anIndex = 0; anIndex < aSize; ++anIndex)
+ aWeightsAttr->setValue(anIndex, 1.0);
+ }
+ else { // non-rational B-spline
+ aWeightsAttr->setSize((int)aWeights.size());
+ std::list<double>::iterator anIt = aWeights.begin();
+ for (int anIndex = 0; anIt != aWeights.end(); ++anIt, ++anIndex)
+ aWeightsAttr->setValue(anIndex, *anIt);
+ }
+
+ AttributeDoubleArrayPtr aKnotsAttr =
+ theResult->data()->realArray(SketchPlugin_BSpline::KNOTS_ID());
+ std::list<double> aKnots = aBSpline->knots();
+ int aSize = (int)aKnots.size();
+ aKnotsAttr->setSize(aSize);
+ std::list<double>::iterator aKIt = aKnots.begin();
+ for (int index = 0; index < aSize; ++index, ++aKIt)
+ aKnotsAttr->setValue(index, *aKIt);
+
+ AttributeIntArrayPtr aMultsAttr =
+ theResult->data()->intArray(SketchPlugin_BSpline::MULTS_ID());
+ std::list<int> aMultiplicities = aBSpline->mults();
+ aSize = (int)aMultiplicities.size();
+ aMultsAttr->setSize(aSize);
+ std::list<int>::iterator aMIt = aMultiplicities.begin();
+ for (int index = 0; index < aSize; ++index, ++aMIt)
+ aMultsAttr->setValue(index, *aMIt);
+}
+
+void SketchPlugin_Offset::attributeChanged(const std::string& theID)
+{
+ if (theID == EDGES_ID()) {
+ AttributeRefListPtr aSelected = reflist(EDGES_ID());
+ if (aSelected->size() == 0) {
+ // Clear list of objects
+ AttributeRefListPtr anOffsetAttr = reflist(SketchPlugin_Constraint::ENTITY_B());
+ std::list<ObjectPtr> anOffsetList = anOffsetAttr->list();
+ std::set<FeaturePtr> aFeaturesToBeRemoved;
+ for (std::list<ObjectPtr>::iterator anIt = anOffsetList.begin();
+ anIt != anOffsetList.end(); ++anIt) {
+ FeaturePtr aFeature = ModelAPI_Feature::feature(*anIt);
+ if (aFeature)
+ aFeaturesToBeRemoved.insert(aFeature);
+ }
+
+ reflist(SketchPlugin_Constraint::ENTITY_A())->clear();
+ anOffsetAttr->clear();
+ intArray(SketchPlugin_Constraint::ENTITY_C())->setSize(0);
+
+ ModelAPI_Tools::removeFeaturesAndReferences(aFeaturesToBeRemoved);
+ }
+ }
+}
+
+bool SketchPlugin_Offset::customAction(const std::string& theActionId)
+{
+ bool isOk = false;
+ if (theActionId == ADD_WIRE_ACTION_ID()) {
+ isOk = findWires();
+ }
+ else {
+ std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
+ Events_InfoMessage("SketchPlugin_Offset", aMsg).arg(getKind()).arg(theActionId).send();
+ }
+ return isOk;
+}
+
+bool SketchPlugin_Offset::findWires()
+{
+ AttributeRefListPtr aSelectedEdges = reflist(EDGES_ID());
+ std::list<ObjectPtr> anEdgesList = aSelectedEdges->list();
+
+ // Empty set
+ std::set<FeaturePtr> anEdgesSet;
+
+ // Processed set
+ std::set<FeaturePtr> aProcessedEdgesSet;
+
+ // Put all selected edges in a set to avoid adding them in reflist(EDGES_ID())
+ std::set<FeaturePtr> aSelectedSet;
+ std::list<ObjectPtr>::const_iterator anEdgesIt = anEdgesList.begin();
+ for (; anEdgesIt != anEdgesList.end(); anEdgesIt++) {
+ FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
+ if (aFeature) {
+ aSelectedSet.insert(aFeature);
+ }
+ }
+
+ bool aWasBlocked = data()->blockSendAttributeUpdated(true);
+
+ // Gather chains of edges
+ for (anEdgesIt = anEdgesList.begin(); anEdgesIt != anEdgesList.end(); anEdgesIt++) {
+ FeaturePtr aFeature = ModelAPI_Feature::feature(*anEdgesIt);
+ if (aFeature.get()) {
+ if (aProcessedEdgesSet.find(aFeature) != aProcessedEdgesSet.end())
+ continue;
+ aProcessedEdgesSet.insert(aFeature);
+
+ // End points (if any)
+ std::shared_ptr<GeomDataAPI_Point2D> aStartPoint, anEndPoint;
+ SketchPlugin_SegmentationTools::getFeaturePoints(aFeature, aStartPoint, anEndPoint);
+
+ std::list<FeaturePtr> aChain;
+ aChain.push_back(aFeature);
+ bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet,
+ aProcessedEdgesSet, aChain, true);
+ if (!isClosed) {
+ findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet,
+ aProcessedEdgesSet, aChain, false);
+ }
+
+ std::list<FeaturePtr>::iterator aChainIt = aChain.begin();
+ for (; aChainIt != aChain.end(); ++aChainIt) {
+ FeaturePtr aChainFeature = (*aChainIt);
+ if (aSelectedSet.find(aChainFeature) == aSelectedSet.end()) {
+ aSelectedEdges->append(aChainFeature->lastResult());
+ }
+ }
+ }
+ }
+ // TODO: hilight selection in the viewer
+
+ data()->blockSendAttributeUpdated(aWasBlocked);
+ return true;
+}
+
+
+AISObjectPtr SketchPlugin_Offset::getAISObject(AISObjectPtr thePrevious)
+{
+ if (!sketch())
+ return thePrevious;
+
+ AISObjectPtr anAIS = SketcherPrs_Factory::offsetObject(this, sketch(),
+ thePrevious);
+ return anAIS;
+}
--- /dev/null
+// Copyright (C) 2020 CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef SketchPlugin_Offset_H_
+#define SketchPlugin_Offset_H_
+
+#include <SketchPlugin.h>
+#include <SketchPlugin_ConstraintBase.h>
+
+#include <GeomDataAPI_Point2D.h>
+
+#include <GeomAPI_Edge.h>
+
+class GeomAlgoAPI_MakeShape;
+
+/**\class SketchPlugin_Offset
+ * \ingroup Plugins
+ * \brief Builds offset curves in the sketch.
+ */
+class SketchPlugin_Offset : public SketchPlugin_ConstraintBase
+{
+public:
+ /// Offset macro feature kind
+ inline static const std::string& ID()
+ {
+ static const std::string ID("SketchOffset");
+ return ID;
+ }
+
+ /// Returns the kind of a feature
+ SKETCHPLUGIN_EXPORT virtual const std::string& getKind()
+ {
+ static std::string MY_KIND = SketchPlugin_Offset::ID();
+ return MY_KIND;
+ }
+
+ /// list of offset edges
+ inline static const std::string& EDGES_ID()
+ {
+ static const std::string ID("segments");
+ return ID;
+ }
+
+ /// attribute to store the offset value
+ inline static const std::string& VALUE_ID()
+ {
+ static const std::string ID("offset_value");
+ return ID;
+ }
+
+ /// attribute to store the reversed offset direction
+ inline static const std::string& REVERSED_ID()
+ {
+ static const std::string ID("reversed");
+ return ID;
+ }
+
+ /// name for add wire action
+ inline static const std::string& ADD_WIRE_ACTION_ID()
+ {
+ static const std::string ID("add_wire");
+ return ID;
+ }
+
+ /// Called on change of any argument-attribute of this object
+ SKETCHPLUGIN_EXPORT virtual void attributeChanged(const std::string& theID);
+
+ /// Creates a new part document if needed
+ SKETCHPLUGIN_EXPORT virtual void execute();
+
+ SKETCHPLUGIN_EXPORT virtual bool isPreviewNeeded() const { return true; }
+
+ /// Find edges connected by coincident boundary constraint and composing a wire with
+ /// the already selected segments. It means that not more than 2 edges can be connected
+ /// with the coincident point.
+ /// \param[in] theActionId action key id (in following form: Action#Index)
+ /// \return \c false in case the action not performed.
+ SKETCHPLUGIN_EXPORT virtual bool customAction(const std::string& theActionId);
+
+ /// Returns the AIS preview
+ SKETCHPLUGIN_EXPORT virtual AISObjectPtr getAISObject(AISObjectPtr thePrevious);
+
+ /// Use plugin manager for features creation.
+ SketchPlugin_Offset();
+
+protected:
+ /// \brief Initializes attributes of derived class.
+ virtual void initAttributes();
+
+private:
+ /// Find all wires connected with the selected edges
+ bool findWires();
+
+ /// Create sketch feature for each edge of the offset result,
+ /// and store it in ENTITY_B(). Original edges are copied to ENTITY_A() to update
+ /// correctly if original selection is modified.
+ void addToSketch (const std::list< std::shared_ptr<GeomAlgoAPI_MakeShape> >& theOffsetAlgos);
+
+ /// Create BSpline or BSplinePeriodic sketch feature from theEdge
+ void mkBSpline (FeaturePtr& theResult, const GeomEdgePtr& theEdge,
+ std::list<ObjectPtr>& thePoolOfFeatures);
+
+ /// Update existent feature by the parameters of the given edge or create a new feature.
+ /// \param[in] theShape a shape to be converted to sketch feature
+ /// \param[in,out] theFeature sketch feature to be updated or created from scratch
+ /// \param[in,out] thePoolOfFeatures list of features to be removed (may be used as a new feature)
+ void updateExistentOrCreateNew (const GeomShapePtr& theShape, FeaturePtr& theFeature,
+ std::list<ObjectPtr>& thePoolOfFeatures);
+
+ /// Find edges that prolongate theEdgeFeature (in a chain) at theEndPoint
+ /// Recursive method.
+ /// \param[in] theFirstEdge Start edge of wire searching
+ /// \param[in] theEdge Current edge
+ /// \param[in] theEndPoint Point of the Current edge, not belonging to a previous edge
+ /// \param[in/out] theEdgesSet All edges to find among. If empty, all sketch edges assumed.
+ /// \param[in/out] theProcessedEdgesSet Already processed (put in chains) edges.
+ /// \param[in/out] theChain Resulting edges
+ /// \param[in] isPrepend if true, push new found edges to theChain front, else to the back
+ /// \return \c true if the chain is closed
+ bool findWireOneWay (const FeaturePtr& theFirstEdge,
+ const FeaturePtr& theEdge,
+ const std::shared_ptr<GeomDataAPI_Point2D>& theEndPoint,
+ std::set<FeaturePtr>& theEdgesSet,
+ std::set<FeaturePtr>& theProcessedEdgesSet,
+ std::list<FeaturePtr>& theChain,
+ const bool isPrepend = false);
+};
+
+#endif
#include <SketchPlugin_MacroCircle.h>
#include <SketchPlugin_MultiRotation.h>
#include <SketchPlugin_MultiTranslation.h>
+#include <SketchPlugin_Offset.h>
#include <SketchPlugin_Trim.h>
#include <SketchPlugin_Split.h>
#include <SketchPlugin_Validators.h>
return FeaturePtr(new SketchPlugin_SketchDrawer);
} else if (theFeatureID == SketchPlugin_SketchCopy::ID()) {
return FeaturePtr(new SketchPlugin_SketchCopy);
+ } else if (theFeatureID == SketchPlugin_Offset::ID()) {
+ return FeaturePtr(new SketchPlugin_Offset);
}
// feature of such kind is not found
return FeaturePtr();
aMsg->setState(SketchPlugin_ConstraintDistanceHorizontal::ID(), aHasSketchPlane);
aMsg->setState(SketchPlugin_ConstraintDistanceVertical::ID(), aHasSketchPlane);
aMsg->setState(SketchPlugin_CurveFitting::ID(), aHasSketchPlane);
-
+ aMsg->setState(SketchPlugin_Offset::ID(), aHasSketchPlane);
// SketchRectangle is a python feature, so its ID is passed just as a string
aMsg->setState("SketchRectangle", aHasSketchPlane);
}
#include "SketchPlugin_Tools.h"
#include "SketchPlugin_Arc.h"
+#include "SketchPlugin_BSpline.h"
#include "SketchPlugin_Circle.h"
#include "SketchPlugin_ConstraintCoincidence.h"
#include "SketchPlugin_ConstraintCoincidenceInternal.h"
#include <SketcherPrs_Tools.h>
#include <ModelAPI_AttributeDouble.h>
+#include <ModelAPI_AttributeInteger.h>
#include <ModelGeomAlgo_Point2D.h>
#include <ModelGeomAlgo_Shape.h>
return aPnt;
}
-std::set<FeaturePtr> findCoincidentConstraints(const FeaturePtr& theFeature)
+std::set<FeaturePtr> findCoincidentConstraints(const ObjectPtr& theObject)
{
std::set<FeaturePtr> aCoincident;
- const std::set<AttributePtr>& aRefsList = theFeature->data()->refsToMe();
+ const std::set<AttributePtr>& aRefsList = theObject->data()->refsToMe();
std::set<AttributePtr>::const_iterator aIt;
for (aIt = aRefsList.cbegin(); aIt != aRefsList.cend(); ++aIt) {
FeaturePtr aConstrFeature = std::dynamic_pointer_cast<ModelAPI_Feature>((*aIt)->owner());
// Useful to find points coincident to a given point.
class CoincidentPoints
{
+ static const int THE_DEFAULT_INDEX = -1;
+
public:
- void addCoincidence(const AttributePoint2DPtr& thePoint1,
- const AttributePoint2DPtr& thePoint2 = AttributePoint2DPtr())
+ void addCoincidence(const AttributePtr& thePoint1, const int theIndex1,
+ const AttributePtr& thePoint2, const int theIndex2)
{
- std::list< std::set<AttributePoint2DPtr> >::iterator aFound1 = find(thePoint1);
- std::list< std::set<AttributePoint2DPtr> >::iterator aFound2 = find(thePoint2);
+ auto aFound1 = find(thePoint1, theIndex1);
+ auto aFound2 = find(thePoint2, theIndex2);
if (aFound1 == myCoincidentPoints.end()) {
if (aFound2 == myCoincidentPoints.end()) {
- std::set<AttributePoint2DPtr> aNewSet;
- aNewSet.insert(thePoint1);
+ std::map<AttributePtr, std::set<int> > aNewSet;
+ aNewSet[thePoint1].insert(theIndex1);
if (thePoint2)
- aNewSet.insert(thePoint2);
+ aNewSet[thePoint2].insert(theIndex2);
myCoincidentPoints.push_back(aNewSet);
} else
- aFound2->insert(thePoint1);
+ (*aFound2)[thePoint1].insert(theIndex1);
} else if (aFound2 == myCoincidentPoints.end()) {
if (thePoint2)
- aFound1->insert(thePoint2);
+ (*aFound1)[thePoint2].insert(theIndex2);
} else {
- aFound1->insert(aFound2->begin(), aFound2->end());
+ for (auto it = aFound2->begin(); it != aFound2->end(); ++it)
+ (*aFound1)[it->first].insert(it->second.begin(), it->second.end());
myCoincidentPoints.erase(aFound2);
}
}
{
collectCoincidentPoints(thePoint);
- std::list< std::set<AttributePoint2DPtr> >::iterator aFound = find(thePoint);
- if (aFound == myCoincidentPoints.end())
- return std::set<AttributePoint2DPtr>();
- return *aFound;
+ std::set<AttributePoint2DPtr> aCoincPoints;
+ auto aFound = find(thePoint, THE_DEFAULT_INDEX);
+ if (aFound != myCoincidentPoints.end()) {
+ for (auto it = aFound->begin(); it != aFound->end(); ++it) {
+ AttributePoint2DPtr aPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(it->first);
+ if (aPoint)
+ aCoincPoints.insert(aPoint);
+ else {
+ AttributePoint2DArrayPtr aPointArray =
+ std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(it->first);
+ if (aPointArray) {
+ // this is a B-spline feature, the connection is possible
+ // to the first or the last point
+ FeaturePtr anOwner = ModelAPI_Feature::feature(aPointArray->owner());
+ if (it->second.find(0) != it->second.end()) {
+ AttributePoint2DPtr aFirstPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+ anOwner->attribute(SketchPlugin_BSpline::START_ID()));
+ aCoincPoints.insert(aFirstPoint);
+ }
+ if (it->second.find(aPointArray->size() - 1) != it->second.end()) {
+ AttributePoint2DPtr aFirstPoint = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
+ anOwner->attribute(SketchPlugin_BSpline::END_ID()));
+ aCoincPoints.insert(aFirstPoint);
+ }
+ }
+ }
+ }
+ }
+ return aCoincPoints;
}
private:
{
// iterate through coincideces for the given feature
std::set<FeaturePtr> aCoincidences = SketchPlugin_Tools::findCoincidentConstraints(theFeature);
- std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
+ if (theFeature->getKind() == SketchPlugin_Point::ID()) {
+ std::set<FeaturePtr> aCoincToRes =
+ SketchPlugin_Tools::findCoincidentConstraints(theFeature->lastResult());
+ aCoincidences.insert(aCoincToRes.begin(), aCoincToRes.end());
+ }\r std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
for (; aCIt != aCoincidences.end(); ++aCIt)
{
if (theCoincidences.find(*aCIt) != theCoincidences.end())
// iterate on coincident attributes
for (int i = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
AttributeRefAttrPtr aRefAttr = (*aCIt)->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
- if (aRefAttr && !aRefAttr->isObject())
- {
- FeaturePtr anOwner = ModelAPI_Feature::feature(aRefAttr->attr()->owner());
- if (anOwner != theFeature)
- coincidences(anOwner, theCoincidences);
+ if (!aRefAttr)
+ continue;
+ FeaturePtr anOwner;
+ if (aRefAttr->isObject()) {
+ FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
+ if (aFeature->getKind() == SketchPlugin_Point::ID())
+ anOwner = aFeature;
}
+ else
+ anOwner = ModelAPI_Feature::feature(aRefAttr->attr()->owner());
+ if (anOwner && anOwner != theFeature)
+ coincidences(anOwner, theCoincidences);
}
}
}
// (two points may be coincident through the third point)
void collectCoincidentPoints(const AttributePoint2DPtr& thePoint)
{
- AttributePoint2DPtr aPoints[2];
+ AttributePtr aPoints[2];
+ int anIndicesInArray[2];
FeaturePtr anOwner = ModelAPI_Feature::feature(thePoint->owner());
std::set<FeaturePtr> aCoincidences;
std::set<FeaturePtr>::const_iterator aCIt = aCoincidences.begin();
for (; aCIt != aCoincidences.end(); ++aCIt) {
- aPoints[0] = AttributePoint2DPtr();
- aPoints[1] = AttributePoint2DPtr();
+ aPoints[0] = aPoints[1] = AttributePtr();
+ anIndicesInArray[0] = anIndicesInArray[1] = THE_DEFAULT_INDEX;
for (int i = 0, aPtInd = 0; i < CONSTRAINT_ATTR_SIZE; ++i) {
AttributeRefAttrPtr aRefAttr = (*aCIt)->refattr(SketchPlugin_Constraint::ATTRIBUTE(i));
- if (aRefAttr && !aRefAttr->isObject())
- aPoints[aPtInd++] = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aRefAttr->attr());
+ if (!aRefAttr)
+ continue;
+ if (aRefAttr->isObject()) {
+ FeaturePtr aFeature = ModelAPI_Feature::feature(aRefAttr->object());
+ if (aFeature && aFeature->getKind() == SketchPlugin_Point::ID())
+ aPoints[aPtInd++] = aFeature->attribute(SketchPlugin_Point::COORD_ID());
+ }
+ else {
+ AttributePoint2DPtr aPointAttr =
+ std::dynamic_pointer_cast<GeomDataAPI_Point2D>(aRefAttr->attr());
+ AttributePoint2DArrayPtr aPointArray =
+ std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(aRefAttr->attr());
+ if (aPointAttr)
+ aPoints[aPtInd++] = aPointAttr;
+ else if (aPointArray) {
+ AttributeIntegerPtr anIndexAttr = (*aCIt)->integer(i == 0 ?
+ SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_A() :
+ SketchPlugin_ConstraintCoincidenceInternal::INDEX_ENTITY_B());
+ aPoints[aPtInd] = aPointArray;
+ anIndicesInArray[aPtInd++] = anIndexAttr->value();
+ }
+ }
}
if (aPoints[0] && aPoints[1])
- addCoincidence(aPoints[0], aPoints[1]);
+ addCoincidence(aPoints[0], anIndicesInArray[0], aPoints[1], anIndicesInArray[1]);
}
}
- std::list< std::set<AttributePoint2DPtr> >::iterator find(const AttributePoint2DPtr& thePoint)
+ std::list< std::map<AttributePtr, std::set<int> > >::iterator find(const AttributePtr& thePoint,
+ const int theIndex)
{
- std::list< std::set<AttributePoint2DPtr> >::iterator aSeek = myCoincidentPoints.begin();
- for (; aSeek != myCoincidentPoints.end(); ++aSeek)
- if (aSeek->find(thePoint) != aSeek->end())
+ auto aSeek = myCoincidentPoints.begin();
+ for (; aSeek != myCoincidentPoints.end(); ++aSeek) {
+ auto aFound = aSeek->find(thePoint);
+ if (aFound != aSeek->end() && aFound->second.find(theIndex) != aFound->second.end())
return aSeek;
+ }
+ // nothing is found, but if the point is a B-spline boundary point, lets check it as poles array
+ FeaturePtr anOwner = ModelAPI_Feature::feature(thePoint->owner());
+ if (anOwner->getKind() == SketchPlugin_BSpline::ID()) {
+ AttributePtr aPointsArray;
+ int anIndex = -1;
+ if (thePoint->id() == SketchPlugin_BSpline::START_ID()) {
+ aPointsArray = anOwner->attribute(SketchPlugin_BSpline::POLES_ID());
+ anIndex = 0;
+ }
+ else if (thePoint->id() == SketchPlugin_BSpline::END_ID()) {
+ aPointsArray = anOwner->attribute(SketchPlugin_BSpline::POLES_ID());
+ anIndex = std::dynamic_pointer_cast<GeomDataAPI_Point2DArray>(aPointsArray)->size() - 1;
+ }
+ if (aPointsArray)
+ return find(aPointsArray, anIndex);
+ }
return myCoincidentPoints.end();
}
private:
- std::list< std::set<AttributePoint2DPtr> > myCoincidentPoints;
+ std::list< std::map<AttributePtr, std::set<int> > > myCoincidentPoints;
};
std::set<AttributePoint2DPtr> findPointsCoincidentToPoint(const AttributePoint2DPtr& thePoint)
return aCoincidentPoints.coincidentPoints(thePoint);
}
+
void resetAttribute(SketchPlugin_Feature* theFeature,
const std::string& theId)
{
aStartAttributeName = SketchPlugin_EllipticArc::START_POINT_ID();
anEndAttributeName = SketchPlugin_EllipticArc::END_POINT_ID();
}
+ else if (aFeatureKind == SketchPlugin_BSpline::ID()) {
+ aStartAttributeName = SketchPlugin_BSpline::START_ID();
+ anEndAttributeName = SketchPlugin_BSpline::END_ID();
+ }
if (!aStartAttributeName.empty() && !anEndAttributeName.empty()) {
theStartPointAttr = std::dynamic_pointer_cast<GeomDataAPI_Point2D>(
theFeature->attribute(aStartAttributeName));
#include <GeomAPI_Shape.h>
#include <GeomAPI_AISObject.h>
#include <GeomDataAPI_Point2D.h>
+#include <GeomDataAPI_Point2DArray.h>
#include <GeomAlgoAPI_ShapeTools.h>
#include <list>
std::shared_ptr<GeomAPI_Pnt2d> getCoincidencePoint(const FeaturePtr theStartCoin);
/// Find all Coincident constraints referred to the feature or its attribute
-std::set<FeaturePtr> findCoincidentConstraints(const FeaturePtr& theFeature);
+std::set<FeaturePtr> findCoincidentConstraints(const ObjectPtr& theObject);
/// Finds lines coincident at point
/// \param[in] theStartCoin coincidence feature
#include "SketchPlugin_MacroArc.h"
#include "SketchPlugin_MacroCircle.h"
#include "SketchPlugin_MultiRotation.h"
+#include "SketchPlugin_Offset.h"
#include "SketchPlugin_Point.h"
#include "SketchPlugin_Sketch.h"
#include "SketchPlugin_Trim.h"
FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theAttribute->owner());
AttributeRefListPtr aSelAttr =
std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(theAttribute);
+ std::set<ObjectPtr> aSelected;
AttributeRefListPtr aRefListOfInitial = std::dynamic_pointer_cast<ModelAPI_AttributeRefList>(
aFeature->attribute(SketchPlugin_Constraint::ENTITY_A()));
std::list<ObjectPtr>::iterator anObjIter;
for(int anInd = 0; anInd < aSelAttr->size(); anInd++) {
ObjectPtr aSelObject = aSelAttr->object(anInd);
+ if (aSelected.find(aSelObject) != aSelected.end()) {
+ theError = "Error: An object selected twice";
+ return false;
+ }
+ aSelected.insert(aSelObject);
+
anObjIter = anInitialObjects.begin();
for (; anObjIter != anInitialObjects.end(); anObjIter++)
if (aSelObject == *anObjIter)
// B-splines are not supported in Copying features
FeaturePtr aSelFeature = ModelAPI_Feature::feature(aSelObject);
- if (aSelFeature && (aSelFeature->getKind() == SketchPlugin_BSpline::ID() ||
+ if (aFeature->getKind() != SketchPlugin_Offset::ID() &&
+ aSelFeature && (aSelFeature->getKind() == SketchPlugin_BSpline::ID() ||
aSelFeature->getKind() == SketchPlugin_BSplinePeriodic::ID())) {
theError = "Not supported";
return false;
}
anObjIter = aCopiedObjects.begin();
- for (; anObjIter != aCopiedObjects.end(); anObjIter++)
- if (aSelObject == *anObjIter) {
+ for (; anObjIter != aCopiedObjects.end(); anObjIter++) {
+ bool isFound = aSelObject == *anObjIter;
+ if (!isFound) {
+ // check in the results of the feature
+ FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(*anObjIter);
+ if (aFeature) {
+ const std::list<ResultPtr>& aResults = aFeature->results();
+ for (std::list<ResultPtr>::const_iterator aResIt = aResults.begin();
+ aResIt != aResults.end() && !isFound; ++aResIt) {
+ isFound = aSelObject == *aResIt;
+ }
+ }
+ }
+ if (isFound) {
std::string aName = aSelObject.get() ? aSelObject->data()->name() : "";
theError = "The object %1 is a result of copy";
theError.arg(aName);
return false;
}
+ }
}
return true;
}
--- /dev/null
+# Copyright (C) 2020 CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+"""
+ TestOffset.py
+ Unit test of SketchPlugin_Offset class
+
+ SketchPlugin_Offset
+ static const std::string ID("SketchOffset");
+ data()->addAttribute(EDGES_ID(), ModelAPI_AttributeRefList::typeId());
+ data()->addAttribute(VALUE_ID(), ModelAPI_AttributeDouble::typeId());
+ data()->addAttribute(REVERSED_ID(), ModelAPI_AttributeBoolean::typeId());
+ data()->addAttribute(SketchPlugin_Constraint::ENTITY_A(), ModelAPI_AttributeRefList::typeId());
+ data()->addAttribute(SketchPlugin_Constraint::ENTITY_B(), ModelAPI_AttributeRefList::typeId());
+ data()->addAttribute(SketchPlugin_Constraint::ENTITY_C(), ModelAPI_AttributeIntArray::typeId());
+"""
+
+from GeomDataAPI import *
+from ModelAPI import *
+import math
+from salome.shaper import model
+
+#=========================================================================
+# Initialization of the test
+#=========================================================================
+
+__updated__ = "2020-06-30"
+
+#=========================================================================
+# Auxiliary functions
+#=========================================================================
+def normalize(theDir):
+ aLen = math.hypot(theDir[0], theDir[1])
+ if aLen < 1.e-10:
+ aLen = 1.0
+ return [theDir[0] / aLen, theDir[1] / aLen]
+
+def checkOffset(theListIn, theListOut, theOutToIn, theDist, isReversed, nbIn, nbOut):
+ TOL = 6.e-5
+ aNbIn = theListIn.size()
+ aNbOut = theListOut.size()
+
+ #print("**checkOffset**")
+ assert (theListIn.size() == nbIn)
+ assert (theListOut.size() == nbOut)
+ assert (theOutToIn.size() == nbOut)
+
+ for ind in range(0, aNbOut):
+ aFeatureOut = ModelAPI_Feature.feature(theListOut.object(ind))
+ assert(aFeatureOut is not None)
+ anInInd = theOutToIn.value(ind)
+ if (anInInd == -1):
+ assert(aFeatureOut.getKind() == "SketchArc")
+ else:
+ aFeatureIn = ModelAPI_Feature.feature(theListIn.object(anInInd))
+ assert(aFeatureIn is not None)
+
+ #print(aFeatureIn.getKind())
+ if (aFeatureIn.getKind() == "SketchLine"):
+ assert(aFeatureOut.getKind() == aFeatureIn.getKind())
+ # Line and its offset are parallel
+ aP1Out = geomDataAPI_Point2D(aFeatureOut.attribute('StartPoint'))
+ aP2Out = geomDataAPI_Point2D(aFeatureOut.attribute('EndPoint'))
+ aP1In = geomDataAPI_Point2D(aFeatureIn.attribute('StartPoint'))
+ aP2In = geomDataAPI_Point2D(aFeatureIn.attribute('EndPoint'))
+ aDirOut = [aP2Out.x() - aP1Out.x(), aP2Out.y() - aP1Out.y()]
+ aDirIn = [ aP2In.x() - aP1In.x(), aP2In.y() - aP1In.y()]
+ aCross = aDirOut[0] * aDirIn[1] - aDirOut[1] * aDirIn[0]
+ assert math.fabs(aCross) < TOL, "aCross = {0}".format(aCross)
+ elif (aFeatureIn.getKind() == "SketchArc"):
+ assert(aFeatureOut.getKind() == aFeatureIn.getKind())
+ # Arc and its offset have the same center
+ aCPOut = geomDataAPI_Point2D(aFeatureOut.attribute('center_point'))
+ aCPIn = geomDataAPI_Point2D(aFeatureIn.attribute('center_point'))
+ assert (math.fabs(aCPOut.x() - aCPIn.x()) < TOL)
+ assert (math.fabs(aCPOut.y() - aCPIn.y()) < TOL)
+
+
+#=========================================================================
+# Start of test
+#=========================================================================
+aSession = ModelAPI_Session.get()
+aDocument = aSession.moduleDocument()
+#=========================================================================
+# Creation of a sketch
+#=========================================================================
+aSession.startOperation()
+aSketchCommonFeature = aDocument.addFeature("Sketch")
+aSketchFeature = featureToCompositeFeature(aSketchCommonFeature)
+origin = geomDataAPI_Point(aSketchFeature.attribute("Origin"))
+origin.setValue(0, 0, 0)
+dirx = geomDataAPI_Dir(aSketchFeature.attribute("DirX"))
+dirx.setValue(1, 0, 0)
+norm = geomDataAPI_Dir(aSketchFeature.attribute("Norm"))
+norm.setValue(0, 0, 1)
+aSession.finishOperation()
+#=========================================================================
+# Creation of an arc and two lines
+#=========================================================================
+# Arc
+aSession.startOperation()
+aSketchArc1 = aSketchFeature.addFeature("SketchArc")
+anArcCentr = geomDataAPI_Point2D(aSketchArc1.attribute("center_point"))
+anArcCentr.setValue(10., 10.)
+anArcStartPoint = geomDataAPI_Point2D(aSketchArc1.attribute("start_point"))
+anArcStartPoint.setValue(50., 0.)
+anArcEndPoint = geomDataAPI_Point2D(aSketchArc1.attribute("end_point"))
+anArcEndPoint.setValue(0., 50.)
+aSession.finishOperation()
+# Line 1
+aSession.startOperation()
+aSketchLine1 = aSketchFeature.addFeature("SketchLine")
+aLine1StartPoint = geomDataAPI_Point2D(aSketchLine1.attribute("StartPoint"))
+aLine1EndPoint = geomDataAPI_Point2D(aSketchLine1.attribute("EndPoint"))
+aLine1StartPoint.setValue(0., 50.)
+aLine1EndPoint.setValue(-20., 0.)
+aSession.finishOperation()
+# Line 2
+aSession.startOperation()
+aSketchLine2 = aSketchFeature.addFeature("SketchLine")
+aLine2StartPoint = geomDataAPI_Point2D(aSketchLine2.attribute("StartPoint"))
+aLine2EndPoint = geomDataAPI_Point2D(aSketchLine2.attribute("EndPoint"))
+aLine2StartPoint.setValue(50., 0.)
+aLine2EndPoint.setValue(-20., 0.)
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 13)
+#=========================================================================
+# Link arc points and lines points by the coincidence constraints
+#=========================================================================
+aSession.startOperation()
+aConstraint = aSketchFeature.addFeature("SketchConstraintCoincidence")
+reflistA = aConstraint.refattr("ConstraintEntityA")
+reflistB = aConstraint.refattr("ConstraintEntityB")
+reflistA.setAttr(anArcEndPoint)
+reflistB.setAttr(aLine1StartPoint)
+aConstraint.execute()
+aSession.finishOperation()
+aSession.startOperation()
+aConstraint = aSketchFeature.addFeature("SketchConstraintCoincidence")
+reflistA = aConstraint.refattr("ConstraintEntityA")
+reflistB = aConstraint.refattr("ConstraintEntityB")
+reflistA.setAttr(anArcStartPoint)
+reflistB.setAttr(aLine2StartPoint)
+aConstraint.execute()
+aSession.finishOperation()
+aSession.startOperation()
+aConstraint = aSketchFeature.addFeature("SketchConstraintCoincidence")
+reflistA = aConstraint.refattr("ConstraintEntityA")
+reflistB = aConstraint.refattr("ConstraintEntityB")
+reflistA.setAttr(aLine1EndPoint)
+reflistB.setAttr(aLine2EndPoint)
+aConstraint.execute()
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 7)
+#=========================================================================
+# Make offset for objects created above
+#=========================================================================
+VALUE = 13
+IS_REVERSED = False
+aSession.startOperation()
+anOffset = aSketchFeature.addFeature("SketchOffset")
+aRefListInitial = anOffset.reflist("segments")
+aRefListInitial.append(aSketchLine1.lastResult())
+aRefListInitial.append(aSketchArc1.lastResult())
+aRefListInitial.append(aSketchLine2.lastResult())
+anOffset.real("offset_value").setValue(VALUE)
+anOffset.boolean("reversed").setValue(IS_REVERSED)
+anOffset.execute()
+aSession.finishOperation()
+assert (model.dof(aSketchFeature) == 7)
+#=========================================================================
+# Verify all offset objects
+#=========================================================================
+aRefListA = anOffset.reflist("ConstraintEntityA")
+aRefListB = anOffset.reflist("ConstraintEntityB")
+anOffsetToBaseMap = anOffset.intArray("ConstraintEntityC")
+checkOffset(aRefListA, aRefListB, anOffsetToBaseMap, VALUE, IS_REVERSED, 3, 6)
+assert (model.dof(aSketchFeature) == 7)
+
+#=========================================================================
+# Remove object from offset
+#=========================================================================
+aSession.startOperation()
+aRefListInitial.remove(aSketchLine2.lastResult())
+aSession.finishOperation()
+checkOffset(aRefListA, aRefListB, anOffsetToBaseMap, VALUE, IS_REVERSED, 2, 4)
+assert (model.dof(aSketchFeature) == 7)
+
+#=========================================================================
+# Clear list of objects
+#=========================================================================
+aSession.startOperation()
+aRefListInitial.clear()
+#TODO: uncomment next line
+#checkOffset(aRefListA, aRefListB, anOffsetToBaseMap, VALUE, IS_REVERSED, 0, 0)
+# add arc once again
+aRefListInitial.append(aSketchArc1.lastResult())
+aSession.finishOperation()
+checkOffset(aRefListA, aRefListB, anOffsetToBaseMap, VALUE, IS_REVERSED, 1, 1)
+assert (model.dof(aSketchFeature) == 7)
+
+#=========================================================================
+# End of test
+#=========================================================================
+
+assert(model.checkPythonDump())
--- /dev/null
+# Copyright (C) 2020 CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+from salome.shaper import model
+
+SKETCH_DOF = 9
+
+model.begin()
+partSet = model.moduleDocument()
+
+### Create Sketch
+Sketch_1 = model.addSketch(partSet, model.defaultPlane("XOY"))
+
+### Create SketchArc
+SketchArc_1 = Sketch_1.addArc(10, 10, 50, 0, 0, 50, False)
+
+### Create SketchLine
+SketchLine_1 = Sketch_1.addLine(0, 50, -20, 0)
+
+### Create SketchLine
+SketchLine_2 = Sketch_1.addLine(50, 0, -20, 0)
+Sketch_1.setCoincident(SketchArc_1.endPoint(), SketchLine_1.startPoint())
+Sketch_1.setCoincident(SketchArc_1.startPoint(), SketchLine_2.startPoint())
+model.do()
+
+assert(model.dof(Sketch_1) == SKETCH_DOF)
+
+### Create SketchOffset
+SketchOffset_1_objects = [SketchLine_1.result(), SketchArc_1.results()[1], SketchLine_2.result()]
+SketchOffset_1 = Sketch_1.addOffset(SketchOffset_1_objects, 13, False)
+model.do()
+
+# DoF should not change
+assert(model.dof(Sketch_1) == SKETCH_DOF)
+# check number of features
+assert(len(SketchOffset_1.offset()) == 5)
+model.testNbSubFeatures(Sketch_1, "SketchPoint", 0)
+model.testNbSubFeatures(Sketch_1, "SketchLine", 4)
+model.testNbSubFeatures(Sketch_1, "SketchArc", 4)
+model.testNbSubFeatures(Sketch_1, "SketchBSpline", 0)
+model.testNbSubFeatures(Sketch_1, "SketchBSplinePeriodic", 0)
+
+model.end()
+
+assert(model.checkPythonDump())
mirrorFeature.rst
translationFeature.rst
rotationFeature.rst
+ offsetFeature.rst
sketchDrawer.rst
sketchCopy.rst
--- /dev/null
+
+ .. _tui_create_offset:
+
+Create Offset
+=============
+
+.. literalinclude:: examples/offset.py
+ :linenos:
+ :language: python
+
+:download:`Download this script <examples/offset.py>`
--- /dev/null
+from salome.shaper import model
+
+model.begin()
+partSet = model.moduleDocument()
+
+Sketch_1 = model.addSketch(partSet, model.defaultPlane("XOY"))
+
+SketchLine_1 = Sketch_1.addLine(0, 0, 0, 100)
+SketchLine_2 = Sketch_1.addLine(0, 100, 100, 100)
+SketchLine_3 = Sketch_1.addLine(100, 100, 100, 0)
+SketchLine_4 = Sketch_1.addLine(0, 0, 100, 0)
+
+Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint())
+Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint())
+Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.endPoint())
+Sketch_1.setCoincident(SketchLine_1.startPoint(), SketchLine_4.startPoint())
+
+SketchOffset_1_objects = [SketchLine_1.result(), SketchLine_2.result(), SketchLine_3.result(), SketchLine_4.result()]
+SketchOffset_1 = Sketch_1.addOffset(SketchOffset_1_objects, 10.0, False)
+
+model.do()
+model.end()
--- /dev/null
+.. |offset.icon| image:: images/offset.png
+
+Offset
+======
+
+Offset operation offsets sketch entities on a given distance.
+Gaps are filled by arcs.
+Offset is performed outside a closed contour or to the right
+of an open one, unless the **Reversed** flag is not set.
+
+To create an Offset in the active Sketch:
+
+#. select in the Main Menu *Sketch - > Offset* item or
+#. click |offset.icon| **Offset** button in Sketch toolbar:
+
+Property panel:
+
+.. image:: images/Offset_panel.png
+ :align: center
+
+.. centered::
+ Offset
+
+Input fields:
+
+- **Edges** is the list of segments (lines, circles, arcs) selected in the view.
+- **Offset value** is the offset distance.
+- **Reversed** sets the reversed offset side (inside a closed contour or to the left of an open one).
+
+Button:
+
+- **Select wire** button adds edges connected by coincident boundary constraint
+ and composing a wire with the already selected segments.
+ Not more than 2 edges can be connected with one coincident point.
+
+**TUI Command**:
+
+.. py:function:: Sketch_1.addOffset(Objects, Distance, isReversed)
+
+ :param list: A list of objects.
+ :param real: An offset distance.
+ :param boolean: Reversed flag.
+ :return: Result object.
+
+Result
+""""""
+
+Created Offset appears in the view.
+
+| The original and the offset objects are marked with a special sign.
+| Offset object is drawn with a thinner line.
+
+.. image:: images/Offset_res.png
+ :align: center
+
+.. centered::
+ Offset created
+
+**See Also** a sample TUI Script of :ref:`tui_create_offset` operation.
SketchConstraintCoincidence SketchConstraintCoincidenceInternal
SketchConstraintMirror SketchConstraintAngle
SketchMultiRotation SketchMultiTranslation
+ SketchOffset
SketchConstraintCollinear SketchConstraintMiddle"
when_nested="accept abort"
title="Sketch"
</integervalue>
<validator id="PartSet_MultyTranslationSelection" />
</feature>
+
+ <!-- Offset curve -->
+ <feature id="SketchOffset"
+ title="Offset"
+ tooltip="Offset a curve to a distance"
+ icon="icons/Sketch/offset.png"
+ helpfile="offsetFeature.html">
+ <sketch_multi_selector id="segments"
+ label="Edges"
+ tooltip="Select edges to offset"
+ shape_types="Edges"
+ use_external="true"
+ greed="true">
+ <validator id="SketchPlugin_CopyValidator" />
+ </sketch_multi_selector>
+ <doublevalue id="offset_value"
+ label="Offset value"
+ tooltip="Offset value"
+ default="1" min="0.000001"
+ use_reset="false">
+ <validator id="GeomValidators_Positive" parameters="1e-07"/>
+ </doublevalue>
+ <boolvalue id="reversed"
+ label="Reversed"
+ tooltip="Reverse the offset"
+ default="false"
+ obligatory="0"/>
+ <action id="add_wire"
+ label="Select wire"
+ tooltip="Add the list of segments composing a wire with the selected items through the coincidence by boundary points"/>
+ </feature>
</group>
<group id="Dimensional constraints">
SketchSolver_ConstraintMultiRotation.h
SketchSolver_ConstraintMultiTranslation.h
SketchSolver_ConstraintMovement.h
+ SketchSolver_ConstraintOffset.h
)
SET(SKETCHSOLVER_SOURCES
SketchSolver_ConstraintMultiRotation.cpp
SketchSolver_ConstraintMultiTranslation.cpp
SketchSolver_ConstraintMovement.cpp
+ SketchSolver_ConstraintOffset.cpp
)
SET(SKETCHSOLVER_LIBRARIES
#include <SketchSolver_ConstraintTangent.h>
#include <SketchSolver_ConstraintMultiRotation.h>
#include <SketchSolver_ConstraintMultiTranslation.h>
+#include <SketchSolver_ConstraintOffset.h>
#include <SketchPlugin_Arc.h>
#include <SketchPlugin_BSpline.h>
#include <SketchPlugin_Line.h>
#include <SketchPlugin_MultiRotation.h>
#include <SketchPlugin_MultiTranslation.h>
+#include <SketchPlugin_Offset.h>
#include <SketchPlugin_Point.h>
#include <GeomAPI_BSpline2d.h>
return SolverConstraintPtr(new SketchSolver_ConstraintAngle(theConstraint));
} else if (theConstraint->getKind() == SketchPlugin_ConstraintPerpendicular::ID()) {
return SolverConstraintPtr(new SketchSolver_ConstraintPerpendicular(theConstraint));
+ } else if (theConstraint->getKind() == SketchPlugin_Offset::ID()) {
+ return SolverConstraintPtr(new SketchSolver_ConstraintOffset(theConstraint));
}
// All other types of constraints
return SolverConstraintPtr(new SketchSolver_Constraint(theConstraint));
--- /dev/null
+// Copyright (C) 2020 CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include <SketchSolver_ConstraintOffset.h>
+
+
+void SketchSolver_ConstraintOffset::getAttributes(
+ EntityWrapperPtr&,
+ std::vector<EntityWrapperPtr>&)
+{
+}
+
+void SketchSolver_ConstraintOffset::process()
+{
+ cleanErrorMsg();
+}
+
+
+void SketchSolver_ConstraintOffset::update()
+{
+ cleanErrorMsg();
+ remove();
+ process();
+}
--- /dev/null
+// Copyright (C) 2020 CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef SketchSolver_ConstraintOffset_H_
+#define SketchSolver_ConstraintOffset_H_
+
+#include <SketchSolver_Constraint.h>
+
+/** \class SketchSolver_ConstraintOffset
+ * \ingroup Plugins
+ * \brief Convert offset to the solver's data model
+ */
+class SketchSolver_ConstraintOffset : public SketchSolver_Constraint
+{
+public:
+ /// Constructor based on SketchPlugin constraint
+ SketchSolver_ConstraintOffset(ConstraintPtr theConstraint) :
+ SketchSolver_Constraint(theConstraint)
+ {}
+
+ /// \brief Update constraint
+ virtual void update();
+
+protected:
+ /// \brief Converts SketchPlugin constraint to a list of SolveSpace constraints
+ virtual void process();
+
+ /// \brief Generate list of entities of mirror constraint
+ virtual void getAttributes(EntityWrapperPtr&, std::vector<EntityWrapperPtr>&);
+};
+
+#endif
SketcherPrs_Mirror.h
SketcherPrs_Transformation.h
SketcherPrs_Angle.h
+ SketcherPrs_Offset.h
)
SET(PROJECT_SOURCES
SketcherPrs_Mirror.cpp
SketcherPrs_Transformation.cpp
SketcherPrs_Angle.cpp
+ SketcherPrs_Offset.cpp
)
SET(PROJECT_LIBRARIES
icons/mirror.png
icons/rotate.png
icons/translate.png
+ icons/offset.png
)
ADD_DEFINITIONS(-DSKETCHERPRS_EXPORTS ${OpenCASCADE_DEFINITIONS} -D_CRT_SECURE_NO_WARNINGS)
#include "SketcherPrs_Mirror.h"
#include "SketcherPrs_Transformation.h"
#include "SketcherPrs_Angle.h"
+#include "SketcherPrs_Offset.h"
// Macros for constraint presentation definition
#define CONSTRAINT_PRS_IMPL(NAME, CLASS) \
CONSTRAINT_PRS_IMPL(lengthDimensionConstraint, SketcherPrs_LengthDimension);
CONSTRAINT_PRS_IMPL(angleConstraint, SketcherPrs_Angle);
CONSTRAINT_PRS_IMPL(radiusConstraint, SketcherPrs_Radius);
+CONSTRAINT_PRS_IMPL(offsetObject, SketcherPrs_Offset);
// Non-standard constraints definition
AISObjectPtr SketcherPrs_Factory::horisontalConstraint(ModelAPI_Feature* theConstraint,
/// \param thePlane the current sketch plane
/// \param thePrevious the previous presentation
GET_CONSTRAINT_PRS(radiusConstraint)
+
+ /// Creates radius dimension presentation
+ /// \param theConstraint the constraint
+ /// \param thePlane the current sketch plane
+ /// \param thePrevious the previous presentation
+ GET_CONSTRAINT_PRS(offsetObject)
};
#endif
--- /dev/null
+// Copyright (C) 2014-2019 CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#include "SketcherPrs_Offset.h"
+#include "SketcherPrs_Tools.h"
+#include "SketcherPrs_PositionMgr.h"
+
+#include <SketchPlugin_Offset.h>
+
+#include <ModelAPI_AttributeRefList.h>
+#include <ModelAPI_AttributeDouble.h>
+
+#include <Graphic3d_AspectLine3d.hxx>
+#include <Prs3d_Root.hxx>
+
+
+IMPLEMENT_STANDARD_RTTIEXT(SketcherPrs_Offset, SketcherPrs_SymbolPrs);
+
+static Handle(Image_AlienPixMap) MyPixMap;
+
+SketcherPrs_Offset::SketcherPrs_Offset(ModelAPI_Feature* theConstraint,
+ SketchPlugin_Sketch* theSketcher)
+ : SketcherPrs_SymbolPrs(theConstraint, theSketcher)
+{
+}
+
+bool SketcherPrs_Offset::IsReadyToDisplay(ModelAPI_Feature* theConstraint,
+ const std::shared_ptr<GeomAPI_Ax3>&/* thePlane*/)
+{
+ bool aReadyToDisplay = false;
+
+ AttributeDoublePtr aValueAttr = theConstraint->real(SketchPlugin_Offset::VALUE_ID());
+ if (aValueAttr->isInitialized()) {
+ AttributeRefListPtr aSelectedEdges = theConstraint->reflist(SketchPlugin_Offset::ENTITY_A());
+ aReadyToDisplay = (aSelectedEdges->list().size() > 0);
+ if (aReadyToDisplay) {
+ AttributeRefListPtr aOffcetEdges = theConstraint->reflist(SketchPlugin_Offset::ENTITY_B());
+ aReadyToDisplay = (aOffcetEdges->list().size() > 0);
+ }
+ }
+ return aReadyToDisplay;
+}
+
+bool SketcherPrs_Offset::updateIfReadyToDisplay(double theStep, bool withColor) const
+{
+ if (!IsReadyToDisplay(myConstraint, plane()))
+ return false;
+ if (!plane())
+ return false;
+
+ // Set points of the presentation
+ SketcherPrs_PositionMgr* aMgr = SketcherPrs_PositionMgr::get();
+
+ AttributeRefListPtr aSelectedEdges = myConstraint->reflist(SketchPlugin_Offset::ENTITY_A());
+ int aNb = aSelectedEdges->size();
+
+ AttributeRefListPtr aOffcetEdges = myConstraint->reflist(SketchPlugin_Offset::ENTITY_B());
+ int aOffNb = aOffcetEdges->size();
+
+ myPntArray = new Graphic3d_ArrayOfPoints(aNb + aOffNb, withColor);
+ int i;
+ ObjectPtr aObj;
+ gp_Pnt aP1;
+ // get position for each source object
+ for (i = 0; i < aNb; i++) {
+ aObj = aSelectedEdges->object(i);
+ if (SketcherPrs_Tools::getShape(aObj).get() == NULL)
+ continue;
+ aP1 = aMgr->getPosition(aObj, this, theStep);
+ myPntArray->SetVertice(i + 1, aP1);
+ }
+ for (i = aNb; i < aNb + aOffNb; i++) {
+ aObj = aOffcetEdges->object(i - aNb);
+ if (SketcherPrs_Tools::getShape(aObj).get() == NULL)
+ continue;
+ aP1 = aMgr->getPosition(aObj, this, theStep);
+ myPntArray->SetVertice(i + 1, aP1);
+ }
+ return true;
+}
+
+void SketcherPrs_Offset::drawLines(const Handle(Prs3d_Presentation)& thePrs,
+ Quantity_Color theColor) const
+{
+ AttributeRefListPtr aSelectedEdges = myConstraint->reflist(SketchPlugin_Offset::ENTITY_A());
+ if (aSelectedEdges.get() == NULL)
+ return;
+ AttributeRefListPtr aOffcetEdges = myConstraint->reflist(SketchPlugin_Offset::ENTITY_B());
+ if (aOffcetEdges.get() == NULL)
+ return;
+
+ if (aSelectedEdges->size() == 0)
+ return;
+
+ if (aOffcetEdges->size() == 0)
+ return;
+
+ // Draw source objects
+ drawListOfShapes(aSelectedEdges, thePrs, theColor);
+ // Draw offcet objects
+ drawListOfShapes(aOffcetEdges, thePrs, theColor);
+}
--- /dev/null
+// Copyright (C) 2014-2019 CEA/DEN, EDF R&D
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+//
+
+#ifndef SketcherPrs_Offset_H
+#define SketcherPrs_Offset_H
+
+#include "SketcherPrs_SymbolPrs.h"
+
+
+DEFINE_STANDARD_HANDLE(SketcherPrs_Offset, SketcherPrs_SymbolPrs)
+
+/**
+* \ingroup GUI
+* A redefinition of standard AIS Interactive Object in order to provide
+* presentation of Equal constraint
+*/
+class SketcherPrs_Offset : public SketcherPrs_SymbolPrs
+{
+public:
+ /// Constructor
+ /// \param theConstraint a constraint feature
+ /// \param theSketcher a sketcher object
+ Standard_EXPORT SketcherPrs_Offset(ModelAPI_Feature* theConstraint,
+ SketchPlugin_Sketch* theSketcher);
+ DEFINE_STANDARD_RTTIEXT(SketcherPrs_Offset, SketcherPrs_SymbolPrs)
+
+ /// Returns true if the constraint feature arguments are correcly
+ /// filled to build AIS presentation
+ /// \param theConstraint a constraint feature
+ /// \param thePlane a coordinate plane of current sketch
+ /// \return boolean result value
+ static bool IsReadyToDisplay(ModelAPI_Feature* theConstraint,
+ const std::shared_ptr<GeomAPI_Ax3>& thePlane);
+protected:
+
+ virtual const char* iconName() const { return "offset.png"; }
+
+ virtual void drawLines(const Handle(Prs3d_Presentation)& thePrs, Quantity_Color theColor) const;
+
+ /// Update myPntArray according to presentation positions
+ /// \return true in case of success
+ virtual bool updateIfReadyToDisplay(double theStep, bool withColor) const;
+};
+
+#endif
\ No newline at end of file
std::shared_ptr<GeomAPI_Shape> getShape(ObjectPtr theObject)
{
ResultConstructionPtr aRes = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(theObject);
+ if (!aRes.get()) {
+ FeaturePtr aFeature = std::dynamic_pointer_cast<ModelAPI_Feature>(theObject);
+ if (aFeature.get())
+ aRes = std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aFeature->lastResult());
+ }
if (aRes.get() != NULL && aRes->data()->isValid()) {
/// essential check as it is called in openGl thread
return aRes->shape();