Salome HOME
2220e4f06520d435f8435d8d2f03d44cfdab1a39
[modules/shaper.git] / src / FeaturesPlugin / FeaturesPlugin_Boolean.cpp
1 // Copyright (C) 2014-2019  CEA/DEN, EDF R&D
2 //
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.
7 //
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.
12 //
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
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "FeaturesPlugin_Boolean.h"
21
22 #include <ModelAPI_Data.h>
23 #include <ModelAPI_Document.h>
24 #include <ModelAPI_AttributeReference.h>
25 #include <ModelAPI_AttributeInteger.h>
26 #include <ModelAPI_ResultBody.h>
27 #include <ModelAPI_AttributeSelectionList.h>
28 #include <ModelAPI_Session.h>
29 #include <ModelAPI_Validator.h>
30 #include <ModelAPI_Tools.h>
31
32 #include <GeomAlgoAPI_Boolean.h>
33 #include <GeomAlgoAPI_CompoundBuilder.h>
34 #include <GeomAlgoAPI_MakeShapeCustom.h>
35 #include <GeomAlgoAPI_MakeShapeList.h>
36 #include <GeomAlgoAPI_Partition.h>
37 #include <GeomAlgoAPI_PaveFiller.h>
38 #include <GeomAlgoAPI_ShapeTools.h>
39 #include <GeomAlgoAPI_Tools.h>
40 #include <GeomAPI_Face.h>
41 #include <GeomAPI_ShapeExplorer.h>
42 #include <GeomAPI_ShapeIterator.h>
43
44 #include <algorithm>
45 #include <map>
46
47 //=================================================================================================
48 FeaturesPlugin_Boolean::FeaturesPlugin_Boolean(const OperationType theOperationType)
49 : myOperationType(theOperationType)
50 {
51 }
52
53 //=================================================================================================
54 void FeaturesPlugin_Boolean::initAttributes()
55 {
56   AttributeSelectionListPtr aSelection =
57     std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(data()->addAttribute(
58     FeaturesPlugin_Boolean::OBJECT_LIST_ID(), ModelAPI_AttributeSelectionList::typeId()));
59
60   aSelection = std::dynamic_pointer_cast<ModelAPI_AttributeSelectionList>(data()->addAttribute(
61     FeaturesPlugin_Boolean::TOOL_LIST_ID(), ModelAPI_AttributeSelectionList::typeId()));
62
63   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), OBJECT_LIST_ID());
64   ModelAPI_Session::get()->validators()->registerNotObligatory(getKind(), TOOL_LIST_ID());
65 }
66
67 //=================================================================================================
68 FeaturesPlugin_Boolean::OperationType FeaturesPlugin_Boolean::operationType()
69 {
70   return myOperationType;
71 }
72
73 //=================================================================================================
74 void FeaturesPlugin_Boolean::parentForShape(const GeomShapePtr& theShape,
75                                             const ResultPtr& theContext,
76                                             ObjectHierarchy& theShapesHierarchy)
77 {
78   ResultBodyPtr aResCompSolidPtr = ModelAPI_Tools::bodyOwner(theContext);
79   if (aResCompSolidPtr.get()) {
80     std::shared_ptr<GeomAPI_Shape> aContextShape = aResCompSolidPtr->shape();
81     if (aContextShape->shapeType() <= GeomAPI_Shape::COMPSOLID) {
82       theShapesHierarchy.AddParent(theShape, aContextShape);
83       parentForShape(aContextShape, aResCompSolidPtr, theShapesHierarchy);
84     }
85   }
86 }
87
88 bool FeaturesPlugin_Boolean::processAttribute(const std::string& theAttributeName,
89                                               ObjectHierarchy& theObjects,
90                                               ListOfShape& thePlanesList,
91                                               ListOfShape& theEdgesAndFaces)
92 {
93   AttributeSelectionListPtr anObjectsSelList = selectionList(theAttributeName);
94   for (int anObjectsIndex = 0; anObjectsIndex < anObjectsSelList->size(); anObjectsIndex++) {
95     AttributeSelectionPtr anObjectAttr = anObjectsSelList->value(anObjectsIndex);
96     GeomShapePtr anObject = anObjectAttr->value();
97     if (!anObject.get()) {
98       // It could be a construction plane.
99       ResultPtr aContext = anObjectAttr->context();
100       anObject = anObjectAttr->context()->shape();
101       if (anObject.get()) {
102         thePlanesList.push_back(anObject);
103         continue;
104       } else
105         return false;
106     }
107
108     if (anObject->shapeType() == GeomAPI_Shape::EDGE ||
109         anObject->shapeType() == GeomAPI_Shape::FACE) {
110       theEdgesAndFaces.push_back(anObject);
111       continue;
112     }
113
114     theObjects.AddObject(anObject);
115
116     ResultPtr aContext = anObjectAttr->context();
117     parentForShape(anObject, aContext, theObjects);
118   }
119   return true;
120 }
121
122 //=================================================================================================
123 void FeaturesPlugin_Boolean::loadNamingDS(std::shared_ptr<ModelAPI_ResultBody> theResultBody,
124                                           const std::shared_ptr<GeomAPI_Shape> theBaseShape,
125                                           const ListOfShape& theTools,
126                                           const std::shared_ptr<GeomAPI_Shape> theResultShape,
127                                           const GeomMakeShapePtr& theMakeShape)
128 {
129   //load result
130   if(theBaseShape->isEqual(theResultShape)) {
131     theResultBody->store(theResultShape, false);
132     return;
133   }
134
135   theResultBody->storeModified(theBaseShape, theResultShape);
136
137   theResultBody->loadModifiedShapes(theMakeShape, theBaseShape, GeomAPI_Shape::EDGE);
138   theResultBody->loadModifiedShapes(theMakeShape, theBaseShape, GeomAPI_Shape::FACE);
139
140   theResultBody->loadDeletedShapes(theMakeShape, theBaseShape, GeomAPI_Shape::FACE);
141
142   for (ListOfShape::const_iterator anIter = theTools.begin();
143        anIter != theTools.end();
144        ++anIter)
145   {
146     GeomAPI_Shape::ShapeType aShapeType =
147       (*anIter)->shapeType() <= GeomAPI_Shape::FACE ? GeomAPI_Shape::FACE
148                                                     : GeomAPI_Shape::EDGE;
149     theResultBody->loadModifiedShapes(theMakeShape, *anIter, aShapeType);
150
151     theResultBody->loadDeletedShapes(theMakeShape, *anIter, GeomAPI_Shape::FACE);
152   }
153 }
154
155 //=================================================================================================
156 bool FeaturesPlugin_Boolean::processObject(
157     const GeomAlgoAPI_Tools::BOPType theBooleanType,
158     const GeomShapePtr& theObject,
159     const ListOfShape& theTools,
160     const ListOfShape& thePlanes,
161     int& theResultIndex,
162     std::vector<FeaturesPlugin_Tools::ResultBaseAlgo>& theResultBaseAlgoList,
163     ListOfShape& theResultShapesList)
164 {
165   ListOfShape aListWithObject;
166   aListWithObject.push_back(theObject);
167   std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeShapeList(new GeomAlgoAPI_MakeShapeList());
168   std::shared_ptr<GeomAlgoAPI_MakeShape> aBoolAlgo;
169   GeomShapePtr aResShape;
170
171   std::list<std::shared_ptr<GeomAPI_Pnt> > aBoundingPoints =
172       GeomAlgoAPI_ShapeTools::getBoundingBox(aListWithObject, 1.0);
173
174   // Resize planes.
175   ListOfShape aToolsWithPlanes = theTools;
176   for (ListOfShape::const_iterator anIt = thePlanes.begin(); anIt != thePlanes.end(); ++anIt) {
177     GeomShapePtr aPlane = *anIt;
178     GeomShapePtr aTool = GeomAlgoAPI_ShapeTools::fitPlaneToBox(aPlane, aBoundingPoints);
179     std::shared_ptr<GeomAlgoAPI_MakeShapeCustom> aMkShCustom(
180         new GeomAlgoAPI_MakeShapeCustom);
181     aMkShCustom->addModified(aPlane, aTool);
182     aMakeShapeList->appendAlgo(aMkShCustom);
183     aToolsWithPlanes.push_back(aTool);
184   }
185
186   if (theBooleanType == GeomAlgoAPI_Tools::BOOL_PARTITION)
187     aBoolAlgo.reset(new GeomAlgoAPI_Partition(aListWithObject, aToolsWithPlanes));
188   else
189     aBoolAlgo.reset(new GeomAlgoAPI_Boolean(aListWithObject,
190                                             aToolsWithPlanes,
191                                             theBooleanType));
192
193   // Checking that the algorithm worked properly.
194   std::string anError;
195   if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aBoolAlgo, getKind(), anError)) {
196     setError(anError);
197     return false;
198   }
199
200   aResShape = aBoolAlgo->shape();
201   if (aResShape.get() && aResShape->shapeType() == GeomAPI_Shape::COMPOUND) {
202     int aSubResultsNb = 0;
203     GeomAPI_ShapeIterator anIt(aResShape);
204     for (; anIt.more(); anIt.next())
205       ++aSubResultsNb;
206
207     if (aSubResultsNb == 1) {
208       anIt.init(aResShape);
209       if (anIt.more())
210         aResShape = anIt.current();
211     }
212   }
213
214   aMakeShapeList->appendAlgo(aBoolAlgo);
215
216   GeomAPI_ShapeIterator aShapeIt(aResShape);
217   if (aShapeIt.more() || aResShape->shapeType() == GeomAPI_Shape::VERTEX) {
218     std::shared_ptr<ModelAPI_ResultBody> aResultBody =
219         document()->createBody(data(), theResultIndex);
220
221     // tools should be added to the list to fulfill the correct history of modification
222     aListWithObject.insert(aListWithObject.end(), theTools.begin(), theTools.end());
223
224     ListOfShape aUsedTools = theTools;
225     aUsedTools.insert(aUsedTools.end(), thePlanes.begin(), thePlanes.end());
226
227     FeaturesPlugin_Tools::loadModifiedShapes(aResultBody,
228                                              aListWithObject,
229                                              aUsedTools,
230                                              aMakeShapeList,
231                                              aResShape);
232     setResult(aResultBody, theResultIndex);
233     ++theResultIndex;
234
235     FeaturesPlugin_Tools::ResultBaseAlgo aRBA;
236     aRBA.resultBody = aResultBody;
237     aRBA.baseShape = theObject;
238     aRBA.makeShape = aMakeShapeList;
239     theResultBaseAlgoList.push_back(aRBA);
240     theResultShapesList.push_back(aResShape);
241   }
242   return true;
243 }
244
245 //=================================================================================================
246 bool FeaturesPlugin_Boolean::processCompsolid(
247     const GeomAlgoAPI_Tools::BOPType theBooleanType,
248     const ObjectHierarchy& theCompsolidHierarchy,
249     const GeomShapePtr& theCompsolid,
250     const ListOfShape& theTools,
251     const ListOfShape& thePlanes,
252     int& theResultIndex,
253     std::vector<FeaturesPlugin_Tools::ResultBaseAlgo>& theResultBaseAlgoList,
254     ListOfShape& theResultShapesList)
255 {
256   ListOfShape aUsedInOperationSolids;
257   ListOfShape aNotUsedSolids;
258   theCompsolidHierarchy.SplitCompound(theCompsolid, aUsedInOperationSolids, aNotUsedSolids);
259
260   std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeShapeList(new GeomAlgoAPI_MakeShapeList());
261
262   std::list<std::shared_ptr<GeomAPI_Pnt> > aBoundingPoints =
263       GeomAlgoAPI_ShapeTools::getBoundingBox(aUsedInOperationSolids, 1.0);
264
265   // Resize planes.
266   ListOfShape aToolsWithPlanes = theTools;
267   for (ListOfShape::const_iterator anIt = thePlanes.begin(); anIt != thePlanes.end(); ++anIt)
268   {
269     GeomShapePtr aPlane = *anIt;
270     GeomShapePtr aTool = GeomAlgoAPI_ShapeTools::fitPlaneToBox(aPlane, aBoundingPoints);
271     std::shared_ptr<GeomAlgoAPI_MakeShapeCustom> aMkShCustom(
272       new GeomAlgoAPI_MakeShapeCustom);
273     aMkShCustom->addModified(aPlane, aTool);
274     aMakeShapeList->appendAlgo(aMkShCustom);
275     aToolsWithPlanes.push_back(aTool);
276   }
277
278   std::shared_ptr<GeomAlgoAPI_MakeShape> aBoolAlgo;
279   if (theBooleanType == GeomAlgoAPI_Tools::BOOL_PARTITION)
280     aBoolAlgo.reset(new GeomAlgoAPI_Partition(aUsedInOperationSolids, aToolsWithPlanes));
281   else
282     aBoolAlgo.reset(new GeomAlgoAPI_Boolean(aUsedInOperationSolids,
283                                             aToolsWithPlanes,
284                                             theBooleanType));
285
286   // Checking that the algorithm worked properly.
287   std::string anError;
288   if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aBoolAlgo, getKind(), anError)) {
289     setError(anError);
290     return false;
291   }
292
293   aMakeShapeList->appendAlgo(aBoolAlgo);
294   GeomShapePtr aResultShape = aBoolAlgo->shape();
295
296   // Add result to not used solids from compsolid.
297   if (!aNotUsedSolids.empty()) {
298     ListOfShape aShapesToAdd = aNotUsedSolids;
299     aShapesToAdd.push_back(aBoolAlgo->shape());
300     std::shared_ptr<GeomAlgoAPI_PaveFiller> aFillerAlgo(
301         new GeomAlgoAPI_PaveFiller(aShapesToAdd, true));
302     if (!aFillerAlgo->isDone()) {
303       std::string aFeatureError = "Error: PaveFiller algorithm failed.";
304       setError(aFeatureError);
305       return false;
306     }
307
308     aMakeShapeList->appendAlgo(aFillerAlgo);
309     aResultShape = aFillerAlgo->shape();
310   }
311
312   GeomAPI_ShapeIterator aShapeIt(aResultShape);
313   if (aShapeIt.more() || aResultShape->shapeType() == GeomAPI_Shape::VERTEX)
314   {
315     std::shared_ptr<ModelAPI_ResultBody> aResultBody =
316         document()->createBody(data(), theResultIndex);
317
318     ListOfShape aCompSolidList;
319     aCompSolidList.push_back(theCompsolid);
320     // tools should be added to the list to fulfill the correct history of modification
321     aCompSolidList.insert(aCompSolidList.end(), theTools.begin(), theTools.end());
322
323     ListOfShape aUsedTools = theTools;
324     aUsedTools.insert(aUsedTools.end(), thePlanes.begin(), thePlanes.end());
325
326     FeaturesPlugin_Tools::loadModifiedShapes(aResultBody,
327                                              aCompSolidList,
328                                              aUsedTools,
329                                              aMakeShapeList,
330                                              aResultShape);
331     setResult(aResultBody, theResultIndex);
332     ++theResultIndex;
333
334     FeaturesPlugin_Tools::ResultBaseAlgo aRBA;
335     aRBA.resultBody = aResultBody;
336     aRBA.baseShape = theCompsolid;
337     aRBA.makeShape = aMakeShapeList;
338     theResultBaseAlgoList.push_back(aRBA);
339     theResultShapesList.push_back(aResultShape);
340   }
341   return true;
342 }
343
344 //=================================================================================================
345 bool FeaturesPlugin_Boolean::processCompound(
346     const GeomAlgoAPI_Tools::BOPType theBooleanType,
347     const ObjectHierarchy& theCompoundHierarchy,
348     const GeomShapePtr& theCompound,
349     const ListOfShape& theTools,
350     int& theResultIndex,
351     std::vector<FeaturesPlugin_Tools::ResultBaseAlgo>& theResultBaseAlgoList,
352     ListOfShape& theResultShapesList)
353 {
354   ListOfShape aUsedInOperationShapes;
355   ListOfShape aNotUsedShapes;
356   theCompoundHierarchy.SplitCompound(theCompound, aUsedInOperationShapes, aNotUsedShapes);
357
358   std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeShapeList(new GeomAlgoAPI_MakeShapeList());
359   std::shared_ptr<GeomAlgoAPI_Boolean> aBoolAlgo(
360       new GeomAlgoAPI_Boolean(aUsedInOperationShapes,
361                               theTools,
362                               theBooleanType));
363
364   // Checking that the algorithm worked properly.
365   std::string anError;
366   if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aBoolAlgo, getKind(), anError)) {
367     setError(anError);
368     return false;
369   }
370
371   aMakeShapeList->appendAlgo(aBoolAlgo);
372   GeomShapePtr aResultShape = aBoolAlgo->shape();
373
374   // Add result to not used shape from compound.
375   if (!aNotUsedShapes.empty()) {
376     ListOfShape aShapesForResult = aNotUsedShapes;
377     if (aResultShape->shapeType() == GeomAPI_Shape::COMPOUND) {
378       for (GeomAPI_ShapeIterator aResultIt(aResultShape); aResultIt.more(); aResultIt.next()) {
379         aShapesForResult.push_back(aResultIt.current());
380       }
381     }
382     else {
383       aShapesForResult.push_back(aResultShape);
384     }
385
386     if (aShapesForResult.size() == 1) {
387       aResultShape = aShapesForResult.front();
388     }
389     else {
390       aResultShape = GeomAlgoAPI_CompoundBuilder::compound(aShapesForResult);
391     }
392   }
393
394   GeomAPI_ShapeIterator aShapeIt(aResultShape);
395   if (aShapeIt.more() || aResultShape->shapeType() == GeomAPI_Shape::VERTEX) {
396     std::shared_ptr<ModelAPI_ResultBody> aResultBody =
397         document()->createBody(data(), theResultIndex);
398
399     ListOfShape aCompoundList;
400     aCompoundList.push_back(theCompound);
401     FeaturesPlugin_Tools::loadModifiedShapes(aResultBody,
402                                              aCompoundList,
403                                              theTools,
404                                              aMakeShapeList,
405                                              aResultShape);
406     setResult(aResultBody, theResultIndex);
407     ++theResultIndex;
408
409     FeaturesPlugin_Tools::ResultBaseAlgo aRBA;
410     aRBA.resultBody = aResultBody;
411     aRBA.baseShape = theCompound;
412     aRBA.makeShape = aMakeShapeList;
413     theResultBaseAlgoList.push_back(aRBA);
414     theResultShapesList.push_back(aResultShape);
415   }
416   return true;
417 }
418
419 //=================================================================================================
420
421 void FeaturesPlugin_Boolean::ObjectHierarchy::AddObject(const GeomShapePtr& theObject)
422 {
423   myObjects.push_back(theObject);
424 }
425
426 void FeaturesPlugin_Boolean::ObjectHierarchy::AddParent(const GeomShapePtr& theShape,
427                                                         const GeomShapePtr& theParent)
428 {
429   myParent[theShape] = theParent;
430   mySubshapes[theParent].push_back(theShape);
431 }
432
433 GeomShapePtr FeaturesPlugin_Boolean::ObjectHierarchy::Parent(const GeomShapePtr& theShape,
434                                                              bool theMarkProcessed)
435 {
436   MapShapeToParent::const_iterator aFound = myParent.find(theShape);
437   GeomShapePtr aParent;
438   if (aFound != myParent.end()) {
439     aParent = aFound->second;
440     if (theMarkProcessed) {
441       // mark the parent and all its subs as processed by Boolean algorithm
442       myProcessedObjects.insert(aParent);
443       const ListOfShape& aSubs = mySubshapes[aParent];
444       for (ListOfShape::const_iterator anIt = aSubs.begin(); anIt != aSubs.end(); ++anIt)
445         myProcessedObjects.insert(*anIt);
446     }
447   }
448   return aParent;
449 }
450
451 void FeaturesPlugin_Boolean::ObjectHierarchy::SplitCompound(const GeomShapePtr& theCompShape,
452                                                             ListOfShape& theUsed,
453                                                             ListOfShape& theNotUsed) const
454 {
455   theUsed.clear();
456   theNotUsed.clear();
457
458   const ListOfShape& aSubs = mySubshapes.find(theCompShape)->second;
459   SetOfShape aSubsSet;
460   aSubsSet.insert(aSubs.begin(), aSubs.end());
461
462   for (GeomAPI_ShapeExplorer anExp(theCompShape, GeomAPI_Shape::SOLID);
463        anExp.more(); anExp.next()) {
464     GeomShapePtr aCurrent = anExp.current();
465     if (aSubsSet.find(aCurrent) == aSubsSet.end())
466       theNotUsed.push_back(aCurrent);
467     else
468       theUsed.push_back(aCurrent);
469   }
470 }
471
472 bool FeaturesPlugin_Boolean::ObjectHierarchy::IsEmpty() const
473 {
474   return myObjects.empty();
475 }
476
477 FeaturesPlugin_Boolean::ObjectHierarchy::Iterator FeaturesPlugin_Boolean::ObjectHierarchy::Begin()
478 {
479   return Iterator(this);
480 }
481
482 FeaturesPlugin_Boolean::ObjectHierarchy::Iterator FeaturesPlugin_Boolean::ObjectHierarchy::End()
483 {
484   return Iterator(this, false);
485 }
486
487 FeaturesPlugin_Boolean::ObjectHierarchy::Iterator::Iterator(
488     FeaturesPlugin_Boolean::ObjectHierarchy* theHierarchy, bool isBegin)
489   : myHierarchy(theHierarchy)
490 {
491   if (isBegin) {
492     myObject = myHierarchy->myObjects.begin();
493     SkipAlreadyProcessed();
494   } else
495     myObject = myHierarchy->myObjects.end();
496 }
497
498 void FeaturesPlugin_Boolean::ObjectHierarchy::Iterator::SkipAlreadyProcessed()
499 {
500   while (myObject != myHierarchy->myObjects.end() &&
501          myHierarchy->myProcessedObjects.find(*myObject) != myHierarchy->myProcessedObjects.end())
502     ++myObject;
503 }
504
505 bool FeaturesPlugin_Boolean::ObjectHierarchy::Iterator::operator==(const Iterator& theOther) const
506 {
507   return myObject == theOther.myObject;
508 }
509
510 bool FeaturesPlugin_Boolean::ObjectHierarchy::Iterator::operator!=(const Iterator& theOther) const
511 {
512   return !operator==(theOther);
513 }
514
515 FeaturesPlugin_Boolean::ObjectHierarchy::Iterator&
516 FeaturesPlugin_Boolean::ObjectHierarchy::Iterator::operator++()
517 {
518   ++myObject;
519   SkipAlreadyProcessed();
520   return *this;
521 }
522
523 FeaturesPlugin_Boolean::ObjectHierarchy::Iterator
524 FeaturesPlugin_Boolean::ObjectHierarchy::Iterator::operator++(int)
525 {
526   Iterator aCurrent;
527   aCurrent.myHierarchy = myHierarchy;
528   aCurrent.myObject = myObject;
529
530   // increase iterator
531   operator++();
532
533   return aCurrent;
534 }
535
536 GeomShapePtr FeaturesPlugin_Boolean::ObjectHierarchy::Iterator::operator*() const
537 {
538   myHierarchy->myProcessedObjects.insert(*myObject);
539   return *myObject;
540 }