1 // Copyright (C) 2014-2023 CEA, EDF
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include "BuildPlugin_Wire.h"
22 #include <ModelAPI_AttributeBoolean.h>
23 #include <ModelAPI_AttributeSelectionList.h>
24 #include <ModelAPI_ResultBody.h>
25 #include <ModelAPI_ResultConstruction.h>
26 #include <ModelAPI_Session.h>
27 #include <ModelAPI_Validator.h>
29 #include <Events_InfoMessage.h>
31 #include <GeomAPI_PlanarEdges.h>
32 #include <GeomAPI_ShapeExplorer.h>
33 #include <GeomAPI_Vertex.h>
35 #include <GeomAlgoAPI_MakeShapeList.h>
36 #include <GeomAlgoAPI_PaveFiller.h>
37 #include <GeomAlgoAPI_SketchBuilder.h>
38 #include <GeomAlgoAPI_Tools.h>
39 #include <GeomAlgoAPI_WireBuilder.h>
41 #include <SketchPlugin_Sketch.h>
45 static bool buildSketchWires(FeaturePtr theSketchFeature, GeomShapePtr theSketchShape,
47 ListOfShape& theWires, GeomMakeShapePtr& theAlgo,
48 std::string& theError);
50 static bool buildWire(const ListOfShape& theEdges, GeomShapePtr& theWire, std::string& theError);
52 //=================================================================================================
53 BuildPlugin_Wire::BuildPlugin_Wire()
57 //=================================================================================================
58 void BuildPlugin_Wire::initAttributes()
60 data()->addAttribute(BASE_OBJECTS_ID(), ModelAPI_AttributeSelectionList::typeId());
62 data()->addAttribute(INTERSECT_ID(), ModelAPI_AttributeBoolean::typeId());
63 ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), INTERSECT_ID());
66 //=================================================================================================
67 void BuildPlugin_Wire::execute()
69 // Get base objects list.
70 AttributeSelectionListPtr aSelectionList = selectionList(BASE_OBJECTS_ID());
71 if(!aSelectionList.get()) {
72 setError("Error: Could not get selection list.");
75 if(aSelectionList->size() == 0) {
76 setError("Error: Empty selection list.");
80 AttributeBooleanPtr anIntersectAttr = boolean(INTERSECT_ID());
81 bool isIntersect = anIntersectAttr->isInitialized() && anIntersectAttr->value();
83 // Collect base shapes.
85 std::list< std::pair<FeaturePtr, GeomShapePtr> > aSketches;
86 for(int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) {
87 AttributeSelectionPtr aSelection = aSelectionList->value(anIndex);
88 GeomShapePtr aShape = aSelection->value();
90 aShape = aSelection->context()->shape();
92 std::shared_ptr<GeomAPI_PlanarEdges> aSketchShape =
93 std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(aShape);
95 FeaturePtr aSketchFeature = ModelAPI_Feature::feature(aSelection->context());
96 aSketches.push_back(std::pair<FeaturePtr, GeomShapePtr>(aSketchFeature, aSketchShape));
100 for(GeomAPI_ShapeExplorer anExp(aShape, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
101 GeomShapePtr anEdge = anExp.current();
102 anEdges.push_back(anEdge);
106 int aResultIndex = 0;
109 if (!anEdges.empty()) {
110 // Create wire from the list of edges.
112 if (!buildWire(anEdges, aWire, anError)) {
118 ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex);
119 aResultBody->store(aWire);
120 for (GeomAPI_ShapeExplorer anExp(aWire, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
121 GeomShapePtr anEdgeInResult = anExp.current();
122 for (ListOfShape::const_iterator anIt = anEdges.cbegin(); anIt != anEdges.cend(); ++anIt) {
123 std::shared_ptr<GeomAPI_Edge> anEdgeInList(new GeomAPI_Edge(*anIt));
124 if (anEdgeInList->isEqual(anEdgeInResult)) {
125 if (anEdgeInList->isSame(anEdgeInResult))
126 aResultBody->generated(anEdgeInResult, "Edge");
128 aResultBody->modified(anEdgeInList, anEdgeInResult);
133 setResult(aResultBody, aResultIndex);
137 // create wires from sketches
138 for (std::list<std::pair<FeaturePtr, GeomShapePtr> >::iterator anIt = aSketches.begin();
139 anIt != aSketches.end(); ++anIt) {
141 GeomMakeShapePtr aMakeShapeList;
142 if (!buildSketchWires(anIt->first, anIt->second, isIntersect,
143 aWires, aMakeShapeList, anError)) {
148 for (ListOfShape::iterator aWIt = aWires.begin(); aWIt != aWires.end(); ++aWIt) {
149 ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex);
151 aSketch.push_back(anIt->second);
152 aResultBody->storeModified(aSketch, *aWIt, aMakeShapeList);
153 aResultBody->loadModifiedShapes(aMakeShapeList, anIt->second, GeomAPI_Shape::EDGE);
154 setResult(aResultBody, aResultIndex);
159 removeResults(aResultIndex);
162 //=================================================================================================
163 bool BuildPlugin_Wire::customAction(const std::string& theActionId)
165 if(theActionId == ADD_CONTOUR_ACTION_ID()) {
168 std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
169 Events_InfoMessage("BuildPlugin_Wire", aMsg).arg(getKind()).arg(theActionId).send();
175 //=================================================================================================
176 bool BuildPlugin_Wire::addContour()
178 // Get base objects list.
179 AttributeSelectionListPtr aSelectionList = selectionList(BASE_OBJECTS_ID());
180 if(aSelectionList->size() == 0) {
181 Events_InfoMessage("BuildPlugin_Wire", "Error: Empty selection list.").send();
185 // Collect attributes to check.
186 ListOfShape anAddedEdges;
187 std::list<AttributeSelectionPtr> anAttributesToCheck;
188 for(int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) {
189 AttributeSelectionPtr aSelection = aSelectionList->value(anIndex);
190 GeomShapePtr anEdgeInList = aSelection->value();
191 if(!anEdgeInList.get()) {
195 // Check that it is edge.
196 if(anEdgeInList->shapeType() != GeomAPI_Shape::EDGE) {
200 // Check that it is edge on sketch.
201 ResultPtr aContext = aSelection->context();
202 ResultConstructionPtr aConstruction =
203 std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aContext);
204 if(!aConstruction.get()) {
207 GeomShapePtr aContextShape = aConstruction->shape();
208 std::shared_ptr<GeomAPI_PlanarEdges> aPlanarEdges =
209 std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(aContextShape);
210 if(!aPlanarEdges.get()) {
214 // Check that sketch have faces.
215 if(aConstruction->facesNum() == 0) {
219 anAddedEdges.push_back(anEdgeInList);
220 anAttributesToCheck.push_back(aSelection);
223 // Check if edges have contours.
224 bool isAnyContourAdded = false;
225 for(std::list<AttributeSelectionPtr>::const_iterator aListIt = anAttributesToCheck.cbegin();
226 aListIt != anAttributesToCheck.cend();
228 AttributeSelectionPtr aSelection = *aListIt;
229 std::shared_ptr<GeomAPI_Edge> anEdgeInList(new GeomAPI_Edge(aSelection->value()));
231 ResultConstructionPtr aConstruction =
232 std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aSelection->context());
234 // Iterate on wires and add wire with this edge.
235 std::shared_ptr<GeomAPI_Shape> aFoundWire;
236 for(int anIndex = 0; anIndex < aConstruction->facesNum(); ++anIndex) {
237 std::shared_ptr<GeomAPI_Face> aFace = aConstruction->face(anIndex);
238 for(GeomAPI_ShapeExplorer
239 aWireExp(aFace, GeomAPI_Shape::WIRE); aWireExp.more(); aWireExp.next()) {
240 GeomShapePtr aWireOnFace = aWireExp.current();
241 for(GeomAPI_ShapeExplorer
242 anExp(aWireOnFace, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
243 std::shared_ptr<GeomAPI_Edge> anEdgeOnFace(new GeomAPI_Edge(anExp.current()));
244 if(anEdgeInList->isEqual(anEdgeOnFace)) {
245 aFoundWire = aWireOnFace;
250 if(aFoundWire.get()) {
255 // If wire with the same edge found. Add all other edges to list.
256 if(aFoundWire.get()) {
257 for(GeomAPI_ShapeExplorer
258 anExp(aFoundWire, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
259 std::shared_ptr<GeomAPI_Edge> anEdgeOnFace(new GeomAPI_Edge(anExp.current()));
260 ListOfShape::const_iterator anEdgesIt = anAddedEdges.cbegin();
261 for(; anEdgesIt != anAddedEdges.cend(); ++anEdgesIt) {
262 if(anEdgeOnFace->isEqual(*anEdgesIt)) {
266 if(anEdgesIt == anAddedEdges.cend()) {
267 isAnyContourAdded = true;
268 anAddedEdges.push_back(anEdgeOnFace);
269 aSelectionList->append(aConstruction, anEdgeOnFace);
275 if(!isAnyContourAdded) {
276 Events_InfoMessage("BuildPlugin_Wire",
277 "Error: Contours already closed or no contours found for selected edges.").send();
286 // ===================== Auxiliary functions ==============================================
288 bool buildWire(const ListOfShape& theEdges, GeomShapePtr& theWire, std::string& theError)
290 theWire = GeomAlgoAPI_WireBuilder::wire(theEdges);
291 if (!theWire.get()) {
292 theError = "Error: Result wire is empty. Probably it has disconnected edges or non-manifold.";
298 bool buildSketchWires(FeaturePtr theSketchFeature, GeomShapePtr theSketchShape, bool isIntersect,
299 ListOfShape& theWires, GeomMakeShapePtr& theAlgo, std::string& theError)
301 ListOfShape aSketchEdges =
302 std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(theSketchShape)->getEdges();
304 std::shared_ptr<GeomAlgoAPI_MakeShapeList> anAlgoList(new GeomAlgoAPI_MakeShapeList);
306 std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aProcessedEdges;
307 // perform sketch builder first
308 AttributePointPtr anOrigin = std::dynamic_pointer_cast<GeomDataAPI_Point>(
309 theSketchFeature->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
310 AttributeDirPtr aNormal = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
311 theSketchFeature->attribute(SketchPlugin_Sketch::NORM_ID()));
312 AttributeDirPtr aDirX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
313 theSketchFeature->attribute(SketchPlugin_Sketch::DIRX_ID()));
314 std::shared_ptr<GeomAlgoAPI_SketchBuilder> aSketchBuilder(new GeomAlgoAPI_SketchBuilder(
315 anOrigin->pnt(), aDirX->dir(), aNormal->dir(), theSketchShape));
317 anAlgoList->appendAlgo(aSketchBuilder);
319 // collect wires from faces
320 const ListOfShape& aFaces = aSketchBuilder->faces();
321 for (ListOfShape::const_iterator anIt = aFaces.begin(); anIt != aFaces.end(); ++anIt) {
322 for (GeomAPI_ShapeExplorer aWExp(*anIt, GeomAPI_Shape::WIRE); aWExp.more(); aWExp.next()) {
323 // skip the wire if at least one its edge was already processed
324 GeomAPI_ShapeExplorer aEExp(aWExp.current(), GeomAPI_Shape::EDGE);
325 for (; aEExp.more(); aEExp.next()) {
326 if (aProcessedEdges.find(aEExp.current()) != aProcessedEdges.end())
327 break; // wire is already processed
331 // mark edges as processed
332 for (aEExp.init(aWExp.current(), GeomAPI_Shape::EDGE); aEExp.more(); aEExp.next())
333 aProcessedEdges.insert(aEExp.current());
335 theWires.push_back(aWExp.current());
339 // collect unused edges
341 for (ListOfShape::iterator anIt = aSketchEdges.begin(); anIt != aSketchEdges.end(); ++anIt) {
342 ListOfShape anImages;
343 aSketchBuilder->modified(*anIt, anImages);
344 for (ListOfShape::iterator anEdge = anImages.begin(); anEdge != anImages.end(); ++anEdge)
345 if (aProcessedEdges.find(*anEdge) == aProcessedEdges.end())
346 aCopy.push_back(*anEdge);
349 if (aCopy.size() > 1) {
351 std::shared_ptr<GeomAlgoAPI_PaveFiller> aGeneralFuse(new GeomAlgoAPI_PaveFiller(aCopy));
352 if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(
353 aGeneralFuse, BuildPlugin_Wire::ID(), theError))
355 anAlgoList->appendAlgo(aGeneralFuse);
357 // collect edges after the split
358 aSketchEdges.clear();
359 for (GeomAPI_ShapeExplorer anExp(aGeneralFuse->shape(), GeomAPI_Shape::EDGE);
360 anExp.more(); anExp.next())
361 aSketchEdges.push_back(anExp.current());
364 aSketchEdges = aCopy;
367 // connect least edges to wires
368 typedef std::list<ListOfShape> ListOfWires;
369 ListOfWires aNewWires;
370 typedef std::map<GeomVertexPtr, ListOfWires::iterator,
371 GeomAPI_Vertex::GeometricComparator> MapVertexWire;
372 MapVertexWire aMapVW;
373 for (ListOfShape::iterator aEIt = aSketchEdges.begin(); aEIt != aSketchEdges.end(); ++aEIt) {
374 GeomEdgePtr anEdge = (*aEIt)->edge();
375 GeomVertexPtr aStartV, aEndV;
376 anEdge->vertices(aStartV, aEndV);
377 MapVertexWire::iterator aFoundStart = aMapVW.find(aStartV);
378 MapVertexWire::iterator aFoundEnd = aMapVW.find(aEndV);
379 if (aFoundStart == aMapVW.end()) {
380 if (aFoundEnd == aMapVW.end()) {
382 aNewWires.push_back(ListOfShape());
383 ListOfWires::iterator aNewW = --aNewWires.end();
384 aNewW->push_back(anEdge);
385 aMapVW[aStartV] = aNewW;
386 aMapVW[aEndV] = aNewW;
391 if (aFoundEnd == aMapVW.end()) {
392 // swap found vertices for correct further processing
393 aFoundEnd = aFoundStart;
397 // both vertices are found
398 aFoundStart->second->push_back(anEdge);
399 if (aFoundStart->second == aFoundEnd->second)
400 aMapVW.erase(aFoundStart);
402 // different wires => merge segments
403 aFoundStart->second->insert(aFoundStart->second->end(),
404 aFoundEnd->second->begin(), aFoundEnd->second->end());
405 for (MapVertexWire::iterator it = aMapVW.begin(); it != aMapVW.end(); ++it)
406 if (it != aFoundEnd && it->second == aFoundEnd->second) {
407 // another boundary of the wire, change link to the whole result
408 it->second = aFoundStart->second;
411 aNewWires.erase(aFoundEnd->second);
412 aMapVW.erase(aFoundStart);
413 aMapVW.erase(aFoundEnd);
418 // add edge to existing wire, substitute the connection point
419 // by the other boundary point of the edge
420 aFoundEnd->second->push_back(anEdge);
421 aMapVW[aStartV] = aFoundEnd->second;
422 aMapVW.erase(aFoundEnd);
425 // generate new wires from the sets of edges
426 for (ListOfWires::iterator anIt = aNewWires.begin(); anIt != aNewWires.end(); ++anIt) {
428 if (!buildWire(*anIt, aWire, theError))
430 theWires.push_back(aWire);
433 theAlgo = anAlgoList;