1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-08-09 13:22:24 +03:00

0027352: Optimal axis-aligned bounding box for a shape

Add method for exact calculation of bounding boxes

Modifications made according to remarks.

Small correction of test case for issue CR27352

Small correction of test cases for issue CR27352

Avoid warning on VS2015
This commit is contained in:
ifv
2016-04-26 15:00:29 +03:00
committed by bugmaster
parent 745c138678
commit 3ba87fdb66
19 changed files with 2643 additions and 250 deletions

View File

@@ -34,7 +34,42 @@
#include <TopLoc_Location.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Shape.hxx>
#include <Adaptor3d_HCurve.hxx>
#include <Adaptor3d_HSurface.hxx>
#include <BRepTools.hxx>
#include <Geom_BSplineSurface.hxx>
#include <Geom_BezierSurface.hxx>
#include <Bnd_Box2d.hxx>
#include <BndLib_Add2dCurve.hxx>
#include <BRepTopAdaptor_FClass2d.hxx>
#include <ElSLib.hxx>
#include <ElCLib.hxx>
#include <Geom_Plane.hxx>
#include <Extrema_ExtSS.hxx>
#include <GeomAdaptor_Surface.hxx>
//
static Standard_Boolean CanUseEdges(const Adaptor3d_Surface& BS);
//
static void FindExactUVBounds(const TopoDS_Face F,
Standard_Real& umin, Standard_Real& umax,
Standard_Real& vmin, Standard_Real& vmax,
const Standard_Real Tol,
Standard_Boolean& isNaturalRestriction);
//
static void AdjustFaceBox(const BRepAdaptor_Surface& BS,
const Standard_Real umin, const Standard_Real umax,
const Standard_Real vmin, const Standard_Real vmax,
Bnd_Box& FaceBox,
const Bnd_Box& EdgeBox, const Standard_Real Tol);
//
static Standard_Boolean IsModifySize(const BRepAdaptor_Surface& theBS,
const gp_Pln& thePln, const gp_Pnt& theP,
const Standard_Real umin, const Standard_Real umax,
const Standard_Real vmin, const Standard_Real vmax,
const BRepTopAdaptor_FClass2d& theFClass,
const Standard_Real theTolU, const Standard_Real theTolV);
//
//=======================================================================
//function : Add
//purpose : Add a shape bounding to a box
@@ -175,3 +210,582 @@ void BRepBndLib::AddClose(const TopoDS_Shape& S, Bnd_Box& B)
}
}
//=======================================================================
//function : AddOptimal
//purpose : Add a shape bounding to a box
//=======================================================================
void BRepBndLib::AddOptimal(const TopoDS_Shape& S, Bnd_Box& B,
const Standard_Boolean useTriangulation,
const Standard_Boolean useShapeTolerance)
{
TopExp_Explorer ex;
// Add the faces
BRepAdaptor_Surface BS;
Handle(Poly_Triangulation) T;
TopLoc_Location l;
Standard_Integer i, nbNodes;
BRepAdaptor_Curve BC;
for (ex.Init(S,TopAbs_FACE); ex.More(); ex.Next()) {
const TopoDS_Face& F = TopoDS::Face(ex.Current());
T = BRep_Tool::Triangulation(F, l);
Bnd_Box aLocBox;
if (useTriangulation && !T.IsNull())
{
nbNodes = T->NbNodes();
const TColgp_Array1OfPnt& Nodes = T->Nodes();
for (i = 1; i <= nbNodes; i++) {
if (l.IsIdentity()) aLocBox.Add(Nodes(i));
else aLocBox.Add(Nodes(i).Transformed(l));
}
// B.Enlarge(T->Deflection());
aLocBox.Enlarge(T->Deflection() + BRep_Tool::Tolerance(F));
Standard_Real xmin, ymin, zmin, xmax, ymax, zmax;
aLocBox.Get(xmin, ymin, zmin, xmax, ymax, zmax);
B.Update(xmin, ymin, zmin, xmax, ymax, zmax);
}
else
{
const Handle(Geom_Surface)& GS = BRep_Tool::Surface(F, l);
if (!GS.IsNull()) {
BS.Initialize(F, Standard_False);
if (CanUseEdges(BS)) {
TopExp_Explorer ex2(F, TopAbs_EDGE);
if (!ex2.More()) {
BS.Initialize(F);
Standard_Real Tol = useShapeTolerance? BRep_Tool::Tolerance(F) : 0.;
BndLib_AddSurface::AddOptimal(BS, Tol, aLocBox);
}
else
{
Standard_Real Tol;
for (;ex2.More();ex2.Next()) {
Bnd_Box anEBox;
const TopoDS_Edge& anE = TopoDS::Edge(ex2.Current());
if(BRep_Tool::Degenerated(anE))
{
continue;
}
BC.Initialize(anE);
Tol = useShapeTolerance? BRep_Tool::Tolerance(anE) : 0.;
BndLib_Add3dCurve::AddOptimal(BC, Tol, anEBox);
aLocBox.Add(anEBox);
}
}
}
else
{
Standard_Real umin, umax, vmin, vmax;
Standard_Boolean isNaturalRestriction = Standard_False;
Standard_Real Tol = useShapeTolerance? BRep_Tool::Tolerance(F) : 0.;
FindExactUVBounds(F, umin, umax, vmin, vmax, Tol, isNaturalRestriction);
BndLib_AddSurface::AddOptimal(BS, umin, umax, vmin, vmax,
Tol, aLocBox);
//
if(!isNaturalRestriction)
{
TopExp_Explorer ex2(F, TopAbs_EDGE);
Bnd_Box EBox;
for (;ex2.More();ex2.Next()) {
Bnd_Box anEBox;
const TopoDS_Edge& anE = TopoDS::Edge(ex2.Current());
if(BRep_Tool::Degenerated(anE))
{
continue;
}
BC.Initialize(anE);
Tol = useShapeTolerance? BRep_Tool::Tolerance(anE) : 0.;
BndLib_Add3dCurve::AddOptimal(BC, Tol, anEBox);
EBox.Add(anEBox);
}
Tol = useShapeTolerance? BRep_Tool::Tolerance(F) : 0.;
AdjustFaceBox(BS, umin, umax, vmin, vmax, aLocBox, EBox,
Tol);
}
}
Standard_Real xmin, ymin, zmin, xmax, ymax, zmax;
aLocBox.Get(xmin, ymin, zmin, xmax, ymax, zmax);
B.Update(xmin, ymin, zmin, xmax, ymax, zmax);
}
}
}
// Add the edges not in faces
Handle(TColStd_HArray1OfInteger) HIndices;
Handle(Poly_PolygonOnTriangulation) Poly;
for (ex.Init(S,TopAbs_EDGE,TopAbs_FACE); ex.More(); ex.Next())
{
const TopoDS_Edge& E = TopoDS::Edge(ex.Current());
Bnd_Box aLocBox;
Handle(Poly_Polygon3D) P3d = BRep_Tool::Polygon3D(E, l);
if (useTriangulation && (!P3d.IsNull()))
{
const TColgp_Array1OfPnt& Nodes = P3d->Nodes();
nbNodes = P3d->NbNodes();
for (i = 1; i <= nbNodes; i++)
{
if (l.IsIdentity()) aLocBox.Add(Nodes(i));
else aLocBox.Add(Nodes(i).Transformed(l));
}
Standard_Real Tol = useShapeTolerance? BRep_Tool::Tolerance(E) : 0.;
aLocBox.Enlarge(P3d->Deflection() + Tol);
}
else
{
BRep_Tool::PolygonOnTriangulation(E, Poly, T, l);
if (useTriangulation && !Poly.IsNull())
{
const TColStd_Array1OfInteger& Indices = Poly->Nodes();
const TColgp_Array1OfPnt& Nodes = T->Nodes();
nbNodes = Indices.Length();
for (i = 1; i <= nbNodes; i++)
{
if (l.IsIdentity()) aLocBox.Add(Nodes(Indices(i)));
else aLocBox.Add(Nodes(Indices(i)).Transformed(l));
}
Standard_Real Tol = useShapeTolerance? BRep_Tool::Tolerance(E) : 0.;
aLocBox.Enlarge(Poly->Deflection() + Tol);
}
else {
if (BRep_Tool::IsGeometric(E))
{
BC.Initialize(E);
Standard_Real Tol = useShapeTolerance? BRep_Tool::Tolerance(E) : 0.;
BndLib_Add3dCurve::AddOptimal(BC, Tol, aLocBox);
}
}
}
if (!aLocBox.IsVoid())
{
Standard_Real xmin, ymin, zmin, xmax, ymax, zmax;
aLocBox.Get(xmin, ymin, zmin, xmax, ymax, zmax);
B.Update(xmin, ymin, zmin, xmax, ymax, zmax);
}
}
// Add the vertices not in edges
for (ex.Init(S,TopAbs_VERTEX,TopAbs_EDGE); ex.More(); ex.Next()) {
Bnd_Box aLocBox;
const TopoDS_Vertex& aV = TopoDS::Vertex(ex.Current());
aLocBox.Add(BRep_Tool::Pnt(aV));
Standard_Real Tol = useShapeTolerance? BRep_Tool::Tolerance(aV) : 0.;
aLocBox.Enlarge(Tol);
Standard_Real xmin, ymin, zmin, xmax, ymax, zmax;
aLocBox.Get(xmin, ymin, zmin, xmax, ymax, zmax);
B.Update(xmin, ymin, zmin, xmax, ymax, zmax);
}
}
//=======================================================================
//function : CanUseEdges
//purpose : Define is it possible using only edges bnd boxes
// to get face bnd box
//=======================================================================
Standard_Boolean CanUseEdges(const Adaptor3d_Surface& BS)
{
GeomAbs_SurfaceType aST = BS.GetType();
if(aST == GeomAbs_Plane ||
aST == GeomAbs_Cylinder ||
aST == GeomAbs_Cone ||
aST == GeomAbs_SurfaceOfExtrusion)
{
return Standard_True;
}
else if(aST == GeomAbs_SurfaceOfRevolution)
{
const Handle(Adaptor3d_HCurve)& aBC = BS.BasisCurve();
if(aBC->GetType() == GeomAbs_Line)
{
return Standard_True;
}
else
{
return Standard_False;
}
}
else if(aST == GeomAbs_OffsetSurface)
{
const Handle(Adaptor3d_HSurface)& aS = BS.BasisSurface();
return CanUseEdges(aS->Surface());
}
else if(aST == GeomAbs_BSplineSurface)
{
Handle(Geom_BSplineSurface) aBSpl = BS.BSpline();
if((aBSpl->UDegree() == 1 && aBSpl->NbUKnots() == 2) ||
(aBSpl->VDegree() == 1 && aBSpl->NbVKnots() == 2))
{
return Standard_True;
}
else
{
return Standard_False;
}
}
else if(aST == GeomAbs_BezierSurface)
{
Handle(Geom_BezierSurface) aBz = BS.Bezier();
if((aBz->UDegree() == 1 ) ||
(aBz->VDegree() == 1 ))
{
return Standard_True;
}
else
{
return Standard_False;
}
}
return Standard_False;
}
//=======================================================================
//function : FindExactUVBounds
//purpose :
//=======================================================================
void FindExactUVBounds(const TopoDS_Face FF,
Standard_Real& umin, Standard_Real& umax,
Standard_Real& vmin, Standard_Real& vmax,
const Standard_Real Tol,
Standard_Boolean& isNaturalRestriction)
{
TopoDS_Face F = FF;
F.Orientation(TopAbs_FORWARD);
TopExp_Explorer ex(F,TopAbs_EDGE);
//
//Check Natural restriction
isNaturalRestriction = BRep_Tool::NaturalRestriction(F); //Can we trust this flag?
BRepAdaptor_Surface aBAS(F, Standard_False);
if(!isNaturalRestriction)
{
//Check by comparing pcurves and surface boundaries
umin = aBAS.FirstUParameter();
umax = aBAS.LastUParameter();
vmin = aBAS.FirstVParameter();
vmax = aBAS.LastVParameter();
Standard_Boolean isUperiodic = aBAS.IsUPeriodic(), isVperiodic = aBAS.IsVPeriodic();
Standard_Real aT1, aT2;
Standard_Real TolU = Max(aBAS.UResolution(Tol), Precision::PConfusion());
Standard_Real TolV = Max(aBAS.VResolution(Tol), Precision::PConfusion());
Standard_Integer Nu = 0, Nv = 0, NbEdges = 0;
gp_Vec2d Du(1, 0), Dv(0, 1);
gp_Pnt2d aP;
gp_Vec2d aV;
for (;ex.More();ex.Next()) {
NbEdges++;
if(NbEdges > 4)
{
break;
}
const TopoDS_Edge& aE = TopoDS::Edge(ex.Current());
const Handle(Geom2d_Curve) aC2D = BRep_Tool::CurveOnSurface(aE, F, aT1, aT2);
if (aC2D.IsNull())
{
break;
}
//
aC2D->D1((aT1 + aT2)/2., aP, aV);
Standard_Real magn = aV.SquareMagnitude();
if(magn < gp::Resolution())
{
break;
}
else
{
aV /= Sqrt(magn);
}
Standard_Real u = aP.X(), v = aP.Y();
if(isUperiodic)
{
ElCLib::InPeriod(u, umin, umax);
}
if(isVperiodic)
{
ElCLib::InPeriod(v, vmin, vmax);
}
//
if(Abs(u - umin) <= TolU || Abs(u - umax) <= TolU)
{
Standard_Real d = Dv * aV;
if(1. - Abs(d) <= Precision::PConfusion())
{
Nu++;
if(Nu > 2)
{
break;
}
}
else
{
break;
}
}
else if(Abs(v - vmin) <= TolV || Abs(v - vmax) <= TolV)
{
Standard_Real d = Du * aV;
if(1. - Abs(d) <= Precision::PConfusion())
{
Nv++;
if(Nv > 2)
{
break;
}
}
else
{
break;
}
}
else
{
break;
}
}
if(Nu == 2 && Nv == 2)
{
isNaturalRestriction = Standard_True;
}
}
//
if(isNaturalRestriction)
{
umin = aBAS.FirstUParameter();
umax = aBAS.LastUParameter();
vmin = aBAS.FirstVParameter();
vmax = aBAS.LastVParameter();
return;
}
// fill box for the given face
Standard_Real aT1, aT2;
Standard_Real TolU = Max(aBAS.UResolution(Tol), Precision::PConfusion());
Standard_Real TolV = Max(aBAS.VResolution(Tol), Precision::PConfusion());
Standard_Real TolUV = Max(TolU, TolV);
Bnd_Box2d aBox;
ex.Init(F,TopAbs_EDGE);
for (;ex.More();ex.Next()) {
const TopoDS_Edge& aE = TopoDS::Edge(ex.Current());
const Handle(Geom2d_Curve) aC2D = BRep_Tool::CurveOnSurface(aE, F, aT1, aT2);
if (aC2D.IsNull())
{
continue;
}
//
BndLib_Add2dCurve::AddOptimal(aC2D, aT1, aT2, TolUV, aBox);
//
}
//
aBox.Get(umin, vmin, umax, vmax);
//
TopLoc_Location aLoc;
Handle(Geom_Surface) aS = BRep_Tool::Surface(FF, aLoc);
Standard_Real aUmin, aUmax, aVmin, aVmax;
aS->Bounds(aUmin, aUmax, aVmin, aVmax);
if(!aS->IsUPeriodic())
{
umin = Max(aUmin, umin);
umax = Min(aUmax, umax);
}
else
{
if(umax - umin > aS->UPeriod())
{
Standard_Real delta = umax - umin - aS->UPeriod();
umin += delta/2.;
umax -= delta/2;
}
}
//
if(!aS->IsVPeriodic())
{
vmin = Max(aVmin, vmin);
vmax = Min(aVmax, vmax);
}
else
{
if(vmax - vmin > aS->VPeriod())
{
Standard_Real delta = vmax - vmin - aS->VPeriod();
vmin += delta/2.;
vmax -= delta/2;
}
}
}
//=======================================================================
//function : Reorder
//purpose :
//=======================================================================
inline void Reorder(Standard_Real& a, Standard_Real& b)
{
if(a > b)
{
Standard_Real t = a;
a = b;
b = t;
}
}
//=======================================================================
//function : IsModifySize
//purpose :
//=======================================================================
Standard_Boolean IsModifySize(const BRepAdaptor_Surface& theBS,
const gp_Pln& thePln, const gp_Pnt& theP,
const Standard_Real umin, const Standard_Real umax,
const Standard_Real vmin, const Standard_Real vmax,
const BRepTopAdaptor_FClass2d& theFClass,
const Standard_Real theTolU, const Standard_Real theTolV)
{
Standard_Real pu1 = 0, pu2, pv1 = 0, pv2;
ElSLib::PlaneParameters(thePln.Position(), theP, pu2, pv2);
Reorder(pu1, pu2);
Reorder(pv1, pv2);
Handle(Geom_Plane) aPlane = new Geom_Plane(thePln);
GeomAdaptor_Surface aGAPln(aPlane, pu1, pu2, pv1, pv2);
Extrema_ExtSS anExtr(aGAPln, theBS, pu1, pu2, pv1, pv2, umin, umax, vmin, vmax, theTolU, theTolV);
if(anExtr.IsDone())
{
if(anExtr.NbExt() > 0)
{
Standard_Integer i, imin = 0;;
Standard_Real dmin = RealLast();
Standard_Real uextr = 0., vextr = 0.;
Extrema_POnSurf P1, P2;
for(i = 1; i <= anExtr.NbExt(); ++i)
{
Standard_Real d = anExtr.SquareDistance(i);
if(d < dmin)
{
imin = i;
dmin = d;
}
}
if(imin > 0)
{
anExtr.Points(imin, P1, P2);
P2.Parameter(uextr, vextr);
}
else
{
return Standard_False;
}
//
gp_Pnt2d aP2d(uextr, vextr);
TopAbs_State aSt = theFClass.Perform(aP2d);
if(aSt == TopAbs_OUT)
{
return Standard_True;
}
}
else
{
return Standard_True; //extrema point seems to be out of face UV bounds
}
}
//
return Standard_False;
}
//
//=======================================================================
//function : AdjustFaceBox
//purpose :
//=======================================================================
void AdjustFaceBox(const BRepAdaptor_Surface& BS,
const Standard_Real umin, const Standard_Real umax,
const Standard_Real vmin, const Standard_Real vmax,
Bnd_Box& FaceBox,
const Bnd_Box& EdgeBox, const Standard_Real Tol)
{
Standard_Real fxmin, fymin, fzmin, fxmax, fymax, fzmax;
Standard_Real exmin, eymin, ezmin, exmax, eymax, ezmax;
//
FaceBox.Get(fxmin, fymin, fzmin, fxmax, fymax, fzmax);
EdgeBox.Get(exmin, eymin, ezmin, exmax, eymax, ezmax);
//
Standard_Real TolU = Max(BS.UResolution(Tol), Precision::PConfusion());
Standard_Real TolV = Max(BS.VResolution(Tol), Precision::PConfusion());
BRepTopAdaptor_FClass2d FClass(BS.Face(), Max(TolU, TolV));
//
Standard_Boolean isModified = Standard_False;
if(exmin > fxmin)
{
//
gp_Pln pl(gp_Ax3(gp_Pnt(fxmin, fymin, fzmin), gp::DX()));
gp_Pnt aP(fxmin, fymax, fzmax);
if(IsModifySize(BS, pl, aP,
umin, umax, vmin, vmax, FClass, TolU, TolV))
{
fxmin = exmin;
isModified = Standard_True;
}
}
if(exmax < fxmax)
{
//
gp_Pln pl(gp_Ax3(gp_Pnt(fxmax, fymax, fzmax), gp::DX()));
gp_Pnt aP(fxmax, fymin, fzmin);
if(IsModifySize(BS, pl, aP,
umin, umax, vmin, vmax, FClass, TolU, TolV))
{
fxmax = exmax;
isModified = Standard_True;
}
}
//
if(eymin > fymin)
{
//
gp_Pln pl(gp_Ax3(gp_Pnt(fxmin, fymin, fzmin), gp::DY()));
gp_Pnt aP(fxmax, fymin, fzmax);
if(IsModifySize(BS, pl, aP,
umin, umax, vmin, vmax, FClass, TolU, TolV))
{
fymin = eymin;
isModified = Standard_True;
}
}
if(eymax < fymax)
{
//
gp_Pln pl(gp_Ax3(gp_Pnt(fxmax, fymax, fzmax), gp::DY()));
gp_Pnt aP(fxmin, fymax, fzmin);
if(IsModifySize(BS, pl, aP,
umin, umax, vmin, vmax, FClass, TolU, TolV))
{
fymax = eymax;
isModified = Standard_True;
}
}
//
if(ezmin > fzmin)
{
//
gp_Pln pl(gp_Ax3(gp_Pnt(fxmin, fymin, fzmin), gp::DZ()));
gp_Pnt aP(fxmax, fymax, fzmin);
if(IsModifySize(BS, pl, aP,
umin, umax, vmin, vmax, FClass, TolU, TolV))
{
fzmin = ezmin;
isModified = Standard_True;
}
}
if(ezmax < fzmax)
{
//
gp_Pln pl(gp_Ax3(gp_Pnt(fxmax, fymax, fzmax), gp::DZ()));
gp_Pnt aP(fxmin, fymin, fzmax);
if(IsModifySize(BS, pl, aP,
umin, umax, vmin, vmax, FClass, TolU, TolV))
{
fzmax = ezmax;
isModified = Standard_True;
}
}
//
if(isModified)
{
FaceBox.SetVoid();
FaceBox.Update(fxmin, fymin, fzmin, fxmax, fymax, fzmax);
}
}

View File

@@ -68,6 +68,18 @@ public:
//! box is closer to the shape S.
Standard_EXPORT static void AddClose (const TopoDS_Shape& S, Bnd_Box& B);
//! Adds the shape S to the bounding box B.
//! This algorith builds precise bounding box,
//! which differs from exact geometry boundaries of shape only on shape entities tolerances
//! Algorithm is the same as for method Add(..), but uses more precise methods for building boxes
//! for geometry objects.
//! If useShapeTolerance = True, bounding box is enlardged by shape tolerances and
//! these tolerances are used for numerical methods of bounding box size calculations,
//! otherwise bounding box is built according to sizes of uderlined geometrical entities,
//! numerical calculation use tolerance Precision::Confusion().
Standard_EXPORT static void AddOptimal (const TopoDS_Shape& S, Bnd_Box& B,
const Standard_Boolean useTriangulation = Standard_True,
const Standard_Boolean useShapeTolerance = Standard_False);