1 // Copyright (C) 2014-2019 CEA/DEN, EDF R&D
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 aResultBody->modified(anEdgeInList, anEdgeInResult);
130 setResult(aResultBody, aResultIndex);
134 // create wires from sketches
135 for (std::list<std::pair<FeaturePtr, GeomShapePtr> >::iterator anIt = aSketches.begin();
136 anIt != aSketches.end(); ++anIt) {
138 GeomMakeShapePtr aMakeShapeList;
139 if (!buildSketchWires(anIt->first, anIt->second, isIntersect,
140 aWires, aMakeShapeList, anError)) {
145 for (ListOfShape::iterator aWIt = aWires.begin(); aWIt != aWires.end(); ++aWIt) {
146 ResultBodyPtr aResultBody = document()->createBody(data(), aResultIndex);
147 ListOfShape aSketches;
148 aSketches.push_back(anIt->second);
149 aResultBody->storeModified(aSketches, *aWIt, aMakeShapeList);
150 aResultBody->loadModifiedShapes(aMakeShapeList, anIt->second, GeomAPI_Shape::EDGE);
151 setResult(aResultBody, aResultIndex);
156 removeResults(aResultIndex);
159 //=================================================================================================
160 bool BuildPlugin_Wire::customAction(const std::string& theActionId)
162 if(theActionId == ADD_CONTOUR_ACTION_ID()) {
165 std::string aMsg = "Error: Feature \"%1\" does not support action \"%2\".";
166 Events_InfoMessage("BuildPlugin_Wire", aMsg).arg(getKind()).arg(theActionId).send();
172 //=================================================================================================
173 bool BuildPlugin_Wire::addContour()
175 // Get base objects list.
176 AttributeSelectionListPtr aSelectionList = selectionList(BASE_OBJECTS_ID());
177 if(aSelectionList->size() == 0) {
178 Events_InfoMessage("BuildPlugin_Wire", "Error: Empty selection list.").send();
182 // Collect attributes to check.
183 ListOfShape anAddedEdges;
184 std::list<AttributeSelectionPtr> anAttributesToCheck;
185 for(int anIndex = 0; anIndex < aSelectionList->size(); ++anIndex) {
186 AttributeSelectionPtr aSelection = aSelectionList->value(anIndex);
187 GeomShapePtr anEdgeInList = aSelection->value();
188 if(!anEdgeInList.get()) {
192 // Check that it is edge.
193 if(anEdgeInList->shapeType() != GeomAPI_Shape::EDGE) {
197 // Check that it is edge on sketch.
198 ResultPtr aContext = aSelection->context();
199 ResultConstructionPtr aConstruction =
200 std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aContext);
201 if(!aConstruction.get()) {
204 GeomShapePtr aContextShape = aConstruction->shape();
205 std::shared_ptr<GeomAPI_PlanarEdges> aPlanarEdges =
206 std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(aContextShape);
207 if(!aPlanarEdges.get()) {
211 // Check that sketch have faces.
212 if(aConstruction->facesNum() == 0) {
216 anAddedEdges.push_back(anEdgeInList);
217 anAttributesToCheck.push_back(aSelection);
220 // Check if edges have contours.
221 bool isAnyContourAdded = false;
222 for(std::list<AttributeSelectionPtr>::const_iterator aListIt = anAttributesToCheck.cbegin();
223 aListIt != anAttributesToCheck.cend();
225 AttributeSelectionPtr aSelection = *aListIt;
226 std::shared_ptr<GeomAPI_Edge> anEdgeInList(new GeomAPI_Edge(aSelection->value()));
228 ResultConstructionPtr aConstruction =
229 std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(aSelection->context());
231 // Iterate on wires and add wire with this edge.
232 std::shared_ptr<GeomAPI_Shape> aFoundWire;
233 for(int anIndex = 0; anIndex < aConstruction->facesNum(); ++anIndex) {
234 std::shared_ptr<GeomAPI_Face> aFace = aConstruction->face(anIndex);
235 for(GeomAPI_ShapeExplorer
236 aWireExp(aFace, GeomAPI_Shape::WIRE); aWireExp.more(); aWireExp.next()) {
237 GeomShapePtr aWireOnFace = aWireExp.current();
238 for(GeomAPI_ShapeExplorer
239 anExp(aWireOnFace, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
240 std::shared_ptr<GeomAPI_Edge> anEdgeOnFace(new GeomAPI_Edge(anExp.current()));
241 if(anEdgeInList->isEqual(anEdgeOnFace)) {
242 aFoundWire = aWireOnFace;
247 if(aFoundWire.get()) {
252 // If wire with the same edge found. Add all other edges to list.
253 if(aFoundWire.get()) {
254 for(GeomAPI_ShapeExplorer
255 anExp(aFoundWire, GeomAPI_Shape::EDGE); anExp.more(); anExp.next()) {
256 std::shared_ptr<GeomAPI_Edge> anEdgeOnFace(new GeomAPI_Edge(anExp.current()));
257 ListOfShape::const_iterator anEdgesIt = anAddedEdges.cbegin();
258 for(; anEdgesIt != anAddedEdges.cend(); ++anEdgesIt) {
259 if(anEdgeOnFace->isEqual(*anEdgesIt)) {
263 if(anEdgesIt == anAddedEdges.cend()) {
264 isAnyContourAdded = true;
265 anAddedEdges.push_back(anEdgeOnFace);
266 aSelectionList->append(aConstruction, anEdgeOnFace);
272 if(!isAnyContourAdded) {
273 Events_InfoMessage("BuildPlugin_Wire",
274 "Error: Contours already closed or no contours found for selected edges.").send();
283 // ===================== Auxiliary functions ==============================================
285 bool buildWire(const ListOfShape& theEdges, GeomShapePtr& theWire, std::string& theError)
287 theWire = GeomAlgoAPI_WireBuilder::wire(theEdges);
288 if (!theWire.get()) {
289 theError = "Error: Result wire is empty. Probably it has disconnected edges or non-manifold.";
295 bool buildSketchWires(FeaturePtr theSketchFeature, GeomShapePtr theSketchShape, bool isIntersect,
296 ListOfShape& theWires, GeomMakeShapePtr& theAlgo, std::string& theError)
298 ListOfShape aSketchEdges =
299 std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(theSketchShape)->getEdges();
301 std::shared_ptr<GeomAlgoAPI_MakeShapeList> anAlgoList(new GeomAlgoAPI_MakeShapeList);
303 std::set<GeomShapePtr, GeomAPI_Shape::Comparator> aProcessedEdges;
304 // perform sketch builder first
305 AttributePointPtr anOrigin = std::dynamic_pointer_cast<GeomDataAPI_Point>(
306 theSketchFeature->attribute(SketchPlugin_Sketch::ORIGIN_ID()));
307 AttributeDirPtr aNormal = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
308 theSketchFeature->attribute(SketchPlugin_Sketch::NORM_ID()));
309 AttributeDirPtr aDirX = std::dynamic_pointer_cast<GeomDataAPI_Dir>(
310 theSketchFeature->attribute(SketchPlugin_Sketch::DIRX_ID()));
311 std::shared_ptr<GeomAlgoAPI_SketchBuilder> aSketchBuilder(new GeomAlgoAPI_SketchBuilder(
312 anOrigin->pnt(), aDirX->dir(), aNormal->dir(), theSketchShape));
314 anAlgoList->appendAlgo(aSketchBuilder);
316 // collect wires from faces
317 const ListOfShape& aFaces = aSketchBuilder->faces();
318 for (ListOfShape::const_iterator anIt = aFaces.begin(); anIt != aFaces.end(); ++anIt) {
319 for (GeomAPI_ShapeExplorer aWExp(*anIt, GeomAPI_Shape::WIRE); aWExp.more(); aWExp.next()) {
320 GeomAPI_ShapeExplorer aEExp(aWExp.current(), GeomAPI_Shape::EDGE);
321 if (aProcessedEdges.find(aEExp.current()) != aProcessedEdges.end())
322 continue; // wire is already processed
323 // mark edges as processed
324 for (; aEExp.more(); aEExp.next())
325 aProcessedEdges.insert(aEExp.current());
327 theWires.push_back(aWExp.current());
331 // collect unused edges
333 for (ListOfShape::iterator anIt = aSketchEdges.begin(); anIt != aSketchEdges.end(); ++anIt) {
334 ListOfShape anImages;
335 aSketchBuilder->modified(*anIt, anImages);
336 for (ListOfShape::iterator anEdge = anImages.begin(); anEdge != anImages.end(); ++anEdge)
337 if (aProcessedEdges.find(*anEdge) == aProcessedEdges.end())
338 aCopy.push_back(*anEdge);
341 if (aCopy.size() > 1) {
343 std::shared_ptr<GeomAlgoAPI_PaveFiller> aGeneralFuse(new GeomAlgoAPI_PaveFiller(aCopy));
344 if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(
345 aGeneralFuse, BuildPlugin_Wire::ID(), theError))
347 anAlgoList->appendAlgo(aGeneralFuse);
349 // collect edges after the split
350 aSketchEdges.clear();
351 for (GeomAPI_ShapeExplorer anExp(aGeneralFuse->shape(), GeomAPI_Shape::EDGE);
352 anExp.more(); anExp.next())
353 aSketchEdges.push_back(anExp.current());
356 aSketchEdges = aCopy;
359 // connect least edges to wires
360 typedef std::list<ListOfShape> ListOfWires;
361 ListOfWires aNewWires;
362 typedef std::map<GeomVertexPtr, ListOfWires::iterator,
363 GeomAPI_Vertex::GeometricComparator> MapVertexWire;
364 MapVertexWire aMapVW;
365 for (ListOfShape::iterator aEIt = aSketchEdges.begin(); aEIt != aSketchEdges.end(); ++aEIt) {
366 GeomEdgePtr anEdge = (*aEIt)->edge();
367 GeomVertexPtr aStartV, aEndV;
368 anEdge->vertices(aStartV, aEndV);
369 MapVertexWire::iterator aFoundStart = aMapVW.find(aStartV);
370 MapVertexWire::iterator aFoundEnd = aMapVW.find(aEndV);
371 if (aFoundStart == aMapVW.end()) {
372 if (aFoundEnd == aMapVW.end()) {
374 aNewWires.push_back(ListOfShape());
375 ListOfWires::iterator aNewW = --aNewWires.end();
376 aNewW->push_back(anEdge);
377 aMapVW[aStartV] = aNewW;
378 aMapVW[aEndV] = aNewW;
383 if (aFoundEnd == aMapVW.end()) {
384 // swap found vertices for correct further processing
385 aFoundEnd = aFoundStart;
389 // both vertices are found => close the loop
390 aFoundStart->second->push_back(anEdge);
391 if (aFoundStart->second != aFoundEnd->second) {
392 aFoundStart->second->insert(aFoundStart->second->end(),
393 aFoundEnd->second->begin(), aFoundEnd->second->end());
394 aNewWires.erase(aFoundEnd->second);
396 aMapVW.erase(aFoundStart);
397 aMapVW.erase(aFoundEnd);
401 // add edge to existing wire, substitute the connection point
402 // by the other boundary point of the edge
403 aFoundEnd->second->push_back(anEdge);
404 aMapVW[aStartV] = aFoundEnd->second;
405 aMapVW.erase(aFoundEnd);
408 // generate new wires from the sets of edges
409 for (ListOfWires::iterator anIt = aNewWires.begin(); anIt != aNewWires.end(); ++anIt) {
411 if (!buildWire(*anIt, aWire, theError))
413 theWires.push_back(aWire);
416 theAlgo = anAlgoList;