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