Salome HOME
support fuzzy parameter in all boolean operations
[modules/shaper.git] / src / FeaturesPlugin / FeaturesPlugin_Partition.cpp
1 // Copyright (C) 2014-2022  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_AttributeDouble.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_ShapeBuilder.h>
41 #include <GeomAlgoAPI_ShapeTools.h>
42 #include <GeomAlgoAPI_Tools.h>
43
44 #include <GeomAPI_Face.h>
45 #include <GeomAPI_ShapeExplorer.h>
46 #include <GeomAPI_ShapeIterator.h>
47
48 #include <iostream>
49 #include <list>
50 #include <sstream>
51
52
53 //=================================================================================================
54 FeaturesPlugin_Partition::FeaturesPlugin_Partition()
55 {
56 }
57
58 //=================================================================================================
59 void FeaturesPlugin_Partition::initAttributes()
60 {
61   data()->addAttribute(BASE_OBJECTS_ID(), ModelAPI_AttributeSelectionList::typeId());
62
63   data()->addAttribute(FUZZY_PARAM_ID(), ModelAPI_AttributeDouble::typeId());
64   // Initialize the fuzzy parameter with a value below Precision::Confusion() to indicate,
65   // that the internal algorithms should use their default fuzzy value, if none was specified
66   // by the user.
67   real(FUZZY_PARAM_ID())->setValue(1.e-8);
68   
69   initVersion(BOP_VERSION_9_4(), selectionList(BASE_OBJECTS_ID()));
70 }
71
72 //=================================================================================================
73 void FeaturesPlugin_Partition::execute()
74 {
75   GeomAPI_ShapeHierarchy anObjects;
76   ListOfShape aPlanes;
77
78   // Getting objects.
79   processAttribute(BASE_OBJECTS_ID(), anObjects, aPlanes);
80   if(anObjects.empty()) {
81     static const std::string aFeatureError = "Error: No objects for partition.";
82     setError(aFeatureError);
83     return;
84   }
85
86   // Getting fuzzy parameter.
87   // Used as additional tolerance to eliminate tiny results.
88   double aFuzzy = real(FUZZY_PARAM_ID())->value();
89
90   ListOfShape aBaseObjects = anObjects.objects();
91   aBaseObjects.insert(aBaseObjects.end(), aPlanes.begin(), aPlanes.end());
92
93   // resize planes to the bounding box of operated shapes
94   std::shared_ptr<GeomAlgoAPI_MakeShapeList> aMakeShapeList(new GeomAlgoAPI_MakeShapeList());
95   resizePlanes(anObjects.objects(), aPlanes, aMakeShapeList);
96
97   // cut unused solids of composolids from the objects of partition
98   ListOfShape aTargetObjects, anUnusedSubs;
99   std::string aError;
100   if (!cutSubs(anObjects, aTargetObjects, anUnusedSubs, aFuzzy, aMakeShapeList, aError)) {
101     setError(aError);
102     return;
103   }
104   aBaseObjects.insert(aBaseObjects.end(), anUnusedSubs.begin(), anUnusedSubs.end());
105
106   // perform partition first time to split target solids by planes
107   std::shared_ptr<GeomAlgoAPI_Partition> aPartitionAlgo(
108       new GeomAlgoAPI_Partition(aTargetObjects, aPlanes, aFuzzy));
109
110   // Checking that the algorithm worked properly.
111   if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aPartitionAlgo, getKind(), aError)) {
112     setError(aError);
113     return;
114   }
115
116   aMakeShapeList->appendAlgo(aPartitionAlgo);
117   GeomShapePtr aResultShape = aPartitionAlgo->shape();
118
119   if (!anUnusedSubs.empty()) {
120     // second pass of a partition to split shared faces of compsolids
121     aTargetObjects.clear();
122     aTargetObjects.push_back(aResultShape);
123     aTargetObjects.insert(aTargetObjects.end(), anUnusedSubs.begin(), anUnusedSubs.end());
124
125     aPartitionAlgo.reset(new GeomAlgoAPI_Partition(aTargetObjects, ListOfShape()));
126
127     // Checking that the algorithm worked properly.
128     if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aPartitionAlgo, getKind(), aError)) {
129       setError(aError);
130       return;
131     }
132
133     aMakeShapeList->appendAlgo(aPartitionAlgo);
134     aResultShape = aPartitionAlgo->shape();
135   }
136
137   int aResultIndex = 0;
138
139   if (data()->version().empty()) {
140     // default behaviors of Partition
141     if(aResultShape->shapeType() == GeomAPI_Shape::COMPOUND) {
142       for(GeomAPI_ShapeIterator anIt(aResultShape); anIt.more(); anIt.next()) {
143         storeResult(aBaseObjects, aPlanes, anIt.current(), aMakeShapeList, aResultIndex);
144         ++aResultIndex;
145       }
146     } else {
147       storeResult(aBaseObjects, aPlanes, aResultShape, aMakeShapeList, aResultIndex);
148       ++aResultIndex;
149     }
150   }
151   else {
152     // merge hierarchies of compounds containing objects and tools
153     GeomShapePtr aFirstShape = aResultShape;
154     GeomAPI_ShapeIterator anIt;
155     if (aResultShape->shapeType() == GeomAPI_Shape::COMPOUND) {
156       anIt = GeomAPI_ShapeIterator(aResultShape);
157       aFirstShape = anIt.current();
158       anIt.next();
159     }
160
161     GeomShapePtr aResultCompound =
162       keepUnusedSubsOfCompound(aFirstShape, anObjects, GeomAPI_ShapeHierarchy(), aMakeShapeList);
163
164     if (anIt.more()) {
165       if (aResultCompound->shapeType() != GeomAPI_Shape::COMPOUND) {
166         // put the shape into compound
167         ListOfShape aShapes;
168         aShapes.push_back(aResultCompound);
169         aResultCompound = GeomAlgoAPI_CompoundBuilder::compound(aShapes);
170       }
171       std::shared_ptr<GeomAlgoAPI_ShapeBuilder> aBuilder(new GeomAlgoAPI_ShapeBuilder);
172       for (; anIt.more(); anIt.next())
173         aBuilder->add(aResultCompound, anIt.current());
174       aMakeShapeList->appendAlgo(aBuilder);
175     }
176
177     storeResult(aBaseObjects, aPlanes, aResultCompound, aMakeShapeList, aResultIndex);
178     ++aResultIndex;
179   }
180
181   // Remove the rest results if there were produced in the previous pass.
182   removeResults(aResultIndex);
183 }
184
185 //=================================================================================================
186 void FeaturesPlugin_Partition::storeResult(
187   ListOfShape& theObjects, ListOfShape& thePlanes,
188   const GeomShapePtr theResultShape,
189   const std::shared_ptr<GeomAlgoAPI_MakeShape> theMakeShape,
190   const int theIndex)
191 {
192   // Create result body.
193   ResultBodyPtr aResultBody = document()->createBody(data(), theIndex);
194
195   // if result is same as one of the base object, no modification was performed
196   for(ListOfShape::const_iterator anObj = theObjects.cbegin(); anObj != theObjects.cend(); ++anObj)
197   {
198     if (anObj->get() && (*anObj)->isSame(theResultShape)) {
199       aResultBody->store(theResultShape, false);
200       setResult(aResultBody, theIndex);
201       return;
202     }
203   }
204
205   aResultBody->storeModified(theObjects, theResultShape, theMakeShape);
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 //=================================================================================================
224 static bool cutSubs(ListOfShape& theSubsToCut,
225                     const ListOfShape& theTools,
226                     const double theFuzzy,
227                     std::shared_ptr<GeomAlgoAPI_MakeShapeList>& theMakeShapeList,
228                     std::string& theError)
229 {
230   if (theSubsToCut.empty() || theTools.empty())
231     return true;
232
233   // cut from current list of solids
234   std::shared_ptr<GeomAlgoAPI_MakeShape> aCutAlgo(
235       new GeomAlgoAPI_Boolean(theSubsToCut, theTools, GeomAlgoAPI_Tools::BOOL_CUT, theFuzzy));
236   if (GeomAlgoAPI_Tools::AlgoError::isAlgorithmFailed(aCutAlgo, "", theError))
237     return false;
238   theMakeShapeList->appendAlgo(aCutAlgo);
239
240   // update list of un-selected objects of the partition
241   GeomAPI_Shape::ShapeType aType = theSubsToCut.front()->shapeType();
242   theSubsToCut.clear();
243   for (GeomAPI_ShapeExplorer anExp(aCutAlgo->shape(), aType); anExp.more(); anExp.next())
244     theSubsToCut.push_back(anExp.current());
245   return true;
246 }
247
248 //=================================================================================================
249 bool FeaturesPlugin_Partition::cutSubs(
250     GeomAPI_ShapeHierarchy& theHierarchy,
251     ListOfShape& theUsed,
252     ListOfShape& theNotUsed,
253     const double theFuzzy,
254     std::shared_ptr<GeomAlgoAPI_MakeShapeList>& theMakeShapeList,
255     std::string& theError)
256 {
257   theUsed.clear();
258   theNotUsed.clear();
259
260   GeomAPI_ShapeHierarchy::iterator anIt = theHierarchy.begin();
261
262   // compose a set of tools for the CUT operation:
263   // find the list of unused subs of the first argument or use itself
264   ListOfShape aToolsForUsed, aToolsForUnused;
265   GeomShapePtr aFirstArgument = theHierarchy.parent(*anIt, false);
266   if (aFirstArgument && aFirstArgument->shapeType() == GeomAPI_Shape::COMPSOLID) {
267     theHierarchy.splitCompound(aFirstArgument, theUsed, aToolsForUsed);
268     theNotUsed = aToolsForUsed;
269   }
270   else {
271     aFirstArgument = *anIt;
272     theUsed.push_back(aFirstArgument);
273   }
274   aToolsForUnused.push_back(aFirstArgument);
275
276   // cut subs
277   bool isOk = true;
278   for (++anIt; anIt != theHierarchy.end() && isOk; ++anIt) {
279     ListOfShape aUsed, aNotUsed;
280
281     GeomShapePtr aParent = theHierarchy.parent(*anIt, false);
282     if (aParent && aParent->shapeType() == GeomAPI_Shape::COMPSOLID) {
283       aParent = theHierarchy.parent(*anIt); // get parent once again to mark its subs as processed
284       theHierarchy.splitCompound(aParent, aUsed, aNotUsed);
285     }
286     else
287       aUsed.push_back(*anIt);
288
289     isOk = ::cutSubs(aUsed, aToolsForUsed, theFuzzy, theMakeShapeList, theError)
290         && ::cutSubs(aNotUsed, aToolsForUnused, theFuzzy, theMakeShapeList, theError);
291     if (isOk) {
292       theUsed.insert(theUsed.end(), aUsed.begin(), aUsed.end());
293       theNotUsed.insert(theNotUsed.end(), aNotUsed.begin(), aNotUsed.end());
294     }
295   }
296
297   return isOk;
298 }