From 922c5eceb93e68e87e6e4d22583de0d1f25ff483 Mon Sep 17 00:00:00 2001 From: azv Date: Fri, 29 Nov 2019 12:24:15 +0300 Subject: [PATCH] Task 3.3 Build/Edge and Build/Wire on a whole Sketch (issue #3083) --- src/BuildAPI/BuildAPI_Edge.cpp | 16 +- src/BuildAPI/BuildAPI_Edge.h | 12 +- src/BuildAPI/BuildAPI_Wire.cpp | 17 +- src/BuildAPI/BuildAPI_Wire.h | 12 +- src/BuildPlugin/BuildPlugin_Edge.cpp | 53 ++-- src/BuildPlugin/BuildPlugin_Edge.h | 7 + src/BuildPlugin/BuildPlugin_Validators.cpp | 54 ++-- src/BuildPlugin/BuildPlugin_Wire.cpp | 236 ++++++++++++++++-- src/BuildPlugin/BuildPlugin_Wire.h | 7 + src/BuildPlugin/CMakeLists.txt | 10 + src/BuildPlugin/Test/TestEdge.py | 6 - .../Test/TestEdge_WholeSketch_1.py | 44 ++++ .../Test/TestEdge_WholeSketch_2.py | 44 ++++ .../Test/TestEdge_WholeSketch_3.py | 44 ++++ .../Test/TestEdge_WholeSketch_4.py | 44 ++++ src/BuildPlugin/Test/TestWire.py | 11 +- .../Test/TestWire_WholeSketch_1.py | 44 ++++ .../Test/TestWire_WholeSketch_2.py | 44 ++++ .../Test/TestWire_WholeSketch_3.py | 48 ++++ .../Test/TestWire_WholeSketch_4.py | 48 ++++ .../Test/TestWire_WholeSketch_5.py | 48 ++++ .../Test/TestWire_WholeSketch_6.py | 48 ++++ src/BuildPlugin/doc/edgeFeature.rst | 8 +- src/BuildPlugin/doc/images/Edge.png | Bin 6287 -> 7076 bytes src/BuildPlugin/doc/images/Wire.png | Bin 10939 -> 8247 bytes src/BuildPlugin/doc/vertexFeature.rst | 2 +- src/BuildPlugin/doc/wireFeature.rst | 6 +- src/BuildPlugin/edge_widget.xml | 6 +- src/BuildPlugin/wire_widget.xml | 5 +- src/GeomAPI/GeomAPI_Edge.cpp | 13 + src/GeomAPI/GeomAPI_Edge.h | 6 + 31 files changed, 861 insertions(+), 82 deletions(-) create mode 100644 src/BuildPlugin/Test/TestEdge_WholeSketch_1.py create mode 100644 src/BuildPlugin/Test/TestEdge_WholeSketch_2.py create mode 100644 src/BuildPlugin/Test/TestEdge_WholeSketch_3.py create mode 100644 src/BuildPlugin/Test/TestEdge_WholeSketch_4.py create mode 100644 src/BuildPlugin/Test/TestWire_WholeSketch_1.py create mode 100644 src/BuildPlugin/Test/TestWire_WholeSketch_2.py create mode 100644 src/BuildPlugin/Test/TestWire_WholeSketch_3.py create mode 100644 src/BuildPlugin/Test/TestWire_WholeSketch_4.py create mode 100644 src/BuildPlugin/Test/TestWire_WholeSketch_5.py create mode 100644 src/BuildPlugin/Test/TestWire_WholeSketch_6.py diff --git a/src/BuildAPI/BuildAPI_Edge.cpp b/src/BuildAPI/BuildAPI_Edge.cpp index 3371f01ea..e0816f833 100644 --- a/src/BuildAPI/BuildAPI_Edge.cpp +++ b/src/BuildAPI/BuildAPI_Edge.cpp @@ -31,11 +31,13 @@ BuildAPI_Edge::BuildAPI_Edge(const std::shared_ptr& theFeature //================================================================================================== BuildAPI_Edge::BuildAPI_Edge(const std::shared_ptr& theFeature, - const std::list& theBaseObjects) + const std::list& theBaseObjects, + const bool theComputeIntersections) : ModelHighAPI_Interface(theFeature) { if(initialize()) { fillAttribute(BuildPlugin_Edge::CREATION_BY_SEGMENTS(), mycreationMethod); + fillAttribute(theComputeIntersections, mycomputeIntersections); setBase(theBaseObjects); } } @@ -83,17 +85,23 @@ void BuildAPI_Edge::dump(ModelHighAPI_Dumper& theDumper) const theDumper << aBase->selection(BuildPlugin_Edge::FIRST_POINT()) << ", " << aBase->selection(BuildPlugin_Edge::SECOND_POINT()); } - else + else { theDumper << aBase->selectionList(BuildPlugin_Edge::BASE_OBJECTS_ID()); + + AttributeBooleanPtr isIntersect = aBase->boolean(BuildPlugin_Edge::INTERSECT_ID()); + if (isIntersect->isInitialized()) + theDumper << ", " << isIntersect; + } theDumper << ")" << std::endl; } //================================================================================================== EdgePtr addEdge(const std::shared_ptr& thePart, - const std::list& theBaseObjects) + const std::list& theBaseObjects, + const bool theComputeIntersections) { std::shared_ptr aFeature = thePart->addFeature(BuildAPI_Edge::ID()); - return EdgePtr(new BuildAPI_Edge(aFeature, theBaseObjects)); + return EdgePtr(new BuildAPI_Edge(aFeature, theBaseObjects, theComputeIntersections)); } EdgePtr addEdge(const std::shared_ptr& thePart, diff --git a/src/BuildAPI/BuildAPI_Edge.h b/src/BuildAPI/BuildAPI_Edge.h index 11a7fa902..e6a7e46d7 100644 --- a/src/BuildAPI/BuildAPI_Edge.h +++ b/src/BuildAPI/BuildAPI_Edge.h @@ -42,7 +42,8 @@ public: /// Constructor with values. BUILDAPI_EXPORT explicit BuildAPI_Edge(const std::shared_ptr& theFeature, - const std::list& theBaseObjects); + const std::list& theBaseObjects, + const bool theComputeIntersections = false); /// Constructor by points. BUILDAPI_EXPORT @@ -54,7 +55,7 @@ public: BUILDAPI_EXPORT virtual ~BuildAPI_Edge(); - INTERFACE_4(BuildPlugin_Edge::ID(), + INTERFACE_5(BuildPlugin_Edge::ID(), baseObjects, BuildPlugin_Edge::BASE_OBJECTS_ID(), ModelAPI_AttributeSelectionList, /** Base objects */, creationMethod, BuildPlugin_Edge::CREATION_METHOD(), @@ -62,7 +63,9 @@ public: firstPoint, BuildPlugin_Edge::FIRST_POINT(), ModelAPI_AttributeSelection, /** First point */, secondPoint, BuildPlugin_Edge::SECOND_POINT(), - ModelAPI_AttributeSelection, /** Second point */) + ModelAPI_AttributeSelection, /** Second point */, + computeIntersections, BuildPlugin_Edge::INTERSECT_ID(), + ModelAPI_AttributeBoolean, /** Intersect edges */) /// Modify base attribute of the feature. BUILDAPI_EXPORT @@ -80,7 +83,8 @@ typedef std::shared_ptr EdgePtr; /// \brief Create Edge feature. BUILDAPI_EXPORT EdgePtr addEdge(const std::shared_ptr& thePart, - const std::list& theBaseObjects); + const std::list& theBaseObjects, + const bool theComputeIntersection = false); /// \ingroup CPPHighAPI /// \brief Create Edge feature. BUILDAPI_EXPORT diff --git a/src/BuildAPI/BuildAPI_Wire.cpp b/src/BuildAPI/BuildAPI_Wire.cpp index 69c68be45..ffd58d48a 100644 --- a/src/BuildAPI/BuildAPI_Wire.cpp +++ b/src/BuildAPI/BuildAPI_Wire.cpp @@ -31,10 +31,12 @@ BuildAPI_Wire::BuildAPI_Wire(const std::shared_ptr& theFeature //================================================================================================== BuildAPI_Wire::BuildAPI_Wire(const std::shared_ptr& theFeature, - const std::list& theBaseObjects) + const std::list& theBaseObjects, + const bool theComputeIntersections) : ModelHighAPI_Interface(theFeature) { if(initialize()) { + fillAttribute(theComputeIntersections, mycomputeIntersections); setBase(theBaseObjects); } } @@ -60,7 +62,13 @@ void BuildAPI_Wire::dump(ModelHighAPI_Dumper& theDumper) const std::string aPartName = theDumper.name(aBase->document()); theDumper << aBase << " = model.addWire(" << aPartName << ", " - << aBase->selectionList(BuildPlugin_Wire::BASE_OBJECTS_ID()) << ")" << std::endl; + << aBase->selectionList(BuildPlugin_Wire::BASE_OBJECTS_ID()); + + AttributeBooleanPtr isIntersect = aBase->boolean(BuildPlugin_Wire::INTERSECT_ID()); + if (isIntersect->isInitialized()) + theDumper << ", " << isIntersect; + + theDumper << ")" << std::endl; } //================================================================================================== @@ -71,8 +79,9 @@ void BuildAPI_Wire::addContour() //================================================================================================== WirePtr addWire(const std::shared_ptr& thePart, - const std::list& theBaseObjects) + const std::list& theBaseObjects, + const bool theComputeIntersections) { std::shared_ptr aFeature = thePart->addFeature(BuildAPI_Wire::ID()); - return WirePtr(new BuildAPI_Wire(aFeature, theBaseObjects)); + return WirePtr(new BuildAPI_Wire(aFeature, theBaseObjects, theComputeIntersections)); } diff --git a/src/BuildAPI/BuildAPI_Wire.h b/src/BuildAPI/BuildAPI_Wire.h index 4934943d7..86e78837b 100644 --- a/src/BuildAPI/BuildAPI_Wire.h +++ b/src/BuildAPI/BuildAPI_Wire.h @@ -42,15 +42,18 @@ public: /// Constructor with values. BUILDAPI_EXPORT explicit BuildAPI_Wire(const std::shared_ptr& theFeature, - const std::list& theBaseObjects); + const std::list& theBaseObjects, + const bool theComputeIntersections = false); /// Destructor. BUILDAPI_EXPORT virtual ~BuildAPI_Wire(); - INTERFACE_1(BuildPlugin_Wire::ID(), + INTERFACE_2(BuildPlugin_Wire::ID(), baseObjects, BuildPlugin_Wire::BASE_OBJECTS_ID(), - ModelAPI_AttributeSelectionList, /** Base objects */) + ModelAPI_AttributeSelectionList, /** Base objects */, + computeIntersections, BuildPlugin_Wire::INTERSECT_ID(), + ModelAPI_AttributeBoolean, /** Intersect edges */) /// Modify base attribute of the feature. BUILDAPI_EXPORT @@ -72,6 +75,7 @@ typedef std::shared_ptr WirePtr; /// \brief Create Wire feature. BUILDAPI_EXPORT WirePtr addWire(const std::shared_ptr& thePart, - const std::list& theBaseObjects); + const std::list& theBaseObjects, + const bool theComputeIntersections = false); #endif // BuildAPI_Wire_H_ diff --git a/src/BuildPlugin/BuildPlugin_Edge.cpp b/src/BuildPlugin/BuildPlugin_Edge.cpp index 0f85ae3e4..2a07d390c 100644 --- a/src/BuildPlugin/BuildPlugin_Edge.cpp +++ b/src/BuildPlugin/BuildPlugin_Edge.cpp @@ -19,14 +19,18 @@ #include "BuildPlugin_Edge.h" +#include #include #include #include #include #include +#include #include #include +#include +#include #include #include @@ -64,6 +68,9 @@ void BuildPlugin_Edge::initAttributes() data()->addAttribute(FIRST_POINT(), ModelAPI_AttributeSelection::typeId()); data()->addAttribute(SECOND_POINT(), ModelAPI_AttributeSelection::typeId()); + + data()->addAttribute(INTERSECT_ID(), ModelAPI_AttributeBoolean::typeId()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), INTERSECT_ID()); } //================================================================================================= @@ -96,7 +103,6 @@ void BuildPlugin_Edge::edgesBySegments() // Collect base shapes. ListOfShape aListOfShapes; std::string aError; - int aResultIndex = 0; for(int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) { AttributeSelectionPtr aSelection = aSelectionList->value(anIndex); GeomShapePtr aShape; @@ -108,29 +114,48 @@ void BuildPlugin_Edge::edgesBySegments() setError("Error: Empty shape selected."); return; } + aListOfShapes.push_back(aShape); + } - if(aShape->shapeType() != GeomAPI_Shape::EDGE) { - setError("Error: Selected shape has wrong type. Only edges acceptable."); - return; - } + AttributeBooleanPtr isIntersect = boolean(INTERSECT_ID()); - // Copy shape. - GeomMakeShapePtr aCopyAlgo(new GeomAlgoAPI_Copy(aShape)); + GeomShapePtr aResult; + std::shared_ptr aPartitionAlgo; + if (isIntersect->isInitialized() && isIntersect->value()) { + aPartitionAlgo.reset(new GeomAlgoAPI_Partition(aListOfShapes, ListOfShape())); std::string anError; - if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aCopyAlgo, getKind(), anError)) { + if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aPartitionAlgo, getKind(), anError)) { setError(anError); return; } - // Store result. - ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex); + aResult = aPartitionAlgo->shape(); + } + else + aResult = GeomAlgoAPI_CompoundBuilder::compound(aListOfShapes); + + int aResultIndex = 0; + + // Explode on edges + std::set aProcessed; + for (GeomAPI_ShapeExplorer anExp(aResult, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) { + GeomShapePtr anEdge = anExp.current(); + if (aProcessed.find(anEdge) != aProcessed.end()) + continue; // vertex is already processed + aProcessed.insert(anEdge); - ListOfShape aBaseShapes; - aBaseShapes.push_back(aShape); - aResultBody->storeModified(aBaseShapes, aCopyAlgo->shape(), aCopyAlgo); - aResultBody->loadModifiedShapes(aCopyAlgo, aShape, GeomAPI_Shape::VERTEX); + std::shared_ptr aCopyAlgo(new GeomAlgoAPI_Copy(anEdge)); + std::shared_ptr aMakeShapeList(new GeomAlgoAPI_MakeShapeList); + if (aPartitionAlgo) + aMakeShapeList->appendAlgo(aPartitionAlgo); + aMakeShapeList->appendAlgo(aCopyAlgo); + + // Store result. + ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex); + aResultBody->storeModified(aListOfShapes, aCopyAlgo->shape(), aMakeShapeList); + aResultBody->loadModifiedShapes(aMakeShapeList, anEdge, GeomAPI_Shape::VERTEX); setResult(aResultBody, aResultIndex); ++aResultIndex; } diff --git a/src/BuildPlugin/BuildPlugin_Edge.h b/src/BuildPlugin/BuildPlugin_Edge.h index 27f1665ba..af142f55b 100644 --- a/src/BuildPlugin/BuildPlugin_Edge.h +++ b/src/BuildPlugin/BuildPlugin_Edge.h @@ -89,6 +89,13 @@ public: return MY_SECOND_POINT_ID; } + /// Attribute name of "Compute intersections" checkbox. + inline static const std::string& INTERSECT_ID() + { + static const std::string MY_INTERSECT_ID("intersect"); + return MY_INTERSECT_ID; + } + /// Request for initialization of data model of the feature: adding all attributes. BUILDPLUGIN_EXPORT virtual void initAttributes(); diff --git a/src/BuildPlugin/BuildPlugin_Validators.cpp b/src/BuildPlugin/BuildPlugin_Validators.cpp index 761fc6fa5..aef6b7fc8 100644 --- a/src/BuildPlugin/BuildPlugin_Validators.cpp +++ b/src/BuildPlugin/BuildPlugin_Validators.cpp @@ -116,12 +116,6 @@ bool BuildPlugin_ValidatorBaseForWire::isValid(const std::shared_ptrselectionList(theArguments.front()); if(!aSelectionList.get()) { theError = "Empty attribute \"%1\"."; @@ -129,25 +123,53 @@ bool BuildPlugin_ValidatorBaseForWire::isValid(const std::shared_ptrsize(); ++anIndex) { AttributeSelectionPtr aSelection = aSelectionList->value(anIndex); GeomShapePtr aShape = aSelection->value(); - if(!aShape.get()) { - if (aSelection->context().get()) - aShape = aSelection->context()->shape(); - } - if (aShape.get()) + ResultPtr aContext = aSelection->context(); + if (!aShape.get() && aContext.get()) + aShape = aContext->shape(); + + bool isProper = aShape.get() && + (aShape->shapeType() == GeomAPI_Shape::EDGE || aShape->shapeType() == aShapeType); + + if (isProper) aListOfShapes.push_back(aShape); + else { + // is it a sketch? + FeaturePtr aFeature = aSelection->contextFeature(); + if (!aFeature.get()) { + GeomShapePtr aValue = aSelection->value(); + // whole sketch is allowed only + if (aContext.get() && !aValue.get()) { + aFeature = ModelAPI_Feature::feature(aContext); + } + } + + if (!aFeature.get()) { + theError = "Error: Incorrect selection."; + return false; + } + + if (aFeature->getKind() != SketchPlugin_Sketch::ID()) { + theError = "Error: %1 shape is not allowed for selection."; + theError.arg(aFeature->getKind()); + return false; + } + } } - // Create wire. - GeomShapePtr aWire = GeomAlgoAPI_WireBuilder::wire(aListOfShapes); - if(!aWire.get()) { - theError = "Result wire empty. Probably it has disconnected edges or non-manifold."; - return false; + if (aShapeType == GeomAPI_Shape::WIRE) { + // Create wire. + GeomShapePtr aWire = GeomAlgoAPI_WireBuilder::wire(aListOfShapes); + if (!aWire.get() && !aListOfShapes.empty()) { + theError = "Result wire empty. Probably it has disconnected edges or non-manifold."; + return false; + } } return true; diff --git a/src/BuildPlugin/BuildPlugin_Wire.cpp b/src/BuildPlugin/BuildPlugin_Wire.cpp index 4fe602e84..c7caa8581 100644 --- a/src/BuildPlugin/BuildPlugin_Wire.cpp +++ b/src/BuildPlugin/BuildPlugin_Wire.cpp @@ -19,20 +19,36 @@ #include "BuildPlugin_Wire.h" +#include #include #include #include +#include +#include #include #include #include +#include -#include +#include +#include +#include +#include #include +#include + #include +static bool buildSketchWires(FeaturePtr theSketchFeature, GeomShapePtr theSketchShape, + bool isIntersect, + ListOfShape& theWires, GeomMakeShapePtr& theAlgo, + std::string& theError); + +static bool buildWire(const ListOfShape& theEdges, GeomShapePtr& theWire, std::string& theError); + //================================================================================================= BuildPlugin_Wire::BuildPlugin_Wire() { @@ -42,6 +58,9 @@ BuildPlugin_Wire::BuildPlugin_Wire() void BuildPlugin_Wire::initAttributes() { data()->addAttribute(BASE_OBJECTS_ID(), ModelAPI_AttributeSelectionList::typeId()); + + data()->addAttribute(INTERSECT_ID(), ModelAPI_AttributeBoolean::typeId()); + ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), INTERSECT_ID()); } //================================================================================================= @@ -58,13 +77,25 @@ void BuildPlugin_Wire::execute() return; } + AttributeBooleanPtr anIntersectAttr = boolean(INTERSECT_ID()); + bool isIntersect = anIntersectAttr->isInitialized() && anIntersectAttr->value(); + // Collect base shapes. ListOfShape anEdges; + std::list< std::pair > aSketches; for(int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) { AttributeSelectionPtr aSelection = aSelectionList->value(anIndex); GeomShapePtr aShape = aSelection->value(); if(!aShape.get()) { aShape = aSelection->context()->shape(); + + std::shared_ptr aSketchShape = + std::dynamic_pointer_cast(aShape); + if (aSketchShape) { + FeaturePtr aSketchFeature = ModelAPI_Feature::feature(aSelection->context()); + aSketches.push_back(std::pair(aSketchFeature, aSketchShape)); + continue; + } } for(GeomAPI_ShapeExplorer anExp(aShape, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) { GeomShapePtr anEdge = anExp.current(); @@ -72,27 +103,57 @@ void BuildPlugin_Wire::execute() } } - // Create wire. - GeomShapePtr aWire = GeomAlgoAPI_WireBuilder::wire(anEdges); - if(!aWire.get()) { - setError("Error: Result wire is empty. Probably it has disconnected edges or non-manifold."); - return; - } + int aResultIndex = 0; + std::string anError; - // Store result. - ResultBodyPtr aResultBody = document()->createBody(data()); - aResultBody->store(aWire); - for(GeomAPI_ShapeExplorer anExp(aWire, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) { - GeomShapePtr anEdgeInResult = anExp.current(); - for(ListOfShape::const_iterator anIt = anEdges.cbegin(); anIt != anEdges.cend(); ++anIt) { - std::shared_ptr anEdgeInList(new GeomAPI_Edge(*anIt)); - if(anEdgeInList->isEqual(anEdgeInResult)) { - aResultBody->modified(anEdgeInList, anEdgeInResult); - break; + if (!anEdges.empty()) { + // Create wire from the list of edges. + GeomShapePtr aWire; + if (!buildWire(anEdges, aWire, anError)) { + setError(anError); + return; + } + + // Store result. + ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex); + aResultBody->store(aWire); + for (GeomAPI_ShapeExplorer anExp(aWire, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) { + GeomShapePtr anEdgeInResult = anExp.current(); + for (ListOfShape::const_iterator anIt = anEdges.cbegin(); anIt != anEdges.cend(); ++anIt) { + std::shared_ptr anEdgeInList(new GeomAPI_Edge(*anIt)); + if (anEdgeInList->isEqual(anEdgeInResult)) { + aResultBody->modified(anEdgeInList, anEdgeInResult); + break; + } } } + setResult(aResultBody, aResultIndex); + ++aResultIndex; } - setResult(aResultBody); + + // create wires from sketches + for (std::list >::iterator anIt = aSketches.begin(); + anIt != aSketches.end(); ++anIt) { + ListOfShape aWires; + GeomMakeShapePtr aMakeShapeList; + if (!buildSketchWires(anIt->first, anIt->second, isIntersect, + aWires, aMakeShapeList, anError)) { + setError(anError); + return; + } + + for (ListOfShape::iterator aWIt = aWires.begin(); aWIt != aWires.end(); ++aWIt) { + ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex); + ListOfShape aSketches; + aSketches.push_back(anIt->second); + aResultBody->storeModified(aSketches, *aWIt, aMakeShapeList); + aResultBody->loadModifiedShapes(aMakeShapeList, anIt->second, GeomAPI_Shape::EDGE); + setResult(aResultBody, aResultIndex); + ++aResultIndex; + } + } + + removeResults(aResultIndex); } //================================================================================================= @@ -216,3 +277,142 @@ bool BuildPlugin_Wire::addContour() return true; } + + + +// ===================== Auxiliary functions ============================================== + +bool buildWire(const ListOfShape& theEdges, GeomShapePtr& theWire, std::string& theError) +{ + theWire = GeomAlgoAPI_WireBuilder::wire(theEdges); + if (!theWire.get()) { + theError = "Error: Result wire is empty. Probably it has disconnected edges or non-manifold."; + return false; + } + return true; +} + +bool buildSketchWires(FeaturePtr theSketchFeature, GeomShapePtr theSketchShape, bool isIntersect, + ListOfShape& theWires, GeomMakeShapePtr& theAlgo, std::string& theError) +{ + ListOfShape aSketchEdges = + std::dynamic_pointer_cast(theSketchShape)->getEdges(); + + std::shared_ptr anAlgoList(new GeomAlgoAPI_MakeShapeList); + if (isIntersect) { + std::set aProcessedEdges; + // perform sketch builder first + AttributePointPtr anOrigin = std::dynamic_pointer_cast( + theSketchFeature->attribute(SketchPlugin_Sketch::ORIGIN_ID())); + AttributeDirPtr aNormal = std::dynamic_pointer_cast( + theSketchFeature->attribute(SketchPlugin_Sketch::NORM_ID())); + AttributeDirPtr aDirX = std::dynamic_pointer_cast( + theSketchFeature->attribute(SketchPlugin_Sketch::DIRX_ID())); + std::shared_ptr aSketchBuilder(new GeomAlgoAPI_SketchBuilder( + anOrigin->pnt(), aDirX->dir(), aNormal->dir(), theSketchShape)); + + anAlgoList->appendAlgo(aSketchBuilder); + + // collect wires from faces + const ListOfShape& aFaces = aSketchBuilder->faces(); + for (ListOfShape::const_iterator anIt = aFaces.begin(); anIt != aFaces.end(); ++anIt) { + for (GeomAPI_ShapeExplorer aWExp(*anIt, GeomAPI_Shape::WIRE); aWExp.more(); aWExp.next()) { + GeomAPI_ShapeExplorer aEExp(aWExp.current(), GeomAPI_Shape::EDGE); + if (aProcessedEdges.find(aEExp.current()) != aProcessedEdges.end()) + continue; // wire is already processed + // mark edges as processed + for (; aEExp.more(); aEExp.next()) + aProcessedEdges.insert(aEExp.current()); + // store the wire + theWires.push_back(aWExp.current()); + } + } + + // collect unused edges + ListOfShape aCopy; + for (ListOfShape::iterator anIt = aSketchEdges.begin(); anIt != aSketchEdges.end(); ++anIt) { + ListOfShape anImages; + aSketchBuilder->modified(*anIt, anImages); + for (ListOfShape::iterator anEdge = anImages.begin(); anEdge != anImages.end(); ++anEdge) + if (aProcessedEdges.find(*anEdge) == aProcessedEdges.end()) + aCopy.push_back(*anEdge); + } + + if (aCopy.size() > 1) { + // split these edges + std::shared_ptr aGeneralFuse(new GeomAlgoAPI_PaveFiller(aCopy)); + if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed( + aGeneralFuse, BuildPlugin_Wire::ID(), theError)) + return false; + anAlgoList->appendAlgo(aGeneralFuse); + + // collect edges after the split + aSketchEdges.clear(); + for (GeomAPI_ShapeExplorer anExp(aGeneralFuse->shape(), GeomAPI_Shape::EDGE); + anExp.more(); anExp.next()) + aSketchEdges.push_back(anExp.current()); + } + else + aSketchEdges = aCopy; + } + + // connect least edges to wires + typedef std::list ListOfWires; + ListOfWires aNewWires; + typedef std::map MapVertexWire; + MapVertexWire aMapVW; + for (ListOfShape::iterator aEIt = aSketchEdges.begin(); aEIt != aSketchEdges.end(); ++aEIt) { + GeomEdgePtr anEdge = (*aEIt)->edge(); + GeomVertexPtr aStartV, aEndV; + anEdge->vertices(aStartV, aEndV); + MapVertexWire::iterator aFoundStart = aMapVW.find(aStartV); + MapVertexWire::iterator aFoundEnd = aMapVW.find(aEndV); + if (aFoundStart == aMapVW.end()) { + if (aFoundEnd == aMapVW.end()) { + // new wire + aNewWires.push_back(ListOfShape()); + ListOfWires::iterator aNewW = --aNewWires.end(); + aNewW->push_back(anEdge); + aMapVW[aStartV] = aNewW; + aMapVW[aEndV] = aNewW; + continue; + } + } + else { + if (aFoundEnd == aMapVW.end()) { + // swap found vertices for correct further processing + aFoundEnd = aFoundStart; + aStartV = aEndV; + } + else { + // both vertices are found => close the loop + aFoundStart->second->push_back(anEdge); + if (aFoundStart->second != aFoundEnd->second) { + aFoundStart->second->insert(aFoundStart->second->end(), + aFoundEnd->second->begin(), aFoundEnd->second->end()); + aNewWires.erase(aFoundEnd->second); + } + aMapVW.erase(aFoundStart); + aMapVW.erase(aFoundEnd); + continue; + } + } + // add edge to existing wire, substitute the connection point + // by the other boundary point of the edge + aFoundEnd->second->push_back(anEdge); + aMapVW[aStartV] = aFoundEnd->second; + aMapVW.erase(aFoundEnd); + } + + // generate new wires from the sets of edges + for (ListOfWires::iterator anIt = aNewWires.begin(); anIt != aNewWires.end(); ++anIt) { + GeomShapePtr aWire; + if (!buildWire(*anIt, aWire, theError)) + return false; + theWires.push_back(aWire); + } + + theAlgo = anAlgoList; + return true; +} diff --git a/src/BuildPlugin/BuildPlugin_Wire.h b/src/BuildPlugin/BuildPlugin_Wire.h index c0b873174..5677bebb3 100644 --- a/src/BuildPlugin/BuildPlugin_Wire.h +++ b/src/BuildPlugin/BuildPlugin_Wire.h @@ -54,6 +54,13 @@ public: return MY_ADD_CONTOUR_ACTION_ID; } + /// Attribute name of "Compute intersections" checkbox. + inline static const std::string& INTERSECT_ID() + { + static const std::string MY_INTERSECT_ID("intersect"); + return MY_INTERSECT_ID; + } + /// \return the kind of a feature. BUILDPLUGIN_EXPORT virtual const std::string& getKind() { diff --git a/src/BuildPlugin/CMakeLists.txt b/src/BuildPlugin/CMakeLists.txt index e8a4e470f..2954b05c7 100644 --- a/src/BuildPlugin/CMakeLists.txt +++ b/src/BuildPlugin/CMakeLists.txt @@ -115,8 +115,18 @@ ADD_UNIT_TESTS(TestVertex.py TestEdge.py TestEdge_ByPoints.py TestEdge_ErrorMsg.py + TestEdge_WholeSketch_1.py + TestEdge_WholeSketch_2.py + TestEdge_WholeSketch_3.py + TestEdge_WholeSketch_4.py TestWire.py TestWire_ErrorMsg.py + TestWire_WholeSketch_1.py + TestWire_WholeSketch_2.py + TestWire_WholeSketch_3.py + TestWire_WholeSketch_4.py + TestWire_WholeSketch_5.py + TestWire_WholeSketch_6.py TestPolyline.py TestInterpolation.py TestFace.py diff --git a/src/BuildPlugin/Test/TestEdge.py b/src/BuildPlugin/Test/TestEdge.py index 83d163b5e..2e194941f 100644 --- a/src/BuildPlugin/Test/TestEdge.py +++ b/src/BuildPlugin/Test/TestEdge.py @@ -114,12 +114,6 @@ assert (len(anEdgeFeature2.results()) == 12) aSession.startOperation() anEdgeFeature3 = aPart.addFeature("Edge") aBaseObjectsList = anEdgeFeature3.selectionList("base_objects") -aBaseObjectsList.append(aSketchResult, None) -aSession.finishOperation() -assert (len(anEdgeFeature3.results()) == 0) - -aSession.startOperation() -aBaseObjectsList.clear() aShapeExplorer = GeomAPI_ShapeExplorer(aBoxShape, GeomAPI_Shape.VERTEX) aShape = aShapeExplorer.current() aBaseObjectsList.append(aBoxResult, aShape) diff --git a/src/BuildPlugin/Test/TestEdge_WholeSketch_1.py b/src/BuildPlugin/Test/TestEdge_WholeSketch_1.py new file mode 100644 index 000000000..aa7a9aa70 --- /dev/null +++ b/src/BuildPlugin/Test/TestEdge_WholeSketch_1.py @@ -0,0 +1,44 @@ +# Copyright (C) 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 +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(0, -20, 45, -15) +SketchLine_2 = Sketch_1.addLine(45, -15, 10, 15) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +SketchLine_3 = Sketch_1.addLine(10, 15, 25, -40) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +model.do() +Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchArc_1 = Sketch_2.addArc(-5, 10, -5, -10, 15, 10, False) +model.do() +Edge_1 = model.addEdge(Part_1_doc, [model.selection("COMPOUND", "all-in-Sketch_1"), model.selection("COMPOUND", "Sketch_2")], False) +model.end() + +model.testNbResults(Edge_1, 4) +model.testNbSubShapes(Edge_1, GeomAPI_Shape.EDGE, [1, 1, 1, 1]) +model.testNbSubShapes(Edge_1, GeomAPI_Shape.VERTEX, [2, 2, 2, 2]) + +assert(model.checkPythonDump()) diff --git a/src/BuildPlugin/Test/TestEdge_WholeSketch_2.py b/src/BuildPlugin/Test/TestEdge_WholeSketch_2.py new file mode 100644 index 000000000..855850244 --- /dev/null +++ b/src/BuildPlugin/Test/TestEdge_WholeSketch_2.py @@ -0,0 +1,44 @@ +# Copyright (C) 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 +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(0, -20, 45, -15) +SketchLine_2 = Sketch_1.addLine(45, -15, 10, 15) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +SketchLine_3 = Sketch_1.addLine(10, 15, 25, -40) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +model.do() +Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchArc_1 = Sketch_2.addArc(-5, 10, -5, -10, 15, 10, False) +model.do() +Edge_1 = model.addEdge(Part_1_doc, [model.selection("COMPOUND", "all-in-Sketch_1"), model.selection("COMPOUND", "Sketch_2")], True) +model.end() + +model.testNbResults(Edge_1, 8) +model.testNbSubShapes(Edge_1, GeomAPI_Shape.EDGE, [1, 1, 1, 1, 1, 1, 1, 1]) +model.testNbSubShapes(Edge_1, GeomAPI_Shape.VERTEX, [2, 2, 2, 2, 2, 2, 2, 2]) + +assert(model.checkPythonDump()) diff --git a/src/BuildPlugin/Test/TestEdge_WholeSketch_3.py b/src/BuildPlugin/Test/TestEdge_WholeSketch_3.py new file mode 100644 index 000000000..c725d124d --- /dev/null +++ b/src/BuildPlugin/Test/TestEdge_WholeSketch_3.py @@ -0,0 +1,44 @@ +# Copyright (C) 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 +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(0, -20, 45, -15) +SketchLine_2 = Sketch_1.addLine(45, -15, 10, 15) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +SketchLine_3 = Sketch_1.addLine(10, 15, 25, -40) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +model.do() +Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchArc_1 = Sketch_2.addArc(-5, 10, -5, -10, 15, 10, False) +model.do() +Edge_1 = model.addEdge(Part_1_doc, [model.selection("COMPOUND", "Sketch_1"), model.selection("EDGE", "Sketch_2/SketchArc_1_2")], False) +model.end() + +model.testNbResults(Edge_1, 4) +model.testNbSubShapes(Edge_1, GeomAPI_Shape.EDGE, [1, 1, 1, 1]) +model.testNbSubShapes(Edge_1, GeomAPI_Shape.VERTEX, [2, 2, 2, 2]) + +assert(model.checkPythonDump()) diff --git a/src/BuildPlugin/Test/TestEdge_WholeSketch_4.py b/src/BuildPlugin/Test/TestEdge_WholeSketch_4.py new file mode 100644 index 000000000..8ddd0ab0e --- /dev/null +++ b/src/BuildPlugin/Test/TestEdge_WholeSketch_4.py @@ -0,0 +1,44 @@ +# Copyright (C) 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 +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(0, -20, 45, -15) +SketchLine_2 = Sketch_1.addLine(45, -15, 10, 15) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +SketchLine_3 = Sketch_1.addLine(10, 15, 25, -40) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +model.do() +Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchArc_1 = Sketch_2.addArc(-5, 10, -5, -10, 15, 10, False) +model.do() +Edge_1 = model.addEdge(Part_1_doc, [model.selection("COMPOUND", "Sketch_1"), model.selection("EDGE", "Sketch_2/SketchArc_1_2")], True) +model.end() + +model.testNbResults(Edge_1, 8) +model.testNbSubShapes(Edge_1, GeomAPI_Shape.EDGE, [1, 1, 1, 1, 1, 1, 1, 1]) +model.testNbSubShapes(Edge_1, GeomAPI_Shape.VERTEX, [2, 2, 2, 2, 2, 2, 2, 2]) + +assert(model.checkPythonDump()) diff --git a/src/BuildPlugin/Test/TestWire.py b/src/BuildPlugin/Test/TestWire.py index 0bbbbce8f..fc4734adc 100644 --- a/src/BuildPlugin/Test/TestWire.py +++ b/src/BuildPlugin/Test/TestWire.py @@ -145,7 +145,7 @@ aSession.finishOperation() assert (len(aWireFeature2.results()) == 1) # ============================================================================= -# Test 4. Check Wire feature failed on incorrect input +# Test 4. Check Wire feature on the whole sketch # ============================================================================= aSession.startOperation() @@ -153,10 +153,15 @@ aWireFeature3 = aPart.addFeature("Wire") aBaseObjectsList = aWireFeature3.selectionList("base_objects") aBaseObjectsList.append(aSketchResult, None) aSession.finishOperation() -assert (len(aWireFeature3.results()) == 0) +assert (len(aWireFeature3.results()) == 1) + +# ============================================================================= +# Test 5. Check Wire feature failed on incorrect input +# ============================================================================= aSession.startOperation() -aBaseObjectsList.clear() +aWireFeature3 = aPart.addFeature("Wire") +aBaseObjectsList = aWireFeature3.selectionList("base_objects") aShapeExplorer = GeomAPI_ShapeExplorer(aBoxShape, GeomAPI_Shape.VERTEX) aShape = aShapeExplorer.current() aBaseObjectsList.append(aBoxResult, aShape) diff --git a/src/BuildPlugin/Test/TestWire_WholeSketch_1.py b/src/BuildPlugin/Test/TestWire_WholeSketch_1.py new file mode 100644 index 000000000..e64e66284 --- /dev/null +++ b/src/BuildPlugin/Test/TestWire_WholeSketch_1.py @@ -0,0 +1,44 @@ +# Copyright (C) 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 +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(0, -20, 45, -15) +SketchLine_2 = Sketch_1.addLine(45, -15, 10, 15) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +SketchLine_3 = Sketch_1.addLine(10, 15, 25, -40) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +model.do() +Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchArc_1 = Sketch_2.addArc(-5, 10, -5, -10, 15, 10, False) +model.do() +Wire_1 = model.addWire(Part_1_doc, [model.selection("COMPOUND", "all-in-Sketch_1"), model.selection("COMPOUND", "Sketch_2")], False) +model.end() + +model.testNbResults(Wire_1, 2) +model.testNbSubShapes(Wire_1, GeomAPI_Shape.EDGE, [3, 1]) +model.testNbSubShapes(Wire_1, GeomAPI_Shape.VERTEX, [6, 2]) + +assert(model.checkPythonDump()) diff --git a/src/BuildPlugin/Test/TestWire_WholeSketch_2.py b/src/BuildPlugin/Test/TestWire_WholeSketch_2.py new file mode 100644 index 000000000..e5db2e180 --- /dev/null +++ b/src/BuildPlugin/Test/TestWire_WholeSketch_2.py @@ -0,0 +1,44 @@ +# Copyright (C) 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 +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(0, -20, 45, -15) +SketchLine_2 = Sketch_1.addLine(45, -15, 10, 15) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) +SketchLine_3 = Sketch_1.addLine(10, 15, 25, -40) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +model.do() +Sketch_2 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchArc_1 = Sketch_2.addArc(-5, 10, -5, -10, 15, 10, False) +model.do() +Wire_1 = model.addWire(Part_1_doc, [model.selection("COMPOUND", "all-in-Sketch_1"), model.selection("COMPOUND", "Sketch_2")], True) +model.end() + +model.testNbResults(Wire_1, 3) +model.testNbSubShapes(Wire_1, GeomAPI_Shape.EDGE, [3, 2, 1]) +model.testNbSubShapes(Wire_1, GeomAPI_Shape.VERTEX, [6, 4, 2]) + +assert(model.checkPythonDump()) diff --git a/src/BuildPlugin/Test/TestWire_WholeSketch_3.py b/src/BuildPlugin/Test/TestWire_WholeSketch_3.py new file mode 100644 index 000000000..bbc4dac3f --- /dev/null +++ b/src/BuildPlugin/Test/TestWire_WholeSketch_3.py @@ -0,0 +1,48 @@ +# Copyright (C) 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 +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(-20.12530657814797, -34.87380706737215, -10.13449613893172, -4.005160196312613) +SketchLine_2 = Sketch_1.addLine(-15.5320876476665, -20.68212021382587, -37.87121169090271, -18.01751586506584) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_2.startPoint(), SketchLine_1.result()) +SketchLine_3 = Sketch_1.addLine(-37.87121169090271, -18.01751586506584, -28.23126689685475, -46.70782775211335) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +SketchLine_4 = Sketch_1.addLine(-10.13449613893172, -4.005160196312613, -17.3289483797767, 22.37844327189705) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_4.startPoint()) +SketchLine_5 = Sketch_1.addLine(-17.3289483797767, 22.37844327189705, -36.60883796787262, 2.754269941156556) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint()) +SketchLine_6 = Sketch_1.addLine(-36.60883796787262, 2.754269941156556, -10.13449613893172, -4.005160196312613) +SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_6.endPoint()) +model.do() +Wire_1 = model.addWire(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")]) +model.end() + +model.testNbResults(Wire_1, 2) +model.testNbSubShapes(Wire_1, GeomAPI_Shape.EDGE, [4, 2]) +model.testNbSubShapes(Wire_1, GeomAPI_Shape.VERTEX, [8, 4]) + +assert(model.checkPythonDump()) diff --git a/src/BuildPlugin/Test/TestWire_WholeSketch_4.py b/src/BuildPlugin/Test/TestWire_WholeSketch_4.py new file mode 100644 index 000000000..fee5553ff --- /dev/null +++ b/src/BuildPlugin/Test/TestWire_WholeSketch_4.py @@ -0,0 +1,48 @@ +# Copyright (C) 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 +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(-20.12530657814797, -34.87380706737215, -10.13449613893172, -4.005160196312613) +SketchLine_2 = Sketch_1.addLine(-15.5320876476665, -20.68212021382587, -37.87121169090271, -18.01751586506584) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_2.startPoint(), SketchLine_1.result()) +SketchLine_3 = Sketch_1.addLine(-37.87121169090271, -18.01751586506584, -28.23126689685475, -46.70782775211335) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +SketchLine_4 = Sketch_1.addLine(-10.13449613893172, -4.005160196312613, -17.3289483797767, 22.37844327189705) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_4.startPoint()) +SketchLine_5 = Sketch_1.addLine(-17.3289483797767, 22.37844327189705, -36.60883796787262, 2.754269941156556) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint()) +SketchLine_6 = Sketch_1.addLine(-36.60883796787262, 2.754269941156556, -10.13449613893172, -4.005160196312613) +SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_6.endPoint()) +model.do() +Wire_1 = model.addWire(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], True) +model.end() + +model.testNbResults(Wire_1, 3) +model.testNbSubShapes(Wire_1, GeomAPI_Shape.EDGE, [3, 2, 2]) +model.testNbSubShapes(Wire_1, GeomAPI_Shape.VERTEX, [6, 4, 4]) + +assert(model.checkPythonDump()) diff --git a/src/BuildPlugin/Test/TestWire_WholeSketch_5.py b/src/BuildPlugin/Test/TestWire_WholeSketch_5.py new file mode 100644 index 000000000..0a0777916 --- /dev/null +++ b/src/BuildPlugin/Test/TestWire_WholeSketch_5.py @@ -0,0 +1,48 @@ +# Copyright (C) 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 +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(-20.13297844091623, -34.87132400696332, -10.14115693918956, -4.003004374441855) +SketchLine_2 = Sketch_1.addLine(-15.36017779982175, -20.12643132586926, -37.81263639928755, -17.52358908612689) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_2.startPoint(), SketchLine_1.result()) +SketchLine_3 = Sketch_1.addLine(-37.81263639928755, -17.52358908612689, -28.23126689685475, -46.70782775211335) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +SketchLine_4 = Sketch_1.addLine(-10.14115693918956, -4.003004374441855, -17.3289483797767, 22.37844327189705) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_4.startPoint()) +SketchLine_5 = Sketch_1.addLine(-17.3289483797767, 22.37844327189705, -39.00238825664131, 1.575954908729718) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint()) +SketchLine_6 = Sketch_1.addLine(-39.00238825664131, 1.575954908729718, -25.86766799285223, -18.90833134866903) +SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_2.result()) +model.do() +Wire_1 = model.addWire(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")]) +model.end() + +model.testNbResults(Wire_1, 2) +model.testNbSubShapes(Wire_1, GeomAPI_Shape.EDGE, [4, 2]) +model.testNbSubShapes(Wire_1, GeomAPI_Shape.VERTEX, [8, 4]) + +assert(model.checkPythonDump()) diff --git a/src/BuildPlugin/Test/TestWire_WholeSketch_6.py b/src/BuildPlugin/Test/TestWire_WholeSketch_6.py new file mode 100644 index 000000000..94f7c7a3e --- /dev/null +++ b/src/BuildPlugin/Test/TestWire_WholeSketch_6.py @@ -0,0 +1,48 @@ +# Copyright (C) 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 +# + +from salome.shaper import model +from GeomAPI import * + +model.begin() +partSet = model.moduleDocument() +Part_1 = model.addPart(partSet) +Part_1_doc = Part_1.document() +Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) +SketchLine_1 = Sketch_1.addLine(-20.13297844091623, -34.87132400696332, -10.14115693918956, -4.003004374441855) +SketchLine_2 = Sketch_1.addLine(-15.36017779982175, -20.12643132586926, -37.81263639928755, -17.52358908612689) +SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_2.startPoint(), SketchLine_1.result()) +SketchLine_3 = Sketch_1.addLine(-37.81263639928755, -17.52358908612689, -28.23126689685475, -46.70782775211335) +SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) +SketchLine_4 = Sketch_1.addLine(-10.14115693918956, -4.003004374441855, -17.3289483797767, 22.37844327189705) +SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_4.startPoint()) +SketchLine_5 = Sketch_1.addLine(-17.3289483797767, 22.37844327189705, -39.00238825664131, 1.575954908729718) +SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_5.startPoint()) +SketchLine_6 = Sketch_1.addLine(-39.00238825664131, 1.575954908729718, -25.86766799285223, -18.90833134866903) +SketchConstraintCoincidence_5 = Sketch_1.setCoincident(SketchLine_5.endPoint(), SketchLine_6.startPoint()) +SketchConstraintCoincidence_6 = Sketch_1.setCoincident(SketchLine_6.endPoint(), SketchLine_2.result()) +model.do() +Wire_1 = model.addWire(Part_1_doc, [model.selection("COMPOUND", "Sketch_1")], True) +model.end() + +model.testNbResults(Wire_1, 3) +model.testNbSubShapes(Wire_1, GeomAPI_Shape.EDGE, [5, 1, 2]) +model.testNbSubShapes(Wire_1, GeomAPI_Shape.VERTEX, [10, 2, 4]) + +assert(model.checkPythonDump()) diff --git a/src/BuildPlugin/doc/edgeFeature.rst b/src/BuildPlugin/doc/edgeFeature.rst index a94a22020..2dbc68c5e 100644 --- a/src/BuildPlugin/doc/edgeFeature.rst +++ b/src/BuildPlugin/doc/edgeFeature.rst @@ -14,7 +14,7 @@ The options to create edges: .. image:: images/edge_by_segments_32x32.png :align: left -**By edges** creates edges using already existing edges in other shapes. +**By edges** creates edges using already existing edges in other shapes or full sketches. .. image:: images/edge_by_points_32x32.png :align: left @@ -30,7 +30,8 @@ By edges .. centered:: Create by edges -Select one or several edges in the viewer. +Select one or several edges in the viewer. Also, the full sketch can be selected. In this case, all edges of the sketch will be processed. +Checkbox **Compute intersections** forces to split sketch edges in the points of intersection. **Apply** button creates edges. @@ -38,10 +39,11 @@ Select one or several edges in the viewer. **TUI Command**: -.. py:function:: model.addEdge(Part_doc, Shapes) +.. py:function:: model.addEdge(Part_doc, Shapes, Intersect) :param part: The current part object. :param list: A list of shapes. + :param bool: Split edges by intersection points. False by default. :return: Result object. Result diff --git a/src/BuildPlugin/doc/images/Edge.png b/src/BuildPlugin/doc/images/Edge.png index 4eaf230b39b01c4c6c147321e7f946c1619d2446..9ca142f3a9dfb2a66b420f462443469aae9fdee8 100644 GIT binary patch delta 6774 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 delta 5985 zcmYj#cTiK$`}GYi0TKuuqy?lZARxVlrhp29s0c_^L8VHsH&O!#h*YJ7qKMLaQAp?_ zh?Eb~giyqw5Rn=>zxe&*op=Ff5H+6wH&^`Z7zlmr~czHCLrsu{V&x(QTLBR%jJy zaZCo4U=WTCMogac9#3hOrvaKb*PiTNexYKLwwuNOV>1|UADkf}vcLQzP-j1XYhc}q z7tX*4iY6Gp(hl9N=E90913-ZnvE7}q2zKMn9Y(I4r&3Q804y(H$XVk{eQCXT@xspD z!NP*An+yHr!97>6O9-NF5kI#;WoOsy&S#o}2N!EAScm=kdmrrj6=B|$mU=f!9qSHp zMU#ntgbb#Tj($1;G9YYrza4ouYm|6G%m>p?xVX5!=xo>?oVFt%Yh_WX>XVC=kE#&} zfl5~{J;=NE*YR-`Z*Azm_r2-;{dmVZdD&!uTshdw0mN||GU<+F1VkMEH3m&h zpB(a@oC*>8-4#Uv5Jv-!Z#hHyX>On9b_`Ih~r`?6+2 zO>wzEhNPL^8_PoCmsy{6a?}R;(iz*d(mOZ>m*csN>&(i%tla-MRG-E`2p} zUW|*sS5q}MvYJVAsbBuo`7B+EGeH{}U>@%vvRi`GJ%wMzs!ypKV^g{+wR@!I>iF|C z0=hrCl0<*SNe68xQ+EBT`hnPVIe8&`R`lboR_HXXgA>sq(YvC(L)RCu4a`EdddOTl_2W#S ztkifOYrx5~h(N?z6oNmo`o^hx@3ROb0y)`GmAkIRh7msvgBbkbWdPLtmz55BShmBS zRo*JLhh>A1`V97}bn-)Pnz*v$A~z=?dmU>AgxE8rTGa?j!*mG;Otw@B`1|`$)*i1< z?)CS50z)K9Qo?~Wp*=pou~TN;pCdRRUvLVnX>JP=gA;;qH{JEDzVg;zwgm_s+J~v( z>xuT--!9aGP%>u5E|yUEzIgKQrn8Ke_4MRE=x|jivfmdMgY49W$e+v!yL8T;Mf#Iz zB77)$#)8B6jmFt!Vgc!kw#*itva<2ox0rRE80D_zt>!RtXj79a@9IX!#AEqVb46gD z&~&)5+Hr%e;^ETxmN;uO7v*c-tPI^;J|4->{nM$V!?F~T4GfwKMLM)A@f@>+ z$e&Kqj4e6q(w(14T%HQdL=EK=i5npQ}Y9u2lJ zAmSn>i-D!%>;;{alW@V*gY~Qo!to-=yqsWP-BpUU!G7qtq2tys}!t!rz}5n z*Oimz_Dp@pwVE0s-Td2->f?d%prCC7`J6A-ySx0>+cSMW>(wIPHKo?Rw0SACABC|3 zXtd)>%9uR3y6#g^$dW?=F?-;NAR7W9CU))$tJ9-i8N4>-ALVJOln<+hKU$TE14qVTYWAD?u#h>#-GHiTjhS9>!v7imwh6Ek?Xp7l`Ij z)s3%+Q=R`Nl*zbR8}%kTqR+kb<|Dk8*I1#>fIGh5W*4vI4Z8|@gIIYKq`;yq_4Mui zpsM+=D-(543R8kqR7i=l-(sfRN59sARg@;fB+-xE+kM&$2j;hOoJ+FoSP z#`uH+*k44j0_up?G2`H&LdNyS@^gF+lsCH^TA{buUGOfgD9sHi1vVA5g$5~@< zSzT^oys&S4z*AEf5ZhTDD`&^HgwQ{brsV+6Br&Auf+~Dzl4g-3Zj1S7w#R?I@)REo z6sz40f}(8gkH1l)QBBtVm-T5x3O2 zJm4nGVMd$Ox@=-3Eq>|9n5?PF4Q2CTAxxM@%oKL~ zTSe+}TGx1fL3rgt$KqUeHv_-VovdwUWUD+-qKG;Jdp5h!rNR!D8nQ+BPnp_HqJC6= zboxoSywWe|-ifK8gsgRBj%HwY)UPhEg@-K;XiwgcA`j1pf``z#qrWa%VI-K}dN!u! zt1QNpeofadMm7}RSsw=``O#wnZAL1PhArsW2;^|6D(`^5l&qnpsq4dn(%00bRz7(+EQBxwns)<^BuOTpSH>$x ze}+g^8o>#9I9|YX3cRcOr-Vh#?k3-M9&vX6$Xs@xBN?YNFk~)3BX$4Of41vp#tQMR z*Y&`Y%pnr7+D)j{KcOqUiPecD$$()Wa`@Wj{mc1!%D$~8?(|FU+rJWDNs=s(^j|rX z{H{ZJzI)o%nz{sVYd`+z3~(o{#4)CU-Hp83s@l4>6&4|?3q_NX^EfN!_~CH}K{~pr zI1-Q!D_J_y!r?xpaAD533!H)1zwOn1Cdn;pbnC>{`d}OWdV4*}K2Kxe@VtM}=8kHx zj;;a_77mbfHw(9i=XW-BFAR3RPs05Tizq_VVN~N7stlU=mj00Tj|`t?(|YT0@|J@? zXy>Wjh$=u!UCG$ci5oCwMX+07=?T@Y-(vntl%{G=)AR?#NuF1XTK6mZ+(5R&!H@-&& zpHXRP)zJ~!Xe@l%wR7!ecYIO5=br)7sFhwQq2!^l=eI(Nv(KiInAb&uqddXeTe+78 zoj3s|zJ)ig3itMbQl0S}MtfcL_uRocd%Wzvt<_ez2vem90YVZX#ES|DG@VHt#q=7{ z)1%A<{K2+QU1f~H0j!%)t(ILOvBY1z8K~-p0G82wPzXSI38hfrOR}dw4-J2?`P_{K zDy_f?B~CYPvGsYhhwLN%3%<`|l?LBc7t4*dL^V$am2)yfQc*8GFd`x3f^Pzqu{Dp7h|;0DKat94E+TZI5Fw-&hIec&2xnZk18(285&@riO1dPo31)0J|kLHMyJ!=NO6fu$Z3VX#2L z^47sXi3rBDL#<#O&y0d(zgc$H6}l~;B7S9fDPbd(m`bBsCi5i7skX!4q-{S zS3xrv;0rP26oa4yu7F8EQFOEo@Mz&az2oYUzr=uf@m&}-FGQXPtjzL=qg!t%5Xbs} zlPeNWHR{l9d`TG?zxQ<6P(#&{?WT<5bE|K|>zS|?cS7D=XEtma*0hCV$|UBCEatDa z5$IjEkkv+DO1k}37WPz%OZefagX!C$3nO~8*~PNjwRzJ6O!F^H=Kb0T`H_KBn}#9} z41Zdc(hc{|Fcl2Z*(uu8{(RG7US|}q-`)A~ezO}VcC6kmt;{BmR$djShfPT#nV@5lpK;px4U~vgtq6&hy#!$~5vK@9$#fFvnWG(Uf|;2!I?qje^OVqO$y27@U;=*M^Q zs(1YkfRCnaEcz=2l%4hv!DutDSl6S*G;l*$S21mi;;d@ z2Xx0>Z%_K+LtkK6W&r%!zUJH~IZ|ZJEn1gFrfgV)SM^O9gWa`1Up(6Xd8^nl5;Mu* z7T)fq%(OXB6Y;23HB`=z&&DlL{GO9Uh&t3bMDDdrK*-#%*V}Eo)!om|ZsbOM^)I8~ z-us%Vy61skt4MTzTJv}rtL63NbNGt&agh0C47*}r`Z}dzE*>GR{x^U~-+NYi#NopE z+?ja&4>ag4uU+W~_G1;dh5$`-e~5(A`A}ILI_dsvds1upZr$%Z{^yFO9dGenDys!? zLK>+M*#vn73rGD9i8x}6YD3s$L)!84UK_z*Ii8e|Pw4#HfZK6SOGG75E~p2?g75-0 z)wYM1jdQW-&WE*uG)(V9Me3?~r8$TVewFZoW_gaM$Apok*?4KUaQruetY%2cwCRpB z3XfuI2@v;o`e_|Uyk9vgV$|G_R_4(dTCq>CT=mFSV;;mH-vL@6`(cd-`=*D>((iNW z1~c^+cRm{?H^R930F+=$4>}fnBZ<-;u6Rw1-Q2lh3+7X2Y5G~oCdlxwK^MN6{b`&! zu*tINdOzjCxxz$PhZKad%*$zM=uFPJ54`?X94LLf_Fs|pv;W*YBT=ySDZnLEFuINfTl5tSYcPE6}4my1`(FeuW58GlMMkzEB8 zuJ--6v-}A*|0DCzMD_#OGq<$X{i1|~iXbIGy+142!p2+@V$hH`zq#{a?q`Vz+O$Vz z)q^ATiFWLTa)9&kh8+fMV^C&suvl6vxhXe9KWre`=#&o6P_<&6jXrl3vG;>7cUt1b zj0tPrr^}-?`l{oGr483sth1ZodA&n~3i4IsM9QWe=G%EdR#EGiJFh6Occ0(&kEy-+ zpPc0Ht0Vh68nD!nwM4YLq&V47#0rAkw}D(Yz6EHlztYV({wNH#)FSa$oI>rs;vf|O zR*HJEh%yONeG3MFS_f&dn=wc3JxpcpndzoC(DIUG>SRLMansezcsg*mc=M;*$#=Wh z$^YnuVqR>;&o1ioM1)MaU*1eUc35jY@dZTi!!gTgwr_KWtzsJ+{~A3Vdbd69+kq4( z_tG2a`@`mCKr5bBZ!NykhfrDl;89ECnZN^E+r1*^bpH*cY=T5Pv-W6+1eoSgb}Amu<__Hr97KDU&edj-C7<_wFGC>;-G z9yo*t&X^k$JTUAt2>(7k7tQumlMl;$H|S)u*%3=2QyoPJlD?Iu0DT)cqrx?-_Ultd zjC(9U)|Hl>>gZu1|MPyFf%-`1$RPajLKDik&YxB5A@gkq8)}Hc>kw}}>7Tr(lBe?@dG5o3Gz$bj^^)@cHx@buAO}OO zc=*|+25HgO7YsQ=qTSj~YiUr<^l1=o1@Nvzr|>`2SkZbKWOeyDeZ#^#kiB}*=Sbl7 zy({c7&H(nCE}=yNWzqa%w228V4k&YAq{QCEykR}Gf>4v9zA6y*9#Epe{(P!m^qjMz zcH60n7G-K&f5VskxfEx^jVp%nnTJ&+Jj)Ze;WW6YA1^6E?<+&f_#Y4+wOSI=aC|mahP+wGiTX$l-VnQYyk$QJa1$sKl;N zF}U#121kpe76?_bJ82G`_n-YFvTok)bH!X23%KVXd>J+nogXefd<#MR2`HVjeiYt- zPESw59a*ZZm##G%cHCfeqeH%#mqI5(3Tl}lNv2B4hin8U4E7@oSWuHhy^g+AAJOPA u0C5Zll3v}-l@i!^tdnNXEy1fl00NAQTekEJ!wpo^95695Gpy8ijQl_J=PSbi diff --git a/src/BuildPlugin/doc/images/Wire.png b/src/BuildPlugin/doc/images/Wire.png index 13ada63fbd5ec2962753526e686494277c0c3655..3248c8c0360d537ed47d344e7b5cd387c6edd79b 100644 GIT binary patch literal 8247 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 literal 10939 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 - + + + - + + - + diff --git a/src/GeomAPI/GeomAPI_Edge.cpp b/src/GeomAPI/GeomAPI_Edge.cpp index 830fbb1b1..c4504272b 100644 --- a/src/GeomAPI/GeomAPI_Edge.cpp +++ b/src/GeomAPI/GeomAPI_Edge.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -69,6 +70,18 @@ GeomAPI_Edge::GeomAPI_Edge(const std::shared_ptr& theShape) } } +void GeomAPI_Edge::vertices(std::shared_ptr& theStartVertex, + std::shared_ptr& theEndVertex) const +{ + const TopoDS_Edge& anEdge = impl(); + TopoDS_Vertex aStart, aEnd; + TopExp::Vertices(anEdge, aStart, aEnd); + theStartVertex.reset(new GeomAPI_Vertex); + theStartVertex->setImpl(new TopoDS_Vertex(aStart)); + theEndVertex.reset(new GeomAPI_Vertex); + theEndVertex->setImpl(new TopoDS_Vertex(aEnd)); +} + static Handle(Geom_Curve) baseCurve(const TopoDS_Edge& theEdge) { double aFirst, aLast; diff --git a/src/GeomAPI/GeomAPI_Edge.h b/src/GeomAPI/GeomAPI_Edge.h index 9d3a4f47b..fdf82ed6f 100644 --- a/src/GeomAPI/GeomAPI_Edge.h +++ b/src/GeomAPI/GeomAPI_Edge.h @@ -27,6 +27,7 @@ class GeomAPI_Pnt; class GeomAPI_Circ; class GeomAPI_Lin; class GeomAPI_Ellipse; +class GeomAPI_Vertex; /**\class GeomAPI_Edge * \ingroup DataModel @@ -44,6 +45,11 @@ public: GEOMAPI_EXPORT GeomAPI_Edge(const std::shared_ptr& theShape); + /// Return vertices of the edge; + GEOMAPI_EXPORT + void vertices(std::shared_ptr& theStartVertex, + std::shared_ptr& theEndVertex) const; + /// Returns \c true if edges have same underlying curve GEOMAPI_EXPORT virtual bool isSameGeometry(const std::shared_ptr theShape) const; -- 2.30.2