1 // Copyright (C) 2014-2023 CEA, EDF
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.
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.
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
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include "GeomAlgoAPI_BoundingBox.h"
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>
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>
44 * This function constructs and returns modified shape from the original one
45 * for singular cases. It is used for the method GetMinDistanceSingular.
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.
54 Standard_Boolean ModifyShape(const TopoDS_Shape &theShape,
55 TopoDS_Shape &theModifiedShape,
56 Standard_Real &theAddDist)
58 TopExp_Explorer anExp;
62 theModifiedShape.Nullify();
64 for ( anExp.Init( theShape, TopAbs_FACE ); anExp.More(); anExp.Next() ) {
66 theModifiedShape = anExp.Current();
69 TopoDS_Shape sh = theShape;
70 while(sh.ShapeType()==TopAbs_COMPOUND) {
71 TopoDS_Iterator it(sh);
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)) ||
78 const Standard_Boolean isShell =
79 (sh.ShapeType()==TopAbs_SHELL || sh.ShapeType()==TopAbs_FACE);
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();
86 B.MakeVertex(V,PC,1.e-7);
88 theAddDist = SS->Radius();
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());
97 B.MakeEdge(E,C,1.e-7);
99 theAddDist = TS->MinorRadius();
100 return Standard_True;
103 // non solid case or any periodic surface (Mantis 22454).
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;
115 TopoDS_Face F1 = BRepBuilderAPI_MakeFace(TrS1, Precision::Confusion());
116 TopoDS_Face F2 = BRepBuilderAPI_MakeFace(TrS2, Precision::Confusion());
120 B.MakeCompound(TopoDS::Compound(aMShape));
124 // The original shape is a solid.
125 BRepBuilderAPI_Sewing aSewing (Precision::Confusion()*10.0);
129 aMShape = aSewing.SewedShape();
133 B.Add(aSolid, aMShape);
137 Handle(ShapeFix_Shape) sfs = new ShapeFix_Shape;
139 sfs->SetPrecision(1.e-6);
140 sfs->SetMaxTolerance(1.0);
142 theModifiedShape = sfs->Shape();
143 return Standard_True;
147 theModifiedShape = theShape;
148 return Standard_False;
151 //=======================================================================
152 // function : GetMinDistanceSingular
153 //=======================================================================
154 double GetMinDistanceSingular(const TopoDS_Shape& aSh1,
155 const TopoDS_Shape& aSh2,
156 gp_Pnt& Ptmp1, gp_Pnt& Ptmp2)
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);
165 if( !IsChange1 && !IsChange2 )
168 BRepExtrema_DistShapeShape dst(tmpSh1,tmpSh2);
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) {
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);
199 Ptmp1 = gp_Pnt(PMin1.X() + aDir.X()*AddDist1,
200 PMin1.Y() + aDir.Y()*AddDist1,
201 PMin1.Z() + aDir.Z()*AddDist1);
205 Ptmp2 = gp_Pnt(PMin2.X() - aDir.X()*AddDist2,
206 PMin2.Y() - aDir.Y()*AddDist2,
207 PMin2.Z() - aDir.Z()*AddDist2);
212 double res = MinDist - AddDist1 - AddDist2;
213 if(res<0.) res = 0.0;
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)
226 Standard_Real aResult = 1.e9;
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
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;
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 ) {
254 aResult = GetMinDistanceSingular(theShape1, theShape2, thePnt1, thePnt2);
257 BRepExtrema_DistShapeShape dst (theShape1, theShape2);
261 for (int i = 1; i <= dst.NbSolution(); i++) {
262 P1 = dst.PointOnShape1(i);
263 P2 = dst.PointOnShape2(i);
265 Standard_Real Dist = P1.Distance(P2);
266 if (aResult < 0 || aResult > Dist) {
277 //=======================================================================
278 // function : PreciseBoundingBox
279 //=======================================================================
280 Standard_Boolean PreciseBoundingBox(const TopoDS_Shape &theShape, Bnd_Box &theBox)
282 if (theBox.IsVoid()) BRepBndLib::Add( theShape, theBox );
283 if (theBox.IsVoid()) return Standard_False;
285 Standard_Real aBound[6];
286 theBox.Get(aBound[0], aBound[2], aBound[4], aBound[1], aBound[3], aBound[5]);
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] =
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
304 const gp_Dir aDir[3] = { gp::DX(), gp::DY(), gp::DZ() };
305 const Standard_Real aPlnSize[3] =
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
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]);
319 if (!aMkFace.IsDone()) {
320 return Standard_False;
323 TopoDS_Shape aFace = aMkFace.Shape();
325 // Get minimal distance between planar face and shape.
326 Standard_Real aMinDist = GetMinDistance(aFace, theShape, aPMin[0], aPMin[1]);
329 return Standard_False;
332 aBound[i] = aPMin[1].Coord(iHalf + 1);
335 // Update Bounding box with the new values.
337 theBox.Update(aBound[0], aBound[2], aBound[4], aBound[1], aBound[3], aBound[5]);
339 return Standard_True;
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)
350 std::cout << "GetBoundingBox " << std::endl;
353 if (!theShape.get()) {
354 theError = "GetBoundingBox : An invalid argument";
358 TopoDS_Shape aShape = theShape->impl<TopoDS_Shape>();
360 //Compute the parameters
364 BRepBuilderAPI_Copy aCopyTool (aShape);
365 if (!aCopyTool.IsDone()) {
366 theError = "GetBoundingBox Error: Bad shape detected";
370 aShape = aCopyTool.Shape();
372 // remove triangulation to obtain more exact boundaries
373 BRepTools::Clean(aShape);
375 BRepBndLib::Add(aShape, B);
377 if (!PreciseBoundingBox(aShape, B)) {
378 theError = "GetBoundingBox Error: Bounding box cannot be precised";
382 B.Get(theXmin, theYmin, theZmin, theXmax, theYmax, theZmax);
384 catch (Standard_Failure& aFail) {
385 theError = aFail.GetMessageString();