Salome HOME
Copyright update 2022
[modules/shaper.git] / src / GeomAlgoAPI / GeomAlgoAPI_ShapeTools.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 "GeomAlgoAPI_ShapeTools.h"
21
22 #include "GeomAlgoAPI_SketchBuilder.h"
23
24 #include <GeomAPI_Ax1.h>
25 #include <GeomAPI_Edge.h>
26 #include <GeomAPI_Dir.h>
27 #include <GeomAPI_Face.h>
28 #include <GeomAPI_Pln.h>
29 #include <GeomAPI_Pnt.h>
30 #include <GeomAPI_Wire.h>
31
32 #include <Approx_CurvilinearParameter.hxx>
33
34 #include <Bnd_Box.hxx>
35
36 #include <BRep_Tool.hxx>
37 #include <BRep_Builder.hxx>
38 #include <BRepAlgo.hxx>
39 #include <BRepAlgo_FaceRestrictor.hxx>
40 #include <BRepAdaptor_Curve.hxx>
41 #include <BRepBndLib.hxx>
42 #include <BRepBuilderAPI_Copy.hxx>
43 #include <BRepBuilderAPI_FindPlane.hxx>
44 #include <BRepBuilderAPI_MakeEdge.hxx>
45 #include <BRepBuilderAPI_MakeFace.hxx>
46 #include <BRepBuilderAPI_MakeVertex.hxx>
47 #include <BRepBuilderAPI_MakeWire.hxx>
48 #include <BRepCheck_Analyzer.hxx>
49 #include <BRepExtrema_DistShapeShape.hxx>
50 #include <BRepExtrema_ExtCF.hxx>
51 #include <BRepGProp.hxx>
52 #include <BRepTools.hxx>
53 #include <BRepTools_WireExplorer.hxx>
54 #include <BRepTopAdaptor_FClass2d.hxx>
55 #include <BRepClass_FaceClassifier.hxx>
56 #include <BRepLib_CheckCurveOnSurface.hxx>
57 #include <BRepLProp.hxx>
58
59 #include <BOPAlgo_Builder.hxx>
60
61 #include <Geom2d_Curve.hxx>
62 #include <Geom2d_Curve.hxx>
63
64 #include <Geom_BSplineCurve.hxx>
65 #include <Geom_CylindricalSurface.hxx>
66 #include <Geom_Line.hxx>
67 #include <Geom_Plane.hxx>
68 #include <Geom_RectangularTrimmedSurface.hxx>
69
70 #include <GeomAdaptor_HCurve.hxx>
71
72 #include <GeomAPI_ProjectPointOnCurve.hxx>
73 #include <GeomAPI_ShapeIterator.h>
74
75 #include <GeomLib_IsPlanarSurface.hxx>
76 #include <GeomLib_Tool.hxx>
77 #include <GeomAPI_IntCS.hxx>
78
79 #include <gp_Pln.hxx>
80 #include <GProp_GProps.hxx>
81
82 #include <IntAna_IntConicQuad.hxx>
83 #include <IntAna_Quadric.hxx>
84
85 #include <ShapeAnalysis.hxx>
86 #include <ShapeAnalysis_Surface.hxx>
87
88 #include <TopoDS.hxx>
89 #include <TopoDS_Edge.hxx>
90 #include <TopoDS_Face.hxx>
91 #include <TopoDS_Shape.hxx>
92 #include <TopoDS_Shell.hxx>
93 #include <TopoDS_Vertex.hxx>
94 #include <TopoDS_Builder.hxx>
95
96 #include <TopExp.hxx>
97 #include <TopExp_Explorer.hxx>
98
99 #include <TopTools_ListIteratorOfListOfShape.hxx>
100
101 #include <NCollection_Vector.hxx>
102
103 #include <LocalAnalysis_SurfaceContinuity.hxx>
104
105 //==================================================================================================
106 static GProp_GProps props(const TopoDS_Shape& theShape)
107 {
108   GProp_GProps aGProps;
109
110   if (theShape.ShapeType() == TopAbs_EDGE || theShape.ShapeType() == TopAbs_WIRE)
111   {
112     BRepGProp::LinearProperties(theShape, aGProps);
113   }
114   else if (theShape.ShapeType() == TopAbs_FACE || theShape.ShapeType() == TopAbs_SHELL)
115   {
116     const Standard_Real anEps = 1.e-6;
117     BRepGProp::SurfaceProperties(theShape, aGProps, anEps);
118   }
119   else if (theShape.ShapeType() == TopAbs_SOLID || theShape.ShapeType() == TopAbs_COMPSOLID)
120   {
121     BRepGProp::VolumeProperties(theShape, aGProps);
122   }
123   else if (theShape.ShapeType() == TopAbs_COMPOUND)
124   {
125     for (TopoDS_Iterator anIt(theShape); anIt.More(); anIt.Next())
126     {
127       aGProps.Add(props(anIt.Value()));
128     }
129   }
130
131   return aGProps;
132 }
133
134 //==================================================================================================
135 double GeomAlgoAPI_ShapeTools::length(const std::shared_ptr<GeomAPI_Shape> theShape)
136 {
137   GProp_GProps aGProps;
138   if (!theShape.get()) {
139     return 0.0;
140   }
141   const TopoDS_Shape& aShape = theShape->impl<TopoDS_Shape>();
142   if (aShape.IsNull()) {
143     return 0.0;
144   }
145
146   BRepGProp::LinearProperties(aShape, aGProps, Standard_True);
147   return  aGProps.Mass();
148 }
149
150 //==================================================================================================
151 double GeomAlgoAPI_ShapeTools::volume(const std::shared_ptr<GeomAPI_Shape> theShape)
152 {
153   if (!theShape.get()) {
154     return 0.0;
155   }
156   const TopoDS_Shape& aShape = theShape->impl<TopoDS_Shape>();
157   if (aShape.IsNull()) {
158     return 0.0;
159   }
160   const Standard_Real anEps = 1.e-6;
161   double aVolume = 0.0;
162   for (TopExp_Explorer anExp(aShape, TopAbs_SOLID); anExp.More(); anExp.Next()) {
163     GProp_GProps aGProps;
164     BRepGProp::VolumeProperties(anExp.Current(), aGProps, anEps);
165     aVolume += aGProps.Mass();
166   }
167   return aVolume;
168 }
169
170 //==================================================================================================
171 double GeomAlgoAPI_ShapeTools::area (const std::shared_ptr<GeomAPI_Shape> theShape)
172 {
173   GProp_GProps aGProps;
174   if (!theShape.get()) {
175     return 0.0;
176   }
177   const TopoDS_Shape& aShape = theShape->impl<TopoDS_Shape>();
178   if (aShape.IsNull()) {
179     return 0.0;
180   }
181   const Standard_Real anEps = 1.e-6;
182
183   BRepGProp::SurfaceProperties(aShape, aGProps, anEps);
184   return aGProps.Mass();
185 }
186
187 //==================================================================================================
188 bool GeomAlgoAPI_ShapeTools::isContinuousFaces(const GeomShapePtr& theFace1,
189                                                const GeomShapePtr& theFace2,
190                                                const GeomPointPtr& thePoint,
191                                                const double & theAngle,
192                                                std::string& theError)
193 {
194
195   #ifdef _DEBUG
196   std::cout << "isContinuousFaces " << std::endl;
197   #endif
198
199   if (!thePoint.get()) {
200       theError = "isContinuousFaces : An invalid argument";
201       return false;
202   }
203   const gp_Pnt& aPoint = thePoint->impl<gp_Pnt>();
204
205   // Getting base shape.
206   if (!theFace1.get()) {
207     theError = "isContinuousFaces : An invalid argument";
208     return false;
209   }
210
211   TopoDS_Shape aShape1 = theFace1->impl<TopoDS_Shape>();
212
213   if (aShape1.IsNull()) {
214     theError = "isContinuousFaces : An invalid argument";
215     return false;
216   }
217
218   // Getting base shape.
219   if (!theFace2.get()) {
220     theError = "isContinuousFaces : An invalid argument";
221     return false;
222   }
223
224   TopoDS_Shape aShape2 = theFace2->impl<TopoDS_Shape>();
225
226   if (aShape2.IsNull()) {
227     theError = "isContinuousFaces : An invalid argument";
228     return false;
229   }
230
231   TopoDS_Face aFace1 = TopoDS::Face(aShape1);
232   if (aFace1.IsNull()) {
233     theError = "isContinuousFaces : An invalid argument";
234     return false;
235   }
236
237   Handle(Geom_Surface) aSurf1 = BRep_Tool::Surface(aFace1);
238   if (aSurf1.IsNull()) {
239     theError = "isContinuousFaces : An invalid surface";
240     return false;
241   }
242
243   ShapeAnalysis_Surface aSAS1(aSurf1);
244   gp_Pnt2d aPointOnFace1 = aSAS1.ValueOfUV(aPoint, Precision::Confusion());
245
246   TopoDS_Face aFace2 = TopoDS::Face(aShape2);
247   if (aFace2.IsNull()) {
248     theError = "isContinuousFaces : An invalid argument";
249     return false;
250   }
251
252   Handle(Geom_Surface) aSurf2 = BRep_Tool::Surface(aFace2);
253   if (aSurf2.IsNull()) {
254     theError = "isContinuousFaces : An invalid surface";
255     return false;
256   }
257
258   ShapeAnalysis_Surface aSAS2(aSurf2);
259   gp_Pnt2d aPointOnFace2= aSAS2.ValueOfUV(aPoint, Precision::Confusion());
260
261   bool aRes = false;
262   try {
263     OCC_CATCH_SIGNALS;
264     LocalAnalysis_SurfaceContinuity aLocAnal(aSurf1,
265                                              aPointOnFace1.X(),
266                                              aPointOnFace1.Y(),
267                                              aSurf2,
268                                              aPointOnFace2.X(),
269                                              aPointOnFace2.Y(),
270                                              GeomAbs_Shape::GeomAbs_G1, // Order
271                                              0.001, // EpsNul
272                                              0.001, // EpsC0
273                                              0.001, // EpsC1
274                                              0.001, // EpsC2
275                                              theAngle * M_PI / 180.0); //EpsG1
276     aRes = aLocAnal.IsG1();
277   }
278   catch (Standard_Failure const& anException) {
279     theError = "LocalAnalysis_SurfaceContinuity error :";
280     theError += anException.GetMessageString();
281   }
282
283   return aRes;
284 }
285
286 //==================================================================================================
287 std::shared_ptr<GeomAPI_Pnt>
288   GeomAlgoAPI_ShapeTools::centreOfMass(const std::shared_ptr<GeomAPI_Shape> theShape)
289 {
290   GProp_GProps aGProps;
291   if (!theShape) {
292     return std::shared_ptr<GeomAPI_Pnt>();
293   }
294   const TopoDS_Shape& aShape = theShape->impl<TopoDS_Shape>();
295   if (aShape.IsNull()) {
296     return std::shared_ptr<GeomAPI_Pnt>();
297   }
298   gp_Pnt aCentre;
299   if (aShape.ShapeType() == TopAbs_VERTEX) {
300     aCentre = BRep_Tool::Pnt(TopoDS::Vertex(aShape));
301   } else {
302     aGProps = props(aShape);
303     aCentre = aGProps.CentreOfMass();
304   }
305
306   return std::shared_ptr<GeomAPI_Pnt>(new GeomAPI_Pnt(aCentre.X(), aCentre.Y(), aCentre.Z()));
307 }
308
309 //==================================================================================================
310 double GeomAlgoAPI_ShapeTools::radius(const std::shared_ptr<GeomAPI_Face>& theCylinder)
311 {
312   double aRadius = -1.0;
313   if (theCylinder->isCylindrical()) {
314     const TopoDS_Shape& aShape = theCylinder->impl<TopoDS_Shape>();
315     Handle(Geom_Surface) aSurf = BRep_Tool::Surface(TopoDS::Face(aShape));
316     Handle(Geom_CylindricalSurface) aCyl = Handle(Geom_CylindricalSurface)::DownCast(aSurf);
317     if (!aCyl.IsNull())
318       aRadius = aCyl->Radius();
319   }
320   return aRadius;
321 }
322
323 //==================================================================================================
324 namespace {
325
326 auto getExtemaDistShape = [](const GeomShapePtr& theShape1,
327     const GeomShapePtr& theShape2) -> BRepExtrema_DistShapeShape
328 {
329   const TopoDS_Shape& aShape1 = theShape1->impl<TopoDS_Shape>();
330   const TopoDS_Shape& aShape2 = theShape2->impl<TopoDS_Shape>();
331
332   BRepExtrema_DistShapeShape aDist(aShape1, aShape2);
333   aDist.Perform();
334   return aDist;
335 };
336 }
337
338 double GeomAlgoAPI_ShapeTools::minimalDistance(const GeomShapePtr& theShape1,
339                                                const GeomShapePtr& theShape2)
340 {
341   BRepExtrema_DistShapeShape aDist = getExtemaDistShape(theShape1, theShape2);
342   return aDist.IsDone() ? aDist.Value() : Precision::Infinite();
343 }
344
345 double GeomAlgoAPI_ShapeTools::minimalDistance(const GeomShapePtr& theShape1,
346                                                const GeomShapePtr& theShape2,
347                                                std::array<double, 3> & fromShape1To2)
348 {
349   BRepExtrema_DistShapeShape aDist = getExtemaDistShape(theShape1, theShape2);
350   const auto & pt1 = aDist.PointOnShape1(1);
351   const auto & pt2 = aDist.PointOnShape2(1) ;
352   fromShape1To2[0] = pt2.X() - pt1.X();
353   fromShape1To2[1] = pt2.Y() - pt1.Y();
354   fromShape1To2[2] = pt2.Z() - pt1.Z();
355   return aDist.IsDone() ? aDist.Value() : Precision::Infinite();
356 }
357
358 //==================================================================================================
359 std::shared_ptr<GeomAPI_Shape> GeomAlgoAPI_ShapeTools::combineShapes(
360   const std::shared_ptr<GeomAPI_Shape> theCompound,
361   const GeomAPI_Shape::ShapeType theType,
362   ListOfShape& theResuts)
363 {
364
365   ListOfShape aResCombinedShapes;
366   ListOfShape aResFreeShapes;
367
368   GeomShapePtr aResult = theCompound;
369
370   if (!theCompound.get()) {
371     return aResult;
372   }
373
374   if (theType != GeomAPI_Shape::SHELL && theType != GeomAPI_Shape::COMPSOLID) {
375     return aResult;
376   }
377
378   TopAbs_ShapeEnum aTS = TopAbs_EDGE;
379   TopAbs_ShapeEnum aTA = TopAbs_FACE;
380   if (theType == GeomAPI_Shape::COMPSOLID) {
381     aTS = TopAbs_FACE;
382     aTA = TopAbs_SOLID;
383   }
384
385   // map from the resulting shapes to minimal index of the used shape from theCompound list
386   std::map<GeomShapePtr, int> anInputOrder;
387   // map from ancestors-shapes to the index of shapes in theCompound
388   NCollection_DataMap<TopoDS_Shape, int> anAncestorsOrder;
389
390   // Get free shapes.
391   int anOrder = 0;
392   const TopoDS_Shape& aShapesComp = theCompound->impl<TopoDS_Shape>();
393   for (TopoDS_Iterator anIter(aShapesComp); anIter.More(); anIter.Next(), anOrder++) {
394     const TopoDS_Shape& aShape = anIter.Value();
395     if (aShape.ShapeType() > aTA) {
396       std::shared_ptr<GeomAPI_Shape> aGeomShape(new GeomAPI_Shape);
397       aGeomShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(aShape));
398       aResFreeShapes.push_back(aGeomShape);
399       anInputOrder[aGeomShape] = anOrder;
400     } else {
401       for (TopExp_Explorer anExp(aShape, aTA); anExp.More(); anExp.Next()) {
402         anAncestorsOrder.Bind(anExp.Current(), anOrder);
403       }
404     }
405   }
406
407   // Map sub-shapes and shapes.
408   TopTools_IndexedDataMapOfShapeListOfShape aMapSA;
409   TopExp::MapShapesAndAncestors(aShapesComp, aTS, aTA, aMapSA);
410   if (aMapSA.IsEmpty()) {
411     return aResult;
412   }
413   theResuts.clear();
414
415   // Get all shapes with common sub-shapes and free shapes.
416   NCollection_Map<TopoDS_Shape> aFreeShapes;
417   NCollection_Vector<NCollection_Map<TopoDS_Shape>> aShapesWithCommonSubshapes;
418   for (TopTools_IndexedDataMapOfShapeListOfShape::Iterator
419       anIter(aMapSA); anIter.More(); anIter.Next()) {
420     TopTools_ListOfShape& aListOfShape = anIter.ChangeValue();
421     if (aListOfShape.IsEmpty()) {
422       continue;
423     }
424     else if (aListOfShape.Size() == 1) {
425       const TopoDS_Shape& aF = aListOfShape.First();
426       aFreeShapes.Add(aF);
427       aListOfShape.Clear();
428     } else {
429       NCollection_List<TopoDS_Shape> aTempList;
430       NCollection_Map<TopoDS_Shape> aTempMap;
431       for (TopTools_ListOfShape::Iterator aListIt(aListOfShape); aListIt.More(); aListIt.Next()) {
432         aTempList.Append(aListIt.Value());
433         aTempMap.Add(aListIt.Value());
434         aFreeShapes.Remove(aListIt.Value());
435       }
436       aListOfShape.Clear();
437       for (NCollection_List<TopoDS_Shape>::Iterator
438           aTempIter(aTempList); aTempIter.More(); aTempIter.Next()) {
439         const TopoDS_Shape& aTempShape = aTempIter.Value();
440         for (TopTools_IndexedDataMapOfShapeListOfShape::Iterator
441             anIter2(aMapSA); anIter2.More(); anIter2.Next()) {
442           TopTools_ListOfShape& aTempListOfShape = anIter2.ChangeValue();
443           if (aTempListOfShape.IsEmpty()) {
444             continue;
445           } else if (aTempListOfShape.Size() == 1 && aTempListOfShape.First() == aTempShape) {
446             aTempListOfShape.Clear();
447           } else if (aTempListOfShape.Size() > 1) {
448             TopTools_ListOfShape::Iterator anIt1(aTempListOfShape);
449             for (; anIt1.More(); anIt1.Next()) {
450               if (anIt1.Value() == aTempShape) {
451                 TopTools_ListOfShape::Iterator anIt2(aTempListOfShape);
452                 for (; anIt2.More(); anIt2.Next())
453                 {
454                   if (anIt2.Value() != anIt1.Value()) {
455                     if (aTempMap.Add(anIt2.Value())) {
456                       aTempList.Append(anIt2.Value());
457                       aFreeShapes.Remove(anIt2.Value());
458                     }
459                   }
460                 }
461                 aTempListOfShape.Clear();
462                 break;
463               }
464             }
465           }
466         }
467       }
468       aShapesWithCommonSubshapes.Append(aTempMap);
469     }
470   }
471
472   // Combine shapes with common sub-shapes.
473   for (NCollection_Vector<NCollection_Map<TopoDS_Shape>>::Iterator
474       anIter(aShapesWithCommonSubshapes); anIter.More(); anIter.Next()) {
475     TopoDS_Shell aShell;
476     TopoDS_CompSolid aCSolid;
477     TopoDS_Builder aBuilder;
478     anOrder = -1;
479     theType ==
480       GeomAPI_Shape::COMPSOLID ? aBuilder.MakeCompSolid(aCSolid) : aBuilder.MakeShell(aShell);
481     NCollection_Map<TopoDS_Shape>& aShapesMap = anIter.ChangeValue();
482     for (TopExp_Explorer anExp(aShapesComp, aTA); anExp.More(); anExp.Next()) {
483       const TopoDS_Shape& aShape = anExp.Current();
484       if (aShapesMap.Contains(aShape)) {
485         theType ==
486           GeomAPI_Shape::COMPSOLID ? aBuilder.Add(aCSolid, aShape) : aBuilder.Add(aShell, aShape);
487         aShapesMap.Remove(aShape);
488         int aThisOrder = anAncestorsOrder.Find(aShape);
489         if (anOrder == -1 || aThisOrder < anOrder)
490           anOrder = aThisOrder; // take the minimum order position
491       }
492     }
493     std::shared_ptr<GeomAPI_Shape> aGeomShape(new GeomAPI_Shape);
494     TopoDS_Shape* aSh = theType == GeomAPI_Shape::COMPSOLID ? new TopoDS_Shape(aCSolid) :
495                                                               new TopoDS_Shape(aShell);
496     aGeomShape->setImpl<TopoDS_Shape>(aSh);
497     aResCombinedShapes.push_back(aGeomShape);
498     anInputOrder[aGeomShape] = anOrder;
499   }
500
501   // Adding free shapes.
502   for (TopExp_Explorer anExp(aShapesComp, aTA); anExp.More(); anExp.Next()) {
503     const TopoDS_Shape& aShape = anExp.Current();
504     if (aFreeShapes.Contains(aShape)) {
505       std::shared_ptr<GeomAPI_Shape> aGeomShape(new GeomAPI_Shape);
506       aGeomShape->setImpl<TopoDS_Shape>(new TopoDS_Shape(aShape));
507       aResFreeShapes.push_back(aGeomShape);
508       anInputOrder[aGeomShape] = anAncestorsOrder.Find(aShape);
509     }
510   }
511
512   if (aResCombinedShapes.size() == 1 && aResFreeShapes.size() == 0) {
513     aResult = aResCombinedShapes.front();
514     theResuts.push_back(aResult);
515   } else if (aResCombinedShapes.size() == 0 && aResFreeShapes.size() == 1) {
516     aResult = aResFreeShapes.front();
517     theResuts.push_back(aResult);
518   } else {
519     TopoDS_Compound aResultComp;
520     TopoDS_Builder aBuilder;
521     aBuilder.MakeCompound(aResultComp);
522     // put to result compound and result list in accordance to the order numbers
523     std::map<GeomShapePtr, int>::iterator anInputIter = anInputOrder.begin();
524     std::map<int, GeomShapePtr> aNums;
525     for (; anInputIter != anInputOrder.end(); anInputIter++)
526       aNums[anInputIter->second] = anInputIter->first;
527     std::map<int, GeomShapePtr>::iterator aNumsIter = aNums.begin();
528     for (; aNumsIter != aNums.end(); aNumsIter++) {
529       aBuilder.Add(aResultComp, (aNumsIter->second)->impl<TopoDS_Shape>());
530       theResuts.push_back(aNumsIter->second);
531     }
532     aResult->setImpl(new TopoDS_Shape(aResultComp));
533   }
534
535   return aResult;
536 }
537
538 //==================================================================================================
539 static void addSimpleShapeToList(const TopoDS_Shape& theShape,
540                                  NCollection_List<TopoDS_Shape>& theList)
541 {
542   if (theShape.IsNull()) {
543     return;
544   }
545
546   if (theShape.ShapeType() == TopAbs_COMPOUND) {
547     for (TopoDS_Iterator anIt(theShape); anIt.More(); anIt.Next()) {
548       addSimpleShapeToList(anIt.Value(), theList);
549     }
550   } else {
551     theList.Append(theShape);
552   }
553 }
554
555 //==================================================================================================
556 static TopoDS_Compound makeCompound(const NCollection_List<TopoDS_Shape> theShapes)
557 {
558   TopoDS_Compound aCompound;
559
560   BRep_Builder aBuilder;
561   aBuilder.MakeCompound(aCompound);
562
563   for (NCollection_List<TopoDS_Shape>::Iterator anIt(theShapes); anIt.More(); anIt.Next()) {
564     aBuilder.Add(aCompound, anIt.Value());
565   }
566
567   return aCompound;
568 }
569
570 //==================================================================================================
571 std::shared_ptr<GeomAPI_Shape> GeomAlgoAPI_ShapeTools::groupSharedTopology(
572   const std::shared_ptr<GeomAPI_Shape> theCompound)
573 {
574   GeomShapePtr aResult = theCompound;
575
576   if (!theCompound.get()) {
577     return aResult;
578   }
579
580   TopoDS_Shape anInShape = aResult->impl<TopoDS_Shape>();
581   NCollection_List<TopoDS_Shape> anUngroupedShapes, aStillUngroupedShapes;
582   addSimpleShapeToList(anInShape, anUngroupedShapes);
583
584   // Iterate over all shapes and find shapes with shared vertices.
585   TopTools_ListOfShape allVertices;
586   TopTools_DataMapOfShapeListOfShape aVertexShapesMap;
587   for (NCollection_List<TopoDS_Shape>::Iterator aShapesIt(anUngroupedShapes);
588     aShapesIt.More();
589     aShapesIt.Next()) {
590     const TopoDS_Shape& aShape = aShapesIt.Value();
591     for (TopExp_Explorer aShapeExp(aShape, TopAbs_VERTEX);
592       aShapeExp.More();
593       aShapeExp.Next()) {
594       const TopoDS_Shape& aVertex = aShapeExp.Current();
595       if (!aVertexShapesMap.IsBound(aVertex)) {
596         NCollection_List<TopoDS_Shape> aList;
597         aList.Append(aShape);
598         allVertices.Append(aVertex);
599         aVertexShapesMap.Bind(aVertex, aList);
600       }
601       else {
602         if (!aVertexShapesMap.ChangeFind(aVertex).Contains(aShape)) {
603           aVertexShapesMap.ChangeFind(aVertex).Append(aShape);
604         }
605       }
606     }
607   }
608
609   // Iterate over the map and group shapes.
610   NCollection_Vector<TopTools_MapOfShape> aGroups; // groups of shapes connected by vertices
611   while (!allVertices.IsEmpty()) {
612     // Get first group of shapes in map, and then unbind it.
613     const TopoDS_Shape& aKey = allVertices.First();
614     TopTools_ListOfShape aConnectedShapes = aVertexShapesMap.Find(aKey);
615     aVertexShapesMap.UnBind(aKey);
616     allVertices.Remove(aKey);
617     // Iterate over shapes in this group and add to it shapes from groups in map.
618     for (TopTools_ListOfShape::Iterator aConnectedIt(aConnectedShapes);
619       aConnectedIt.More(); aConnectedIt.Next()) {
620       const TopoDS_Shape& aConnected = aConnectedIt.Value();
621       TopTools_ListOfShape aKeysToUnbind;
622       for (TopTools_ListOfShape::Iterator aKeysIt(allVertices);
623         aKeysIt.More();
624         aKeysIt.Next()) {
625         const TopTools_ListOfShape& anOtherConnected = aVertexShapesMap(aKeysIt.Value());
626         if (!anOtherConnected.Contains(aConnected)) {
627           // Other connected group does not contain shape from our connected group
628           continue;
629         }
630         // Other is connected to our, so add them to our connected
631         for (TopTools_ListOfShape::Iterator anOtherIt(anOtherConnected);
632           anOtherIt.More();
633           anOtherIt.Next()) {
634           const TopoDS_Shape& aShape = anOtherIt.Value();
635           if (!aConnectedShapes.Contains(aShape)) {
636             aConnectedShapes.Append(aShape);
637           }
638         }
639         // Save key to unbind from this map.
640         aKeysToUnbind.Append(aKeysIt.Value());
641       }
642       // Unbind groups from map that we added to our group.
643       for (TopTools_ListOfShape::Iterator aKeysIt(aKeysToUnbind);
644         aKeysIt.More();
645         aKeysIt.Next()) {
646         aVertexShapesMap.UnBind(aKeysIt.Value());
647         allVertices.Remove(aKeysIt.Value());
648       }
649     }
650     // Sort shapes from the most complicated to the simplest ones
651     TopTools_MapOfShape aSortedGroup;
652     for (int aST = TopAbs_COMPOUND; aST <= TopAbs_SHAPE; ++aST) {
653       TopTools_ListOfShape::Iterator anIt(aConnectedShapes);
654       while (anIt.More()) {
655         if (anIt.Value().ShapeType() == aST) {
656           aSortedGroup.Add(anIt.Value());
657           aConnectedShapes.Remove(anIt);
658         }
659         else {
660           anIt.Next();
661         }
662       }
663     }
664     aGroups.Append(aSortedGroup);
665   }
666
667   TopoDS_Compound aCompound;
668   BRep_Builder aBuilder;
669   aBuilder.MakeCompound(aCompound);
670   ListOfShape aSolids;
671   for (NCollection_Vector<TopTools_MapOfShape>::Iterator anIt(aGroups); anIt.More(); anIt.Next()) {
672     const TopTools_MapOfShape& aGroup = anIt.ChangeValue();
673     GeomShapePtr aGeomShape(new GeomAPI_Shape());
674     if (aGroup.Size() == 1) {
675       TopTools_MapOfShape::Iterator aOneShapeIter(aGroup);
676       aGeomShape->setImpl(new TopoDS_Shape(aOneShapeIter.Value()));
677     } else {
678       // make sub-shapes in the group have order same as in original shape
679       TopTools_ListOfShape anOrderedGoup;
680       NCollection_List<TopoDS_Shape>::Iterator anUngrouped(anUngroupedShapes);
681       for (; anUngrouped.More(); anUngrouped.Next()) {
682         if (aGroup.Contains(anUngrouped.Value()))
683           anOrderedGoup.Append(anUngrouped.Value());
684       }
685       aGeomShape->setImpl(new TopoDS_Shape(makeCompound(anOrderedGoup)));
686       aGeomShape = GeomAlgoAPI_ShapeTools::combineShapes(aGeomShape,
687                                                          GeomAPI_Shape::COMPSOLID,
688                                                          aSolids);
689     }
690     aBuilder.Add(aCompound, aGeomShape->impl<TopoDS_Shape>());
691   }
692
693   if (!aCompound.IsNull()) {
694     aResult->setImpl(new TopoDS_Shape(aCompound));
695   }
696
697   return aResult;
698 }
699
700 //==================================================================================================
701 bool GeomAlgoAPI_ShapeTools::hasSharedTopology(const ListOfShape& theShapes,
702                                                const GeomAPI_Shape::ShapeType theShapeType)
703 {
704   TopTools_IndexedMapOfShape aSubs;
705   for (ListOfShape::const_iterator anIt = theShapes.begin(); anIt != theShapes.end(); ++anIt) {
706     TopTools_IndexedMapOfShape aCurSubs;
707     TopExp::MapShapes((*anIt)->impl<TopoDS_Shape>(), (TopAbs_ShapeEnum)theShapeType, aCurSubs);
708     for (TopTools_IndexedMapOfShape::Iterator aSubIt(aCurSubs); aSubIt.More(); aSubIt.Next()) {
709       if (aSubs.Contains(aSubIt.Value()))
710         return true;
711       else
712         aSubs.Add(aSubIt.Value());
713     }
714   }
715   return false;
716 }
717
718 //==================================================================================================
719 std::list<std::shared_ptr<GeomAPI_Pnt> >
720   GeomAlgoAPI_ShapeTools::getBoundingBox(const ListOfShape& theShapes, const double theEnlarge)
721 {
722   // Bounding box of all objects.
723   Bnd_Box aBndBox;
724
725   // Getting box.
726   for (ListOfShape::const_iterator
727     anObjectsIt = theShapes.begin(); anObjectsIt != theShapes.end(); anObjectsIt++) {
728     const TopoDS_Shape& aShape = (*anObjectsIt)->impl<TopoDS_Shape>();
729     BRepBndLib::Add(aShape, aBndBox);
730   }
731
732   if (theEnlarge != 0.0) {
733     // We enlarge bounding box just to be sure that plane will be large enough to cut all objects.
734     aBndBox.Enlarge(theEnlarge);
735   }
736
737   Standard_Real aXArr[2] = {aBndBox.CornerMin().X(), aBndBox.CornerMax().X()};
738   Standard_Real aYArr[2] = {aBndBox.CornerMin().Y(), aBndBox.CornerMax().Y()};
739   Standard_Real aZArr[2] = {aBndBox.CornerMin().Z(), aBndBox.CornerMax().Z()};
740   std::list<std::shared_ptr<GeomAPI_Pnt> > aResultPoints;
741   for (int i = 0; i < 2; i++) {
742     for (int j = 0; j < 2; j++) {
743       for (int k = 0; k < 2; k++) {
744         std::shared_ptr<GeomAPI_Pnt> aPnt(new GeomAPI_Pnt(aXArr[i], aYArr[j], aZArr[k]));
745         aResultPoints.push_back(aPnt);
746       }
747     }
748   }
749
750   return aResultPoints;
751 }
752
753 //==================================================================================================
754 std::shared_ptr<GeomAPI_Face> GeomAlgoAPI_ShapeTools::fitPlaneToBox(
755   const std::shared_ptr<GeomAPI_Shape> thePlane,
756   const std::list<std::shared_ptr<GeomAPI_Pnt> >& thePoints)
757 {
758   std::shared_ptr<GeomAPI_Face> aResultFace;
759
760   if (!thePlane.get()) {
761     return aResultFace;
762   }
763
764   const TopoDS_Shape& aShape = thePlane->impl<TopoDS_Shape>();
765   if (aShape.ShapeType() != TopAbs_FACE) {
766     return aResultFace;
767   }
768
769   TopoDS_Face aFace = TopoDS::Face(aShape);
770   Handle(Geom_Surface) aSurf = BRep_Tool::Surface(aFace);
771   if (aSurf.IsNull()) {
772     return aResultFace;
773   }
774
775   GeomLib_IsPlanarSurface isPlanar(aSurf);
776   if (!isPlanar.IsPlanar()) {
777     return aResultFace;
778   }
779
780   if (thePoints.size() != 8) {
781     return aResultFace;
782   }
783
784   const gp_Pln& aFacePln = isPlanar.Plan();
785   Handle(Geom_Plane) aFacePlane = new Geom_Plane(aFacePln);
786   IntAna_Quadric aQuadric(aFacePln);
787   Standard_Real UMin, UMax, VMin, VMax;
788   UMin = UMax = VMin = VMax = 0;
789   for (std::list<std::shared_ptr<GeomAPI_Pnt> >::const_iterator
790        aPointsIt = thePoints.begin(); aPointsIt != thePoints.end(); aPointsIt++) {
791     const gp_Pnt& aPnt = (*aPointsIt)->impl<gp_Pnt>();
792     gp_Lin aLin(aPnt, aFacePln.Axis().Direction());
793     IntAna_IntConicQuad anIntAna(aLin, aQuadric);
794     const gp_Pnt& aPntOnFace = anIntAna.Point(1);
795     Standard_Real aPntU(0), aPntV(0);
796     GeomLib_Tool::Parameters(aFacePlane, aPntOnFace, Precision::Confusion(), aPntU, aPntV);
797     if (aPntU < UMin) UMin = aPntU;
798     if (aPntU > UMax) UMax = aPntU;
799     if (aPntV < VMin) VMin = aPntV;
800     if (aPntV > VMax) VMax = aPntV;
801   }
802   aResultFace.reset(new GeomAPI_Face());
803   aResultFace->setImpl(new TopoDS_Face(BRepLib_MakeFace(aFacePln, UMin, UMax, VMin, VMax).Face()));
804
805   return aResultFace;
806 }
807
808 //==================================================================================================
809 void GeomAlgoAPI_ShapeTools::findBounds(const std::shared_ptr<GeomAPI_Shape> theShape,
810                                         std::shared_ptr<GeomAPI_Vertex>& theV1,
811                                         std::shared_ptr<GeomAPI_Vertex>& theV2)
812 {
813   static GeomVertexPtr aVertex;
814   if (!aVertex) {
815     aVertex = GeomVertexPtr(new GeomAPI_Vertex);
816     aVertex->setImpl(new TopoDS_Vertex());
817   }
818
819   theV1 = aVertex;
820   theV2 = aVertex;
821
822   if (theShape) {
823     const TopoDS_Shape& aShape = theShape->impl<TopoDS_Shape>();
824     TopoDS_Vertex aV1, aV2;
825     ShapeAnalysis::FindBounds(aShape, aV1, aV2);
826
827     std::shared_ptr<GeomAPI_Vertex> aGeomV1(new GeomAPI_Vertex()), aGeomV2(new GeomAPI_Vertex());
828     aGeomV1->setImpl(new TopoDS_Vertex(aV1));
829     aGeomV2->setImpl(new TopoDS_Vertex(aV2));
830     theV1 = aGeomV1;
831     theV2 = aGeomV2;
832   }
833 }
834
835 //==================================================================================================
836 void GeomAlgoAPI_ShapeTools::makeFacesWithHoles(const std::shared_ptr<GeomAPI_Pnt> theOrigin,
837                                                 const std::shared_ptr<GeomAPI_Dir> theDirection,
838                                                 const ListOfShape& theWires,
839                                                 ListOfShape& theFaces)
840 {
841   BRepBuilderAPI_MakeFace aMKFace(gp_Pln(theOrigin->impl<gp_Pnt>(),
842                                           theDirection->impl<gp_Dir>()));
843   TopoDS_Face aFace = aMKFace.Face();
844
845   BRepAlgo_FaceRestrictor aFRestrictor;
846   aFRestrictor.Init(aFace, Standard_False, Standard_True);
847   for (ListOfShape::const_iterator anIt = theWires.cbegin();
848       anIt != theWires.cend();
849       ++anIt) {
850     TopoDS_Wire aWire = TopoDS::Wire((*anIt)->impl<TopoDS_Shape>());
851     aFRestrictor.Add(aWire);
852   }
853
854   aFRestrictor.Perform();
855
856   if (!aFRestrictor.IsDone()) {
857     return;
858   }
859
860   for (; aFRestrictor.More(); aFRestrictor.Next()) {
861     GeomShapePtr aShape(new GeomAPI_Shape());
862     aShape->setImpl(new TopoDS_Shape(aFRestrictor.Current()));
863     theFaces.push_back(aShape);
864   }
865 }
866
867 //==================================================================================================
868 std::shared_ptr<GeomAPI_Pln> GeomAlgoAPI_ShapeTools::findPlane(const ListOfShape& theShapes)
869 {
870   TopoDS_Compound aCompound;
871   BRep_Builder aBuilder;
872   aBuilder.MakeCompound(aCompound);
873
874   for (ListOfShape::const_iterator anIt = theShapes.cbegin(); anIt != theShapes.cend(); ++anIt) {
875     aBuilder.Add(aCompound, (*anIt)->impl<TopoDS_Shape>());
876   }
877   BRepBuilderAPI_FindPlane aFindPlane(aCompound);
878
879   if (aFindPlane.Found() != Standard_True) {
880     return std::shared_ptr<GeomAPI_Pln>();
881   }
882
883   Handle(Geom_Plane) aPlane = aFindPlane.Plane();
884   gp_Pnt aLoc = aPlane->Location();
885   gp_Dir aDir = aPlane->Axis().Direction();
886
887   std::shared_ptr<GeomAPI_Pnt> aGeomPnt(new GeomAPI_Pnt(aLoc.X(), aLoc.Y(), aLoc.Z()));
888   std::shared_ptr<GeomAPI_Dir> aGeomDir(new GeomAPI_Dir(aDir.X(), aDir.Y(), aDir.Z()));
889
890   std::shared_ptr<GeomAPI_Pln> aPln(new GeomAPI_Pln(aGeomPnt, aGeomDir));
891
892   return aPln;
893 }
894
895 //==================================================================================================
896 bool GeomAlgoAPI_ShapeTools::isSubShapeInsideShape(
897   const std::shared_ptr<GeomAPI_Shape> theSubShape,
898   const std::shared_ptr<GeomAPI_Shape> theBaseShape)
899 {
900   if (!theSubShape.get() || !theBaseShape.get()) {
901     return false;
902   }
903
904   const TopoDS_Shape& aSubShape = theSubShape->impl<TopoDS_Shape>();
905   const TopoDS_Shape& aBaseShape = theBaseShape->impl<TopoDS_Shape>();
906
907   if (aSubShape.ShapeType() == TopAbs_VERTEX) {
908     // If sub-shape is a vertex check distance to shape. If it is <= Precision::Confusion() then OK.
909     BRepExtrema_DistShapeShape aDist(aBaseShape, aSubShape);
910     aDist.Perform();
911     if (!aDist.IsDone() || aDist.Value() > Precision::Confusion()) {
912       return false;
913     }
914   } else if (aSubShape.ShapeType() == TopAbs_EDGE) {
915     if (aBaseShape.ShapeType() == TopAbs_FACE) {
916       // Check that edge is on face surface.
917       TopoDS_Face aFace = TopoDS::Face(aBaseShape);
918       TopoDS_Edge anEdge = TopoDS::Edge(aSubShape);
919       BRepLib_CheckCurveOnSurface aCheck(anEdge, aFace);
920       aCheck.Perform();
921       if (!aCheck.IsDone() || aCheck.MaxDistance() > Precision::Confusion()) {
922         return false;
923       }
924
925       // Check intersections.
926       TopoDS_Vertex aV1, aV2;
927       ShapeAnalysis::FindBounds(anEdge, aV1, aV2);
928       gp_Pnt aPnt1 = BRep_Tool::Pnt(aV1);
929       gp_Pnt aPnt2 = BRep_Tool::Pnt(aV2);
930       for (TopExp_Explorer anExp(aBaseShape, TopAbs_EDGE); anExp.More(); anExp.Next()) {
931         const TopoDS_Shape& anEdgeOnFace = anExp.Current();
932         BRepExtrema_DistShapeShape aDist(anEdgeOnFace, anEdge);
933         aDist.Perform();
934         if (aDist.IsDone() && aDist.Value() <= Precision::Confusion()) {
935           // Edge intersect face bound. Check that it is not on edge begin or end.
936           for (Standard_Integer anIndex = 1; anIndex <= aDist.NbSolution(); ++anIndex) {
937             gp_Pnt aPntOnSubShape = aDist.PointOnShape2(anIndex);
938             if (aPntOnSubShape.Distance(aPnt1) > Precision::Confusion()
939                 && aPntOnSubShape.Distance(aPnt2) > Precision::Confusion()) {
940               return false;
941             }
942           }
943         }
944       }
945
946       // No intersections found. Edge is inside or outside face. Check it.
947       BRepAdaptor_Curve aCurveAdaptor(anEdge);
948       gp_Pnt aPointToCheck =
949         aCurveAdaptor.Value((aCurveAdaptor.FirstParameter() +
950                               aCurveAdaptor.LastParameter()) / 2.0);
951       Handle(Geom_Surface) aSurface = BRep_Tool::Surface(aFace);
952       ShapeAnalysis_Surface aSAS(aSurface);
953       gp_Pnt2d aPointOnFace = aSAS.ValueOfUV(aPointToCheck, Precision::Confusion());
954       BRepTopAdaptor_FClass2d aFClass2d(aFace, Precision::Confusion());
955       if (aFClass2d.Perform(aPointOnFace) == TopAbs_OUT) {
956         return false;
957       }
958
959     } else {
960       return false;
961     }
962   } else {
963     return false;
964   }
965
966   return true;
967 }
968
969 //==================================================================================================
970 bool GeomAlgoAPI_ShapeTools::isShapeValid(const std::shared_ptr<GeomAPI_Shape> theShape)
971 {
972   if (!theShape.get()) {
973     return false;
974   }
975
976   BRepCheck_Analyzer aChecker(theShape->impl<TopoDS_Shape>());
977   return (aChecker.IsValid() == Standard_True);
978 }
979
980 //==================================================================================================
981 std::shared_ptr<GeomAPI_Shape>
982   GeomAlgoAPI_ShapeTools::getFaceOuterWire(const std::shared_ptr<GeomAPI_Shape> theFace)
983 {
984   GeomShapePtr anOuterWire;
985
986   if (!theFace.get() || !theFace->isFace()) {
987     return anOuterWire;
988   }
989
990   TopoDS_Face aFace = TopoDS::Face(theFace->impl<TopoDS_Shape>());
991   TopoDS_Wire aWire = BRepTools::OuterWire(aFace);
992
993   anOuterWire.reset(new GeomAPI_Shape());
994   anOuterWire->setImpl(new TopoDS_Shape(aWire));
995
996   return anOuterWire;
997 }
998
999 //==================================================================================================
1000 static bool boundaryOfEdge(const std::shared_ptr<GeomAPI_Edge> theEdge,
1001                           const std::shared_ptr<GeomAPI_Vertex> theVertex,
1002                           double& theParam)
1003 {
1004   GeomPointPtr aPoint = theVertex->point();
1005   GeomPointPtr aFirstPnt = theEdge->firstPoint();
1006   double aFirstPntTol = theEdge->firstPointTolerance();
1007   GeomPointPtr aLastPnt = theEdge->lastPoint();
1008   double aLastPntTol = theEdge->lastPointTolerance();
1009
1010   double aFirst, aLast;
1011   theEdge->getRange(aFirst, aLast);
1012
1013   bool isFirst = aPoint->distance(aFirstPnt) <= aFirstPntTol;
1014   bool isLast = aPoint->distance(aLastPnt) <= aLastPntTol;
1015   if (isFirst)
1016     theParam = aFirst;
1017   else if (isLast)
1018     theParam = aLast;
1019
1020   return isFirst != isLast;
1021 }
1022
1023 bool GeomAlgoAPI_ShapeTools::isTangent(const std::shared_ptr<GeomAPI_Edge> theEdge1,
1024                                        const std::shared_ptr<GeomAPI_Edge> theEdge2,
1025                                        const std::shared_ptr<GeomAPI_Vertex> theTgPoint)
1026 {
1027   double aParE1 = 0, aParE2 = 0;
1028   if (!boundaryOfEdge(theEdge1, theTgPoint, aParE1) ||
1029       !boundaryOfEdge(theEdge2, theTgPoint, aParE2))
1030     return false;
1031
1032   BRepAdaptor_Curve aC1(theEdge1->impl<TopoDS_Edge>());
1033   BRepAdaptor_Curve aC2(theEdge2->impl<TopoDS_Edge>());
1034   return BRepLProp::Continuity(aC1, aC2, aParE1, aParE2) >= GeomAbs_G1;
1035 }
1036
1037 //==================================================================================================
1038 bool GeomAlgoAPI_ShapeTools::isParallel(const std::shared_ptr<GeomAPI_Edge> theEdge,
1039                                         const std::shared_ptr<GeomAPI_Face> theFace)
1040 {
1041   if (!theEdge.get() || !theFace.get()) {
1042     return false;
1043   }
1044
1045   TopoDS_Edge anEdge = TopoDS::Edge(theEdge->impl<TopoDS_Shape>());
1046   TopoDS_Face aFace  = TopoDS::Face(theFace->impl<TopoDS_Shape>());
1047
1048   BRepExtrema_ExtCF anExt(anEdge, aFace);
1049   return anExt.IsParallel() == Standard_True;
1050 }
1051
1052 //==================================================================================================
1053 std::list<std::shared_ptr<GeomAPI_Vertex> > GeomAlgoAPI_ShapeTools::intersect(
1054   const std::shared_ptr<GeomAPI_Edge> theEdge, const std::shared_ptr<GeomAPI_Face> theFace)
1055 {
1056   std::list<std::shared_ptr<GeomAPI_Vertex> > aResult;
1057   if (!theEdge.get() || !theFace.get()) {
1058     return aResult;
1059   }
1060
1061   TopoDS_Edge anEdge = TopoDS::Edge(theEdge->impl<TopoDS_Shape>());
1062   double aFirstOnCurve, aLastOnCurve;
1063   Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirstOnCurve, aLastOnCurve);
1064
1065   TopoDS_Face aFace  = TopoDS::Face(theFace->impl<TopoDS_Shape>());
1066   Handle(Geom_Surface) aSurf = BRep_Tool::Surface(aFace);
1067
1068   GeomAPI_IntCS anIntAlgo(aCurve, aSurf);
1069   if (!anIntAlgo.IsDone())
1070     return aResult;
1071   // searching for points-intersection
1072   for (int anIntNum = 1; anIntNum <= anIntAlgo.NbPoints() + anIntAlgo.NbSegments(); anIntNum++) {
1073     gp_Pnt anInt;
1074     if (anIntNum <= anIntAlgo.NbPoints()) {
1075       anInt = anIntAlgo.Point(anIntNum);
1076     } else { // take the middle point on the segment of the intersection
1077       Handle(Geom_Curve) anIntCurve = anIntAlgo.Segment(anIntNum - anIntAlgo.NbPoints());
1078       anIntCurve->D0((anIntCurve->FirstParameter() + anIntCurve->LastParameter()) / 2., anInt);
1079     }
1080     aResult.push_back(std::shared_ptr<GeomAPI_Vertex>(
1081       new GeomAPI_Vertex(anInt.X(), anInt.Y(), anInt.Z())));
1082   }
1083   return aResult;
1084 }
1085
1086 //==================================================================================================
1087 void GeomAlgoAPI_ShapeTools::splitShape(const std::shared_ptr<GeomAPI_Shape>& theBaseShape,
1088                                       const GeomAlgoAPI_ShapeTools::PointToRefsMap& thePointsInfo,
1089                                       std::set<std::shared_ptr<GeomAPI_Shape> >& theShapes)
1090 {
1091   // to split shape at least one point should be presented in the points container
1092   if (thePointsInfo.empty())
1093     return;
1094
1095     // General Fuse to split edge by vertices
1096   BOPAlgo_Builder aBOP;
1097   TopoDS_Edge aBaseEdge = theBaseShape->impl<TopoDS_Edge>();
1098   // Rebuild closed edge to place vertex to one of split points.
1099   // This will prevent edge to be split on same vertex.
1100   if (BRep_Tool::IsClosed(aBaseEdge))
1101   {
1102     Standard_Real aFirst, aLast;
1103     Handle(Geom_Curve) aCurve = BRep_Tool::Curve(aBaseEdge, aFirst, aLast);
1104
1105     PointToRefsMap::const_iterator aPIt = thePointsInfo.begin();
1106     std::shared_ptr<GeomAPI_Pnt> aPnt = aPIt->first;
1107     gp_Pnt aPoint(aPnt->x(), aPnt->y(), aPnt->z());
1108
1109     TopAbs_Orientation anOrientation = aBaseEdge.Orientation();
1110     aBaseEdge = BRepBuilderAPI_MakeEdge(aCurve, aPoint, aPoint).Edge();
1111     aBaseEdge.Orientation(anOrientation);
1112   }
1113   aBOP.AddArgument(aBaseEdge);
1114
1115   PointToRefsMap::const_iterator aPIt = thePointsInfo.begin();
1116   for (; aPIt != thePointsInfo.end(); ++aPIt) {
1117     std::shared_ptr<GeomAPI_Pnt> aPnt = aPIt->first;
1118     TopoDS_Vertex aV = BRepBuilderAPI_MakeVertex(gp_Pnt(aPnt->x(), aPnt->y(), aPnt->z()));
1119     aBOP.AddArgument(aV);
1120   }
1121
1122   aBOP.Perform();
1123   if (aBOP.HasErrors())
1124     return;
1125
1126   // Collect splits
1127   const TopTools_ListOfShape& aSplits = aBOP.Modified(aBaseEdge);
1128   TopTools_ListIteratorOfListOfShape anIt(aSplits);
1129   for (; anIt.More(); anIt.Next()) {
1130     std::shared_ptr<GeomAPI_Shape> anEdge(new GeomAPI_Shape);
1131     anEdge->setImpl(new TopoDS_Shape(anIt.Value()));
1132     theShapes.insert(anEdge);
1133   }
1134 }
1135
1136 //==================================================================================================
1137 void GeomAlgoAPI_ShapeTools::splitShape_p(const std::shared_ptr<GeomAPI_Shape>& theBaseShape,
1138                                           const std::list<std::shared_ptr<GeomAPI_Pnt> >& thePoints,
1139                                           std::set<std::shared_ptr<GeomAPI_Shape> >& theShapes)
1140 {
1141   // General Fuse to split edge by vertices
1142   BOPAlgo_Builder aBOP;
1143   TopoDS_Edge aBaseEdge = theBaseShape->impl<TopoDS_Edge>();
1144   // Rebuild closed edge to place vertex to one of split points.
1145   // This will prevent edge to be split on seam vertex.
1146   if (BRep_Tool::IsClosed(aBaseEdge))
1147   {
1148     Standard_Real aFirst, aLast;
1149     Handle(Geom_Curve) aCurve = BRep_Tool::Curve(aBaseEdge, aFirst, aLast);
1150
1151     std::list<std::shared_ptr<GeomAPI_Pnt> >::const_iterator aPIt = thePoints.begin();
1152     gp_Pnt aPoint((*aPIt)->x(), (*aPIt)->y(), (*aPIt)->z());
1153
1154     TopAbs_Orientation anOrientation = aBaseEdge.Orientation();
1155     aBaseEdge = BRepBuilderAPI_MakeEdge(aCurve, aPoint, aPoint).Edge();
1156     aBaseEdge.Orientation(anOrientation);
1157   }
1158   aBOP.AddArgument(aBaseEdge);
1159
1160   std::list<std::shared_ptr<GeomAPI_Pnt> >::const_iterator aPtIt = thePoints.begin();
1161   for (; aPtIt != thePoints.end(); ++aPtIt) {
1162     std::shared_ptr<GeomAPI_Pnt> aPnt = *aPtIt;
1163     TopoDS_Vertex aV = BRepBuilderAPI_MakeVertex(gp_Pnt(aPnt->x(), aPnt->y(), aPnt->z()));
1164     aBOP.AddArgument(aV);
1165   }
1166
1167   aBOP.Perform();
1168   if (aBOP.HasErrors())
1169     return;
1170
1171   // Collect splits
1172   const TopTools_ListOfShape& aSplits = aBOP.Modified(aBaseEdge);
1173   TopTools_ListIteratorOfListOfShape anIt(aSplits);
1174   for (; anIt.More(); anIt.Next()) {
1175     std::shared_ptr<GeomAPI_Shape> anEdge(new GeomAPI_Shape);
1176     anEdge->setImpl(new TopoDS_Shape(anIt.Value()));
1177     theShapes.insert(anEdge);
1178   }
1179 }
1180
1181 //==================================================================================================
1182 std::shared_ptr<GeomAPI_Shape> GeomAlgoAPI_ShapeTools::findShape(
1183                                   const std::list<std::shared_ptr<GeomAPI_Pnt> >& thePoints,
1184                                   const std::set<std::shared_ptr<GeomAPI_Shape> >& theShapes)
1185 {
1186   std::shared_ptr<GeomAPI_Shape> aResultShape;
1187
1188   if (thePoints.size() == 2) {
1189     std::list<std::shared_ptr<GeomAPI_Pnt> >::const_iterator aPntIt = thePoints.begin();
1190     std::shared_ptr<GeomAPI_Pnt> aFirstPoint = *aPntIt;
1191     aPntIt++;
1192     std::shared_ptr<GeomAPI_Pnt> aLastPoint = *aPntIt;
1193
1194     std::set<std::shared_ptr<GeomAPI_Shape> >::const_iterator anIt = theShapes.begin(),
1195                                                               aLast = theShapes.end();
1196     for (; anIt != aLast; anIt++) {
1197       GeomShapePtr aShape = *anIt;
1198       std::shared_ptr<GeomAPI_Edge> anEdge(new GeomAPI_Edge(aShape));
1199       if (anEdge.get()) {
1200         std::shared_ptr<GeomAPI_Pnt> anEdgeFirstPoint = anEdge->firstPoint();
1201         std::shared_ptr<GeomAPI_Pnt> anEdgeLastPoint = anEdge->lastPoint();
1202         if (anEdgeFirstPoint->isEqual(aFirstPoint) &&
1203             anEdgeLastPoint->isEqual(aLastPoint))
1204             aResultShape = aShape;
1205       }
1206     }
1207   }
1208
1209   return aResultShape;
1210 }
1211
1212 //==================================================================================================
1213 #ifdef FEATURE_MULTIROTATION_TWO_DIRECTIONS
1214 std::shared_ptr<GeomAPI_Dir> GeomAlgoAPI_ShapeTools::buildDirFromAxisAndShape(
1215                                     const std::shared_ptr<GeomAPI_Shape> theBaseShape,
1216                                     const std::shared_ptr<GeomAPI_Ax1> theAxis)
1217 {
1218   gp_Pnt aCentreOfMassPoint =
1219     GeomAlgoAPI_ShapeTools::centreOfMass(theBaseShape)->impl<gp_Pnt>();
1220   Handle(Geom_Line) aLine = new Geom_Line(theAxis->impl<gp_Ax1>());
1221   GeomAPI_ProjectPointOnCurve aPrjTool(aCentreOfMassPoint, aLine);
1222   gp_Pnt aPoint = aPrjTool.NearestPoint();
1223
1224   std::shared_ptr<GeomAPI_Dir> aDir(new GeomAPI_Dir(aCentreOfMassPoint.X()-aPoint.X(),
1225                                                     aCentreOfMassPoint.Y()-aPoint.Y(),
1226                                                     aCentreOfMassPoint.Z()-aPoint.Z()));
1227   return aDir;
1228 }
1229 #endif
1230
1231 //==================================================================================================
1232 static TopoDS_Wire fixParametricGaps(const TopoDS_Wire& theWire)
1233 {
1234   TopoDS_Wire aFixedWire;
1235   Handle(Geom_Curve) aPrevCurve;
1236   double aPrevLastParam = -Precision::Infinite();
1237
1238   BRep_Builder aBuilder;
1239   aBuilder.MakeWire(aFixedWire);
1240
1241   BRepTools_WireExplorer aWExp(theWire);
1242   for (; aWExp.More(); aWExp.Next()) {
1243     TopoDS_Edge anEdge = aWExp.Current();
1244     double aFirst, aLast;
1245     Handle(Geom_Curve) aCurve = BRep_Tool::Curve(anEdge, aFirst, aLast);
1246     if (aCurve == aPrevCurve && Abs(aFirst - aPrevLastParam) > Precision::Confusion()) {
1247       // if parametric gap occurs, create new edge based on the copied curve
1248       aCurve = Handle(Geom_Curve)::DownCast(aCurve->Copy());
1249       TopoDS_Vertex aV1, aV2;
1250       TopExp::Vertices(anEdge, aV1, aV2);
1251       anEdge = TopoDS::Edge(anEdge.EmptyCopied());
1252       aBuilder.UpdateEdge(anEdge, aCurve, BRep_Tool::Tolerance(anEdge));
1253       aBuilder.Add(anEdge, aV1);
1254       aBuilder.Add(anEdge, aV2);
1255     }
1256
1257     aBuilder.Add(aFixedWire, anEdge);
1258
1259     aPrevCurve = aCurve;
1260     aPrevLastParam = aLast;
1261   }
1262
1263   return aFixedWire;
1264 }
1265
1266 //==================================================================================================
1267 std::shared_ptr<GeomAPI_Edge> GeomAlgoAPI_ShapeTools::wireToEdge(
1268       const std::shared_ptr<GeomAPI_Wire>& theWire)
1269 {
1270   GeomEdgePtr anEdge;
1271   if (theWire) {
1272     TopoDS_Wire aWire = theWire->impl<TopoDS_Wire>();
1273     BRepTools_WireExplorer aWExp(aWire);
1274     TopoDS_Edge aNewEdge = aWExp.Current();
1275     aWExp.Next();
1276     if (aWExp.More()) {
1277       // Workaround for the closed wire to avoid jumping of its start point:
1278       // split this wire for two parts, convert them to edges, then compose together
1279       if (BRep_Tool::IsClosed(aWire)) {
1280         aWire = TopoDS::Wire(BRepBuilderAPI_Copy(aWire).Shape());
1281         aWExp.Init(aWire);
1282         aNewEdge = aWExp.Current();
1283
1284         BRep_Builder().Remove(aWire, aNewEdge);
1285         GeomWirePtr aSplitWire(new GeomAPI_Wire);
1286         aSplitWire->setImpl(new TopoDS_Wire(aWire));
1287         GeomEdgePtr aMergedEdge = wireToEdge(aSplitWire);
1288
1289         aWire = BRepBuilderAPI_MakeWire(aNewEdge, aMergedEdge->impl<TopoDS_Edge>());
1290       }
1291
1292       // Workaround: when concatenate a wire consisting of two edges based on the same B-spline
1293       // curve (non-periodic, but having equal start and end points), first of which is placed
1294       // at the end on the curve and second is placed at the start, this workaround copies
1295       // second curve to avoid treating these edges as a single curve by setting trim parameters.
1296       aWire = fixParametricGaps(aWire);
1297       aWire = BRepAlgo::ConcatenateWire(aWire, GeomAbs_G1); // join smooth parts of wire
1298       aNewEdge = BRepAlgo::ConcatenateWireC0(aWire); // join C0 parts of wire
1299
1300       // Reapproximate the result edge to have the parameter equal to curvilinear abscissa.
1301       static const int THE_MAX_DEGREE = 14;
1302       static const int THE_MAX_INTERVALS = 32;
1303       double aFirst, aLast;
1304       Handle(Geom_Curve) aCurve = BRep_Tool::Curve(aNewEdge, aFirst, aLast);
1305       Handle(GeomAdaptor_HCurve) aHCurve = new GeomAdaptor_HCurve(aCurve);
1306       Approx_CurvilinearParameter anApprox(aHCurve, Precision::Confusion(), aCurve->Continuity(),
1307                                            THE_MAX_DEGREE, THE_MAX_INTERVALS);
1308       if (anApprox.HasResult()) {
1309         Handle(Geom_BSplineCurve) aNewCurve = anApprox.Curve3d();
1310         TColStd_Array1OfReal aKnots = aNewCurve->Knots();
1311         BSplCLib::Reparametrize(aFirst, aLast, aKnots);
1312         aNewCurve->SetKnots(aKnots);
1313         BRep_Builder().UpdateEdge(aNewEdge, aNewCurve, BRep_Tool::Tolerance(aNewEdge));
1314       }
1315     }
1316     anEdge = GeomEdgePtr(new GeomAPI_Edge);
1317     anEdge->setImpl(new TopoDS_Edge(aNewEdge));
1318   }
1319   return anEdge;
1320 }
1321
1322 //==================================================================================================
1323 ListOfShape GeomAlgoAPI_ShapeTools::getLowLevelSubShapes(const GeomShapePtr& theShape)
1324 {
1325   ListOfShape aSubShapes;
1326
1327   if (!theShape->isCompound() && !theShape->isCompSolid() &&
1328       !theShape->isShell() && !theShape->isWire()) {
1329     return aSubShapes;
1330   }
1331
1332   for (GeomAPI_ShapeIterator anIt(theShape); anIt.more(); anIt.next()) {
1333     GeomShapePtr aSubShape = anIt.current();
1334     if (aSubShape->isVertex() || aSubShape->isEdge() ||
1335         aSubShape->isFace() || aSubShape->isSolid()) {
1336       aSubShapes.push_back(aSubShape);
1337     } else {
1338       aSubShapes.splice(aSubShapes.end(), getLowLevelSubShapes(aSubShape));
1339     }
1340   }
1341
1342   return aSubShapes;
1343 }
1344
1345 //==================================================================================================
1346 static void getMinMaxPointsOnLine(const std::list<std::shared_ptr<GeomAPI_Pnt> >& thePoints,
1347                                   const gp_Dir theDir,
1348                                   double& theMin, double& theMax)
1349 {
1350   theMin = RealLast();
1351   theMax = RealFirst();
1352   // Project bounding points on theDir
1353   for (std::list<std::shared_ptr<GeomAPI_Pnt> >::const_iterator
1354          aPointsIt = thePoints.begin(); aPointsIt != thePoints.end(); aPointsIt++) {
1355     const gp_Pnt& aPnt = (*aPointsIt)->impl<gp_Pnt>();
1356     gp_Dir aPntDir (aPnt.XYZ());
1357     Standard_Real proj = (theDir*aPntDir) * aPnt.XYZ().Modulus();
1358     if (proj < theMin) theMin = proj;
1359     if (proj > theMax) theMax = proj;
1360   }
1361 }
1362
1363 //==================================================================================================
1364 void GeomAlgoAPI_ShapeTools::computeThroughAll(const ListOfShape& theObjects,
1365                                                const ListOfShape& theBaseShapes,
1366                                                const std::shared_ptr<GeomAPI_Dir> theDir,
1367                                                double& theToSize, double& theFromSize)
1368 {
1369   // Bounding box of objects
1370   std::list<std::shared_ptr<GeomAPI_Pnt> > aBndObjs =
1371       GeomAlgoAPI_ShapeTools::getBoundingBox(theObjects);
1372   if (aBndObjs.size() != 8) {
1373     return;
1374   }
1375
1376   // the value to enlarge the bounding box of each object to make the extruded shape
1377   // a little bit larger than overall objects to get the correct result of Boolean CUT operation
1378   double anEnlargement = 0.1 * aBndObjs.front()->distance(aBndObjs.back());
1379
1380   // Prism direction
1381   if (theDir.get()) {
1382     // One direction for all prisms
1383     gp_Dir aDir = theDir->impl<gp_Dir>();
1384
1385     // Bounding box of the base
1386     std::list<std::shared_ptr<GeomAPI_Pnt> > aBndBases =
1387         GeomAlgoAPI_ShapeTools::getBoundingBox(theBaseShapes);
1388     if (aBndBases.size() != 8) {
1389       return;
1390     }
1391
1392     // Objects bounds
1393     Standard_Real lowBnd, upperBnd;
1394     getMinMaxPointsOnLine(aBndObjs, aDir, lowBnd, upperBnd);
1395
1396     // Base bounds
1397     Standard_Real lowBase, upperBase;
1398     getMinMaxPointsOnLine(aBndBases, aDir, lowBase, upperBase);
1399
1400     // ----------.-----.---------.--------------.-----------> theDir
1401     //       lowBnd   lowBase   upperBase    upperBnd
1402
1403     theToSize = upperBnd - lowBase;
1404     theFromSize = upperBase - lowBnd;
1405   } else {
1406     // Direction is a normal to each base shape (different normals to bases)
1407     // So we calculate own sizes for each base shape
1408     theToSize = 0.0;
1409     theFromSize = 0.0;
1410
1411     for (ListOfShape::const_iterator anIt = theBaseShapes.begin();
1412          anIt != theBaseShapes.end(); ++anIt) {
1413       const GeomShapePtr& aBaseShape_i = (*anIt);
1414       ListOfShape aBaseShapes_i;
1415       aBaseShapes_i.push_back(aBaseShape_i);
1416
1417       // Bounding box of the base
1418       std::list<std::shared_ptr<GeomAPI_Pnt> > aBndBases =
1419           GeomAlgoAPI_ShapeTools::getBoundingBox(aBaseShapes_i, anEnlargement);
1420       if (aBndBases.size() != 8) {
1421         return;
1422       }
1423
1424       // Direction (normal to aBaseShapes_i)
1425       // Code like in GeomAlgoAPI_Prism
1426       gp_Dir aDir;
1427       const TopoDS_Shape& aBaseShape = aBaseShape_i->impl<TopoDS_Shape>();
1428       BRepBuilderAPI_FindPlane aFindPlane(aBaseShape);
1429       if (aFindPlane.Found() == Standard_True) {
1430         Handle(Geom_Plane) aPlane;
1431         if (aBaseShape.ShapeType() == TopAbs_FACE || aBaseShape.ShapeType() == TopAbs_SHELL) {
1432           TopExp_Explorer anExp(aBaseShape, TopAbs_FACE);
1433           const TopoDS_Shape& aFace = anExp.Current();
1434           Handle(Geom_Surface) aSurface = BRep_Tool::Surface(TopoDS::Face(aFace));
1435           if (aSurface->DynamicType() == STANDARD_TYPE(Geom_RectangularTrimmedSurface)) {
1436             Handle(Geom_RectangularTrimmedSurface) aTrimSurface =
1437               Handle(Geom_RectangularTrimmedSurface)::DownCast(aSurface);
1438             aSurface = aTrimSurface->BasisSurface();
1439           }
1440           if (aSurface->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
1441             return;
1442           }
1443           aPlane = Handle(Geom_Plane)::DownCast(aSurface);
1444         } else {
1445           aPlane = aFindPlane.Plane();
1446         }
1447         aDir = aPlane->Axis().Direction();
1448       } else {
1449         return;
1450       }
1451
1452       // Objects bounds
1453       Standard_Real lowBnd, upperBnd;
1454       getMinMaxPointsOnLine(aBndObjs, aDir, lowBnd, upperBnd);
1455
1456       // Base bounds
1457       Standard_Real lowBase, upperBase;
1458       getMinMaxPointsOnLine(aBndBases, aDir, lowBase, upperBase);
1459
1460       // ----------.-----.---------.--------------.-----------> theDir
1461       //       lowBnd   lowBase   upperBase    upperBnd
1462
1463       double aToSize_i = upperBnd - lowBase;
1464       double aFromSize_i = upperBase - lowBnd;
1465
1466       if (aToSize_i > theToSize) theToSize = aToSize_i;
1467       if (aFromSize_i > theFromSize) theFromSize = aFromSize_i;
1468     }
1469   }
1470 }