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