Salome HOME
5bcbe9f2ba6ef13baeb6f878f7096be023fd5a21
[modules/shaper.git] / src / ModelGeomAlgo / ModelGeomAlgo_Shape.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 <GeomAPI_Shape.h>
21
22 #include "ModelGeomAlgo_Shape.h"
23
24 #include <ModelAPI_AttributeSelection.h>
25 #include <ModelAPI_CompositeFeature.h>
26 #include <ModelAPI_Feature.h>
27 #include <ModelAPI_Result.h>
28 #include <ModelAPI_ResultBody.h>
29 #include <ModelAPI_ResultConstruction.h>
30
31 #include <GeomAPI_Circ.h>
32 #include <GeomAPI_Ellipse.h>
33 #include <GeomAPI_PlanarEdges.h>
34 #include <GeomAPI_Pnt.h>
35
36 #include <GeomAlgoAPI_ShapeTools.h>
37
38
39 #ifdef WIN32
40 #pragma warning(disable : 4996) // for sprintf
41 #endif
42
43 namespace ModelGeomAlgo_Shape
44 {
45   void shapesOfType(const FeaturePtr& theFeature,
46                     const GeomAPI_Shape::ShapeType& theType,
47                     std::set<ResultPtr>& theShapeResults)
48   {
49     theShapeResults.clear();
50     std::list<ResultPtr> aResults = theFeature->results();
51     std::list<ResultPtr>::const_iterator aRIter = aResults.cbegin();
52     for (; aRIter != aResults.cend(); aRIter++) {
53       ResultPtr aResult = *aRIter;
54       GeomShapePtr aShape = aResult->shape();
55       if (aShape.get() && aShape->shapeType() == theType)
56         theShapeResults.insert(aResult);
57     }
58   }
59
60   // Check the point is within shape's bounding box
61   static bool isPointWithinBB(const GeomPointPtr& thePoint,
62                               const GeomShapePtr& theShape,
63                               const double theTolerance)
64   {
65     double aXMin, aXMax, aYMin, aYMax, aZMin, aZMax;
66     return theShape->computeSize(aXMin, aYMin, aZMin, aXMax, aYMax, aZMax) &&
67            thePoint->x() >= aXMin - theTolerance && thePoint->x() <= aXMax + theTolerance &&
68            thePoint->y() >= aYMin - theTolerance && thePoint->y() <= aYMax + theTolerance &&
69            thePoint->z() >= aZMin - theTolerance && thePoint->z() <= aZMax + theTolerance;
70   }
71
72   // Select sub-shape of the given type, which contains the given point
73   static std::list<GeomShapePtr> findSubShape(const GeomShapePtr& theShape,
74                                               const GeomAPI_Shape::ShapeType& theType,
75                                               const GeomPointPtr& thePoint,
76                                               const double theTolerance)
77   {
78     std::list<GeomShapePtr> aFoundSubs;
79     std::list<GeomShapePtr> aSubs = theShape->subShapes(theType);
80     for (std::list<GeomShapePtr>::const_iterator aSubIt = aSubs.begin();
81          aSubIt != aSubs.end(); ++aSubIt) {
82       GeomPointPtr aMiddlePoint = (*aSubIt)->middlePoint();
83       if (!aMiddlePoint)
84         continue;
85
86       double aDistance = aMiddlePoint->distance(thePoint);
87       bool isFound = aDistance < theTolerance;
88       // issue #19019: special workaround for faces, because if the face contains B-spline contour,
89       // the middle point is calculated with respect to its poles, but not a curve itself.
90       // Thus, in some operations (like BOP) the curve may have different number of poles
91       // from time to time, as a result, the face parametric boundaries are floating
92       // as well as the middle point.
93       // The workaround is to find a distance from the picking point to the face, if the distance
94       // between the picking point and the middle point on the face is small to some extend.
95       static const double THE_THRESHOLD = 100.;
96       if (!isFound && aDistance < THE_THRESHOLD * theTolerance && (*aSubIt)->isFace()) {
97         GeomVertexPtr aVertex(new GeomAPI_Vertex(thePoint));
98         aDistance = GeomAlgoAPI_ShapeTools::minimalDistance(aVertex, *aSubIt);
99         isFound = aDistance < theTolerance;
100       }
101       if (isFound)
102         aFoundSubs.push_back(*aSubIt);
103     }
104     return aFoundSubs;
105   }
106
107   // Find circular/elliptic edge, which center/focus coincide with the given point
108   static GeomShapePtr findEdgeByCenter(const GeomShapePtr& theBaseShape,
109                                        const GeomPointPtr& theCenter,
110                                        const double theTolerance,
111                                        int& theCenterType)
112   {
113     theCenterType = (int)ModelAPI_AttributeSelection::NOT_CENTER;
114     std::list<GeomShapePtr> anEdges = theBaseShape->subShapes(GeomAPI_Shape::EDGE);
115     for (std::list<GeomShapePtr>::const_iterator anIt = anEdges.begin();
116          anIt != anEdges.end(); ++anIt) {
117       GeomEdgePtr anEdge = (*anIt)->edge();
118       if (!anEdge)
119         continue;
120
121       if (anEdge->isCircle() || anEdge->isArc()) {
122         GeomCirclePtr aCircle = anEdge->circle();
123         if (aCircle->center()->distance(theCenter) < theTolerance) {
124           theCenterType = (int)ModelAPI_AttributeSelection::CIRCLE_CENTER;
125           return *anIt;
126         }
127       }
128       else if (anEdge->isEllipse()) {
129         GeomEllipsePtr anEllipse = anEdge->ellipse();
130         if (anEllipse->firstFocus()->distance(theCenter) < theTolerance)
131           theCenterType = (int)ModelAPI_AttributeSelection::ELLIPSE_FIRST_FOCUS;
132         else if (anEllipse->secondFocus()->distance(theCenter) < theTolerance)
133           theCenterType = (int)ModelAPI_AttributeSelection::ELLIPSE_SECOND_FOCUS;
134
135         if (theCenterType != (int)ModelAPI_AttributeSelection::NOT_CENTER)
136           return *anIt;
137       }
138     }
139
140     // not found
141     return GeomShapePtr();
142   }
143
144   static void appendSubshapeOfResult(std::list<SubshapeOfResult>& theList,
145       const ResultPtr& theResult,
146       const GeomShapePtr& theSubshape,
147       int theCenterType = (int)ModelAPI_AttributeSelection::NOT_CENTER)
148   {
149     SubshapeOfResult aSR;
150     aSR.myResult = theResult;
151     aSR.mySubshape = theSubshape;
152     aSR.myCenterType = theCenterType;
153     // compound subshapes from other compounds should be processed as whole results
154     if (aSR.mySubshape && aSR.mySubshape->shapeType() <= GeomAPI_Shape::COMPSOLID) {
155       if (theResult->shape()->isEqual(theSubshape))
156         aSR.mySubshape = GeomShapePtr();
157       else {
158         ResultBodyPtr aResult = std::dynamic_pointer_cast<ModelAPI_ResultBody>(theResult);
159         for (int i = 0; aResult && i < aResult->numberOfSubs(); ++i) {
160           ResultBodyPtr aSub = aResult->subResult(i);
161           if (aSub->shape()->isEqual(theSubshape)) {
162             aSR.myResult = aSub;
163             aSR.mySubshape = GeomShapePtr();
164             break;
165           }
166         }
167       }
168     }
169     theList.push_back(aSR);
170   }
171
172   static void appendSubshapeOfResult(std::list<SubshapeOfResult>& theList,
173       const ResultPtr& theResult,
174       const std::list<GeomShapePtr>& theSubshape)
175   {
176     for (std::list<GeomShapePtr>::const_iterator anIt = theSubshape.begin();
177          anIt != theSubshape.end(); ++anIt)
178       appendSubshapeOfResult(theList, theResult, *anIt);
179   }
180
181   static bool findSubshapeInCompsolid(const ResultBodyPtr& theCompsolid,
182                                       const std::shared_ptr<GeomAPI_Pnt>& thePoint,
183                                       const GeomAPI_Shape::ShapeType& theShapeType,
184                                       const double theTolerance,
185                                       std::list<SubshapeOfResult>& theSelected)
186   {
187     bool isSubshapeFound = false;
188     int aNbSolids = theCompsolid->numberOfSubs();
189     for (int i = 0; i < aNbSolids; ++i) {
190       ResultPtr aSubResult = theCompsolid->subResult(i);
191
192       // process subs of compsolid
193       ResultBodyPtr aSubCompSolid = std::dynamic_pointer_cast<ModelAPI_ResultBody>(aSubResult);
194       if (theShapeType != GeomAPI_Shape::COMPSOLID &&
195           aSubCompSolid && aSubCompSolid->numberOfSubs() > 0) {
196         isSubshapeFound = findSubshapeInCompsolid(aSubCompSolid,
197             thePoint, theShapeType, theTolerance, theSelected);
198       }
199       else {
200         GeomShapePtr aSubSolid = aSubResult->shape();
201         if (aSubSolid && isPointWithinBB(thePoint, aSubSolid, theTolerance)) {
202           std::list<GeomShapePtr> aSubshapes =
203               findSubShape(aSubSolid, theShapeType, thePoint, theTolerance);
204           if (!aSubshapes.empty()) {
205             appendSubshapeOfResult(theSelected, aSubResult, aSubshapes);
206             isSubshapeFound = true;
207           }
208         }
209       }
210     }
211     return isSubshapeFound;
212   }
213
214   bool findSubshapeByPoint(const std::shared_ptr<ModelAPI_Feature>& theFeature,
215                            const std::shared_ptr<GeomAPI_Pnt>& thePoint,
216                            const GeomAPI_Shape::ShapeType& theShapeType,
217                            std::list<SubshapeOfResult>& theSelected)
218   {
219     static const double TOLERANCE = 1.5e-6;
220
221     theSelected.clear();
222
223     const std::list<ResultPtr>& aResults = theFeature->results();
224     for (std::list<ResultPtr>::const_iterator aResIt = aResults.begin();
225          aResIt != aResults.end(); ++aResIt) {
226       bool isSubshapeFound = false;
227       GeomShapePtr aCurShape = (*aResIt)->shape();
228       // first of all, check the point is within bounding box of the result
229       if (!aCurShape || !isPointWithinBB(thePoint, aCurShape, TOLERANCE))
230         continue;
231       // now, process all sub-shapes of the given type and check their inner points,
232       // but skip the case the selected type is COMPOUND and the shape is a list of sketch edges
233       // (it will be processed later)
234       std::shared_ptr<GeomAPI_PlanarEdges> aSketchEdges =
235           std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(aCurShape);
236       if (theShapeType != GeomAPI_Shape::COMPOUND || !aSketchEdges) {
237         ResultBodyPtr aCompSolid = std::dynamic_pointer_cast<ModelAPI_ResultBody>(*aResIt);
238         if (aCompSolid) {
239           isSubshapeFound = findSubshapeInCompsolid(aCompSolid,
240               thePoint, theShapeType, TOLERANCE, theSelected);
241         }
242
243         if (!isSubshapeFound) {
244           std::list<GeomShapePtr> aSubshapes =
245               findSubShape(aCurShape, theShapeType, thePoint, TOLERANCE);
246           if (!aSubshapes.empty()) {
247             appendSubshapeOfResult(theSelected, *aResIt, aSubshapes);
248             isSubshapeFound = true;
249           }
250         }
251       }
252       if (isSubshapeFound)
253         continue;
254
255       // special case for ResultConstruction if the FACE is selected
256       ResultConstructionPtr aResConstr =
257           std::dynamic_pointer_cast<ModelAPI_ResultConstruction>(*aResIt);
258       if (aResConstr && theShapeType >= GeomAPI_Shape::FACE) {
259         int aNbFaces = aResConstr->facesNum();
260         for (int aFaceInd = 0; aFaceInd < aNbFaces; ++aFaceInd) {
261           GeomFacePtr aCurFace = aResConstr->face(aFaceInd);
262           // check the point is within bounding box of the face
263           if (!isPointWithinBB(thePoint, aCurFace, TOLERANCE))
264             continue;
265           std::list<GeomShapePtr> aSubshapes =
266               findSubShape(aCurFace, theShapeType, thePoint, TOLERANCE);
267           if (!aSubshapes.empty()) {
268             appendSubshapeOfResult(theSelected, *aResIt, aSubshapes);
269             isSubshapeFound = true;
270           }
271         }
272       }
273       if (isSubshapeFound)
274         continue;
275
276       // next special case: the full sketch is selected
277       // the selection type is a COMPOUND
278       if (aSketchEdges && theShapeType == GeomAPI_Shape::COMPOUND &&
279           aSketchEdges->middlePoint()->distance(thePoint) < TOLERANCE) {
280         // select whole result
281         appendSubshapeOfResult(theSelected, *aResIt, GeomShapePtr());
282         continue;
283       }
284
285       // another special case: the center of circle or the focus of ellipse is selected;
286       // return the corresponding edge and a status of the center
287       if (theShapeType == GeomAPI_Shape::VERTEX) {
288         int aCenterType;
289         GeomShapePtr aSubshape = findEdgeByCenter(aCurShape, thePoint, TOLERANCE, aCenterType);
290         if (aSubshape) {
291           appendSubshapeOfResult(theSelected, *aResIt, aSubshape, aCenterType);
292           continue;
293         }
294       }
295     }
296
297     bool processSketch = theSelected.empty() || (theSelected.size() == 1 &&
298         theSelected.front().myCenterType != (int)ModelAPI_AttributeSelection::NOT_CENTER);
299     // one more special case: the selected entity is a separated sketch point
300     // or an auxiliary sketch edge; they are not included into the sketch result;
301     // thus, it is necessary to pass through the sketch sub-features and find selected.
302     if (processSketch && !aResults.empty() &&
303        (theShapeType == GeomAPI_Shape::VERTEX || theShapeType == GeomAPI_Shape::EDGE)) {
304       CompositeFeaturePtr aCF = std::dynamic_pointer_cast<ModelAPI_CompositeFeature>(theFeature);
305       std::shared_ptr<GeomAPI_PlanarEdges> aSketchEdges =
306           std::dynamic_pointer_cast<GeomAPI_PlanarEdges>(aResults.front()->shape());
307
308       if (aSketchEdges && aCF) {
309         int aNbSubs = aCF->numberOfSubs();
310         for (int aSubInd = 0; aSubInd < aNbSubs; ++aSubInd) {
311           FeaturePtr aSub = aCF->subFeature(aSubInd);
312           const std::list<ResultPtr>& aSubResults = aSub->results();
313           for (std::list<ResultPtr>::const_iterator aSRIt = aSubResults.begin();
314                aSRIt != aSubResults.end(); ++aSRIt) {
315             GeomShapePtr aCurShape = (*aSRIt)->shape();
316             std::list<GeomShapePtr> aSubshapes =
317                 findSubShape(aCurShape, theShapeType, thePoint, TOLERANCE);
318             if (!aSubshapes.empty()) {
319               appendSubshapeOfResult(theSelected, aResults.front(), aSubshapes);
320               break;
321             }
322           }
323         }
324       }
325     }
326
327     return !theSelected.empty();
328   }
329 } // namespace ModelGeomAlgo_Shape