From: Artem Zhidkov Date: Sat, 4 Jul 2020 07:45:53 +0000 (+0300) Subject: Task #3231: Sketcher Offset of a curve. X-Git-Tag: V9_6_0a1~60^2~3 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=e14a0493c3a3a7c475cf146e2ec55329d4164681;p=modules%2Fshaper.git Task #3231: Sketcher Offset of a curve. Fix issues --- diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.cpp b/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.cpp index 53bc674a4..a90d1e953 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.cpp +++ b/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.cpp @@ -24,11 +24,14 @@ #include #include +#include #include #include +#include #include #include #include +#include #include static GeomShapePtr fromTopoDS(const TopoDS_Shape& theShape) @@ -38,7 +41,8 @@ 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; @@ -61,14 +65,68 @@ GeomAlgoAPI_WireBuilder::GeomAlgoAPI_WireBuilder(const ListOfShape& theShapes) } } + 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; diff --git a/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.h b/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.h index 6824c1550..9c4370c5f 100644 --- a/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.h +++ b/src/GeomAlgoAPI/GeomAlgoAPI_WireBuilder.h @@ -33,9 +33,12 @@ 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); + 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. diff --git a/src/SketchPlugin/CMakeLists.txt b/src/SketchPlugin/CMakeLists.txt index 59f65d257..401570245 100644 --- a/src/SketchPlugin/CMakeLists.txt +++ b/src/SketchPlugin/CMakeLists.txt @@ -328,7 +328,8 @@ ADD_UNIT_TESTS( TestMultiRotation05.py TestMultiRotationWithParameter.py TestMultiTranslation.py - TestOffset.py + TestOffset1.py + TestOffset2.py TestPresentation.py TestProjection.py TestProjectionBSpline.py diff --git a/src/SketchPlugin/SketchPlugin_Offset.cpp b/src/SketchPlugin/SketchPlugin_Offset.cpp index 7ea47ee0e..f3f8d2b89 100644 --- a/src/SketchPlugin/SketchPlugin_Offset.cpp +++ b/src/SketchPlugin/SketchPlugin_Offset.cpp @@ -143,10 +143,14 @@ void SketchPlugin_Offset::execute() // 5.b. Find a chain of edges std::list 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); @@ -161,7 +165,7 @@ void SketchPlugin_Offset::execute() } } std::shared_ptr aWireBuilder( - new GeomAlgoAPI_WireBuilder(aTopoChain)); + new GeomAlgoAPI_WireBuilder(aTopoChain, !isClosed)); GeomShapePtr aWireShape = aWireBuilder->shape(); GeomWirePtr aWire (new GeomAPI_Wire (aWireShape)); @@ -283,7 +287,8 @@ bool SketchPlugin_Offset::findWireOneWay (const FeaturePtr& theFirstEdge, } // 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, @@ -647,8 +652,27 @@ void SketchPlugin_Offset::mkBSpline (FeaturePtr& theResult, 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 anOffsetList = anOffsetAttr->list(); + std::set aFeaturesToBeRemoved; + for (std::list::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) @@ -701,9 +725,12 @@ bool SketchPlugin_Offset::findWires() std::list 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::iterator aChainIt = aChain.begin(); for (; aChainIt != aChain.end(); ++aChainIt) { diff --git a/src/SketchPlugin/Test/TestOffset.py b/src/SketchPlugin/Test/TestOffset.py deleted file mode 100644 index b673927cf..000000000 --- a/src/SketchPlugin/Test/TestOffset.py +++ /dev/null @@ -1,214 +0,0 @@ -# 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()) diff --git a/src/SketchPlugin/Test/TestOffset1.py b/src/SketchPlugin/Test/TestOffset1.py new file mode 100644 index 000000000..24b162247 --- /dev/null +++ b/src/SketchPlugin/Test/TestOffset1.py @@ -0,0 +1,222 @@ +# 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()) diff --git a/src/SketchPlugin/Test/TestOffset2.py b/src/SketchPlugin/Test/TestOffset2.py new file mode 100644 index 000000000..e15e35d7c --- /dev/null +++ b/src/SketchPlugin/Test/TestOffset2.py @@ -0,0 +1,61 @@ +# 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())