Salome HOME
cd4f34cd33a915148c3ba1fc8ebf5da5774cd3fb
[modules/shaper.git] / src / FeaturesPlugin / FeaturesPlugin_Partition.cpp
1 // Copyright (C) 2014-2017  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
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
19 //
20
21 #include "FeaturesPlugin_Partition.h"
22
23 #include <ModelAPI_AttributeBoolean.h>
24 #include <ModelAPI_AttributeInteger.h>
25 #include <ModelAPI_AttributeReference.h>
26 #include <ModelAPI_AttributeSelectionList.h>
27 #include <ModelAPI_BodyBuilder.h>
28 #include <ModelAPI_Data.h>
29 #include <ModelAPI_Document.h>
30 #include <ModelAPI_ResultBody.h>
31 #include <ModelAPI_Session.h>
32 #include <ModelAPI_Tools.h>
33 #include <ModelAPI_Validator.h>
34
35 #include <GeomAlgoAPI_Boolean.h>
36 #include <GeomAlgoAPI_CompoundBuilder.h>
37 #include <GeomAlgoAPI_MakeShapeCustom.h>
38 #include <GeomAlgoAPI_MakeShapeList.h>
39 #include <GeomAlgoAPI_Partition.h>
40 #include <GeomAlgoAPI_ShapeTools.h>
41
42 #include <GeomAPI_Face.h>
43 #include <GeomAPI_ShapeExplorer.h>
44 #include <GeomAPI_ShapeIterator.h>
45
46 #include <iostream>
47 #include <list>
48 #include <sstream>
49
50 typedef std::list<std::pair<GeomShapePtr, ListOfShape> > CompsolidSubs;
51
52 static GeomShapePtr findBase(const GeomShapePtr theObjectShape,
53                              const GeomShapePtr theResultShape,
54                              const GeomAPI_Shape::ShapeType theShapeType,
55                              const std::shared_ptr<GeomAlgoAPI_MakeShape> theMakeShape);
56
57 static void pullObjectsAndPlanes(const AttributeSelectionListPtr& theSelectedList,
58                                  CompsolidSubs& theObjects, ListOfShape& thePlanes);
59
60 static void resizePlanes(const CompsolidSubs& theObjects, ListOfShape& thePlanes,
61                          std::shared_ptr<GeomAlgoAPI_MakeShapeList>& theMakeShapeList);
62
63 static void unusedSubsOfComposolid(const CompsolidSubs& theObjects, CompsolidSubs& theNotUsed);
64
65 static bool cutUnusedSubs(CompsolidSubs& theObjects, CompsolidSubs& theNotUsed,
66                           std::shared_ptr<GeomAlgoAPI_MakeShapeList>& theMakeShapeList,
67                           std::string& theError);
68
69 static bool isAlgoFailed(const std::shared_ptr<GeomAlgoAPI_MakeShape>& theAlgo,
70                          std::string& theError);
71
72
73 //=================================================================================================
74 FeaturesPlugin_Partition::FeaturesPlugin_Partition()
75 {
76 }
77
78 //=================================================================================================
79 void FeaturesPlugin_Partition::initAttributes()
80 {
81   data()->addAttribute(BASE_OBJECTS_ID(), ModelAPI_AttributeSelectionList::typeId());
82 }
83
84 //=================================================================================================
85 void FeaturesPlugin_Partition::execute()
86 {
87   CompsolidSubs anObjects;
88   ListOfShape aPlanes;
89
90   // Getting objects.
91   pullObjectsAndPlanes(selectionList(BASE_OBJECTS_ID()), anObjects, aPlanes);
92   if(anObjects.empty()) {
93     static const std::string aFeatureError = "Error: No objects for partition.";
94     setError(aFeatureError);
95     return;
96   }
97
98   ListOfShape aBaseObjects;
99   for (CompsolidSubs::iterator anIt = anObjects.begin(); anIt != anObjects.end(); ++anIt)
100     aBaseObjects.insert(aBaseObjects.end(), anIt->second.begin(), anIt->second.end());
101   aBaseObjects.insert(aBaseObjects.end(), aPlanes.begin(), aPlanes.end());
102
103   // resize planes to the bounding box of operated shapes
104   std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeShapeList(new GeomAlgoAPI_MakeShapeList());
105   resizePlanes(anObjects, aPlanes, aMakeShapeList);
106
107   // cut unused solids of composolids from the objects of partition
108   CompsolidSubs anUnusedSubs;
109   unusedSubsOfComposolid(anObjects, anUnusedSubs);
110   for (CompsolidSubs::iterator anIt = anUnusedSubs.begin(); anIt != anUnusedSubs.end(); ++anIt)
111     aBaseObjects.insert(aBaseObjects.end(), anIt->second.begin(), anIt->second.end());
112
113   std::string aError;
114   if (!cutUnusedSubs(anObjects, anUnusedSubs, aMakeShapeList, aError)) {
115     setError(aError);
116     return;
117   }
118
119   // perform partition first time to split target solids
120   ListOfShape aTargetObjects;
121   for (CompsolidSubs::iterator anIt = anObjects.begin(); anIt != anObjects.end(); ++anIt)
122     aTargetObjects.insert(aTargetObjects.end(), anIt->second.begin(), anIt->second.end());
123
124   std::shared_ptr<GeomAlgoAPI_Partition> aPartitionAlgo(
125     new GeomAlgoAPI_Partition(aTargetObjects, aPlanes));
126
127   // Checking that the algorithm worked properly.
128   if (isAlgoFailed(aPartitionAlgo, aError)) {
129     setError(aError);
130     return;
131   }
132
133   aMakeShapeList->appendAlgo(aPartitionAlgo);
134   GeomShapePtr aResultShape = aPartitionAlgo->shape();
135
136   if (!anUnusedSubs.empty()) {
137     // second pass of a partition to split shared faces of compsolids
138     aTargetObjects.clear();
139     aTargetObjects.push_back(aResultShape);
140     for (CompsolidSubs::iterator anIt = anUnusedSubs.begin(); anIt != anUnusedSubs.end(); ++anIt)
141       aTargetObjects.insert(aTargetObjects.end(), anIt->second.begin(), anIt->second.end());
142
143     aPartitionAlgo.reset(new GeomAlgoAPI_Partition(aTargetObjects, ListOfShape()));
144
145     // Checking that the algorithm worked properly.
146     if (isAlgoFailed(aPartitionAlgo, aError)) {
147       setError(aError);
148       return;
149     }
150
151     aMakeShapeList->appendAlgo(aPartitionAlgo);
152     aResultShape = aPartitionAlgo->shape();
153   }
154
155   int aResultIndex = 0;
156   if(aResultShape->shapeType() == GeomAPI_Shape::COMPOUND) {
157     for(GeomAPI_ShapeIterator anIt(aResultShape); anIt.more(); anIt.next()) {
158       storeResult(aBaseObjects, aPlanes, anIt.current(), aMakeShapeList, aResultIndex);
159       ++aResultIndex;
160     }
161   } else {
162     storeResult(aBaseObjects, aPlanes, aResultShape, aMakeShapeList, aResultIndex);
163     ++aResultIndex;
164   }
165
166   // Remove the rest results if there were produced in the previous pass.
167   removeResults(aResultIndex);
168 }
169
170 //=================================================================================================
171 void FeaturesPlugin_Partition::storeResult(
172   ListOfShape& theObjects, ListOfShape& thePlanes,
173   const GeomShapePtr theResultShape,
174   const std::shared_ptr<GeomAlgoAPI_MakeShape> theMakeShape,
175   const int theIndex)
176 {
177   // Find base. The most complicated is the real modified object (#1799 if box is partitioned by
178   // two planes the box is the base, not planes, independently on the order in the list).
179   GeomShapePtr aBaseShape;
180   for(ListOfShape::const_iterator anIt = theObjects.cbegin(); anIt != theObjects.cend(); ++anIt) {
181     GeomShapePtr anObjectShape = *anIt;
182     GeomShapePtr aCandidate =
183       findBase(anObjectShape, theResultShape, GeomAPI_Shape::VERTEX, theMakeShape);
184     if(!aCandidate.get()) {
185       aCandidate = findBase(anObjectShape, theResultShape, GeomAPI_Shape::EDGE, theMakeShape);
186     }
187     if (!aCandidate.get())
188       aCandidate = findBase(anObjectShape, theResultShape, GeomAPI_Shape::FACE, theMakeShape);
189
190     if(aCandidate.get()) {
191       if (!aBaseShape.get() || aBaseShape->shapeType() > aCandidate->shapeType()) {
192         aBaseShape = aCandidate;
193       }
194     }
195   }
196
197   // Create result body.
198   ResultBodyPtr aResultBody = document()->createBody(data(), theIndex);
199
200   // Store modified shape.
201   if(!aBaseShape.get() || aBaseShape->isEqual(theResultShape)) {
202     aResultBody->store(theResultShape, false);
203     setResult(aResultBody, theIndex);
204     return;
205   }
206
207   const int aDelTag = 1;
208   /// sub solids will be placed at labels 3, 4, etc. if result is compound of solids
209   const int aSubTag = 2;
210   int aModTag = aSubTag + 10000;
211   const std::string aModName = "Modified";
212
213   aResultBody->storeModified(aBaseShape, theResultShape, aSubTag);
214
215   std::shared_ptr<GeomAPI_DataMapOfShapeShape> aMapOfSubShapes = theMakeShape->mapOfSubShapes();
216   theObjects.insert(theObjects.end(), thePlanes.begin(), thePlanes.end());
217   int anIndex = 1;
218   for(ListOfShape::const_iterator anIt = theObjects.cbegin(); anIt != theObjects.cend(); ++anIt) {
219     GeomShapePtr aShape = *anIt;
220     std::string aModEdgeName = aModName + "_Edge_" + std::to_string((long long)anIndex);
221     aResultBody->loadAndOrientModifiedShapes(theMakeShape.get(), aShape, GeomAPI_Shape::EDGE,
222       aModTag, aModEdgeName, *aMapOfSubShapes.get(), false, true, true);
223     std::string aModFaceName = aModName + "_Face_" + std::to_string((long long)anIndex++);
224     aResultBody->loadAndOrientModifiedShapes(theMakeShape.get(), aShape, GeomAPI_Shape::FACE,
225       aModTag + 1, aModFaceName, *aMapOfSubShapes.get(), false, true, true);
226     aResultBody->loadDeletedShapes(theMakeShape.get(), aShape, GeomAPI_Shape::FACE, aDelTag);
227   }
228
229   setResult(aResultBody, theIndex);
230 }
231
232
233 //=================     Auxiliary functions     ===================================================
234
235 GeomShapePtr findBase(const GeomShapePtr theObjectShape,
236                       const GeomShapePtr theResultShape,
237                       const GeomAPI_Shape::ShapeType theShapeType,
238                       const std::shared_ptr<GeomAlgoAPI_MakeShape> theMakeShape)
239 {
240   GeomShapePtr aBaseShape;
241   std::shared_ptr<GeomAPI_DataMapOfShapeShape> aMapOfSubShapes = theMakeShape->mapOfSubShapes();
242   for(GeomAPI_ShapeExplorer anObjectSubShapesExp(theObjectShape, theShapeType);
243       anObjectSubShapesExp.more();
244       anObjectSubShapesExp.next()) {
245     GeomShapePtr anObjectSubShape = anObjectSubShapesExp.current();
246     ListOfShape aModifiedShapes;
247     theMakeShape->modified(anObjectSubShape, aModifiedShapes);
248     for(ListOfShape::const_iterator
249         aModIt = aModifiedShapes.cbegin(); aModIt != aModifiedShapes.cend(); ++aModIt) {
250       GeomShapePtr aModShape = *aModIt;
251       if(aMapOfSubShapes->isBound(aModShape)) {
252         aModShape = aMapOfSubShapes->find(aModShape);
253       }
254       if(theResultShape->isSubShape(aModShape)) {
255         aBaseShape = theObjectShape;
256         break;
257       }
258     }
259     if(aBaseShape.get()) {
260       break;
261     }
262   }
263
264   return aBaseShape;
265 }
266
267 static CompsolidSubs::iterator findOrAdd(CompsolidSubs& theList, const GeomShapePtr& theCompsolid)
268 {
269   CompsolidSubs::iterator aFound = theList.begin();
270   for (; aFound != theList.end(); ++aFound)
271     if (aFound->first == theCompsolid)
272       break;
273   if (aFound == theList.end()) {
274     theList.push_back(std::pair<GeomShapePtr, ListOfShape>(theCompsolid, ListOfShape()));
275     aFound = --theList.end();
276   }
277   return aFound;
278 }
279
280 void pullObjectsAndPlanes(const AttributeSelectionListPtr& theSelectedList,
281                           CompsolidSubs& theObjects, ListOfShape& thePlanes)
282 {
283   std::map<ResultBodyPtr, GeomShapePtr> aMapCompsolidShape;
284
285   int aSize = theSelectedList->size();
286   for (int anIndex = 0; anIndex < aSize; ++anIndex) {
287     AttributeSelectionPtr anObjectAttr = theSelectedList->value(anIndex);
288     ResultPtr aContext = anObjectAttr->context();
289     GeomShapePtr anObject = anObjectAttr->value();
290     if (anObject) {
291       GeomShapePtr anOwnerShape = anObject;
292       // check the result is a compsolid and store all used subs in a single list
293       ResultBodyPtr aResCompSolidPtr = ModelAPI_Tools::bodyOwner(aContext);
294       if (aResCompSolidPtr && aResCompSolidPtr->shape()->shapeType() == GeomAPI_Shape::COMPSOLID) {
295         std::map<ResultBodyPtr, GeomShapePtr>::const_iterator
296             aFound = aMapCompsolidShape.find(aResCompSolidPtr);
297         if (aFound != aMapCompsolidShape.end())
298           anOwnerShape = aFound->second;
299         else {
300           anOwnerShape = aResCompSolidPtr->shape();
301           aMapCompsolidShape[aResCompSolidPtr] = anOwnerShape;
302         }
303       }
304
305       CompsolidSubs::iterator aFound = findOrAdd(theObjects, anOwnerShape);
306       aFound->second.push_back(anObject);
307     }
308     else {
309       // It could be a construction plane.
310       thePlanes.push_back(anObjectAttr->context()->shape());
311     }
312   }
313 }
314
315 void resizePlanes(const CompsolidSubs& theObjects, ListOfShape& thePlanes,
316                   std::shared_ptr<GeomAlgoAPI_MakeShapeList>& theMakeShapeList)
317 {
318   ListOfShape aSolidsInOperation;
319   for (CompsolidSubs::const_iterator anIt = theObjects.begin(); anIt != theObjects.end(); ++anIt)
320     aSolidsInOperation.insert(aSolidsInOperation.end(), anIt->second.begin(), anIt->second.end());
321
322   std::list<std::shared_ptr<GeomAPI_Pnt> > aBoundingPoints =
323       GeomAlgoAPI_ShapeTools::getBoundingBox(aSolidsInOperation, 1.0);
324
325   ListOfShape aPlanesCopy = thePlanes;
326   thePlanes.clear();
327
328   // Resize planes to fit in bounding box
329   for (ListOfShape::const_iterator anIt = aPlanesCopy.begin(); anIt != aPlanesCopy.end(); ++anIt) {
330     GeomShapePtr aPlane = *anIt;
331     GeomShapePtr aTool = GeomAlgoAPI_ShapeTools::fitPlaneToBox(aPlane, aBoundingPoints);
332     std::shared_ptr<GeomAlgoAPI_MakeShapeCustom> aMkShCustom(new GeomAlgoAPI_MakeShapeCustom);
333     aMkShCustom->addModified(aPlane, aTool);
334     theMakeShapeList->appendAlgo(aMkShCustom);
335     thePlanes.push_back(aTool);
336   }
337 }
338
339 void unusedSubsOfComposolid(const CompsolidSubs& theObjects, CompsolidSubs& theNotUsed)
340 {
341   for (CompsolidSubs::const_iterator aCSIt = theObjects.begin();
342        aCSIt != theObjects.end(); ++aCSIt) {
343     if (aCSIt->first->shapeType() != GeomAPI_Shape::COMPSOLID)
344       continue;
345
346     // check the compsolid is selected
347     if (aCSIt->second.size() == 1 && aCSIt->first->isEqual(aCSIt->second.front()))
348       continue;
349
350     // process all sub-solids of compsolid
351     ListOfShape aNotUsedSolids;
352     for (GeomAPI_ShapeExplorer anExp(aCSIt->first, GeomAPI_Shape::SOLID);
353          anExp.more(); anExp.next()) {
354       GeomShapePtr aSolidInCompSolid = anExp.current();
355       ListOfShape::const_iterator anIt = aCSIt->second.begin();
356       for (; anIt != aCSIt->second.end(); ++anIt)
357         if (aSolidInCompSolid->isEqual(*anIt))
358           break;
359
360       if (anIt == aCSIt->second.end())
361         aNotUsedSolids.push_back(aSolidInCompSolid);
362     }
363
364     if (!aNotUsedSolids.empty())
365       theNotUsed.push_back(std::pair<GeomShapePtr, ListOfShape>(aCSIt->first, aNotUsedSolids));
366   }
367 }
368
369 static bool cutSubs(const GeomShapePtr& theFirstArgument,
370                     CompsolidSubs& theSubsToCut,
371                     const ListOfShape& theTools,
372                     std::shared_ptr<GeomAlgoAPI_MakeShapeList>& theMakeShapeList,
373                     std::string& theError)
374 {
375   if (theTools.empty())
376     return true;
377
378   std::shared_ptr<GeomAlgoAPI_MakeShape> aCutAlgo;
379
380   for (CompsolidSubs::iterator aUIt= theSubsToCut.begin(); aUIt != theSubsToCut.end(); ++aUIt) {
381     if (aUIt->first == theFirstArgument)
382       continue; // no need to split unused subs of the first compsolid
383
384     // cut from current list of solids
385     aCutAlgo.reset(
386         new GeomAlgoAPI_Boolean(aUIt->second, theTools, GeomAlgoAPI_Boolean::BOOL_CUT));
387     if (isAlgoFailed(aCutAlgo, theError))
388       return false;
389     theMakeShapeList->appendAlgo(aCutAlgo);
390
391     // update list of un-selected objects of the partition
392     GeomAPI_Shape::ShapeType aType = aUIt->second.front()->shapeType();
393     aUIt->second.clear();
394     for (GeomAPI_ShapeExplorer anExp(aCutAlgo->shape(), aType); anExp.more(); anExp.next())
395       aUIt->second.push_back(anExp.current());
396   }
397   return true;
398 }
399
400 bool cutUnusedSubs(CompsolidSubs& theObjects, CompsolidSubs& theNotUsed,
401                    std::shared_ptr<GeomAlgoAPI_MakeShapeList>& theMakeShapeList,
402                    std::string& theError)
403 {
404   GeomShapePtr aFirstArgument = theObjects.front().first;
405
406   // compose a set of tools for the CUT operation:
407   // find the list of unused subs of the first argument or use itself
408   ListOfShape aToolsForUsed;
409   CompsolidSubs::iterator aUIt = theNotUsed.begin();
410   for (; aUIt != theNotUsed.end(); ++aUIt)
411     if (aUIt->first == aFirstArgument) {
412       aToolsForUsed.insert(aToolsForUsed.end(), aUIt->second.begin(), aUIt->second.end());
413       break;
414     }
415   ListOfShape aToolsForUnused;
416   aToolsForUnused.push_back(aFirstArgument);
417
418   // cut subs
419   return cutSubs(aFirstArgument, theObjects, aToolsForUsed, theMakeShapeList, theError)
420       && cutSubs(aFirstArgument, theNotUsed, aToolsForUnused, theMakeShapeList, theError);
421 }
422
423 bool isAlgoFailed(const std::shared_ptr<GeomAlgoAPI_MakeShape>& theAlgo, std::string& theError)
424 {
425   if (!theAlgo->isDone()) {
426     theError = "Error: Partition algorithm failed.";
427     return true;
428   }
429   if (theAlgo->shape()->isNull()) {
430     theError = "Error: Resulting shape is Null.";
431     return true;
432   }
433   if (!theAlgo->isValid()) {
434     theError = "Error: Resulting shape is not valid.";
435     return true;
436   }
437
438   theError.clear();
439   return false;
440 }