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