Salome HOME
Merge remote-tracking branch 'remotes/origin/HigherLevelObjectsHistory'
[modules/shaper.git] / src / FeaturesPlugin / FeaturesPlugin_Partition.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_Partition.h"
21
22 #include <ModelAPI_AttributeBoolean.h>
23 #include <ModelAPI_AttributeInteger.h>
24 #include <ModelAPI_AttributeReference.h>
25 #include <ModelAPI_AttributeSelectionList.h>
26 #include <ModelAPI_BodyBuilder.h>
27 #include <ModelAPI_Data.h>
28 #include <ModelAPI_Document.h>
29 #include <ModelAPI_ResultBody.h>
30 #include <ModelAPI_Session.h>
31 #include <ModelAPI_Tools.h>
32 #include <ModelAPI_Validator.h>
33
34 #include <GeomAlgoAPI_Boolean.h>
35 #include <GeomAlgoAPI_CompoundBuilder.h>
36 #include <GeomAlgoAPI_MakeShapeCustom.h>
37 #include <GeomAlgoAPI_MakeShapeList.h>
38 #include <GeomAlgoAPI_Partition.h>
39 #include <GeomAlgoAPI_ShapeTools.h>
40 #include <GeomAlgoAPI_Tools.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
70 //=================================================================================================
71 FeaturesPlugin_Partition::FeaturesPlugin_Partition()
72 {
73 }
74
75 //=================================================================================================
76 void FeaturesPlugin_Partition::initAttributes()
77 {
78   data()->addAttribute(BASE_OBJECTS_ID(), ModelAPI_AttributeSelectionList::typeId());
79 }
80
81 //=================================================================================================
82 void FeaturesPlugin_Partition::execute()
83 {
84   CompsolidSubs anObjects;
85   ListOfShape aPlanes;
86
87   // Getting objects.
88   pullObjectsAndPlanes(selectionList(BASE_OBJECTS_ID()), anObjects, aPlanes);
89   if(anObjects.empty()) {
90     static const std::string aFeatureError = "Error: No objects for partition.";
91     setError(aFeatureError);
92     return;
93   }
94
95   ListOfShape aBaseObjects;
96   for (CompsolidSubs::iterator anIt = anObjects.begin(); anIt != anObjects.end(); ++anIt)
97     aBaseObjects.insert(aBaseObjects.end(), anIt->second.begin(), anIt->second.end());
98   aBaseObjects.insert(aBaseObjects.end(), aPlanes.begin(), aPlanes.end());
99
100   // resize planes to the bounding box of operated shapes
101   std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeShapeList(new GeomAlgoAPI_MakeShapeList());
102   resizePlanes(anObjects, aPlanes, aMakeShapeList);
103
104   // cut unused solids of composolids from the objects of partition
105   CompsolidSubs anUnusedSubs;
106   unusedSubsOfComposolid(anObjects, anUnusedSubs);
107   for (CompsolidSubs::iterator anIt = anUnusedSubs.begin(); anIt != anUnusedSubs.end(); ++anIt)
108     aBaseObjects.insert(aBaseObjects.end(), anIt->second.begin(), anIt->second.end());
109
110   std::string aError;
111   if (!cutUnusedSubs(anObjects, anUnusedSubs, aMakeShapeList, aError)) {
112     setError(aError);
113     return;
114   }
115
116   // perform partition first time to split target solids
117   ListOfShape aTargetObjects;
118   for (CompsolidSubs::iterator anIt = anObjects.begin(); anIt != anObjects.end(); ++anIt)
119     aTargetObjects.insert(aTargetObjects.end(), anIt->second.begin(), anIt->second.end());
120
121   std::shared_ptr<GeomAlgoAPI_Partition> aPartitionAlgo(
122     new GeomAlgoAPI_Partition(aTargetObjects, aPlanes));
123
124   // Checking that the algorithm worked properly.
125   if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aPartitionAlgo, getKind(), aError)) {
126     setError(aError);
127     return;
128   }
129
130   aMakeShapeList->appendAlgo(aPartitionAlgo);
131   GeomShapePtr aResultShape = aPartitionAlgo->shape();
132
133   if (!anUnusedSubs.empty()) {
134     // second pass of a partition to split shared faces of compsolids
135     aTargetObjects.clear();
136     aTargetObjects.push_back(aResultShape);
137     for (CompsolidSubs::iterator anIt = anUnusedSubs.begin(); anIt != anUnusedSubs.end(); ++anIt)
138       aTargetObjects.insert(aTargetObjects.end(), anIt->second.begin(), anIt->second.end());
139
140     aPartitionAlgo.reset(new GeomAlgoAPI_Partition(aTargetObjects, ListOfShape()));
141
142     // Checking that the algorithm worked properly.
143     if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aPartitionAlgo, getKind(), aError)) {
144       setError(aError);
145       return;
146     }
147
148     aMakeShapeList->appendAlgo(aPartitionAlgo);
149     aResultShape = aPartitionAlgo->shape();
150   }
151
152   int aResultIndex = 0;
153   if(aResultShape->shapeType() == GeomAPI_Shape::COMPOUND) {
154     for(GeomAPI_ShapeIterator anIt(aResultShape); anIt.more(); anIt.next()) {
155       storeResult(aBaseObjects, aPlanes, anIt.current(), aMakeShapeList, aResultIndex);
156       ++aResultIndex;
157     }
158   } else {
159     storeResult(aBaseObjects, aPlanes, aResultShape, aMakeShapeList, aResultIndex);
160     ++aResultIndex;
161   }
162
163   // Remove the rest results if there were produced in the previous pass.
164   removeResults(aResultIndex);
165 }
166
167 //=================================================================================================
168 void FeaturesPlugin_Partition::storeResult(
169   ListOfShape& theObjects, ListOfShape& thePlanes,
170   const GeomShapePtr theResultShape,
171   const std::shared_ptr<GeomAlgoAPI_MakeShape> theMakeShape,
172   const int theIndex)
173 {
174   // Create result body.
175   ResultBodyPtr aResultBody = document()->createBody(data(), theIndex);
176
177   // if result is same as one of the base object, no modification was performed
178   for(ListOfShape::const_iterator anObj = theObjects.cbegin(); anObj != theObjects.cend(); ++anObj)
179   {
180     if (anObj->get() && (*anObj)->isSame(theResultShape)) {
181       aResultBody->store(theResultShape, false);
182       setResult(aResultBody, theIndex);
183       return;
184     }
185   }
186
187   aResultBody->storeModified(theObjects, theResultShape, theMakeShape);
188
189   std::shared_ptr<GeomAPI_DataMapOfShapeShape> aMapOfSubShapes = theMakeShape->mapOfSubShapes();
190   theObjects.insert(theObjects.end(), thePlanes.begin(), thePlanes.end());
191   for (ListOfShape::const_iterator anIt = theObjects.cbegin();
192        anIt != theObjects.cend();
193        ++anIt)
194   {
195     GeomShapePtr aShape = *anIt;
196     aResultBody->loadModifiedShapes(theMakeShape, aShape, GeomAPI_Shape::EDGE);
197     aResultBody->loadModifiedShapes(theMakeShape, aShape, GeomAPI_Shape::FACE);
198     aResultBody->loadDeletedShapes(theMakeShape, aShape, GeomAPI_Shape::FACE);
199   }
200
201   setResult(aResultBody, theIndex);
202 }
203
204
205 //=================     Auxiliary functions     ===================================================
206
207 static CompsolidSubs::iterator findOrAdd(CompsolidSubs& theList, const GeomShapePtr& theCompsolid)
208 {
209   CompsolidSubs::iterator aFound = theList.begin();
210   for (; aFound != theList.end(); ++aFound)
211     if (aFound->first == theCompsolid)
212       break;
213   if (aFound == theList.end()) {
214     theList.push_back(std::pair<GeomShapePtr, ListOfShape>(theCompsolid, ListOfShape()));
215     aFound = --theList.end();
216   }
217   return aFound;
218 }
219
220 void pullObjectsAndPlanes(const AttributeSelectionListPtr& theSelectedList,
221                           CompsolidSubs& theObjects, ListOfShape& thePlanes)
222 {
223   std::map<ResultBodyPtr, GeomShapePtr> aMapCompsolidShape;
224
225   int aSize = theSelectedList->size();
226   for (int anIndex = 0; anIndex < aSize; ++anIndex) {
227     AttributeSelectionPtr anObjectAttr = theSelectedList->value(anIndex);
228     ResultPtr aContext = anObjectAttr->context();
229     GeomShapePtr anObject = anObjectAttr->value();
230     if (anObject) {
231       GeomShapePtr anOwnerShape = anObject;
232       // check the result is a compsolid and store all used subs in a single list
233       ResultBodyPtr aResCompSolidPtr = ModelAPI_Tools::bodyOwner(aContext);
234       if (aResCompSolidPtr && aResCompSolidPtr->shape()->shapeType() == GeomAPI_Shape::COMPSOLID) {
235         std::map<ResultBodyPtr, GeomShapePtr>::const_iterator
236             aFound = aMapCompsolidShape.find(aResCompSolidPtr);
237         if (aFound != aMapCompsolidShape.end())
238           anOwnerShape = aFound->second;
239         else {
240           anOwnerShape = aResCompSolidPtr->shape();
241           aMapCompsolidShape[aResCompSolidPtr] = anOwnerShape;
242         }
243       }
244
245       CompsolidSubs::iterator aFound = findOrAdd(theObjects, anOwnerShape);
246       aFound->second.push_back(anObject);
247     }
248     else {
249       // It could be a construction plane.
250       thePlanes.push_back(anObjectAttr->context()->shape());
251     }
252   }
253 }
254
255 void resizePlanes(const CompsolidSubs& theObjects, ListOfShape& thePlanes,
256                   std::shared_ptr<GeomAlgoAPI_MakeShapeList>& theMakeShapeList)
257 {
258   ListOfShape aSolidsInOperation;
259   for (CompsolidSubs::const_iterator anIt = theObjects.begin(); anIt != theObjects.end(); ++anIt)
260     aSolidsInOperation.insert(aSolidsInOperation.end(), anIt->second.begin(), anIt->second.end());
261
262   std::list<std::shared_ptr<GeomAPI_Pnt> > aBoundingPoints =
263       GeomAlgoAPI_ShapeTools::getBoundingBox(aSolidsInOperation, 1.0);
264
265   ListOfShape aPlanesCopy = thePlanes;
266   thePlanes.clear();
267
268   // Resize planes to fit in bounding box
269   for (ListOfShape::const_iterator anIt = aPlanesCopy.begin(); anIt != aPlanesCopy.end(); ++anIt) {
270     GeomShapePtr aPlane = *anIt;
271     GeomShapePtr aTool = GeomAlgoAPI_ShapeTools::fitPlaneToBox(aPlane, aBoundingPoints);
272     std::shared_ptr<GeomAlgoAPI_MakeShapeCustom> aMkShCustom(new GeomAlgoAPI_MakeShapeCustom);
273     aMkShCustom->addModified(aPlane, aTool);
274     theMakeShapeList->appendAlgo(aMkShCustom);
275     thePlanes.push_back(aTool);
276   }
277 }
278
279 void unusedSubsOfComposolid(const CompsolidSubs& theObjects, CompsolidSubs& theNotUsed)
280 {
281   for (CompsolidSubs::const_iterator aCSIt = theObjects.begin();
282        aCSIt != theObjects.end(); ++aCSIt) {
283     if (aCSIt->first->shapeType() != GeomAPI_Shape::COMPSOLID)
284       continue;
285
286     // check the compsolid is selected
287     if (aCSIt->second.size() == 1 && aCSIt->first->isEqual(aCSIt->second.front()))
288       continue;
289
290     // process all sub-solids of compsolid
291     ListOfShape aNotUsedSolids;
292     for (GeomAPI_ShapeExplorer anExp(aCSIt->first, GeomAPI_Shape::SOLID);
293          anExp.more(); anExp.next()) {
294       GeomShapePtr aSolidInCompSolid = anExp.current();
295       ListOfShape::const_iterator anIt = aCSIt->second.begin();
296       for (; anIt != aCSIt->second.end(); ++anIt)
297         if (aSolidInCompSolid->isEqual(*anIt))
298           break;
299
300       if (anIt == aCSIt->second.end())
301         aNotUsedSolids.push_back(aSolidInCompSolid);
302     }
303
304     if (!aNotUsedSolids.empty())
305       theNotUsed.push_back(std::pair<GeomShapePtr, ListOfShape>(aCSIt->first, aNotUsedSolids));
306   }
307 }
308
309 static bool cutSubs(const GeomShapePtr& theFirstArgument,
310                     CompsolidSubs& theSubsToCut,
311                     const ListOfShape& theTools,
312                     std::shared_ptr<GeomAlgoAPI_MakeShapeList>& theMakeShapeList,
313                     std::string& theError)
314 {
315   if (theTools.empty())
316     return true;
317
318   std::shared_ptr<GeomAlgoAPI_MakeShape> aCutAlgo;
319
320   for (CompsolidSubs::iterator aUIt= theSubsToCut.begin(); aUIt != theSubsToCut.end(); ++aUIt) {
321     if (aUIt->first == theFirstArgument)
322       continue; // no need to split unused subs of the first compsolid
323
324     // cut from current list of solids
325     aCutAlgo.reset(
326         new GeomAlgoAPI_Boolean(aUIt->second, theTools, GeomAlgoAPI_Boolean::BOOL_CUT));
327     if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aCutAlgo, "", theError))
328       return false;
329     theMakeShapeList->appendAlgo(aCutAlgo);
330
331     // update list of un-selected objects of the partition
332     GeomAPI_Shape::ShapeType aType = aUIt->second.front()->shapeType();
333     aUIt->second.clear();
334     for (GeomAPI_ShapeExplorer anExp(aCutAlgo->shape(), aType); anExp.more(); anExp.next())
335       aUIt->second.push_back(anExp.current());
336   }
337   return true;
338 }
339
340 bool cutUnusedSubs(CompsolidSubs& theObjects, CompsolidSubs& theNotUsed,
341                    std::shared_ptr<GeomAlgoAPI_MakeShapeList>& theMakeShapeList,
342                    std::string& theError)
343 {
344   GeomShapePtr aFirstArgument = theObjects.front().first;
345
346   // compose a set of tools for the CUT operation:
347   // find the list of unused subs of the first argument or use itself
348   ListOfShape aToolsForUsed;
349   CompsolidSubs::iterator aUIt = theNotUsed.begin();
350   for (; aUIt != theNotUsed.end(); ++aUIt)
351     if (aUIt->first == aFirstArgument) {
352       aToolsForUsed.insert(aToolsForUsed.end(), aUIt->second.begin(), aUIt->second.end());
353       break;
354     }
355   ListOfShape aToolsForUnused;
356   aToolsForUnused.push_back(aFirstArgument);
357
358   // cut subs
359   return cutSubs(aFirstArgument, theObjects, aToolsForUsed, theMakeShapeList, theError)
360       && cutSubs(aFirstArgument, theNotUsed, aToolsForUnused, theMakeShapeList, theError);
361 }