1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-08-14 13:30:48 +03:00

0029311: Implementation of the Oriented Bounding Boxes (OBB) functionality

1. The class Bnd_OBB has been created to describe the Oriented Bounding Box.

2. Several key methods have been implemented: Bnd_OBB::IsOut(...), Bnd_OBB::Add(...) and Bnd_OBB::Enlarge(...).

3. Interface of Bnd_Box class has changed. New methods have been created. See Bnd_Box.hxx for detailed information.

4. BRepBndLib and Draw_Box classes have been amended in order to provide correct work with Bnd_OBB class.

5. Interface of "bounding" DRAW-command has been changed. Please see help for detailed information.

6. New DRAW-command "isbbinterf" has been created. Please see help for detailed information.

7. "boundingstr" and "optbounding" DRAW-commands have been eliminated because their function can be made by "bounding" DRAW-command (e.g. see tests/bugs/vis/buc60857 or samples/tcl/snowflake.tcl test cases).

8. Documentation has been updated.
This commit is contained in:
nbv
2017-11-08 15:47:09 +03:00
committed by bugmaster
parent 8d1a539c4a
commit 1a0339b464
83 changed files with 3204 additions and 1087 deletions

View File

@@ -24,6 +24,7 @@
#include <Standard_Boolean.hxx>
class TopoDS_Shape;
class Bnd_Box;
class Bnd_OBB;
//! This package provides the bounding boxes for curves
@@ -82,6 +83,25 @@ public:
const Standard_Boolean useShapeTolerance = Standard_False);
//! Computes the Oriented Bounding box for the shape <theS>.
//! Two independent methods of computation are implemented:
//! first method based on set of points (so, it demands the
//! triangulated shape or shape with planar faces and linear edges).
//! The second method is based on use of inertia axes and is called
//! if use of the first method is impossible.
//! If theIsTriangulationUsed == FALSE then the triangulation will
//! be ignored at all.
//! If theIsShapeToleranceUsed == TRUE then resulting box will be
//! extended on the tolerance of the shape.
//! theIsOptimal flag defines the algorithm for construction of initial
//! Bnd_Box for the second method (if theIsOptimal == TRUE then
//! this box will be created by AddOptimal(...) method).
Standard_EXPORT static
void AddOBB(const TopoDS_Shape& theS,
Bnd_OBB& theOBB,
const Standard_Boolean theIsTriangulationUsed = Standard_True,
const Standard_Boolean theIsOptimal = Standard_False,
const Standard_Boolean theIsShapeToleranceUsed = Standard_True);
protected:

View File

@@ -0,0 +1,494 @@
// Copyright (c) 1999-2017 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <Adaptor3d_HCurve.hxx>
#include <Adaptor3d_HSurface.hxx>
#include <GeomAdaptor_HCurve.hxx>
#include <BRepBndLib.hxx>
#include <GProp_GProps.hxx>
#include <TopoDS_Shape.hxx>
#include <BRep_Tool.hxx>
#include <TopoDS.hxx>
#include <Bnd_OBB.hxx>
#include <BRepGProp.hxx>
#include <TopExp_Explorer.hxx>
#include <GProp_PrincipalProps.hxx>
#include <gp_Ax3.hxx>
#include <BRepBuilderAPI_Transform.hxx>
#include <Bnd_Box.hxx>
#include <NCollection_List.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <Geom_Plane.hxx>
#include <Geom_Line.hxx>
#include <TColStd_Array1OfInteger.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <BRepAdaptor_HSurface.hxx>
#include <Geom_OffsetCurve.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Geom_BezierCurve.hxx>
#include <Geom_BSplineSurface.hxx>
#include <Geom_BezierSurface.hxx>
//=======================================================================
// Function : IsLinear
// purpose : Returns TRUE if theC is line-like.
//=======================================================================
static Standard_Boolean IsLinear(const Adaptor3d_Curve& theC)
{
const GeomAbs_CurveType aCT = theC.GetType();
if(aCT == GeomAbs_OffsetCurve)
{
return IsLinear(GeomAdaptor_Curve(theC.OffsetCurve()->BasisCurve()));
}
if((aCT == GeomAbs_BSplineCurve) || (aCT == GeomAbs_BezierCurve))
{
// Indeed, curves with C0-continuity and degree==1, may be
// represented with set of points. It will be possible made
// in the future.
return ((theC.Degree() == 1) &&
(theC.Continuity() != GeomAbs_C0));
}
if(aCT == GeomAbs_Line)
{
return Standard_True;
}
return Standard_False;
}
//=======================================================================
// Function : IsPlanar
// purpose : Returns TRUE if theS is plane-like.
//=======================================================================
static Standard_Boolean IsPlanar(const Adaptor3d_Surface& theS)
{
const GeomAbs_SurfaceType aST = theS.GetType();
if(aST == GeomAbs_OffsetSurface)
{
return IsPlanar(theS.BasisSurface()->Surface());
}
if(aST == GeomAbs_SurfaceOfExtrusion)
{
return IsLinear(theS.BasisCurve()->Curve());
}
if((aST == GeomAbs_BSplineSurface) || (aST == GeomAbs_BezierSurface))
{
if((theS.UDegree() != 1) || (theS.VDegree() != 1))
return Standard_False;
// Indeed, surfaces with C0-continuity and degree==1, may be
// represented with set of points. It will be possible made
// in the future.
return ((theS.UContinuity() != GeomAbs_C0) && (theS.VContinuity() != GeomAbs_C0));
}
if(aST == GeomAbs_Plane)
{
return Standard_True;
}
return Standard_False;
}
//=======================================================================
// Function : PointsForOBB
// purpose : Returns number of points for array.
//
// Attention!!!
// 1. Start index for thePts must be 0 strictly.
// 2. Currently, infinite edges/faces (e.g. half-space) are not
// processed correctly because computation of UV-bounds is a costly operation.
//=======================================================================
static Standard_Integer PointsForOBB(const TopoDS_Shape& theS,
const Standard_Boolean theIsTriangulationUsed,
TColgp_Array1OfPnt* thePts = 0,
TColStd_Array1OfReal* theArrOfToler = 0)
{
Standard_Integer aRetVal = 0;
TopExp_Explorer anExpF, anExpE;
// get all vertices from the shape
for(anExpF.Init(theS, TopAbs_VERTEX); anExpF.More(); anExpF.Next())
{
const TopoDS_Vertex &aVert = TopoDS::Vertex(anExpF.Current());
if(thePts)
{
const gp_Pnt aP = BRep_Tool::Pnt(aVert);
(*thePts)(aRetVal) = aP;
}
if(theArrOfToler)
{
(*theArrOfToler) (aRetVal) = BRep_Tool::Tolerance(aVert);
}
++aRetVal;
}
if(aRetVal == 0)
return 0;
// analyze the faces of the shape on planarity and existence of triangulation
TopLoc_Location aLoc;
for(anExpF.Init(theS, TopAbs_FACE); anExpF.More(); anExpF.Next())
{
const TopoDS_Face &aF = TopoDS::Face(anExpF.Current());
const BRepAdaptor_Surface anAS(aF, Standard_False);
if (!IsPlanar(anAS.Surface()))
{
if (!theIsTriangulationUsed)
// not planar and triangulation usage disabled
return 0;
}
else
{
// planar face
for(anExpE.Init(aF, TopAbs_EDGE); anExpE.More(); anExpE.Next())
{
const TopoDS_Edge &anE = TopoDS::Edge(anExpE.Current());
const BRepAdaptor_Curve anAC(anE);
if (!IsLinear(anAC))
{
if (!theIsTriangulationUsed)
// not linear and triangulation usage disabled
return 0;
break;
}
}
if (!anExpE.More())
// skip planar face with linear edges as its vertices have already been added
continue;
}
// Use triangulation of the face
const Handle(Poly_Triangulation) &aTrng = BRep_Tool::Triangulation(aF, aLoc);
if (aTrng.IsNull())
// no triangulation on the face
return 0;
const Standard_Integer aCNode = aTrng->NbNodes();
const TColgp_Array1OfPnt& aNodesArr = aTrng->Nodes();
for (Standard_Integer i = 1; i <= aCNode; i++)
{
if (thePts)
{
const gp_Pnt aP = aLoc.IsIdentity() ? aNodesArr(i) :
aNodesArr(i).Transformed(aLoc);
(*thePts)(aRetVal) = aP;
}
if (theArrOfToler)
{
(*theArrOfToler) (aRetVal) = aTrng->Deflection();
}
++aRetVal;
}
}
// Consider edges without faces
for(anExpE.Init(theS, TopAbs_EDGE, TopAbs_FACE); anExpE.More(); anExpE.Next())
{
const TopoDS_Edge &anE = TopoDS::Edge(anExpE.Current());
const BRepAdaptor_Curve anAC(anE);
if (IsLinear(anAC))
// skip linear edge as its vertices have already been added
continue;
if (!theIsTriangulationUsed)
// not linear and triangulation usage disabled
return 0;
const Handle(Poly_Polygon3D) &aPolygon = BRep_Tool::Polygon3D(anE, aLoc);
if (aPolygon.IsNull())
return 0;
const Standard_Integer aCNode = aPolygon->NbNodes();
const TColgp_Array1OfPnt& aNodesArr = aPolygon->Nodes();
for (Standard_Integer i = 1; i <= aCNode; i++)
{
if (thePts)
{
const gp_Pnt aP = aLoc.IsIdentity() ? aNodesArr(i) :
aNodesArr(i).Transformed(aLoc);
(*thePts)(aRetVal) = aP;
}
if (theArrOfToler)
{
(*theArrOfToler) (aRetVal) = aPolygon->Deflection();
}
++aRetVal;
}
}
return aRetVal;
}
//=======================================================================
// Function : IsWCS
// purpose : Returns 0 if the theDir does not match any axis of WCS.
// Otherwise, returns the index of correspond axis.
//=======================================================================
static Standard_Integer IsWCS(const gp_Dir& theDir)
{
const Standard_Real aToler = Precision::Angular()*Precision::Angular();
const Standard_Real aX = theDir.X(),
aY = theDir.Y(),
aZ = theDir.Z();
const Standard_Real aVx = aY*aY + aZ*aZ,
aVy = aX*aX + aZ*aZ,
aVz = aX*aX + aY*aY;
if(aVz < aToler)
return 3; // Z-axis
if(aVy < aToler)
return 2; // Y-axis
if(aVx < aToler)
return 1; // X-axis
return 0;
}
//=======================================================================
// Function : CheckPoints
// purpose : Collects points for DiTO algorithm for OBB construction on
// linear/planar shapes and shapes having triangulation
// (http://www.idt.mdh.se/~tla/publ/FastOBBs.pdf).
//=======================================================================
static Standard_Boolean CheckPoints(const TopoDS_Shape& theS,
const Standard_Boolean theIsTriangulationUsed,
const Standard_Boolean theIsShapeToleranceUsed,
Bnd_OBB& theOBB)
{
const Standard_Integer aNbPnts = PointsForOBB(theS, theIsTriangulationUsed);
if(aNbPnts < 1)
return Standard_False;
TColgp_Array1OfPnt anArrPnts(0, theOBB.IsVoid() ? aNbPnts - 1 : aNbPnts + 7);
TColStd_Array1OfReal anArrOfTolerances;
if(theIsShapeToleranceUsed)
{
anArrOfTolerances.Resize(anArrPnts.Lower(), anArrPnts.Upper(), Standard_False);
anArrOfTolerances.Init(0.0);
}
TColStd_Array1OfReal *aPtrArrTol = theIsShapeToleranceUsed ? &anArrOfTolerances : 0;
PointsForOBB(theS, theIsTriangulationUsed, &anArrPnts, aPtrArrTol);
if(!theOBB.IsVoid())
{
// All points of old OBB have zero-tolerance
theOBB.GetVertex(&anArrPnts(aNbPnts));
}
#if 0
for(Standard_Integer i = anArrPnts.Lower(); i <= anArrPnts.Upper(); i++)
{
const gp_Pnt &aP = anArrPnts(i);
std::cout << "point p" << i << " " << aP.X() << ", " <<
aP.Y() << ", " <<
aP.Z() << ", "<< std::endl;
}
#endif
theOBB.ReBuild(anArrPnts, aPtrArrTol);
return (!theOBB.IsVoid());
}
//=======================================================================
// Function : ComputeProperties
// purpose : Computes properties of theS.
//=======================================================================
static void ComputeProperties(const TopoDS_Shape& theS,
GProp_GProps& theGCommon)
{
TopExp_Explorer anExp;
for(anExp.Init(theS, TopAbs_SOLID); anExp.More(); anExp.Next())
{
GProp_GProps aG;
BRepGProp::VolumeProperties(anExp.Current(), aG, Standard_True);
theGCommon.Add(aG);
}
for(anExp.Init(theS, TopAbs_FACE, TopAbs_SOLID); anExp.More(); anExp.Next())
{
GProp_GProps aG;
BRepGProp::SurfaceProperties(anExp.Current(), aG, Standard_True);
theGCommon.Add(aG);
}
for(anExp.Init(theS, TopAbs_EDGE, TopAbs_FACE); anExp.More(); anExp.Next())
{
GProp_GProps aG;
BRepGProp::LinearProperties(anExp.Current(), aG, Standard_True);
theGCommon.Add(aG);
}
for(anExp.Init(theS, TopAbs_VERTEX, TopAbs_EDGE); anExp.More(); anExp.Next())
{
GProp_GProps aG(BRep_Tool::Pnt(TopoDS::Vertex(anExp.Current())));
theGCommon.Add(aG);
}
}
//=======================================================================
// Function : ComputePCA
// purpose : Creates OBB with axes of inertia.
//=======================================================================
static void ComputePCA(const TopoDS_Shape& theS,
Bnd_OBB& theOBB,
const Standard_Boolean theIsTriangulationUsed,
const Standard_Boolean theIsOptimal,
const Standard_Boolean theIsShapeToleranceUsed)
{
// Compute the transformation matrix to obtain more tight bounding box
GProp_GProps aGCommon;
ComputeProperties(theS, aGCommon);
// Transform the shape to the local coordinate system
gp_Trsf aTrsf;
const Standard_Integer anIdx1 =
IsWCS(aGCommon.PrincipalProperties().FirstAxisOfInertia());
const Standard_Integer anIdx2 =
IsWCS(aGCommon.PrincipalProperties().SecondAxisOfInertia());
if((anIdx1 == 0) || (anIdx2 == 0))
{
// Coordinate system in which the shape will have the optimal bounding box
gp_Ax3 aLocCoordSys(aGCommon.CentreOfMass(),
aGCommon.PrincipalProperties().ThirdAxisOfInertia(),
aGCommon.PrincipalProperties().FirstAxisOfInertia());
aTrsf.SetTransformation(aLocCoordSys);
}
const TopoDS_Shape aST = (aTrsf.Form() == gp_Identity) ? theS :
theS.Moved(TopLoc_Location(aTrsf));
// Initial axis-aligned BndBox
Bnd_Box aShapeBox;
if(theIsOptimal)
{
BRepBndLib::AddOptimal(aST, aShapeBox, theIsTriangulationUsed, theIsShapeToleranceUsed);
}
else
{
BRepBndLib::Add(aST, aShapeBox);
}
gp_Pnt aPMin = aShapeBox.CornerMin();
gp_Pnt aPMax = aShapeBox.CornerMax();
gp_XYZ aXDir(1, 0, 0);
gp_XYZ aYDir(0, 1, 0);
gp_XYZ aZDir(0, 0, 1);
// Compute the center of the box
gp_XYZ aCenter = (aPMin.XYZ() + aPMax.XYZ()) / 2.;
// Compute the half diagonal size of the box.
// It takes into account the gap.
gp_XYZ anOBBHSize = (aPMax.XYZ() - aPMin.XYZ()) / 2.;
// Apply transformation if necessary
if(aTrsf.Form() != gp_Identity)
{
aTrsf.Invert();
aTrsf.Transforms(aCenter);
// Make transformation
const Standard_Real * aMat = &aTrsf.HVectorialPart().Value(1, 1);
// Compute axes directions of the box
aXDir = gp_XYZ(aMat[0], aMat[3], aMat[6]);
aYDir = gp_XYZ(aMat[1], aMat[4], aMat[7]);
aZDir = gp_XYZ(aMat[2], aMat[5], aMat[8]);
}
if(theOBB.IsVoid())
{
// Create the OBB box
// Set parameters to the OBB
theOBB.SetCenter(aCenter);
theOBB.SetXComponent(aXDir, anOBBHSize.X());
theOBB.SetYComponent(aYDir, anOBBHSize.Y());
theOBB.SetZComponent(aZDir, anOBBHSize.Z());
theOBB.SetAABox(aTrsf.Form() == gp_Identity);
}
else
{
// Recreate the OBB box
TColgp_Array1OfPnt aListOfPnts(0, 15);
theOBB.GetVertex(&aListOfPnts(0));
const Standard_Real aX = anOBBHSize.X();
const Standard_Real aY = anOBBHSize.Y();
const Standard_Real aZ = anOBBHSize.Z();
const gp_XYZ aXext = aX*aXDir,
aYext = aY*aYDir,
aZext = aZ*aZDir;
Standard_Integer aPntIdx = 8;
aListOfPnts(aPntIdx++) = aCenter - aXext - aYext - aZext;
aListOfPnts(aPntIdx++) = aCenter + aXext - aYext - aZext;
aListOfPnts(aPntIdx++) = aCenter - aXext + aYext - aZext;
aListOfPnts(aPntIdx++) = aCenter + aXext + aYext - aZext;
aListOfPnts(aPntIdx++) = aCenter - aXext - aYext + aZext;
aListOfPnts(aPntIdx++) = aCenter + aXext - aYext + aZext;
aListOfPnts(aPntIdx++) = aCenter - aXext + aYext + aZext;
aListOfPnts(aPntIdx++) = aCenter + aXext + aYext + aZext;
theOBB.ReBuild(aListOfPnts);
}
}
//=======================================================================
// Function : AddOBB
// purpose :
//=======================================================================
void BRepBndLib::AddOBB(const TopoDS_Shape& theS,
Bnd_OBB& theOBB,
const Standard_Boolean theIsTriangulationUsed,
const Standard_Boolean theIsOptimal,
const Standard_Boolean theIsShapeToleranceUsed)
{
if(CheckPoints(theS, theIsTriangulationUsed, theIsShapeToleranceUsed, theOBB))
return;
ComputePCA(theS, theOBB, theIsTriangulationUsed, theIsOptimal, theIsShapeToleranceUsed);
}

View File

@@ -1,2 +1,3 @@
BRepBndLib.cxx
BRepBndLib.hxx
BRepBndLib_1.cxx

View File

@@ -50,15 +50,35 @@
#include <DrawTrSurf.hxx>
#include <Geom_Plane.hxx>
#include <OSD_Timer.hxx>
#include <Draw_Segment3D.hxx>
#include <Draw_Marker3D.hxx>
#include <Draw_MarkerShape.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <stdio.h>
Standard_IMPORT Draw_Viewer dout;
//=======================================================================
//function : ConvertBndToShape
//purpose : Creates TopoDS_Solid from theBox
//=======================================================================
static void ConvertBndToShape(const Bnd_OBB& theBox,
const char* const theName)
{
const gp_Pnt &aBaryCenter = theBox.Center();
const gp_XYZ &aXDir = theBox.XDirection(),
&aYDir = theBox.YDirection(),
&aZDir = theBox.ZDirection();
Standard_Real aHalfX = theBox.XHSize(),
aHalfY = theBox.YHSize(),
aHalfZ = theBox.ZHSize();
gp_Ax2 anAxes(aBaryCenter, aZDir, aXDir);
anAxes.SetLocation(aBaryCenter.XYZ() - aHalfX*aXDir - aHalfY*aYDir - aHalfZ*aZDir);
TopoDS_Solid aBox = BRepPrimAPI_MakeBox(anAxes, 2.0*aHalfX, 2.0*aHalfY, 2.0*aHalfZ);
DBRep::Set(theName, aBox);
}
//=======================================================================
// addpcurve
@@ -400,31 +420,6 @@ static Standard_Integer orientsolid(Draw_Interpretor& ,Standard_Integer n,const
}
//=======================================================================
//function : boundingstr
//purpose :
//=======================================================================
static Standard_Integer boundingstr(Draw_Interpretor& di,Standard_Integer n,const char** a)
{
if (n < 2) return 1;
TopoDS_Shape S = DBRep::Get(a[1]);
if (S.IsNull()) return 1;
Bnd_Box B;
BRepBndLib::Add(S,B);
Standard_Real axmin,aymin,azmin,axmax,aymax,azmax;
B.Get(axmin,aymin,azmin,axmax,aymax,azmax);
di << axmin<<" "<< aymin<<" "<< azmin<<" "<< axmax<<" "<< aymax<<" "<< azmax;
if (n >= 8) {
Draw::Set(a[2],axmin) ;
Draw::Set(a[3],aymin) ;
Draw::Set(a[4],azmin) ;
Draw::Set(a[5],axmax) ;
Draw::Set(a[6],aymax) ;
Draw::Set(a[7],azmax) ;
}
return 0;
}
//=======================================================================
//function : getcoords
//purpose :
@@ -454,76 +449,380 @@ static Standard_Integer getcoords(Draw_Interpretor& di,Standard_Integer n,const
}
//=======================================================================
//function : bounding
//function : BoundBox
//purpose :
//=======================================================================
static Standard_Integer bounding(Draw_Interpretor& di,Standard_Integer n,const char** a)
static Standard_Integer BoundBox(Draw_Interpretor& theDI,
Standard_Integer theNArg,
const char** theArgVal)
{
if (n < 2) return 1;
Standard_Real axmin,aymin,azmin,axmax,aymax,azmax;
Bnd_Box B; Handle(Draw_Box) DB;
if(theNArg < 2)
{
theDI << "Use: " << theArgVal[0] << " {-s shape | -c xmin ymin zmin xmax ymax zmax} "
"[-obb]\n\t\t[-shape name] [-dump] [-notriangulation]\n\t\t"
"[-perfmeter name NbIters] [-nodraw] [-optimal] [-exttoler]\n\t\t"
"[-save xmin ymin zmin xmax ymax zmax]\n\n\n";
theDI << "Computes a bounding box (BndBox). Two types of the source data are supported:\n";
theDI << " * \"-s\"-option sets the shape, which will be circumscribed by the BndBox;\n";
theDI << " * \"-c\"-option sets two opposite corners (having (xmin, ymin, zmin) and\n\t"
"(xmax, ymax, zmax) coordinates) of the resulting\n\taxis-aligned BndBox (AABB).\n";
theDI << "\nThe following options are supported:\n";
theDI << " * \"-obb\". If it is switched on then the oriented BndBox (OBB) will "
"be\n\tcreated. Otherwise, AABB will be created.\n";
theDI << " * \"-shape\". If it is switched on then the resulting BndBox will be "
"stored\n\tas a shape (solid) with specified name.\n";
theDI << " * \"-nodraw\". If it is switched on then the resulting BndBox will not be\n\t"
"drawn as DRAW-object.\n";
theDI << " * \"-dump\". Prints the information about the created BndBox.\n";
theDI << " * \"-notriangulation\". By default, AABB is built from existing mesh.\n\t"
"This option allows ignoring triangulation.\n";
theDI << " * \"-save\". Stores the information about created AABB in "
"specified variables.\n";
theDI << " * \"-optimal\". If it is switched on then the AABB will be optimal.\n\t"
"This option is useful for OBB, too. It allows constructing\n\toptimal "
"initial AABB.\n";
theDI << " * \"-exttoler\". If it is switched on then the resulting box will be "
"extended\n\ton the tolerance of the source shape.\n";
theDI << " * \"-perfmeter\" - Auxiliary option. It provides compatibility "
"with\n\tOCCT-test system. \"name\" is the counter name for "
"\"chrono\"-TCL-command.\n\tNbIters is the number of iterations.\n";
return 1;
}
TopoDS_Shape aShape;
if (n == 2) {
TopoDS_Shape S = DBRep::Get(a[1]);
if (S.IsNull()) return 1;
BRepBndLib::Add(S,B);
B.Get(axmin,aymin,azmin,axmax,aymax,azmax);
DB = new Draw_Box(gp_Pnt(axmin,aymin,azmin),gp_Pnt(axmax,aymax,azmax),Draw_orange);
dout<<DB;
di << axmin<<" "<< aymin<<" "<< azmin<<" "<< axmax<<" "<< aymax<<" "<< azmax;
Standard_Boolean isOBB = Standard_False;
Standard_Boolean hasToPrint = Standard_False,
isTriangulationReq = Standard_True,
isOptimal = Standard_False,
isTolerUsed = Standard_False,
hasToDraw = Standard_True;
Standard_Integer aNbIters = 1;
Standard_Integer aStartIdxToSave = -1,
aStartIdxToCreate = -1,
aNameToShape = -1,
anIdxCounterName = -1;
for(Standard_Integer anAIdx = 1; anAIdx < theNArg; anAIdx++)
{
if(theArgVal[anAIdx][0] != '-')
{
theDI << "Error: Wrong option \"" << theArgVal[anAIdx] <<
"\". Please use \'-\' symbol\n";
return 1;
}
if(!strcmp(theArgVal[anAIdx], "-s"))
{
aShape = DBRep::Get(theArgVal[++anAIdx]);
if(aShape.IsNull())
{
theDI << "Error: Argument " << theArgVal[anAIdx] << " is not a shape.\n";
return 1;
}
}
else if(!strcmp(theArgVal[anAIdx], "-c"))
{
aStartIdxToCreate = anAIdx + 1;
anAIdx += 6;
}
else if(!strncmp(theArgVal[anAIdx], "-obb", 4))
{
isOBB = Standard_True;
}
else if(!strncmp(theArgVal[anAIdx], "-shape", 4))
{
aNameToShape = ++anAIdx;
}
else if(!strncmp(theArgVal[anAIdx], "-dump", 4))
{
hasToPrint = Standard_True;
}
else if(!strncmp(theArgVal[anAIdx], "-save", 4))
{
aStartIdxToSave = anAIdx+1;
anAIdx += 6;
}
else if(!strncmp(theArgVal[anAIdx], "-notriangulation", 9))
{
isTriangulationReq = Standard_False;
}
else if(!strncmp(theArgVal[anAIdx], "-perfmeter", 8))
{
anIdxCounterName = ++anAIdx;
aNbIters = Draw::Atoi(theArgVal[++anAIdx]);
}
else if(!strncmp(theArgVal[anAIdx], "-optimal", 4))
{
isOptimal = Standard_True;
}
else if(!strcmp(theArgVal[anAIdx], "-exttoler"))
{
isTolerUsed = Standard_True;
}
else if(!strcmp(theArgVal[anAIdx], "-nodraw"))
{
hasToDraw = Standard_False;
}
else
{
theDI << "Error: Unknown option \"" << theArgVal[anAIdx] << "\".\n";
return 1;
}
}
else if (n == 7) {
axmin=Draw::Atof(a[1]);
aymin=Draw::Atof(a[2]);
azmin=Draw::Atof(a[3]);
axmax=Draw::Atof(a[4]);
aymax=Draw::Atof(a[5]);
azmax=Draw::Atof(a[6]);
DB = new Draw_Box(gp_Pnt(axmin,aymin,azmin),gp_Pnt(axmax,aymax,azmax),Draw_orange);
dout<<DB;
if(aShape.IsNull() && (aStartIdxToCreate < 0))
{
theDI << "Error: One of the options \'-s\' or \'-c\' must be set necessarily.\n";
return 1;
}
if(aStartIdxToCreate > 0)
{
if(!aShape.IsNull())
{
theDI << "Error: Options \'-s\' and \'-c\' are fail for using simultaneously.\n";
return 1;
}
else if(isOBB)
{
theDI << "Error: Options \'-c\' and \"-obb\" are fail for using simultaneously.\n";
return 1;
}
}
if(isOptimal)
{
if(aShape.IsNull())
{
theDI << "Error: Options \"-optimal\" is used without any shape. "
"Use \'-s\'-option.\n";
return 1;
}
}
if(isTolerUsed)
{
if(aShape.IsNull())
{
theDI << "Error: Option \"-exttoler\" is used without any shape. "
"Use \'-s\'-option.\n";
return 1;
}
}
Handle(Draw_Box) aDB;
OSD_Timer aTimer;
if(isOBB)
{
if(aStartIdxToSave > 0)
{
theDI << "Error: Option \"-save\" work only with axes-aligned boxes.\n";
return 1;
}
Bnd_OBB anOBB;
Standard_Integer aN = aNbIters;
aTimer.Start();
while(aN-- > 0)
{
anOBB.SetVoid();
BRepBndLib::AddOBB(aShape, anOBB, isTriangulationReq, isOptimal, isTolerUsed);
}
aTimer.Stop();
if(anOBB.IsVoid())
{
theDI << "Void box.\n";
hasToPrint = Standard_False;
}
const gp_Pnt &aBaryCenter= anOBB.Center();
const gp_XYZ &aXDir = anOBB.XDirection(),
&aYDir = anOBB.YDirection(),
&aZDir = anOBB.ZDirection();
Standard_Real aHalfX = anOBB.XHSize(),
aHalfY = anOBB.YHSize(),
aHalfZ = anOBB.ZHSize();
if(hasToPrint)
{
theDI << "Oriented bounding box\n";
theDI << "Center: " << aBaryCenter.X() << " " <<
aBaryCenter.Y() << " " <<
aBaryCenter.Z() << "\n";
theDI << "X-axis: " << aXDir.X() << " " << aXDir.Y() << " " << aXDir.Z() << "\n";
theDI << "Y-axis: " << aYDir.X() << " " << aYDir.Y() << " " << aYDir.Z() << "\n";
theDI << "Z-axis: " << aZDir.X() << " " << aZDir.Y() << " " << aZDir.Z() << "\n";
theDI << "Half X: " << aHalfX << "\n" <<
"Half Y: " << aHalfY << "\n" << "Half Z: " << aHalfZ << "\n";
}
if(hasToDraw)
aDB = new Draw_Box(anOBB, Draw_orange);
if(aNameToShape > 0)
{
ConvertBndToShape(anOBB, theArgVal[aNameToShape]);
}
}
else // if(!isOBB)
{
Standard_Real aXmin = RealFirst(), aYmin = RealFirst(), aZmin = RealFirst(),
aXMax = RealLast(), aYMax = RealLast(), aZMax = RealLast();
Bnd_Box anAABB;
if(aStartIdxToCreate < 0)
{
Standard_Integer aN = aNbIters;
if(isOptimal)
{
aTimer.Start();
while(aN-- > 0)
{
anAABB.SetVoid();
BRepBndLib::AddOptimal(aShape, anAABB, isTriangulationReq, isTolerUsed);
}
aTimer.Stop();
}
else
{
aTimer.Start();
while(aN-- > 0)
{
anAABB.SetVoid();
BRepBndLib::Add(aShape, anAABB, isTriangulationReq);
}
aTimer.Stop();
}
}
else
{
if(anIdxCounterName > 0)
{
theDI << "Error: Option \"-perfmeter\"does not work if the option \'-c\' "
"is switched on.\n";
return 1;
}
Standard_Integer anIdx = aStartIdxToCreate;
aXmin = Draw::Atof(theArgVal[anIdx++]);
aYmin = Draw::Atof(theArgVal[anIdx++]);
aZmin = Draw::Atof(theArgVal[anIdx++]);
aXMax = Draw::Atof(theArgVal[anIdx++]);
aYMax = Draw::Atof(theArgVal[anIdx++]);
aZMax = Draw::Atof(theArgVal[anIdx++]);
anAABB.Add(gp_Pnt(aXmin, aYmin, aZmin));
anAABB.Add(gp_Pnt(aXMax, aYMax, aZMax));
}
if(anAABB.IsVoid())
{
theDI << "Void box.\n";
hasToPrint = Standard_False;
}
if(hasToPrint || (aStartIdxToSave>0))
{
anAABB.Get(aXmin, aYmin, aZmin, aXMax, aYMax, aZMax);
if(hasToPrint)
{
theDI << "Axes-aligned bounding box\n";
theDI << "X-range: " << aXmin << " " << aXMax << "\n" <<
"Y-range: " << aYmin << " " << aYMax << "\n" <<
"Z-range: " << aZmin << " " << aZMax << "\n";
}
if(aStartIdxToSave > 0)
{
Draw::Set(theArgVal[aStartIdxToSave++], aXmin);
Draw::Set(theArgVal[aStartIdxToSave++], aYmin);
Draw::Set(theArgVal[aStartIdxToSave++], aZmin);
Draw::Set(theArgVal[aStartIdxToSave++], aXMax);
Draw::Set(theArgVal[aStartIdxToSave++], aYMax);
Draw::Set(theArgVal[aStartIdxToSave++], aZMax);
}
}
if(hasToDraw)
aDB = new Draw_Box(anAABB, Draw_orange);
if(aNameToShape > 0)
{
ConvertBndToShape(anAABB, theArgVal[aNameToShape]);
}
}
if(hasToDraw)
dout << aDB;
if(anIdxCounterName > 0)
{
theDI << "COUNTER " << theArgVal[anIdxCounterName] << ": " << aTimer.ElapsedTime() << "\n";
}
return 0;
}
//=======================================================================
//function : optbounding
//function : IsBoxesInterfered
//purpose :
//=======================================================================
static Standard_Integer optbounding(Draw_Interpretor& di,Standard_Integer n,const char** a)
static Standard_Integer IsBoxesInterfered(Draw_Interpretor& theDI,
Standard_Integer theNArg,
const char** theArgVal)
{
if (n < 2)
if(theNArg < 2)
{
di << "Usage: optbounding shape [usetri [usetol]]\n";
di << "usetri and usetol can be 0 or 1, by default usetri = 1, usetol = 0\n";
theDI << "Use: isbbinterf shape1 shape2 [-o].\n";
return 1;
}
Standard_Real axmin,aymin,azmin,axmax,aymax,azmax;
Bnd_Box B; Handle(Draw_Box) DB;
TopoDS_Shape S = DBRep::Get(a[1]);
if (S.IsNull())
const TopoDS_Shape aShape1 = DBRep::Get(theArgVal[1]);
const TopoDS_Shape aShape2 = DBRep::Get(theArgVal[2]);
Standard_Boolean isOBB = (theNArg > 3) && (!strcmp(theArgVal[3], "-o"));
if(isOBB)
{
di << "Null shape\n";
return 1;
Bnd_OBB anOBB1, anOBB2;
BRepBndLib::AddOBB(aShape1, anOBB1);
BRepBndLib::AddOBB(aShape2, anOBB2);
if(anOBB1.IsOut(anOBB2))
{
theDI << "The shapes are NOT interfered by OBB.\n";
}
else
{
theDI << "The shapes are interfered by OBB.\n";
}
}
Standard_Boolean useTri = Standard_True;
Standard_Boolean useTol = Standard_False;
if(n > 2 )
else
{
Standard_Integer ii = atoi(a[2]);
useTri = ii > 0;
Bnd_Box anAABB1, anAABB2;
BRepBndLib::Add(aShape1, anAABB1);
BRepBndLib::Add(aShape2, anAABB2);
if(anAABB1.IsOut(anAABB2))
{
theDI << "The shapes are NOT interfered by AABB.\n";
}
else
{
theDI << "The shapes are interfered by AABB.\n";
}
}
if(n > 3 )
{
Standard_Integer ii = atoi(a[3]);
useTol = ii > 0;
}
BRepBndLib::AddOptimal(S, B, useTri, useTol);
B.Get(axmin, aymin, azmin, axmax, aymax, azmax);
DB = new Draw_Box(gp_Pnt(axmin,aymin,azmin),gp_Pnt(axmax,aymax,azmax),Draw_vert);
dout<<DB;
di << axmin<<" "<< aymin<<" "<< azmin<<" "<< axmax<<" "<< aymax<<" "<< azmax;
return 0;
}
//=======================================================================
//function : gbounding
//purpose :
@@ -600,7 +899,7 @@ static Standard_Integer gbounding(Draw_Interpretor& di,Standard_Integer n,const
if (Is3d)
{
B.Get(axmin,aymin,azmin,axmax,aymax,azmax);
DB = new Draw_Box(gp_Pnt(axmin,aymin,azmin),gp_Pnt(axmax,aymax,azmax),Draw_vert);
DB = new Draw_Box(B, Draw_vert);
dout<<DB;
di << axmin<<" "<< aymin<<" "<< azmin<<" "<< axmax<<" "<< aymax<<" "<< azmax;
}
@@ -1208,25 +1507,21 @@ void BRepTest::BasicCommands(Draw_Interpretor& theCommands)
__FILE__,
getcoords,g);
theCommands.Add("bounding",
"bounding shape [ xmin ymin zmin xmax ymax zmax] ; draw bounds",
__FILE__,
bounding,g);
theCommands.Add("bounding", "enter the comand w/o any arguments to obtain the help.",
__FILE__, BoundBox, g);
theCommands.Add("optbounding",
"optbounding shape [usetri (0/1) [usetol (0/1)]] ; ",
__FILE__,
optbounding,g);
//
theCommands.Add("gbounding",
"gbounding surf/curve/curve2d [-o] ",
__FILE__,
gbounding,g);
theCommands.Add("boundingstr",
"boundingstr shape [ xmin ymin zmin xmax ymax zmax] ; print bounding box",
__FILE__,
boundingstr,g);
theCommands.Add("isbbinterf", "isbbinterf shape1 shape2 [-o]\n"
"Checks whether the bounding-boxes created from "
"the given shapes are interfered. If \"-o\"-option "
"is switched on then the oriented boxes will be checked. "
"Otherwise, axes-aligned boxes will be checked.",
__FILE__, IsBoxesInterfered, g);
theCommands.Add("nurbsconvert",
"nurbsconvert result name [result name]",

679
src/Bnd/Bnd_OBB.cxx Normal file
View File

@@ -0,0 +1,679 @@
// Created by: Eugeny MALTCHIKOV
// Copyright (c) 2017 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <Bnd_OBB.hxx>
#include <Bnd_B3d.hxx>
#include <NCollection_Array1.hxx>
#include <Precision.hxx>
#include <TColStd_Array1OfReal.hxx>
class OBBTool
{
public:
//! Constructor. theL - list of points.
//! theLT is a pointer to the list of tolerances
//! (i-th element of this array is a tolerance
//! of i-th point in theL). If theLT is empty
//! then the tolerance of every point is equal to 0.
//! Attention! The objects, which theL and theLT links on,
//! must be available during all time of OBB creation
//! (i.e. while the object of OBBTool exists).
OBBTool(const TColgp_Array1OfPnt& theL,
const TColStd_Array1OfReal *theLT = 0);
//! DiTO algorithm for OBB construction
//! (http://www.idt.mdh.se/~tla/publ/FastOBBs.pdf)
void ProcessDiTetrahedron();
//! Creates OBB with already computed parameters
void BuildBox(Bnd_OBB& theBox);
protected:
//! Works with the triangle set by the points in myTriIdx.
//! If theIsBuiltTrg == TRUE, new set of triangles will be
//! recomputed.
void ProcessTriangle(const Standard_Integer theIdx1,
const Standard_Integer theIdx2,
const Standard_Integer theIdx3,
const Standard_Boolean theIsBuiltTrg);
//! Computes myTriIdx[2]
void FillToTriangle3();
//! Computes myTriIdx[3] and myTriIdx[4]
void FillToTriangle5(const gp_XYZ& theNormal,
const gp_XYZ& theBarryCenter);
//! Returns half of the Surface area of the box
static Standard_Real ComputeQuality(const Standard_Real* const thePrmArr)
{
const Standard_Real aDX = thePrmArr[1] - thePrmArr[0],
aDY = thePrmArr[3] - thePrmArr[2],
aDZ = thePrmArr[5] - thePrmArr[4];
return (aDX*aDY + aDY*aDZ + aDX*aDZ);
}
protected:
//! Assignment operator is forbidden
OBBTool& operator=(const OBBTool&);
private:
//! Number of the initial axes.
static const Standard_Integer myNbInitAxes = 7;
//! Number of extremal points
static const Standard_Integer myNbExtremalPoints = 2 * myNbInitAxes;
//! The source list of points
const TColgp_Array1OfPnt& myPntsList;
//! Pointer to the array of tolerances
const TColStd_Array1OfReal *myListOfTolers;
//! Points of ditetrahedron
//! given by their indices in myLExtremalPoints.
Standard_Integer myTriIdx[5];
//! List of extremal points
gp_XYZ myLExtremalPoints[myNbExtremalPoints];
//! The axes of the box (always normalized or
//! can be null-vector)
gp_XYZ myAxes[3];
//! The surface area of the OBB
Standard_Real myQualityCriterion;
};
//=======================================================================
// Function : SetMinMax
// purpose :
// ATTENTION!!! thePrmArr must be initialized before this method calling.
//=======================================================================
static inline void SetMinMax(Standard_Real* const thePrmArr,
const Standard_Real theNewParam)
{
if(theNewParam < thePrmArr[0])
{
thePrmArr[0] = theNewParam;
}
else if(theNewParam > thePrmArr[1])
{
thePrmArr[1] = theNewParam;
}
}
//=======================================================================
// Function : Constructor
// purpose :
//=======================================================================
OBBTool::
OBBTool(const TColgp_Array1OfPnt& theL,
const TColStd_Array1OfReal *theLT) :myPntsList(theL),
myListOfTolers(theLT),
myQualityCriterion(RealLast())
{
const Standard_Real aSqrt3 = Sqrt(3);
// Origin of all initial axis is (0,0,0).
// All axes must be normalized.
const gp_XYZ anInitialAxesArray[myNbInitAxes] = {gp_XYZ(1.0, 0.0, 0.0),
gp_XYZ(0.0, 1.0, 0.0),
gp_XYZ(0.0, 0.0, 1.0),
gp_XYZ(1.0, 1.0, 1.0) / aSqrt3,
gp_XYZ(1.0, 1.0, -1.0) / aSqrt3,
gp_XYZ(1.0, -1.0, 1.0) / aSqrt3,
gp_XYZ(1.0, -1.0, -1.0) / aSqrt3};
// Minimal and maximal point on every axis
const Standard_Integer aNbPoints = 2 * myNbInitAxes;
for(Standard_Integer i = 0; i < 5; i++)
{
myTriIdx[i] = INT_MAX;
}
// Min and Max parameter
Standard_Real aParams[aNbPoints];
for(Standard_Integer i = 0; i < aNbPoints; i += 2)
{
aParams[i] = RealLast();
aParams[i + 1] = RealFirst();
}
// Look for the extremal points (myLExtremalPoints)
for(Standard_Integer i = myPntsList.Lower() ; i <= myPntsList.Upper(); i++)
{
const gp_XYZ &aCurrPoint = myPntsList(i).XYZ();
for(Standard_Integer anAxeInd = 0, aPrmInd = 0; anAxeInd < myNbInitAxes; anAxeInd++, aPrmInd++)
{
const Standard_Real aParam = aCurrPoint.Dot(anInitialAxesArray[anAxeInd]);
if(aParam < aParams[aPrmInd])
{
myLExtremalPoints[aPrmInd] = aCurrPoint;
aParams[aPrmInd] = aParam;
}
aPrmInd++;
if(aParam > aParams[aPrmInd])
{
myLExtremalPoints[aPrmInd] = aCurrPoint;
aParams[aPrmInd] = aParam;
}
}
}
// Compute myTriIdx[0] and myTriIdx[1].
Standard_Real aMaxSqDist = -1.0;
for(Standard_Integer aPrmInd = 0; aPrmInd < aNbPoints; aPrmInd += 2)
{
const gp_Pnt &aP1 = myLExtremalPoints[aPrmInd],
&aP2 = myLExtremalPoints[aPrmInd + 1];
const Standard_Real aSqDist = aP1.SquareDistance(aP2);
if(aSqDist > aMaxSqDist)
{
aMaxSqDist = aSqDist;
myTriIdx[0] = aPrmInd;
myTriIdx[1] = aPrmInd + 1;
}
}
FillToTriangle3();
}
//=======================================================================
// Function : FillToTriangle3
// purpose : Two value of myTriIdx array is known. Let us find myTriIdx[2].
// It must be in maximal distance from the infinite axis going
// through the points with indexes myTriIdx[0] and myTriIdx[1].
//=======================================================================
void OBBTool::FillToTriangle3()
{
const gp_XYZ &aP0 = myLExtremalPoints[myTriIdx[0]];
const gp_XYZ anAxis = myLExtremalPoints[myTriIdx[1]] - aP0;
Standard_Real aMaxSqDist = -1.0;
for(Standard_Integer i = 0; i < myNbExtremalPoints; i++)
{
if((i == myTriIdx[0]) || (i == myTriIdx[1]))
continue;
const gp_XYZ &aP = myLExtremalPoints[i];
const Standard_Real aDistToAxe = anAxis.CrossSquareMagnitude(aP - aP0);
if(aDistToAxe > aMaxSqDist)
{
myTriIdx[2] = i;
aMaxSqDist = aDistToAxe;
}
}
}
//=======================================================================
// Function : FillToTriangle5
// purpose : Three value of myTriIdx array is known.
// Let us find myTriIdx[3] and myTriIdx[4].
// They must be in the different sides of the plane of
// triangle set by points myTriIdx[0], myTriIdx[1] and
// myTriIdx[2]. Moreover, the distance from these points
// to the triangle plane must be maximal.
//=======================================================================
void OBBTool::FillToTriangle5(const gp_XYZ& theNormal,
const gp_XYZ& theBarryCenter)
{
Standard_Real aParams[2] = {0.0, 0.0};
for(Standard_Integer aPtIdx = 0; aPtIdx < myNbExtremalPoints; aPtIdx++)
{
if((aPtIdx == myTriIdx[0]) || (aPtIdx == myTriIdx[1]) || (aPtIdx == myTriIdx[2]))
continue;
const gp_XYZ &aCurrPoint = myLExtremalPoints[aPtIdx];
const Standard_Real aParam = theNormal.Dot(aCurrPoint - theBarryCenter);
if(aParam < aParams[0])
{
myTriIdx[3] = aPtIdx;
aParams[0] = aParam;
}
else if(aParam > aParams[1])
{
myTriIdx[4] = aPtIdx;
aParams[1] = aParam;
}
}
// The points must be in the different sides of the triangle plane.
if(aParams[0] > -Precision::Confusion())
{
myTriIdx[3] = INT_MAX;
}
if(aParams[1] < Precision::Confusion())
{
myTriIdx[4] = INT_MAX;
}
}
//=======================================================================
// Function : ProcessTriangle
// purpose : Choose the optimal box with triple axes containing normal
// to the triangle and some edge of the triangle (3rd axis is
// computed from these two ones).
//=======================================================================
void OBBTool::ProcessTriangle(const Standard_Integer theIdx1,
const Standard_Integer theIdx2,
const Standard_Integer theIdx3,
const Standard_Boolean theIsBuiltTrg)
{
const Standard_Integer aNbAxes = 3;
//Some vertex of the triangle
const gp_XYZ aP0 = myLExtremalPoints[theIdx1];
// All axes must be normalized in order to provide correct area computation
// (see ComputeQuality(...) method).
gp_XYZ aYAxis[aNbAxes] = {(myLExtremalPoints[theIdx2] - myLExtremalPoints[theIdx1]),
(myLExtremalPoints[theIdx3] - myLExtremalPoints[theIdx2]),
(myLExtremalPoints[theIdx1] - myLExtremalPoints[theIdx3])};
// Normal to the triangle plane
gp_XYZ aZAxis = aYAxis[0].Crossed(aYAxis[1]);
Standard_Real aSqMod = aZAxis.SquareModulus();
if(aSqMod < Precision::SquareConfusion())
return;
aZAxis /= Sqrt(aSqMod);
gp_XYZ aXAxis[aNbAxes];
for(Standard_Integer i = 0; i < aNbAxes; i++)
{
aXAxis[i] = aYAxis[i].Crossed(aZAxis).Normalized();
aYAxis[i].Normalize();
}
if(theIsBuiltTrg)
FillToTriangle5(aZAxis, aP0);
// Min and Max parameter
const Standard_Integer aNbPoints = 2 * aNbAxes;
Standard_Integer aMinIdx = -1;
for(Standard_Integer anAxeInd = 0; anAxeInd < aNbAxes; anAxeInd++)
{
const gp_XYZ &aAX = aXAxis[anAxeInd],
&aAY = aYAxis[anAxeInd];
Standard_Real aParams[aNbPoints] = {0.0, 0.0, 0.0,
0.0, 0.0, 0.0};
for(Standard_Integer aPtIdx = 0; aPtIdx < myNbExtremalPoints; aPtIdx++)
{
if(aPtIdx == theIdx1)
continue;
const gp_XYZ aCurrPoint = myLExtremalPoints[aPtIdx] - aP0;
SetMinMax(&aParams[0], aAX.Dot(aCurrPoint));
SetMinMax(&aParams[2], aAY.Dot(aCurrPoint));
SetMinMax(&aParams[4], aZAxis.Dot(aCurrPoint));
}
const Standard_Real anArea = ComputeQuality(aParams);
if(anArea < myQualityCriterion)
{
myQualityCriterion = anArea;
aMinIdx = anAxeInd;
}
}
if(aMinIdx < 0)
return;
myAxes[0] = aXAxis[aMinIdx];
myAxes[1] = aYAxis[aMinIdx];
myAxes[2] = aZAxis;
}
//=======================================================================
// Function : ProcessDiTetrahedron
// purpose : DiTo-algorithm (http://www.idt.mdh.se/~tla/publ/FastOBBs.pdf)
//=======================================================================
void OBBTool::ProcessDiTetrahedron()
{
ProcessTriangle(myTriIdx[0], myTriIdx[1], myTriIdx[2], Standard_True);
if(myTriIdx[3] <= myNbExtremalPoints)
{
ProcessTriangle(myTriIdx[0], myTriIdx[1], myTriIdx[3], Standard_False);
ProcessTriangle(myTriIdx[1], myTriIdx[2], myTriIdx[3], Standard_False);
ProcessTriangle(myTriIdx[0], myTriIdx[2], myTriIdx[3], Standard_False);
}
if(myTriIdx[4] <= myNbExtremalPoints)
{
ProcessTriangle(myTriIdx[0], myTriIdx[1], myTriIdx[4], Standard_False);
ProcessTriangle(myTriIdx[1], myTriIdx[2], myTriIdx[4], Standard_False);
ProcessTriangle(myTriIdx[0], myTriIdx[2], myTriIdx[4], Standard_False);
}
}
//=======================================================================
// Function : BuildBox
// purpose :
//=======================================================================
void OBBTool::BuildBox(Bnd_OBB& theBox)
{
theBox.SetVoid();
// In fact, use Precision::SquareConfusion().
const Standard_Boolean isOBB = myAxes[0].SquareModulus()*
myAxes[1].SquareModulus()*
myAxes[2].SquareModulus() > 1.0e-14;
const gp_Dir aXDir = isOBB ? myAxes[0] : gp_Dir(1, 0, 0);
const gp_Dir aYDir = isOBB ? myAxes[1] : gp_Dir(0, 1, 0);
const gp_Dir aZDir = isOBB ? myAxes[2] : gp_Dir(0, 0, 1);
const Standard_Integer aNbPoints = 6;
Standard_Real aParams[aNbPoints];
gp_XYZ aFCurrPoint = myPntsList.First().XYZ();
aParams[0] = aParams[1] = aFCurrPoint.Dot(aXDir.XYZ());
aParams[2] = aParams[3] = aFCurrPoint.Dot(aYDir.XYZ());
aParams[4] = aParams[5] = aFCurrPoint.Dot(aZDir.XYZ());
if(myListOfTolers != 0)
{
const Standard_Real aTol = myListOfTolers->First();
aParams[0] -= aTol;
aParams[1] += aTol;
aParams[2] -= aTol;
aParams[3] += aTol;
aParams[4] -= aTol;
aParams[5] += aTol;
}
for(Standard_Integer i = myPntsList.Lower() + 1; i <= myPntsList.Upper(); i++)
{
const gp_XYZ &aCurrPoint = myPntsList(i).XYZ();
const Standard_Real aDx = aCurrPoint.Dot(aXDir.XYZ()),
aDy = aCurrPoint.Dot(aYDir.XYZ()),
aDz = aCurrPoint.Dot(aZDir.XYZ());
if(myListOfTolers == 0)
{
SetMinMax(&aParams[0], aDx);
SetMinMax(&aParams[2], aDy);
SetMinMax(&aParams[4], aDz);
}
else
{
const Standard_Real aTol = myListOfTolers->Value(i);
aParams[0] = Min(aParams[0], aDx - aTol);
aParams[1] = Max(aParams[1], aDx + aTol);
aParams[2] = Min(aParams[2], aDy - aTol);
aParams[3] = Max(aParams[3], aDy + aTol);
aParams[4] = Min(aParams[4], aDz - aTol);
aParams[5] = Max(aParams[5], aDz + aTol);
}
}
//Half-sizes
const Standard_Real aHX = 0.5*(aParams[1] - aParams[0]);
const Standard_Real aHY = 0.5*(aParams[3] - aParams[2]);
const Standard_Real aHZ = 0.5*(aParams[5] - aParams[4]);
const gp_XYZ aCenter = 0.5*((aParams[1] + aParams[0])*aXDir.XYZ() +
(aParams[3] + aParams[2])*aYDir.XYZ() +
(aParams[5] + aParams[4])*aZDir.XYZ());
theBox.SetCenter(aCenter);
theBox.SetXComponent(aXDir, aHX);
theBox.SetYComponent(aYDir, aHY);
theBox.SetZComponent(aZDir, aHZ);
theBox.SetAABox(!isOBB);
}
// =======================================================================
// function : ReBuild
// purpose : http://www.idt.mdh.se/~tla/publ/
// =======================================================================
void Bnd_OBB::ReBuild(const TColgp_Array1OfPnt& theListOfPoints,
const TColStd_Array1OfReal *theListOfTolerances)
{
switch(theListOfPoints.Length())
{
case 1:
ProcessOnePoint(theListOfPoints.First());
if(theListOfTolerances)
Enlarge(theListOfTolerances->First());
return;
case 2:
{
const Standard_Real aTol1 = (theListOfTolerances == 0) ? 0.0 :
theListOfTolerances->First();
const Standard_Real aTol2 = (theListOfTolerances == 0) ? 0.0 :
theListOfTolerances->Last();
const gp_XYZ &aP1 = theListOfPoints.First().XYZ(),
&aP2 = theListOfPoints.Last().XYZ();
const gp_XYZ aDP = aP2 - aP1;
const Standard_Real aDPm = aDP.Modulus();
myIsAABox = Standard_False;
myHDims[1] = myHDims[2] = Max(aTol1, aTol2);
if(aDPm < Precision::Confusion())
{
ProcessOnePoint(aP1);
Enlarge(myHDims[1] + Precision::Confusion());
return;
}
myHDims[0] = 0.5*(aDPm+aTol1+aTol2);
myAxes[0] = aDP/aDPm;
if(Abs(myAxes[0].X()) > Abs(myAxes[0].Y()))
{
// Z-coord. is maximal or X-coord. is maximal
myAxes[1].SetCoord(-myAxes[0].Z(), 0.0, myAxes[0].X());
}
else
{
// Z-coord. is maximal or Y-coord. is maximal
myAxes[1].SetCoord(0.0, -myAxes[0].Z(), myAxes[0].Y());
}
myAxes[2] = myAxes[0].Crossed(myAxes[1]).Normalized();
myCenter = aP1 + 0.5*(aDPm - aTol1 + aTol2)*myAxes[0];
}
return;
default:
break;
}
OBBTool aTool(theListOfPoints, theListOfTolerances);
aTool.ProcessDiTetrahedron();
aTool.BuildBox(*this);
}
// =======================================================================
// function : IsOut
// purpose :
// =======================================================================
Standard_Boolean Bnd_OBB::IsOut(const Bnd_OBB& theOther) const
{
if (IsVoid() || theOther.IsVoid())
return Standard_True;
if (myIsAABox && theOther.myIsAABox)
{
return ((Abs(theOther.myCenter.X() - myCenter.X()) > theOther.myHDims[0] + myHDims[0]) ||
(Abs(theOther.myCenter.Y() - myCenter.Y()) > theOther.myHDims[1] + myHDims[1]) ||
(Abs(theOther.myCenter.Z() - myCenter.Z()) > theOther.myHDims[2] + myHDims[2]));
}
// According to the Separating Axis Theorem for Oriented Bounding Boxes
// it is necessary to check the 15 separating axes (Ls):
// - 6 axes of the boxes;
// - 9 cross products of the axes of the boxes.
// If any of these axes is valid, the boxes do not interfere.
// The algorithm is following:
// 1. Compute the "length" for j-th BndBox (j=1...2) according to the formula:
// L(j)=Sum(myHDims[i]*Abs(myAxes[i].Dot(Ls)))
// 2. If (theCenter2 - theCenter1).Dot(Ls) > (L(1) + L(2))
// then the considered OBBs are not interfered in terms of the axis Ls.
//
// If OBBs are not interfered in terms of at least one axis (of 15) then
// they are not interfered at all.
// Precomputed difference between centers
gp_XYZ D = theOther.myCenter - myCenter;
// Check the axes of the this box, i.e. L is one of myAxes
// Since the Dot product of two of these directions is null, it could be skipped:
// myXDirection.Dot(myYDirection) = 0
for(Standard_Integer i = 0; i < 3; ++i)
{
// Length of the second segment
Standard_Real aLSegm2 = 0;
for(Standard_Integer j = 0; j < 3; ++j)
aLSegm2 += theOther.myHDims[j] * Abs(theOther.myAxes[j].Dot(myAxes[i]));
// Distance between projected centers
Standard_Real aDistCC = Abs(D.Dot(myAxes[i]));
if(aDistCC > myHDims[i] + aLSegm2)
return Standard_True;
}
// Check the axes of the Other box, i.e. L is one of theOther.myAxes
for(Standard_Integer i = 0; i < 3; ++i)
{
// Length of the first segment
Standard_Real aLSegm1 = 0.;
for(Standard_Integer j = 0; j < 3; ++j)
aLSegm1 += myHDims[j] * Abs(myAxes[j].Dot(theOther.myAxes[i]));
// Distance between projected centers
Standard_Real aDistCC = Abs(D.Dot(theOther.myAxes[i]));
if(aDistCC > aLSegm1 + theOther.myHDims[i])
return Standard_True;
}
const Standard_Real aTolNull = Epsilon(1.0);
// Check the axes produced by the cross products
for(Standard_Integer i = 0; i < 3; ++i)
{
for(Standard_Integer j = 0; j < 3; ++j)
{
// Separating axis
gp_XYZ aLAxe = myAxes[i].Crossed(theOther.myAxes[j]);
const Standard_Real aNorm = aLAxe.Modulus();
if(aNorm < aTolNull)
continue;
aLAxe /= aNorm;
// Length of the first segment
Standard_Real aLSegm1 = 0.;
for(Standard_Integer k = 0; k < 3; ++k)
aLSegm1 += myHDims[k] * Abs(myAxes[k].Dot(aLAxe));
// Length of the second segment
Standard_Real aLSegm2 = 0.;
for(Standard_Integer k = 0; k < 3; ++k)
aLSegm2 += theOther.myHDims[k] * Abs(theOther.myAxes[k].Dot(aLAxe));
// Distance between projected centers
Standard_Real aDistCC = Abs(D.Dot(aLAxe));
if(aDistCC > aLSegm1 + aLSegm2)
return Standard_True;
}
}
return Standard_False;
}
// =======================================================================
// function : IsOut
// purpose :
// =======================================================================
Standard_Boolean Bnd_OBB::IsOut(const gp_Pnt& theP) const
{
// 1. Project the point to myAxes[i] (i=0...2).
// 2. Check, whether the absolute value of the correspond
// projection parameter is greater than myHDims[i].
// In this case, IsOut method will return TRUE.
const gp_XYZ aRV = theP.XYZ() - myCenter;
return ((Abs(myAxes[0].Dot(aRV)) > myHDims[0]) ||
(Abs(myAxes[1].Dot(aRV)) > myHDims[1]) ||
(Abs(myAxes[2].Dot(aRV)) > myHDims[2]));
}
// =======================================================================
// function : IsCompletelyInside
// purpose : Checks if every vertex of theOther is completely inside *this
// =======================================================================
Standard_Boolean Bnd_OBB::IsCompletelyInside(const Bnd_OBB& theOther) const
{
if(IsVoid() || theOther.IsVoid())
return Standard_False;
gp_Pnt aVert[8];
theOther.GetVertex(aVert);
for(Standard_Integer i = 0; i < 8; i++)
{
if(IsOut(aVert[i]))
return Standard_False;
}
return Standard_True;
}
// =======================================================================
// function : Add
// purpose :
// =======================================================================
void Bnd_OBB::Add(const gp_Pnt& theP)
{
gp_Pnt aList[9];
GetVertex(aList);
aList[8] = theP;
ReBuild(TColgp_Array1OfPnt(aList[0], 0, 8));
}
// =======================================================================
// function : Add
// purpose :
// =======================================================================
void Bnd_OBB::Add(const Bnd_OBB& theOther)
{
gp_Pnt aList[16];
GetVertex(&aList[0]);
theOther.GetVertex(&aList[8]);
ReBuild(TColgp_Array1OfPnt(aList[0], 0, 15));
}

290
src/Bnd/Bnd_OBB.hxx Normal file
View File

@@ -0,0 +1,290 @@
// Created by: Eugeny MALTCHIKOV
// Copyright (c) 2017 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#ifndef _Bnd_OBB_HeaderFile
#define _Bnd_OBB_HeaderFile
#include <Standard.hxx>
#include <Standard_DefineAlloc.hxx>
#include <Standard_Handle.hxx>
#include <Standard_Real.hxx>
#include <Standard_Boolean.hxx>
#include <Bnd_Box.hxx>
#include <gp_Dir.hxx>
#include <gp_Pnt.hxx>
#include <gp_XYZ.hxx>
#include <TColgp_Array1OfPnt.hxx>
#include <TColStd_Array1OfReal.hxx>
//! The class describes the Oriented Bounding Box (OBB),
//! much tighter enclosing volume for the shape than the
//! Axis Aligned Bounding Box (AABB).
//! The OBB is defined by a center of the box, the axes and the halves
//! of its three dimensions.
//! The OBB can be used more effectively than AABB as a rejection mechanism
//! for non-interfering objects.
class Bnd_OBB
{
public:
DEFINE_STANDARD_ALLOC
//! Empty constructor
Bnd_OBB() :myIsAABox(Standard_False)
{
myHDims[0] = myHDims[1] = myHDims[2] = -1.0;
}
//! Constructor taking all defining parameters
Bnd_OBB(const gp_Pnt& theCenter,
const gp_Dir& theXDirection,
const gp_Dir& theYDirection,
const gp_Dir& theZDirection,
const Standard_Real theHXSize,
const Standard_Real theHYSize,
const Standard_Real theHZSize) :myCenter (theCenter.XYZ()),
myIsAABox(Standard_False)
{
myAxes[0] = theXDirection.XYZ();
myAxes[1] = theYDirection.XYZ();
myAxes[2] = theZDirection.XYZ();
Standard_ASSERT_VOID(theHXSize >= 0.0, "Negative value of X-size");
Standard_ASSERT_VOID(theHYSize >= 0.0, "Negative value of Y-size");
Standard_ASSERT_VOID(theHZSize >= 0.0, "Negative value of Z-size");
myHDims[0] = theHXSize;
myHDims[1] = theHYSize;
myHDims[2] = theHZSize;
}
//! Constructor to create OBB from AABB.
Bnd_OBB(const Bnd_Box& theBox) : myIsAABox(Standard_True)
{
Standard_Real aX1, aY1, aZ1, aX2, aY2, aZ2;
theBox.Get(aX1, aY1, aZ1, aX2, aY2, aZ2);
myAxes[0].SetCoord(1.0, 0.0, 0.0);
myAxes[1].SetCoord(0.0, 1.0, 0.0);
myAxes[2].SetCoord(0.0, 0.0, 1.0);
myHDims[0] = 0.5*(aX2 - aX1);
myHDims[1] = 0.5*(aY2 - aY1);
myHDims[2] = 0.5*(aZ2 - aZ1);
myCenter.SetCoord(0.5*(aX2 + aX1), 0.5*(aY2 + aY1), 0.5*(aZ2 + aZ1));
}
//! Created new OBB covering every point in theListOfPoints.
//! Tolerance of every such point is set by *theListOfTolerances array.
//! If this array is not void (not null-pointer) then the resulted Bnd_OBB
//! will be enlarged using tolerances of points lying on the box surface.
Standard_EXPORT void ReBuild(const TColgp_Array1OfPnt& theListOfPoints,
const TColStd_Array1OfReal *theListOfTolerances = 0);
//! Sets the center of OBB
void SetCenter(const gp_Pnt& theCenter)
{
myCenter = theCenter.XYZ();
}
//! Sets the X component of OBB - direction and size
void SetXComponent(const gp_Dir& theXDirection,
const Standard_Real theHXSize)
{
Standard_ASSERT_VOID(theHXSize >= 0.0, "Negative value of X-size");
myAxes[0] = theXDirection.XYZ();
myHDims[0] = theHXSize;
}
//! Sets the Y component of OBB - direction and size
void SetYComponent(const gp_Dir& theYDirection,
const Standard_Real theHYSize)
{
Standard_ASSERT_VOID(theHYSize >= 0.0, "Negative value of Y-size");
myAxes[1] = theYDirection.XYZ();
myHDims[1] = theHYSize;
}
//! Sets the Z component of OBB - direction and size
void SetZComponent(const gp_Dir& theZDirection,
const Standard_Real theHZSize)
{
Standard_ASSERT_VOID(theHZSize >= 0.0, "Negative value of Z-size");
myAxes[2] = theZDirection.XYZ();
myHDims[2] = theHZSize;
}
//! Returns the center of OBB
const gp_XYZ& Center() const
{
return myCenter;
}
//! Returns the X Direction of OBB
const gp_XYZ& XDirection() const
{
return myAxes[0];
}
//! Returns the Y Direction of OBB
const gp_XYZ& YDirection() const
{
return myAxes[1];
}
//! Returns the Z Direction of OBB
const gp_XYZ& ZDirection() const
{
return myAxes[2];
}
//! Returns the X Dimension of OBB
Standard_Real XHSize() const
{
return myHDims[0];
}
//! Returns the Y Dimension of OBB
Standard_Real YHSize() const
{
return myHDims[1];
}
//! Returns the Z Dimension of OBB
Standard_Real ZHSize() const
{
return myHDims[2];
}
//! Checks if the box is empty.
Standard_Boolean IsVoid() const
{
return ((myHDims[0] < 0.0) || (myHDims[1] < 0.0) || (myHDims[2] < 0.0));
}
//! Clears this box
void SetVoid()
{
myHDims[0] = myHDims[1] = myHDims[2] = -1.0;
myCenter = myAxes[0] = myAxes[1] = myAxes[2] = gp_XYZ();
myIsAABox = Standard_False;
}
//! Sets the flag for axes aligned box
void SetAABox(const Standard_Boolean& theFlag)
{
myIsAABox = theFlag;
}
//! Returns TRUE if the box is axes aligned
Standard_Boolean IsAABox() const
{
return myIsAABox;
}
//! Enlarges the box with the given value
void Enlarge(const Standard_Real theGapAdd)
{
const Standard_Real aGap = Abs(theGapAdd);
myHDims[0] += aGap;
myHDims[1] += aGap;
myHDims[2] += aGap;
}
//! Returns the array of vertices in <this>.
//! The local coordinate of the vertex depending on the
//! index of the array are follow:
//! Index == 0: (-XHSize(), -YHSize(), -ZHSize())
//! Index == 1: ( XHSize(), -YHSize(), -ZHSize())
//! Index == 2: (-XHSize(), YHSize(), -ZHSize())
//! Index == 3: ( XHSize(), YHSize(), -ZHSize())
//! Index == 4: (-XHSize(), -YHSize(), ZHSize())
//! Index == 5: ( XHSize(), -YHSize(), ZHSize())
//! Index == 6: (-XHSize(), YHSize(), ZHSize())
//! Index == 7: ( XHSize(), YHSize(), ZHSize()).
Standard_Boolean GetVertex(gp_Pnt theP[8]) const
{
if(IsVoid())
return Standard_False;
theP[0].SetXYZ(myCenter - myHDims[0]*myAxes[0] - myHDims[1]*myAxes[1] - myHDims[2]*myAxes[2]);
theP[1].SetXYZ(myCenter + myHDims[0]*myAxes[0] - myHDims[1]*myAxes[1] - myHDims[2]*myAxes[2]);
theP[2].SetXYZ(myCenter - myHDims[0]*myAxes[0] + myHDims[1]*myAxes[1] - myHDims[2]*myAxes[2]);
theP[3].SetXYZ(myCenter + myHDims[0]*myAxes[0] + myHDims[1]*myAxes[1] - myHDims[2]*myAxes[2]);
theP[4].SetXYZ(myCenter - myHDims[0]*myAxes[0] - myHDims[1]*myAxes[1] + myHDims[2]*myAxes[2]);
theP[5].SetXYZ(myCenter + myHDims[0]*myAxes[0] - myHDims[1]*myAxes[1] + myHDims[2]*myAxes[2]);
theP[6].SetXYZ(myCenter - myHDims[0]*myAxes[0] + myHDims[1]*myAxes[1] + myHDims[2]*myAxes[2]);
theP[7].SetXYZ(myCenter + myHDims[0]*myAxes[0] + myHDims[1]*myAxes[1] + myHDims[2]*myAxes[2]);
return Standard_True;
}
//! Returns square diagonal of this box
Standard_Real SquareExtent() const
{
return (4.0*myHDims[0] * myHDims[0] +
myHDims[1] * myHDims[1] +
myHDims[1] * myHDims[1]);
}
//! Check if the box do not interfere the other box.
Standard_EXPORT Standard_Boolean IsOut(const Bnd_OBB& theOther) const;
//! Check if the point is inside of <this>.
Standard_EXPORT Standard_Boolean IsOut(const gp_Pnt& theP) const;
//! Check if the theOther is completely inside *this.
Standard_EXPORT Standard_Boolean IsCompletelyInside(const Bnd_OBB& theOther) const;
//! Rebuilds this in order to include all previous objects
//! (which it was created from) and theOther.
Standard_EXPORT void Add(const Bnd_OBB& theOther);
//! Rebuilds this in order to include all previous objects
//! (which it was created from) and theP.
Standard_EXPORT void Add(const gp_Pnt& theP);
protected:
void ProcessOnePoint(const gp_Pnt& theP)
{
myIsAABox = Standard_True;
myHDims[0] = myHDims[1] = myHDims[2] = 0.0;
myAxes[0].SetCoord(1.0, 0.0, 0.0);
myAxes[1].SetCoord(0.0, 1.0, 0.0);
myAxes[2].SetCoord(0.0, 0.0, 1.0);
myCenter = theP.XYZ();
}
private:
//! Center of the OBB
gp_XYZ myCenter;
//! Directions of the box's axes
//! (all vectors are already normalized)
gp_XYZ myAxes[3];
//! Half-size dimensions of the OBB
Standard_Real myHDims[3];
//! To be set if the OBB is axis aligned box;
Standard_Boolean myIsAABox;
};
#endif

View File

@@ -24,6 +24,8 @@ Bnd_Box2d.hxx
Bnd_HArray1OfBox.hxx
Bnd_HArray1OfBox2d.hxx
Bnd_HArray1OfSphere.hxx
Bnd_OBB.cxx
Bnd_OBB.hxx
Bnd_Range.cxx
Bnd_Range.hxx
Bnd_SeqOfBox.hxx

View File

@@ -24,116 +24,111 @@
IMPLEMENT_STANDARD_RTTIEXT(Draw_Box,Draw_Drawable3D)
//=======================================================================
//function : Draw_Box
//function : Constructor
//purpose :
//=======================================================================
Draw_Box::Draw_Box(const gp_Pnt& p1, const gp_Pnt& p2, const Draw_Color& col) :
myFirst(p1), myLast(p2),myColor(col)
Draw_Box::Draw_Box(const Bnd_OBB& theOBB,
const Draw_Color& theColor) :myOBB(theOBB), myColor(theColor)
{
Standard_Real t;
if (myLast.X() < myFirst.X()) {
t = myFirst.X();
myFirst.SetX(myLast.X());
myLast.SetX(t);
}
if (myLast.Y() < myFirst.Y()) {
t = myFirst.Y();
myFirst.SetY(myLast.Y());
myLast.SetY(t);
}
if (myLast.Z() < myFirst.Z()) {
t = myFirst.Z();
myFirst.SetZ(myLast.Z());
myLast.SetZ(t);
}
}
//=======================================================================
//function : ToWCS
//purpose :
//=======================================================================
void Draw_Box::ToWCS(const Standard_Real theX,
const Standard_Real theY,
const Standard_Real theZ,
gp_Pnt& theP) const
{
const gp_XYZ & aC = myOBB.Center();
const gp_XYZ aXDir = myOBB.XDirection(),
aYDir = myOBB.YDirection(),
aZDir = myOBB.ZDirection();
theP.SetXYZ(aC + theX*aXDir + theY*aYDir + theZ*aZDir);
}
//=======================================================================
//function : MoveX
//purpose :
//=======================================================================
void Draw_Box::MoveX(const Standard_Real theShift, gp_Pnt& thePt) const
{
const gp_XYZ aXDir = myOBB.XDirection();
thePt.SetXYZ(thePt.XYZ() + theShift*aXDir);
}
//=======================================================================
//function : MoveY
//purpose :
//=======================================================================
void Draw_Box::MoveY(const Standard_Real theShift, gp_Pnt& thePt) const
{
const gp_XYZ aYDir = myOBB.YDirection();
thePt.SetXYZ(thePt.XYZ() + theShift*aYDir);
}
//=======================================================================
//function : MoveZ
//purpose :
//=======================================================================
void Draw_Box::MoveZ(const Standard_Real theShift, gp_Pnt& thePt) const
{
const gp_XYZ aZDir = myOBB.ZDirection();
thePt.SetXYZ(thePt.XYZ() + theShift*aZDir);
}
//=======================================================================
//function : DrawOn
//purpose :
//=======================================================================
void Draw_Box::DrawOn(Draw_Display& dis) const
void Draw_Box::DrawOn(Draw_Display& theDIS) const
{
dis.SetColor(myColor);
gp_Pnt P = myFirst;
if(myOBB.IsVoid())
{
return;
}
dis.MoveTo(P);
P.SetX(myLast.X());
dis.DrawTo(P);
P.SetY(myLast.Y());
dis.DrawTo(P);
P.SetZ(myLast.Z());
dis.DrawTo(P);
P.SetX(myFirst.X());
dis.DrawTo(P);
P.SetY(myFirst.Y());
dis.DrawTo(P);
P.SetZ(myFirst.Z());
dis.DrawTo(P);
P.SetX(myLast.X());
dis.MoveTo(P);
P.SetZ(myLast.Z());
dis.DrawTo(P);
P.SetX(myFirst.X());
dis.DrawTo(P);
theDIS.SetColor(myColor);
P.SetX(myLast.X());
dis.MoveTo(P);
P.SetY(myLast.Y());
dis.DrawTo(P);
const Standard_Real aHx = myOBB.XHSize(),
aHy = myOBB.YHSize(),
aHz = myOBB.ZHSize();
gp_Pnt aP;
ToWCS(-aHx, -aHy, -aHz, aP);
theDIS.MoveTo(aP);
for(Standard_Integer i = 0; i<2; i++)
{
MoveX(2.0*aHx, aP);
theDIS.DrawTo(aP);
MoveY(2.0*aHy, aP);
theDIS.DrawTo(aP);
MoveX(-2.0*aHx, aP);
theDIS.DrawTo(aP);
MoveY(-2.0*aHy, aP);
theDIS.DrawTo(aP);
ToWCS(-aHx, -aHy, aHz, aP);
theDIS.MoveTo(aP);
}
P.SetX(myFirst.X());
dis.MoveTo(P);
P.SetZ(myFirst.Z());
dis.DrawTo(P);
P.SetY(myFirst.Y());
dis.DrawTo(P);
P.SetY(myLast.Y());
dis.MoveTo(P);
P.SetX(myLast.X());
dis.DrawTo(P);
}
//=======================================================================
//function : First
//purpose :
//=======================================================================
const gp_Pnt& Draw_Box::First() const
{
return myFirst;
}
//=======================================================================
//function : First
//purpose :
//=======================================================================
void Draw_Box::First(const gp_Pnt& P)
{
myFirst = P;
}
//=======================================================================
//function : Last
//purpose :
//=======================================================================
const gp_Pnt& Draw_Box::Last() const
{
return myLast;
}
//=======================================================================
//function : Last
//purpose :
//=======================================================================
void Draw_Box::Last(const gp_Pnt& P)
{
myLast = P;
for(Standard_Integer i = 0; i < 4; i++)
{
switch(i)
{
case 0: ToWCS(-aHx, -aHy, -aHz, aP); break;
case 1: ToWCS(aHx, -aHy, -aHz, aP); break;
case 2: ToWCS(aHx, aHy, -aHz, aP); break;
case 3: ToWCS(-aHx, aHy, -aHz, aP); break;
default: break;
}
theDIS.MoveTo(aP);
MoveZ(2.0*aHz, aP);
theDIS.DrawTo(aP);
}
}

View File

@@ -20,10 +20,9 @@
#include <Standard.hxx>
#include <Standard_Type.hxx>
#include <gp_Pnt.hxx>
#include <Bnd_OBB.hxx>
#include <Draw_Color.hxx>
#include <Draw_Drawable3D.hxx>
class gp_Pnt;
class Draw_Color;
class Draw_Display;
@@ -34,46 +33,41 @@ DEFINE_STANDARD_HANDLE(Draw_Box, Draw_Drawable3D)
//! a 3d box
class Draw_Box : public Draw_Drawable3D
{
public:
//! Constructor
Standard_EXPORT Draw_Box(const Bnd_OBB& theOBB,
const Draw_Color& theColor);
//! Draws myOBB
Standard_EXPORT void DrawOn (Draw_Display& theDis) const Standard_OVERRIDE;
Standard_EXPORT Draw_Box(const gp_Pnt& p1, const gp_Pnt& p2, const Draw_Color& col);
Standard_EXPORT void DrawOn (Draw_Display& dis) const Standard_OVERRIDE;
Standard_EXPORT const gp_Pnt& First() const;
Standard_EXPORT void First (const gp_Pnt& P);
Standard_EXPORT const gp_Pnt& Last() const;
Standard_EXPORT void Last (const gp_Pnt& P);
DEFINE_STANDARD_RTTIEXT(Draw_Box,Draw_Drawable3D)
protected:
//! Converts the point (theX, theY, theZ) in local coordinate system to WCS.
void ToWCS(const Standard_Real theX,
const Standard_Real theY,
const Standard_Real theZ,
gp_Pnt& theP) const;
//! Moves the point thePt along X-direction of myOBB on the distance theShift.
void MoveX(const Standard_Real theShift, gp_Pnt& thePt) const;
//! Moves the point thePt along Y-direction of myOBB on the distance theShift.
void MoveY(const Standard_Real theShift, gp_Pnt& thePt) const;
//! Moves the point thePt along Z-direction of myOBB on the distance theShift.
void MoveZ(const Standard_Real theShift, gp_Pnt& thePt) const;
private:
//! Oriented bounding box
Bnd_OBB myOBB;
gp_Pnt myFirst;
gp_Pnt myLast;
//! Color value
Draw_Color myColor;
};
#endif // _Draw_Box_HeaderFile