#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 <TopoDS.hxx>
#include <TopoDS_Wire.hxx>
+#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>
static GeomShapePtr fromTopoDS(const TopoDS_Shape& theShape)
return aResultShape;
}
-GeomAlgoAPI_WireBuilder::GeomAlgoAPI_WireBuilder(const ListOfShape& theShapes)
+GeomAlgoAPI_WireBuilder::GeomAlgoAPI_WireBuilder(const ListOfShape& theShapes,
+ const bool theForceOpenWire)
{
TopTools_ListOfShape aListOfEdges;
}
}
+ bool isSplitWire = false;
+ gp_Pnt aSplitPoint;
+ if (theForceOpenWire) {
+ // 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[i]) < Max(Tol1[i], Tol2[j])) {
+ aSplitPoint = P1[i];
+ isSplitWire = true;
+ }
+ }
+
BRepBuilderAPI_MakeWire* aWireBuilder = new BRepBuilderAPI_MakeWire;
aWireBuilder->Add(aListOfEdges);
if (aWireBuilder->Error() == BRepBuilderAPI_WireDone) {
setImpl(aWireBuilder);
setBuilderType(OCCT_BRepBuilderAPI_MakeShape);
- // store generated/modified shapes
+ // 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;
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);
+ 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.
TestMultiRotation05.py
TestMultiRotationWithParameter.py
TestMultiTranslation.py
- TestOffset.py
+ TestOffset1.py
+ TestOffset2.py
TestPresentation.py
TestProjection.py
TestProjectionBSpline.py
// 5.b. Find a chain of edges
std::list<FeaturePtr> aChain;
aChain.push_back(aFeature);
- if (aStartPoint && anEndPoint) { // not closed edge
- bool isClosed = findWireOneWay(aFeature, aFeature, aStartPoint, anEdgesSet, aProcessedEdgesSet, aChain, true);
- if (!isClosed)
- findWireOneWay(aFeature, aFeature, anEndPoint, anEdgesSet, aProcessedEdgesSet, aChain, false);
+ 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);
}
}
std::shared_ptr<GeomAlgoAPI_WireBuilder> aWireBuilder(
- new GeomAlgoAPI_WireBuilder(aTopoChain));
+ new GeomAlgoAPI_WireBuilder(aTopoChain, !isClosed));
GeomShapePtr aWireShape = aWireBuilder->shape();
GeomWirePtr aWire (new GeomAPI_Wire (aWireShape));
}
// 5. Continue gathering the chain (recursive)
- return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet, theProcessedEdgesSet, theChain, isPrepend);
+ return findWireOneWay (theFirstEdge, aNextEdgeFeature, aP2, theEdgesSet,
+ theProcessedEdgesSet, theChain, isPrepend);
}
static void setRefListValue(AttributeRefListPtr theList, int theListSize,
void SketchPlugin_Offset::attributeChanged(const std::string& theID)
{
-//// if (theID == EDGES_ID())
-//// removeCreated();
+ 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)
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);
+ 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) {
+++ /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
-#
-
-"""
- 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 constraint
-#=========================================================================
-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()
-assert (model.dof(aSketchFeature) == 9)
-#=========================================================================
-# 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) == 9)
-#=========================================================================
-# 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) == 9)
-
-#=========================================================================
-# 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) == 9)
-
-#=========================================================================
-# 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) == 9)
-
-#=========================================================================
-# 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
+#
+
+"""
+ 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())