Salome HOME
Updated copyright comment
[modules/shaper.git] / src / GeomAPI / GeomAPI_Shape.cpp
1 // Copyright (C) 2014-2024  CEA, EDF
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 <GeomAPI_Pnt.h>
23 #include <GeomAPI_Vertex.h>
24 #include <GeomAPI_Edge.h>
25 #include <GeomAPI_Wire.h>
26 #include <GeomAPI_Face.h>
27 #include <GeomAPI_Shell.h>
28 #include <GeomAPI_Solid.h>
29 #include <GeomAPI_Trsf.h>
30
31 #include <BRep_Tool.hxx>
32 #include <BRepAlgoAPI_Section.hxx>
33 #include <BRepBndLib.hxx>
34 #include <BRepBuilderAPI_FindPlane.hxx>
35 #include <BRepBuilderAPI_Copy.hxx>
36 #include <BRepExtrema_DistShapeShape.hxx>
37 #include <BRepTools.hxx>
38 #include <Bnd_Box.hxx>
39 #include <Geom_Circle.hxx>
40 #include <Geom_Conic.hxx>
41 #include <Geom_Curve.hxx>
42 #include <Geom_Ellipse.hxx>
43 #include <Geom_Hyperbola.hxx>
44 #include <Geom_Line.hxx>
45 #include <Geom_Parabola.hxx>
46 #include <Geom_Plane.hxx>
47 #include <Geom_RectangularTrimmedSurface.hxx>
48 #include <Geom_TrimmedCurve.hxx>
49 #include <GeomLib_IsPlanarSurface.hxx>
50 #include <TopExp_Explorer.hxx>
51 #include <TopoDS.hxx>
52 #include <TopoDS_Iterator.hxx>
53 #include <TopoDS_Shape.hxx>
54 #include <NCollection_List.hxx>
55
56 #include <BOPAlgo_CheckerSI.hxx>
57 #include <BOPDS_DS.hxx>
58 #include <BOPTools_AlgoTools.hxx>
59
60 #include <sstream>
61 #include <algorithm> // for std::transform
62
63 #include <BRepTools.hxx>
64
65 #define MY_SHAPE implPtr<TopoDS_Shape>()
66
67 GeomAPI_Shape::GeomAPI_Shape()
68     : GeomAPI_Interface(new TopoDS_Shape())
69 {
70 }
71
72 std::shared_ptr<GeomAPI_Shape> GeomAPI_Shape::emptyCopied() const
73 {
74   GeomShapePtr aShape(new GeomAPI_Shape());
75   aShape->setImpl(new TopoDS_Shape(MY_SHAPE->EmptyCopied()));
76   return aShape;
77 }
78
79 bool GeomAPI_Shape::isNull() const
80 {
81   return MY_SHAPE->IsNull() == Standard_True;
82 }
83
84 bool GeomAPI_Shape::isEqual(const std::shared_ptr<GeomAPI_Shape> theShape) const
85 {
86   if (!theShape.get())
87     return false;
88   if (isNull())
89     return theShape->isNull();
90   if (theShape->isNull())
91     return false;
92
93   return MY_SHAPE->IsEqual(theShape->impl<TopoDS_Shape>()) == Standard_True;
94 }
95
96 bool GeomAPI_Shape::isSame(const std::shared_ptr<GeomAPI_Shape> theShape) const
97 {
98   bool isNullShape = !theShape.get() || theShape->isNull();;
99   if (isNull())
100     return isNullShape;
101   if (isNullShape)
102     return false;
103
104   return MY_SHAPE->IsSame(theShape->impl<TopoDS_Shape>()) == Standard_True;
105 }
106
107 bool GeomAPI_Shape::isSameGeometry(const std::shared_ptr<GeomAPI_Shape> theShape) const
108 {
109   if (isFace())
110     return face()->isSameGeometry(theShape);
111   else if (isEdge())
112     return edge()->isSameGeometry(theShape);
113   return false;
114 }
115
116 bool GeomAPI_Shape::isVertex() const
117 {
118   const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
119   return !aShape.IsNull() && aShape.ShapeType() == TopAbs_VERTEX;
120 }
121
122 bool GeomAPI_Shape::isEdge() const
123 {
124   const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
125   return !aShape.IsNull() && aShape.ShapeType() == TopAbs_EDGE;
126 }
127
128 bool GeomAPI_Shape::isWire() const
129 {
130   const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
131   return !aShape.IsNull() && aShape.ShapeType() == TopAbs_WIRE;
132 }
133
134 bool GeomAPI_Shape::isFace() const
135 {
136   const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
137   return !aShape.IsNull() && aShape.ShapeType() == TopAbs_FACE;
138 }
139
140 bool GeomAPI_Shape::isShell() const
141 {
142   const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
143   return !aShape.IsNull() && aShape.ShapeType() == TopAbs_SHELL;
144 }
145
146 bool GeomAPI_Shape::isCompound() const
147 {
148   const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
149   return !aShape.IsNull() && aShape.ShapeType() == TopAbs_COMPOUND;
150 }
151
152 bool GeomAPI_Shape::isCollectionOfSolids() const
153 {
154   const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
155   if (aShape.IsNull())
156     return false;
157
158   if (aShape.ShapeType() == TopAbs_SOLID ||
159       aShape.ShapeType() == TopAbs_COMPSOLID)
160     return true;
161
162   if (aShape.ShapeType() != TopAbs_COMPOUND)
163     return false;
164
165   TopTools_ListOfShape aLS;
166   TopTools_MapOfShape aMFence;
167   BOPTools_AlgoTools::TreatCompound(aShape, aLS, &aMFence);
168   TopTools_ListOfShape::Iterator it(aLS);
169   for (; it.More(); it.Next()) {
170     const TopoDS_Shape& aSx = it.Value();
171     if (aSx.ShapeType() != TopAbs_SOLID &&
172         aSx.ShapeType() != TopAbs_COMPSOLID)
173       return false;
174   }
175   return true;
176 }
177
178 bool GeomAPI_Shape::isCompoundOfSolids() const
179 {
180   const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
181   if (aShape.IsNull() || aShape.ShapeType() != TopAbs_COMPOUND)
182     return false;
183   bool isAtLeastOne = false;
184   for(TopoDS_Iterator aSubs(aShape); aSubs.More(); aSubs.Next()) {
185     if (aSubs.Value().IsNull() || aSubs.Value().ShapeType() != TopAbs_SOLID)
186       return false;
187     isAtLeastOne = true;
188   }
189   return isAtLeastOne;
190 }
191
192 // LCOV_EXCL_START
193 GeomAPI_Shape::ShapeType GeomAPI_Shape::typeOfCompoundShapes() const
194 {
195   const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
196   if (aShape.IsNull() || aShape.ShapeType() != TopAbs_COMPOUND)
197     return SHAPE;
198   int aType = -1;
199   for(TopoDS_Iterator aSubs(aShape); aSubs.More(); aSubs.Next()) {
200     if (!aSubs.Value().IsNull()) {
201       if (aType == -1)
202         aType = aSubs.Value().ShapeType();
203       else if (aSubs.Value().ShapeType() != aType)
204         return SHAPE;
205     }
206   }
207   return (GeomAPI_Shape::ShapeType) aType;
208 }
209 // LCOV_EXCL_STOP
210
211 // adds the nopt-compound elements recursively to the list
212 static void addSimpleToList(const TopoDS_Shape& theShape, NCollection_List<TopoDS_Shape>& theList)
213 {
214   if (!theShape.IsNull()) {
215     if (theShape.ShapeType() == TopAbs_COMPOUND) {
216       for(TopoDS_Iterator aSubs(theShape); aSubs.More(); aSubs.Next()) {
217         addSimpleToList(aSubs.Value(), theList);
218       }
219     } else {
220       theList.Append(theShape);
221     }
222   }
223 }
224
225 bool GeomAPI_Shape::isConnectedTopology() const
226 {
227   const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
228   if (aShape.IsNull() || aShape.ShapeType() != TopAbs_COMPOUND)
229     return false;
230   // list of simple elements that are not detected in connection to others
231   NCollection_List<TopoDS_Shape> aNotConnected;
232   addSimpleToList(aShape, aNotConnected);
233   if (aNotConnected.IsEmpty()) // an empty compound
234     return false;
235
236   // collect here the group of connected subs, starting with one first element
237   NCollection_List<TopoDS_Shape> aNewConnected;
238   aNewConnected.Append(aNotConnected.First());
239   aNotConnected.RemoveFirst();
240   // iterate until some new element become connected
241   while(!aNewConnected.IsEmpty() && !aNotConnected.IsEmpty()) {
242     NCollection_List<TopoDS_Shape> aNew; // very new connected to new connected
243     NCollection_List<TopoDS_Shape>::Iterator aNotIter(aNotConnected);
244     while(aNotIter.More()) {
245       // optimization to avoid TopExp_Explorer double-cycle, collect all vertices in the list first
246       NCollection_List<TopoDS_Shape> aNotVertices;
247       for(TopExp_Explorer anExp1(aNotIter.Value(), TopAbs_VERTEX); anExp1.More(); anExp1.Next()) {
248         aNotVertices.Append(anExp1.Current());
249       }
250
251       bool aConnected =  false;
252       NCollection_List<TopoDS_Shape>::Iterator aNewIter(aNewConnected);
253       for(; !aConnected && aNewIter.More(); aNewIter.Next()) {
254         // checking topological connecion of aNotIter and aNewIter
255         // (if shapes are connected, vertices are connected for sure)
256         TopExp_Explorer anExp2(aNewIter.Value(), TopAbs_VERTEX);
257         for(; !aConnected && anExp2.More(); anExp2.Next()) {
258           NCollection_List<TopoDS_Shape>::Iterator aNotVIter(aNotVertices);
259           for(; aNotVIter.More(); aNotVIter.Next()) {
260             if (aNotVIter.Value().IsSame(anExp2.Current())) {
261               aConnected = true;
262               break;
263             }
264           }
265         }
266       }
267       if (aConnected) {
268         aNew.Append(aNotIter.Value());
269         aNotConnected.Remove(aNotIter);
270       } else {
271         aNotIter.Next();
272       }
273     }
274     // remove all new connected and put to this list very new connected
275     aNewConnected.Clear();
276     aNewConnected.Append(aNew);
277   }
278   return aNotConnected.IsEmpty() == Standard_True;
279 }
280
281 bool GeomAPI_Shape::isSolid() const
282 {
283   const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
284   return !aShape.IsNull() && aShape.ShapeType() == TopAbs_SOLID;
285 }
286
287 bool GeomAPI_Shape::isCompSolid() const
288 {
289   const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
290   return !aShape.IsNull() && aShape.ShapeType() == TopAbs_COMPSOLID;
291 }
292
293 bool GeomAPI_Shape::isPlanar() const
294 {
295   TopoDS_Shape aShape = impl<TopoDS_Shape>();
296
297   if(aShape.IsNull()) {
298     return false;
299   }
300
301   TopAbs_ShapeEnum aShapeType = aShape.ShapeType();
302   if(aShapeType == TopAbs_COMPOUND) {
303     TopoDS_Iterator anIt(aShape);
304     int aShNum = 0;
305     for(; anIt.More(); anIt.Next()) {
306       ++aShNum;
307     }
308     if(aShNum == 1) {
309       anIt.Initialize(aShape);
310       aShape = anIt.Value();
311     }
312   }
313
314   aShapeType = aShape.ShapeType();
315   if(aShapeType == TopAbs_VERTEX) {
316     return true;
317   } else if(aShapeType == TopAbs_FACE) {
318     Handle(Geom_Surface) aSurface = BRep_Tool::Surface(TopoDS::Face(aShape));
319     if(aSurface->DynamicType() == STANDARD_TYPE(Geom_RectangularTrimmedSurface)) {
320       Handle(Geom_RectangularTrimmedSurface) aTrimSurface =
321           Handle(Geom_RectangularTrimmedSurface)::DownCast(aSurface);
322       aSurface = aTrimSurface->BasisSurface();
323     }
324     return GeomLib_IsPlanarSurface(aSurface).IsPlanar();
325   } else {
326     BRepBuilderAPI_FindPlane aFindPlane(aShape);
327     bool isFound = aFindPlane.Found() == Standard_True;
328
329     if(!isFound) {
330
331       auto checkEdge = [](const TopoDS_Shape& theShape){
332         if(theShape.ShapeType()!= TopAbs_EDGE) 
333           return false;
334
335         Standard_Real aFirst, aLast;
336         Handle(Geom_Curve) aCurve = BRep_Tool::Curve(TopoDS::Edge(theShape), aFirst, aLast);
337         Handle(Standard_Type) aType = aCurve->DynamicType();
338         if(aType == STANDARD_TYPE(Geom_TrimmedCurve)) {
339           Handle(Geom_TrimmedCurve) aTrimCurve = Handle(Geom_TrimmedCurve)::DownCast(aCurve);
340           aType = aTrimCurve->BasisCurve()->DynamicType();
341         }
342
343         if(aType == STANDARD_TYPE(Geom_Line)
344             || aType == STANDARD_TYPE(Geom_Conic)
345             || aType == STANDARD_TYPE(Geom_Circle)
346             || aType == STANDARD_TYPE(Geom_Ellipse)
347             || aType == STANDARD_TYPE(Geom_Hyperbola)
348             || aType == STANDARD_TYPE(Geom_Parabola)) {
349           return true;
350         }
351         return false;
352       };
353
354       if(aShapeType == TopAbs_WIRE){
355         //check if wire consist of only one edge
356         int aNbEdges = 0;
357         TopExp_Explorer anExp(aShape, TopAbs_EDGE);
358         for (TopExp_Explorer anExp(aShape, TopAbs_EDGE); anExp.More(); anExp.Next()) {
359           aNbEdges++;
360           if(aNbEdges == 1){
361             const TopoDS_Edge& anEdge = TopoDS::Edge(anExp.Current());
362             isFound = checkEdge(anEdge);
363           }
364           else{
365             //if more than one edge, check is not valid
366             isFound = false;
367             break;
368           }
369         }
370       }
371       else if(aShapeType == TopAbs_EDGE){
372         isFound = checkEdge(aShape);
373       }
374     }
375
376     return isFound;
377   }
378
379   return false;
380 }
381
382 std::shared_ptr<GeomAPI_Vertex> GeomAPI_Shape::vertex() const
383 {
384   GeomVertexPtr aVertex;
385   if (isVertex()) {
386     const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
387     aVertex = GeomVertexPtr(new GeomAPI_Vertex);
388     aVertex->setImpl(new TopoDS_Shape(aShape));
389   }
390   return aVertex;
391 }
392
393 std::shared_ptr<GeomAPI_Edge> GeomAPI_Shape::edge() const
394 {
395   GeomEdgePtr anEdge;
396   if (isEdge()) {
397     const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
398     anEdge = GeomEdgePtr(new GeomAPI_Edge);
399     anEdge->setImpl(new TopoDS_Shape(aShape));
400   }
401   return anEdge;
402 }
403
404 std::shared_ptr<GeomAPI_Wire> GeomAPI_Shape::wire() const
405 {
406   GeomWirePtr aWire;
407   if (isWire()) {
408     const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
409     aWire = GeomWirePtr(new GeomAPI_Wire);
410     aWire->setImpl(new TopoDS_Shape(aShape));
411   }
412   return aWire;
413 }
414
415 std::shared_ptr<GeomAPI_Face> GeomAPI_Shape::face() const
416 {
417   GeomFacePtr aFace;
418   if (isFace()) {
419     const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
420     aFace = GeomFacePtr(new GeomAPI_Face);
421     aFace->setImpl(new TopoDS_Shape(aShape));
422   }
423   return aFace;
424 }
425
426 std::shared_ptr<GeomAPI_Shell> GeomAPI_Shape::shell() const
427 {
428   GeomShellPtr aShell;
429   if (isShell()) {
430     const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
431     aShell = GeomShellPtr(new GeomAPI_Shell);
432     aShell->setImpl(new TopoDS_Shape(aShape));
433   }
434   return aShell;
435 }
436
437 std::shared_ptr<GeomAPI_Solid> GeomAPI_Shape::solid() const
438 {
439   GeomSolidPtr aSolid;
440   if (isSolid()) {
441     const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
442     aSolid = GeomSolidPtr(new GeomAPI_Solid);
443     aSolid->setImpl(new TopoDS_Shape(aShape));
444   }
445   return aSolid;
446 }
447
448 std::list<std::shared_ptr<GeomAPI_Shape> >
449 GeomAPI_Shape::subShapes(const ShapeType theSubShapeType, const bool theOnlyUnique) const
450 {
451   ListOfShape aSubs;
452   const TopoDS_Shape& aShape = impl<TopoDS_Shape>();
453   if (aShape.IsNull())
454     return aSubs;
455
456   TopTools_MapOfShape alreadyThere;
457
458   // process multi-level compounds
459   if (shapeType() == COMPOUND && theSubShapeType == COMPOUND) {
460     for (TopoDS_Iterator anIt(aShape); anIt.More(); anIt.Next()) {
461       const TopoDS_Shape& aCurrent = anIt.Value();
462       if (aCurrent.ShapeType() == TopAbs_COMPOUND) {
463         if (!theOnlyUnique || alreadyThere.Add(aCurrent)) {
464           GeomShapePtr aSub(new GeomAPI_Shape);
465           aSub->setImpl(new TopoDS_Shape(aCurrent));
466           aSubs.push_back(aSub);
467         }
468       }
469     }
470     // add self
471     GeomShapePtr aSub(new GeomAPI_Shape);
472     aSub->setImpl(new TopoDS_Shape(aShape));
473     aSubs.push_back(aSub);
474   }
475   else {
476     for (TopExp_Explorer anExp(aShape, (TopAbs_ShapeEnum)theSubShapeType);
477          anExp.More(); anExp.Next()) {
478       if (!theOnlyUnique || alreadyThere.Add(anExp.Current())) {
479         GeomShapePtr aSub(new GeomAPI_Shape);
480         aSub->setImpl(new TopoDS_Shape(anExp.Current()));
481         aSubs.push_back(aSub);
482       }
483     }
484   }
485   return aSubs;
486 }
487
488 GeomAPI_Shape::ShapeType GeomAPI_Shape::shapeType() const
489 {
490   const TopoDS_Shape& aShape = impl<TopoDS_Shape>();
491   if (aShape.IsNull())
492     return GeomAPI_Shape::SHAPE;
493
494   ShapeType aST = GeomAPI_Shape::SHAPE;
495
496   switch(aShape.ShapeType()) {
497   case TopAbs_COMPOUND:
498     aST = GeomAPI_Shape::COMPOUND;
499     break;
500   case TopAbs_COMPSOLID:
501     aST = GeomAPI_Shape::COMPSOLID;
502     break;
503   case TopAbs_SOLID:
504     aST = GeomAPI_Shape::SOLID;
505     break;
506   case TopAbs_SHELL:
507     aST = GeomAPI_Shape::SHELL;
508     break;
509   case TopAbs_FACE:
510     aST = GeomAPI_Shape::FACE;
511     break;
512   case TopAbs_WIRE:
513     aST = GeomAPI_Shape::WIRE;
514     break;
515   case TopAbs_EDGE:
516     aST = GeomAPI_Shape::EDGE;
517     break;
518   case TopAbs_VERTEX:
519     aST = GeomAPI_Shape::VERTEX;
520     break;
521   case TopAbs_SHAPE:
522     aST = GeomAPI_Shape::SHAPE;
523     break;
524   }
525
526   return aST;
527 }
528
529 GeomAPI_Shape::ShapeType GeomAPI_Shape::shapeTypeByStr(std::string theType)
530 {
531   std::transform(theType.begin(), theType.end(), theType.begin(),
532                  [](char c) { return static_cast<char>(::toupper(c)); });
533   if (theType == "COMPOUND" || theType == "COMPOUNDS")
534     return COMPOUND;
535   if (theType == "COMPSOLID" || theType == "COMPSOLIDS")
536     return COMPSOLID;
537   if (theType == "SOLID" || theType == "SOLIDS")
538     return SOLID;
539   if (theType == "SHELL" || theType == "SHELLS")
540     return SHELL;
541   if (theType == "FACE" || theType == "FACES")
542     return FACE;
543   if (theType == "WIRE" || theType == "WIRES")
544     return WIRE;
545   if (theType == "EDGE" || theType == "EDGES")
546     return EDGE;
547   if (theType == "VERTEX" || theType == "VERTICES")
548     return VERTEX;
549   return SHAPE; // default
550 }
551
552 std::string GeomAPI_Shape::shapeTypeStr() const
553 {
554   ShapeType aShapeType = shapeType();
555   std::string aShapeTypeStr;
556
557   switch(aShapeType) {
558     case COMPOUND: {
559       aShapeTypeStr = "COMPOUND";
560       break;
561     }
562     case COMPSOLID: {
563       aShapeTypeStr = "COMPSOLID";
564       break;
565     }
566     case SOLID: {
567       aShapeTypeStr = "SOLID";
568       break;
569     }
570     case SHELL: {
571       aShapeTypeStr = "SHELL";
572       break;
573     }
574     case FACE: {
575       aShapeTypeStr = "FACE";
576       break;
577     }
578     case WIRE: {
579       aShapeTypeStr = "WIRE";
580       break;
581     }
582     case EDGE: {
583       aShapeTypeStr = "EDGE";
584       break;
585     }
586     case VERTEX: {
587       aShapeTypeStr = "VERTEX";
588       break;
589     }
590     case SHAPE: {
591       aShapeTypeStr = "SHAPE";
592       break;
593     }
594   }
595
596   return aShapeTypeStr;
597 }
598
599 GeomAPI_Shape::Orientation GeomAPI_Shape::orientation() const
600 {
601   TopAbs_Orientation anOrientation = MY_SHAPE->Orientation();
602
603   switch(anOrientation) {
604     case TopAbs_FORWARD:  return FORWARD;
605     case TopAbs_REVERSED: return REVERSED;
606     case TopAbs_INTERNAL: return INTERNAL;
607     case TopAbs_EXTERNAL: return EXTERNAL;
608     default:              return FORWARD;
609   }
610 }
611
612 void GeomAPI_Shape::setOrientation(const GeomAPI_Shape::Orientation theOrientation)
613 {
614   switch(theOrientation) {
615     case FORWARD:  MY_SHAPE->Orientation(TopAbs_FORWARD);  break;
616     case REVERSED: MY_SHAPE->Orientation(TopAbs_REVERSED); break;
617     case INTERNAL: MY_SHAPE->Orientation(TopAbs_INTERNAL); break;
618     case EXTERNAL: MY_SHAPE->Orientation(TopAbs_EXTERNAL); break;
619   }
620 }
621
622 void GeomAPI_Shape::reverse()
623 {
624   MY_SHAPE->Reverse();
625 }
626
627 bool GeomAPI_Shape::isSubShape(const std::shared_ptr<GeomAPI_Shape> theShape,
628                                const bool theCheckOrientation) const
629 {
630   if(!theShape.get()) {
631     return false;
632   }
633
634   const TopoDS_Shape& aShapeToSearch = theShape->impl<TopoDS_Shape>();
635   if(aShapeToSearch.IsNull()) {
636     return false;
637   }
638
639   for(TopExp_Explorer anExp(*MY_SHAPE, aShapeToSearch.ShapeType()); anExp.More(); anExp.Next()) {
640     if(theCheckOrientation ?
641        aShapeToSearch.IsEqual(anExp.Current()) : aShapeToSearch.IsSame(anExp.Current())) {
642       return true;
643     }
644   }
645
646   return false;
647 }
648
649 bool GeomAPI_Shape::computeSize(double& theXmin, double& theYmin, double& theZmin,
650                                 double& theXmax, double& theYmax, double& theZmax) const
651 {
652   const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
653   if (aShape.IsNull())
654     return false;
655   Bnd_Box aBndBox;
656   // Workaround: compute optimal bounding box for the compounds of edges/vertices, because sometimes
657   // the bounding box of sketch is calculated if the transformation is applied twice (issue #20167).
658   bool isShape1D = false;
659   if (aShape.ShapeType() == TopAbs_COMPOUND) {
660     isShape1D = true;
661     for (TopoDS_Iterator anIt(aShape); anIt.More() && isShape1D; anIt.Next())
662       if (anIt.Value().ShapeType() < TopAbs_WIRE)
663         isShape1D = false;
664   }
665   if (isShape1D)
666     BRepBndLib::AddOptimal(aShape, aBndBox, false, true);
667   else
668     BRepBndLib::Add(aShape, aBndBox, false);
669   if (aBndBox.IsVoid())
670     return false;
671   aBndBox.Get(theXmin, theYmin, theZmin, theXmax, theYmax, theZmax);
672   return true;
673 }
674
675 GeomPointPtr GeomAPI_Shape::middlePoint() const
676 {
677   GeomPointPtr aMiddlePoint;
678
679   switch (shapeType()) {
680   case VERTEX:
681     aMiddlePoint = vertex()->point();
682     break;
683   case EDGE:
684     aMiddlePoint = edge()->middlePoint();
685     break;
686   case WIRE:
687     aMiddlePoint = wire()->middlePoint();
688     break;
689   case FACE:
690     aMiddlePoint = face()->middlePoint();
691     break;
692   case SHELL:
693     aMiddlePoint = shell()->middlePoint();
694     break;
695   case SOLID:
696     aMiddlePoint = solid()->middlePoint();
697     break;
698   default: {
699       // get middle point as center of the bounding box
700       double aMinX, aMinY, aMinZ, aMaxX, aMaxY, aMaxZ;
701       computeSize(aMinX, aMinY, aMinZ, aMaxX, aMaxY, aMaxZ);
702       aMiddlePoint = GeomPointPtr(new GeomAPI_Pnt(
703           (aMinX + aMaxX) * 0.5, (aMinY + aMaxY) * 0.5, (aMinZ + aMaxZ) * 0.5));
704     }
705   }
706
707   return aMiddlePoint;
708 }
709
710 // LCOV_EXCL_START
711 std::string GeomAPI_Shape::getShapeStream(const bool theWithTriangulation) const
712 {
713   std::ostringstream aStream;
714   const TopoDS_Shape& aShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
715   if (!theWithTriangulation) { // make a copy of shape without triangulation
716     BRepBuilderAPI_Copy aCopy(aShape, Standard_False, Standard_False);
717     const TopoDS_Shape& aCopyShape = aCopy.Shape();
718     // make all faces unchecked to make the stream of shapes the same
719     TopExp_Explorer aFaceExp(aCopyShape, TopAbs_FACE);
720     for(; aFaceExp.More(); aFaceExp.Next()) {
721       aFaceExp.Current().TShape()->Checked(Standard_False);
722     }
723     BRepTools::Write(aCopyShape, aStream);
724   } else {
725     BRepTools::Write(aShape, aStream);
726   }
727   return aStream.str();
728 }
729 // LCOV_EXCL_STOP
730
731 GeomShapePtr GeomAPI_Shape::intersect(const GeomShapePtr theShape) const
732 {
733   const TopoDS_Shape& aShape1 = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
734   const TopoDS_Shape& aShape2 = theShape->impl<TopoDS_Shape>();
735
736   BRepAlgoAPI_Section aCommon(aShape1, aShape2);
737   if (!aCommon.IsDone())
738     return GeomShapePtr();
739
740   TopoDS_Shape aResult = aCommon.Shape();
741   if (aResult.ShapeType() == TopAbs_COMPOUND) {
742     NCollection_List<TopoDS_Shape> aSubs;
743     addSimpleToList(aResult, aSubs);
744     if(aSubs.Size() == 1) {
745       aResult = aSubs.First();
746     } else if(aSubs.Size() == 0) {
747       return GeomShapePtr();
748     }
749   }
750
751   GeomShapePtr aResShape(new GeomAPI_Shape);
752   aResShape->setImpl(new TopoDS_Shape(aResult));
753   return aResShape;
754 }
755
756 bool GeomAPI_Shape::isIntersect(const GeomShapePtr theShape) const
757 {
758   if(!theShape.get()) {
759     return false;
760   }
761
762   const TopoDS_Shape& aShape1 = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
763   const TopoDS_Shape& aShape2 = theShape->impl<TopoDS_Shape>();
764
765   BRepExtrema_DistShapeShape aDist(aShape1, aShape2);
766   aDist.Perform();
767   if(aDist.IsDone() && aDist.Value() < Precision::Confusion()) {
768     return true;
769   }
770
771   return false;
772 }
773
774 void GeomAPI_Shape::translate(const std::shared_ptr<GeomAPI_Dir> theDir, const double theOffset)
775 {
776   gp_Dir aDir = theDir->impl<gp_Dir>();
777   gp_Vec aTrsfVec(aDir.XYZ() * theOffset);
778   gp_Trsf aTranslation;
779   aTranslation.SetTranslation(aTrsfVec);
780   TopoDS_Shape aResult = MY_SHAPE->Moved(aTranslation);
781   setImpl(new TopoDS_Shape(aResult));
782 }
783
784 void GeomAPI_Shape::move(const std::shared_ptr<GeomAPI_Trsf> theTransformation)
785 {
786   TopoDS_Shape aResult = MY_SHAPE->Moved(theTransformation->impl<gp_Trsf>());
787   setImpl(new TopoDS_Shape(aResult));
788 }
789
790 bool GeomAPI_Shape::isSelfIntersected(const int theLevelOfCheck) const
791 {
792   BOPAlgo_CheckerSI aCSI;  // checker of self-interferences
793   aCSI.SetLevelOfCheck(theLevelOfCheck);
794   TopTools_ListOfShape aList;
795   const TopoDS_Shape& aThisShape = const_cast<GeomAPI_Shape*>(this)->impl<TopoDS_Shape>();
796   aList.Append(aThisShape);
797   aCSI.SetArguments(aList);
798   aCSI.Perform();
799   if (aCSI.HasErrors() || aCSI.DS().Interferences().Extent() > 0) {
800     return true;
801   }
802
803   return false;
804 }
805
806 bool GeomAPI_Shape::Comparator::operator()(const std::shared_ptr<GeomAPI_Shape>& theShape1,
807                                            const std::shared_ptr<GeomAPI_Shape>& theShape2) const
808 {
809   const TopoDS_Shape& aShape1 = theShape1->impl<TopoDS_Shape>();
810   const TopoDS_Shape& aShape2 = theShape2->impl<TopoDS_Shape>();
811   bool isLess = aShape1.TShape() < aShape2.TShape();
812   if (aShape1.TShape() == aShape2.TShape()) {
813     Standard_Integer aHash1 = aShape1.Location().HashCode(IntegerLast());
814     Standard_Integer aHash2 = aShape2.Location().HashCode(IntegerLast());
815     isLess = aHash1 < aHash2;
816   }
817   return isLess;
818 }
819
820 bool GeomAPI_Shape::ComparatorWithOri::operator()(
821     const std::shared_ptr<GeomAPI_Shape>& theShape1,
822     const std::shared_ptr<GeomAPI_Shape>& theShape2) const
823 {
824   const TopoDS_Shape& aShape1 = theShape1->impl<TopoDS_Shape>();
825   const TopoDS_Shape& aShape2 = theShape2->impl<TopoDS_Shape>();
826   bool isLess = aShape1.TShape() < aShape2.TShape();
827   if (aShape1.TShape() == aShape2.TShape()) {
828     Standard_Integer aHash1 = aShape1.Location().HashCode(IntegerLast());
829     Standard_Integer aHash2 = aShape2.Location().HashCode(IntegerLast());
830     isLess = (aHash1 < aHash2) ||
831              (aHash1 == aHash2 && aShape1.Orientation() < aShape2.Orientation());
832   }
833   return isLess;
834 }
835
836 int GeomAPI_Shape::Hash::operator()(const std::shared_ptr<GeomAPI_Shape>& theShape) const
837 {
838   const TopoDS_Shape& aShape = theShape->impl<TopoDS_Shape>();
839   return aShape.HashCode(IntegerLast());
840 }
841
842 bool GeomAPI_Shape::Equal::operator()(const std::shared_ptr<GeomAPI_Shape>& theShape1,
843                                       const std::shared_ptr<GeomAPI_Shape>& theShape2) const
844 {
845   const TopoDS_Shape& aShape1 = theShape1->impl<TopoDS_Shape>();
846   const TopoDS_Shape& aShape2 = theShape2->impl<TopoDS_Shape>();
847
848   Standard_Integer aHash1 = aShape1.Location().HashCode(IntegerLast());
849   Standard_Integer aHash2 = aShape2.Location().HashCode(IntegerLast());
850
851   return aShape1.TShape() == aShape2.TShape() && aHash1 == aHash2;
852 }