]> SALOME platform Git repositories - modules/shaper.git/blob - src/GeomAlgoAPI/GeomAlgoAPI_BoundingBox.cpp
Salome HOME
Updated copyright comment
[modules/shaper.git] / src / GeomAlgoAPI / GeomAlgoAPI_BoundingBox.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 "GeomAlgoAPI_BoundingBox.h"
21
22 #include <BRepBuilderAPI_Copy.hxx>
23 #include <Bnd_Box.hxx>
24 #include <BRepTools.hxx>
25 #include <BRep_Tool.hxx>
26 #include <BRepBndLib.hxx>
27 #include <BRep_Builder.hxx>
28 #include <Geom_Circle.hxx>
29 #include <TopoDS_Shape.hxx>
30 #include <TopoDS.hxx>
31 #include <gp_Pln.hxx>
32 #include <BRepBuilderAPI_MakeFace.hxx>
33 #include <Geom_RectangularTrimmedSurface.hxx>
34 #include <BRepExtrema_DistShapeShape.hxx>
35 #include <ShapeFix_Shape.hxx>
36 #include <BRepBuilderAPI_Sewing.hxx>
37 #include <Standard_ErrorHandler.hxx>
38 #include <TopExp_Explorer.hxx>
39 #include <BRepClass3d_SolidClassifier.hxx>
40 #include <Geom_SphericalSurface.hxx>
41 #include <Geom_ToroidalSurface.hxx>
42
43 /**
44 * This function constructs and returns modified shape from the original one
45 * for singular cases. It is used for the method GetMinDistanceSingular.
46 *
47 * \param theShape the original shape
48 * \param theModifiedShape output parameter. The modified shape.
49 * \param theAddDist output parameter. The added distance for modified shape.
50 * \retval true if the shape is modified; false otherwise.
51 *
52 * \internal
53 */
54 Standard_Boolean ModifyShape(const TopoDS_Shape  &theShape,
55                              TopoDS_Shape  &theModifiedShape,
56                              Standard_Real &theAddDist)
57 {
58   TopExp_Explorer anExp;
59   int nbf = 0;
60
61   theAddDist = 0.;
62   theModifiedShape.Nullify();
63
64   for ( anExp.Init( theShape, TopAbs_FACE ); anExp.More(); anExp.Next() ) {
65     nbf++;
66     theModifiedShape = anExp.Current();
67   }
68   if(nbf==1) {
69     TopoDS_Shape sh = theShape;
70     while(sh.ShapeType()==TopAbs_COMPOUND) {
71       TopoDS_Iterator it(sh);
72       sh = it.Value();
73     }
74     Handle(Geom_Surface) S = BRep_Tool::Surface(TopoDS::Face(theModifiedShape));
75     if(S->IsKind(STANDARD_TYPE(Geom_SphericalSurface)) ||
76        S->IsKind(STANDARD_TYPE(Geom_ToroidalSurface)) ||
77        S->IsUPeriodic()) {
78       const Standard_Boolean isShell =
79         (sh.ShapeType()==TopAbs_SHELL || sh.ShapeType()==TopAbs_FACE);
80
81       if (!isShell && S->IsKind(STANDARD_TYPE(Geom_SphericalSurface))) {
82         Handle(Geom_SphericalSurface) SS = Handle(Geom_SphericalSurface)::DownCast(S);
83         gp_Pnt PC = SS->Location();
84           BRep_Builder B;
85           TopoDS_Vertex V;
86           B.MakeVertex(V,PC,1.e-7);
87           theModifiedShape = V;
88           theAddDist = SS->Radius();
89           return Standard_True;
90         }
91         if (!isShell && S->IsKind(STANDARD_TYPE(Geom_ToroidalSurface))) {
92           Handle(Geom_ToroidalSurface) TS = Handle(Geom_ToroidalSurface)::DownCast(S);
93           gp_Ax3 ax3 = TS->Position();
94           Handle(Geom_Circle) C = new Geom_Circle(ax3.Ax2(),TS->MajorRadius());
95           BRep_Builder B;
96           TopoDS_Edge E;
97           B.MakeEdge(E,C,1.e-7);
98           theModifiedShape = E;
99           theAddDist = TS->MinorRadius();
100           return Standard_True;
101         }
102
103         // non solid case or any periodic surface (Mantis 22454).
104         double U1,U2,V1,V2;
105         // changes for 0020677: EDF 1219 GEOM: MinDistance gives 0 instead of 20.88
106         //S->Bounds(U1,U2,V1,V2); changed by
107         BRepTools::UVBounds(TopoDS::Face(theModifiedShape),U1,U2,V1,V2);
108         // end of changes for 020677 (dmv)
109         Handle(Geom_RectangularTrimmedSurface) TrS1 =
110           new Geom_RectangularTrimmedSurface(S,U1,(U1+U2)/2.,V1,V2);
111         Handle(Geom_RectangularTrimmedSurface) TrS2 =
112           new Geom_RectangularTrimmedSurface(S,(U1+U2)/2.,U2,V1,V2);
113         TopoDS_Shape aMShape;
114
115         TopoDS_Face F1 = BRepBuilderAPI_MakeFace(TrS1, Precision::Confusion());
116         TopoDS_Face F2 = BRepBuilderAPI_MakeFace(TrS2, Precision::Confusion());
117
118         if (isShell) {
119           BRep_Builder B;
120           B.MakeCompound(TopoDS::Compound(aMShape));
121           B.Add(aMShape, F1);
122           B.Add(aMShape, F2);
123         } else {
124           // The original shape is a solid.
125           BRepBuilderAPI_Sewing aSewing (Precision::Confusion()*10.0);
126           aSewing.Add(F1);
127           aSewing.Add(F2);
128           aSewing.Perform();
129           aMShape = aSewing.SewedShape();
130           BRep_Builder B;
131           TopoDS_Solid aSolid;
132           B.MakeSolid(aSolid);
133           B.Add(aSolid, aMShape);
134           aMShape = aSolid;
135         }
136
137         Handle(ShapeFix_Shape) sfs = new ShapeFix_Shape;
138         sfs->Init(aMShape);
139         sfs->SetPrecision(1.e-6);
140         sfs->SetMaxTolerance(1.0);
141         sfs->Perform();
142         theModifiedShape = sfs->Shape();
143         return Standard_True;
144       }
145     }
146
147     theModifiedShape = theShape;
148     return Standard_False;
149   }
150
151 //=======================================================================
152 // function : GetMinDistanceSingular
153 //=======================================================================
154 double GetMinDistanceSingular(const TopoDS_Shape& aSh1,
155                               const TopoDS_Shape& aSh2,
156                               gp_Pnt& Ptmp1, gp_Pnt& Ptmp2)
157 {
158   TopoDS_Shape     tmpSh1;
159   TopoDS_Shape     tmpSh2;
160   Standard_Real    AddDist1 = 0.;
161   Standard_Real    AddDist2 = 0.;
162   Standard_Boolean IsChange1 = ModifyShape(aSh1, tmpSh1, AddDist1);
163   Standard_Boolean IsChange2 = ModifyShape(aSh2, tmpSh2, AddDist2);
164
165   if( !IsChange1 && !IsChange2 )
166     return -2.0;
167
168   BRepExtrema_DistShapeShape dst(tmpSh1,tmpSh2);
169   if (dst.IsDone()) {
170     double MinDist = 1.e9;
171     gp_Pnt PMin1, PMin2, P1, P2;
172     for (int i = 1; i <= dst.NbSolution(); i++) {
173       P1 = dst.PointOnShape1(i);
174       P2 = dst.PointOnShape2(i);
175       Standard_Real Dist = P1.Distance(P2);
176       if (MinDist > Dist) {
177         MinDist = Dist;
178         PMin1 = P1;
179         PMin2 = P2;
180       }
181     }
182     if(MinDist<1.e-7) {
183       Ptmp1 = PMin1;
184       Ptmp2 = PMin2;
185     }
186     else {
187       gp_Dir aDir(gp_Vec(PMin1,PMin2));
188       if( MinDist > (AddDist1+AddDist2) ) {
189         Ptmp1 = gp_Pnt(PMin1.X() + aDir.X()*AddDist1,
190                        PMin1.Y() + aDir.Y()*AddDist1,
191                        PMin1.Z() + aDir.Z()*AddDist1);
192         Ptmp2 = gp_Pnt(PMin2.X() - aDir.X()*AddDist2,
193                        PMin2.Y() - aDir.Y()*AddDist2,
194                        PMin2.Z() - aDir.Z()*AddDist2);
195         return (MinDist - AddDist1 - AddDist2);
196       }
197       else {
198         if( AddDist1 > 0 ) {
199           Ptmp1 = gp_Pnt(PMin1.X() + aDir.X()*AddDist1,
200                          PMin1.Y() + aDir.Y()*AddDist1,
201                          PMin1.Z() + aDir.Z()*AddDist1);
202           Ptmp2 = Ptmp1;
203         }
204         else {
205           Ptmp2 = gp_Pnt(PMin2.X() - aDir.X()*AddDist2,
206                          PMin2.Y() - aDir.Y()*AddDist2,
207                          PMin2.Z() - aDir.Z()*AddDist2);
208           Ptmp1 = Ptmp2;
209         }
210       }
211     }
212     double res = MinDist - AddDist1 - AddDist2;
213     if(res<0.) res = 0.0;
214     return res;
215   }
216   return -2.0;
217 }
218
219 //=======================================================================
220 // function : GetMinDistance
221 //=======================================================================
222 Standard_Real GetMinDistance(const TopoDS_Shape& theShape1,
223                              const TopoDS_Shape& theShape2,
224                              gp_Pnt& thePnt1, gp_Pnt& thePnt2)
225 {
226   Standard_Real aResult = 1.e9;
227
228   // Issue 0020231: A min distance bug with torus and vertex.
229   // Make GetMinDistance() return zero if a sole VERTEX is inside any of SOLIDs
230
231   // which of shapes consists of only one vertex?
232   TopExp_Explorer exp1(theShape1,TopAbs_VERTEX), exp2(theShape2,TopAbs_VERTEX);
233   TopoDS_Shape V1 = exp1.More() ? exp1.Current() : TopoDS_Shape();
234   TopoDS_Shape V2 = exp2.More() ? exp2.Current() : TopoDS_Shape();
235   exp1.Next(); exp2.Next();
236   if ( exp1.More() ) V1.Nullify();
237   if ( exp2.More() ) V2.Nullify();
238   // vertex and container of solids
239   TopoDS_Shape V = V1.IsNull() ? V2 : V1;
240   TopoDS_Shape S = V1.IsNull() ? theShape1 : theShape2;
241   if ( !V.IsNull() ) {
242     // classify vertex against solids
243     gp_Pnt p = BRep_Tool::Pnt( TopoDS::Vertex( V ) );
244     for ( exp1.Init( S, TopAbs_SOLID ); exp1.More(); exp1.Next() ) {
245       BRepClass3d_SolidClassifier classifier( exp1.Current(), p, 1e-6);
246       if ( classifier.State() == TopAbs_IN ) {
247         thePnt1 = p;
248         thePnt2 = p;
249         return 0.0;
250       }
251     }
252   }
253
254   aResult = GetMinDistanceSingular(theShape1, theShape2, thePnt1, thePnt2);
255
256
257   BRepExtrema_DistShapeShape dst (theShape1, theShape2);
258   if (dst.IsDone()) {
259     gp_Pnt P1, P2;
260
261     for (int i = 1; i <= dst.NbSolution(); i++) {
262       P1 = dst.PointOnShape1(i);
263       P2 = dst.PointOnShape2(i);
264
265       Standard_Real Dist = P1.Distance(P2);
266       if (aResult < 0 || aResult > Dist) {
267         aResult = Dist;
268         thePnt1 = P1;
269         thePnt2 = P2;
270       }
271     }
272   }
273
274   return aResult;
275 }
276
277 //=======================================================================
278 // function : PreciseBoundingBox
279 //=======================================================================
280 Standard_Boolean PreciseBoundingBox(const TopoDS_Shape &theShape, Bnd_Box &theBox)
281 {
282   if (theBox.IsVoid()) BRepBndLib::Add( theShape, theBox );
283   if (theBox.IsVoid()) return Standard_False;
284
285   Standard_Real aBound[6];
286   theBox.Get(aBound[0], aBound[2], aBound[4], aBound[1], aBound[3], aBound[5]);
287
288   Standard_Integer i;
289   const gp_Pnt aMid(0.5*(aBound[1] + aBound[0]),  // XMid
290                     0.5*(aBound[3] + aBound[2]),  // YMid
291                     0.5*(aBound[5] + aBound[4])); // ZMid
292   const gp_XYZ aSize(aBound[1] - aBound[0],       // DX
293                      aBound[3] - aBound[2],       // DY
294                      aBound[5] - aBound[4]);      // DZ
295   const gp_Pnt aPnt[6] =
296     {
297       gp_Pnt(aBound[0] - (aBound[1] - aBound[0]), aMid.Y(), aMid.Z()), // XMin
298       gp_Pnt(aBound[1] + (aBound[1] - aBound[0]), aMid.Y(), aMid.Z()), // XMax
299       gp_Pnt(aMid.X(), aBound[2] - (aBound[3] - aBound[2]), aMid.Z()), // YMin
300       gp_Pnt(aMid.X(), aBound[3] + (aBound[3] - aBound[2]), aMid.Z()), // YMax
301       gp_Pnt(aMid.X(), aMid.Y(), aBound[4] - (aBound[5] - aBound[4])), // ZMin
302       gp_Pnt(aMid.X(), aMid.Y(), aBound[5] + (aBound[5] - aBound[4]))  // ZMax
303     };
304   const gp_Dir aDir[3] = { gp::DX(), gp::DY(), gp::DZ() };
305   const Standard_Real aPlnSize[3] =
306     {
307       0.5*Max(aSize.Y(), aSize.Z()), // XMin, XMax planes
308       0.5*Max(aSize.X(), aSize.Z()), // YMin, YMax planes
309       0.5*Max(aSize.X(), aSize.Y())  // ZMin, ZMax planes
310     };
311   gp_Pnt aPMin[2];
312
313   for (i = 0; i < 6; i++) {
314     const Standard_Integer iHalf = i/2;
315     const gp_Pln aPln(aPnt[i], aDir[iHalf]);
316     BRepBuilderAPI_MakeFace aMkFace(aPln, -aPlnSize[iHalf], aPlnSize[iHalf],
317                                     -aPlnSize[iHalf], aPlnSize[iHalf]);
318
319     if (!aMkFace.IsDone()) {
320       return Standard_False;
321     }
322
323     TopoDS_Shape aFace = aMkFace.Shape();
324
325     // Get minimal distance between planar face and shape.
326     Standard_Real aMinDist = GetMinDistance(aFace, theShape, aPMin[0], aPMin[1]);
327
328     if (aMinDist < 0.) {
329       return Standard_False;
330     }
331
332     aBound[i] = aPMin[1].Coord(iHalf + 1);
333   }
334
335   // Update Bounding box with the new values.
336   theBox.SetVoid();
337   theBox.Update(aBound[0], aBound[2], aBound[4], aBound[1], aBound[3], aBound[5]);
338
339   return Standard_True;
340 }
341
342 //=================================================================================================
343 bool GetBoundingBox(const std::shared_ptr<GeomAPI_Shape>& theShape,
344                     double& theXmin, double& theXmax,
345                     double& theYmin, double& theYmax,
346                     double& theZmin, double& theZmax,
347                     std::string& theError)
348 {
349   #ifdef _DEBUG
350   std::cout << "GetBoundingBox " << std::endl;
351   #endif
352
353   if (!theShape.get()) {
354     theError = "GetBoundingBox : An invalid argument";
355     return false;
356   }
357
358   TopoDS_Shape aShape = theShape->impl<TopoDS_Shape>();
359
360   //Compute the parameters
361   Bnd_Box B;
362   try {
363     OCC_CATCH_SIGNALS;
364     BRepBuilderAPI_Copy aCopyTool (aShape);
365     if (!aCopyTool.IsDone()) {
366       theError = "GetBoundingBox Error: Bad shape detected";
367       return false;
368     }
369
370     aShape = aCopyTool.Shape();
371
372     // remove triangulation to obtain more exact boundaries
373     BRepTools::Clean(aShape);
374
375     BRepBndLib::Add(aShape, B);
376
377     if (!PreciseBoundingBox(aShape, B)) {
378       theError = "GetBoundingBox Error: Bounding box cannot be precised";
379       return false;
380     }
381
382     B.Get(theXmin, theYmin, theZmin, theXmax, theYmax, theZmax);
383   }
384   catch (Standard_Failure& aFail) {
385     theError = aFail.GetMessageString();
386     return false;
387   }
388   return true;
389 }