/*************************************************************************** * Copyright (c) 2005 Imetric 3D GmbH * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #ifndef BASE_BOUNDBOX_H #define BASE_BOUNDBOX_H #include #include #include "Matrix.h" #include "Tools2D.h" #include "ViewProj.h" namespace Base { class ViewProjMethod; /** The 3D bounding box class. */ template class BoundBox3 { // helper function static bool isOnRayW(Precision min, Precision max, Precision val); static bool isOnRayS(Precision min, Precision max, Precision val); public: using num_type = Precision; using traits_type = float_traits; /** Public attributes */ //@{ Precision MinX; Precision MinY; Precision MinZ; Precision MaxX; Precision MaxY; Precision MaxZ; //@} /** Builds box from pairs of x,y,z values. */ inline explicit BoundBox3 (Precision fMinX = std::numeric_limits::max(), Precision fMinY = std::numeric_limits::max(), Precision fMinZ = std::numeric_limits::max(), Precision fMaxX = -std::numeric_limits::max(), Precision fMaxY = -std::numeric_limits::max(), Precision fMaxZ = -std::numeric_limits::max()); BoundBox3 (const BoundBox3 &rcBB) = default; BoundBox3 (BoundBox3 &&rcBB) noexcept = default; /** Builds box from an array of points. */ inline BoundBox3 (const Vector3 *pclVect, std::size_t ulCt); /** Defines a bounding box around the center \a rcCnt with the * distances \a fDistance in each coordinate. */ BoundBox3 (const Vector3& point, Precision distance); ~BoundBox3 (); /// Assignment operator inline BoundBox3& operator = (const BoundBox3 &rcBound) = default; inline BoundBox3& operator = (BoundBox3 &&rcBound) noexcept = default; /** Methods for intersection, cuttíng and union of bounding boxes */ //@{ /** Checks for intersection. */ inline bool Intersect (const BoundBox3 &rcBB) const; /** Checks for intersection. */ inline bool operator && (const BoundBox3 &rcBB) const; /** Checks for intersection. */ inline bool Intersect (const BoundBox2d &rcBB) const; /** Checks for intersection. */ inline bool operator && (const BoundBox2d &rcBB) const; /** Computes the intersection between two bounding boxes. * The result is also a bounding box. */ BoundBox3 Intersected (const BoundBox3 &rcBB) const; /** The union of two bounding boxes. */ BoundBox3 United (const BoundBox3 &rcBB) const; /** Appends the point to the box. The box can grow but not shrink. */ inline void Add (const Vector3 &rclVect); /** Appends the bounding box to this box. The box can grow but not shrink. */ inline void Add (const BoundBox3 &rcBB); //@} /** Test methods */ //@{ /** Checks if this point lies inside the box. * @note It's up to the client programmer to make sure that this bounding box is valid. */ inline bool IsInBox (const Vector3 &rcVct) const; /** Checks if this 3D box lies inside the box. * @note It's up to the client programmer to make sure that both bounding boxes are valid. */ inline bool IsInBox (const BoundBox3 &rcBB) const; /** Checks if this 2D box lies inside the box. * @note It's up to the client programmer to make sure that both bounding boxes are valid. */ inline bool IsInBox (const BoundBox2d &rcbb) const; /** Checks whether the bounding box is valid. */ bool IsValid () const; //@} enum OCTANT {OCT_LDB = 0, OCT_RDB, OCT_LUB, OCT_RUB, OCT_LDF, OCT_RDF, OCT_LUF, OCT_RUF}; bool GetOctantFromVector (const Vector3 &rclVct, OCTANT &rclOctant) const; BoundBox3 CalcOctant (typename BoundBox3::OCTANT Octant) const; enum SIDE { LEFT =0, RIGHT=1, TOP=2, BOTTOM=3, FRONT=4, BACK=5, INVALID=255 }; enum CORNER { TLB = 0, // top-left-back TLF = 1, // top-left-front TRF = 2, // top-right-front TRB = 3, // top-right-back BLB = 4, // bottom-left-back BLF = 5, // bottom-left-front BRF = 6, // bottom-right-front BRB = 7, // bottom-right-back }; enum EDGE { TLB_TLF = 0, TLF_TRF = 1, TRF_TRB = 2, TRB_TLB = 3, BLB_BLF = 4, BLF_BRF = 5, BRF_BRB = 6, BRB_BLB = 7, TLB_BLB = 8, TLF_BLF = 9, TRF_BRF = 10, TRB_BRB = 11 }; /** * Returns the corner point \a usPoint. */ inline Vector3 CalcPoint (unsigned short usPoint) const; /** Returns the plane of the given side. */ void CalcPlane (unsigned short usPlane, Vector3& rBase, Vector3& rNormal) const; /** Calculates the two points of an edge. */ bool CalcEdge (unsigned short usEdge, Vector3& rcP0, Vector3& rcP1) const; /** Intersection point of an inner search ray with the bounding box, built of * the base \a rcVct and the direction \a rcVctDir. \a rcVct must lie inside the * bounding box. */ bool IntersectionPoint (const Vector3 &rcVct, const Vector3 &rcVctDir, Vector3& cVctRes, Precision epsilon) const; /** Checks for intersection with line incl. search tolerance. */ bool IsCutLine (const Vector3& rcBase, const Vector3& rcDir, Precision fTolerance = 0.0F) const; /** Checks if this plane specified by (point,normal) cuts this box. * @note It's up to the client programmer to make sure that this bounding box is valid. */ inline bool IsCutPlane (const Vector3 &rclBase, const Vector3 &rclNormal) const; /** Computes the intersection points of line and bounding box. */ bool IntersectWithLine (const Vector3& rcBase, const Vector3& rcDir, Vector3& rcP0, Vector3& rcP1) const; /** Computes the intersection point of line and a plane of the bounding box. */ bool IntersectPlaneWithLine (unsigned short usSide, const Vector3& rcBase, const Vector3& rcDir, Vector3& rcP0) const; /** Returns the side of the bounding box the ray exits. */ typename BoundBox3::SIDE GetSideFromRay (const Vector3 &rclPt, const Vector3 &rclDir) const; /** Returns the side of the bounding box the ray exits. */ typename BoundBox3::SIDE GetSideFromRay (const Vector3 &rclPt, const Vector3 &rclDir, Vector3& rcInt) const; /** * Searches for the closest point of the bounding box. */ Vector3 ClosestPoint (const Vector3 &rclPt) const; /** Projects the box onto a plane and returns a 2D box. */ BoundBox2d ProjectBox(const ViewProjMethod* proj) const; /** Transform the corners of this box with the given matrix and create a new bounding box. * @note It's up to the client programmer to make sure that this bounding box is valid. */ BoundBox3 Transformed(const Matrix4D& mat) const; /** Returns the center of the box. */ inline Vector3 GetCenter () const; /** Returns the minimum point of the box. */ inline Vector3 GetMinimum () const; /** Returns the maximum point of the box. */ inline Vector3 GetMaximum () const; /** Compute the diagonal length of this bounding box. * @note It's up to the client programmer to make sure that this bounding box is valid. */ inline Precision CalcDiagonalLength () const; void SetVoid (); /** Enlarges the box with \a fLen. */ inline void Enlarge (Precision fLen); /** Shrinks the box with \a fLen. */ inline void Shrink (Precision fLen); /** Calculates expansion in x-direction. */ inline Precision LengthX () const; /** Calculates expansion in y-direction. */ inline Precision LengthY () const; /** Calculates expansion in z-direction. */ inline Precision LengthZ () const; /** Calculates the volume. If the box is invalid a negative value is returned */ inline Precision Volume () const; /** Moves in x-direction. */ inline void MoveX (Precision value); /** Moves in y-direction. */ inline void MoveY (Precision value); /** Moves in z-direction. */ inline void MoveZ (Precision value); /** Scales in x-direction. */ inline void ScaleX (Precision value); /** Scales in y-direction. */ inline void ScaleY (Precision value); /** Scales in z-direction. */ inline void ScaleZ (Precision value); /** Prints the values to stream. */ void Print (std::ostream&) const; }; template bool BoundBox3::isOnRayW(Precision min, Precision max, Precision val) { // Checks if point val lies on the ray [min,max] return ((min <= val) && (val <= max)); } template bool BoundBox3::isOnRayS(Precision min, Precision max, Precision val) { // Checks if point val lies on the ray [min,max[ return ((min <= val) && (val < max)); } // NOLINTBEGIN(bugprone-easily-swappable-parameters) template inline BoundBox3::BoundBox3 (Precision fMinX, Precision fMinY, Precision fMinZ, Precision fMaxX, Precision fMaxY, Precision fMaxZ) : MinX(fMinX), MinY(fMinY), MinZ(fMinZ), MaxX(fMaxX), MaxY(fMaxY), MaxZ(fMaxZ) { } // NOLINTEND(bugprone-easily-swappable-parameters) template inline BoundBox3::BoundBox3 (const Vector3 *pclVect, std::size_t ulCt) : MinX( std::numeric_limits::max()) , MinY( std::numeric_limits::max()) , MinZ( std::numeric_limits::max()) , MaxX(-std::numeric_limits::max()) , MaxY(-std::numeric_limits::max()) , MaxZ(-std::numeric_limits::max()) { const Vector3 *pI = nullptr; const Vector3 *pEnd = pclVect + ulCt; for (pI = pclVect; pI < pEnd; ++pI) { MinX = std::min(MinX, pI->x); MinY = std::min(MinY, pI->y); MinZ = std::min(MinZ, pI->z); MaxX = std::max(MaxX, pI->x); MaxY = std::max(MaxY, pI->y); MaxZ = std::max(MaxZ, pI->z); } } template inline BoundBox3::BoundBox3 (const Vector3& point, Precision distance) : MinX(point.x - distance) , MinY(point.y - distance) , MinZ(point.z - distance) , MaxX(point.x + distance) , MaxY(point.y + distance) , MaxZ(point.z + distance) { } template inline BoundBox3::~BoundBox3 () = default; template inline bool BoundBox3::Intersect (const BoundBox3 &rcBB) const { if (rcBB.MaxX < this->MinX || rcBB.MinX > this->MaxX) { return false; } if (rcBB.MaxY < this->MinY || rcBB.MinY > this->MaxY) { return false; } if (rcBB.MaxZ < this->MinZ || rcBB.MinZ > this->MaxZ) { return false; } return true; } template bool BoundBox3::operator && (const BoundBox3 &rcBB) const { return Intersect(rcBB); } template inline bool BoundBox3::Intersect (const BoundBox2d &rcBB) const { if (rcBB.MaxX < this->MinX || rcBB.MinX > this->MaxX) { return false; } if (rcBB.MaxY < this->MinY || rcBB.MinY > this->MaxY) { return false; } return true; } template inline bool BoundBox3::operator && (const BoundBox2d &rcBB) const { return Intersect(rcBB); } template inline BoundBox3 BoundBox3::Intersected(const BoundBox3 &rcBB) const { BoundBox3 cBBRes; cBBRes.MinX = std::max (MinX, rcBB.MinX); cBBRes.MaxX = std::min (MaxX, rcBB.MaxX); cBBRes.MinY = std::max (MinY, rcBB.MinY); cBBRes.MaxY = std::min (MaxY, rcBB.MaxY); cBBRes.MinZ = std::max (MinZ, rcBB.MinZ); cBBRes.MaxZ = std::min (MaxZ, rcBB.MaxZ); return cBBRes; } template inline BoundBox3 BoundBox3::United(const BoundBox3 &rcBB) const { BoundBox3 cBBRes; cBBRes.MinX = std::min (MinX, rcBB.MinX); cBBRes.MaxX = std::max (MaxX, rcBB.MaxX); cBBRes.MinY = std::min (MinY, rcBB.MinY); cBBRes.MaxY = std::max (MaxY, rcBB.MaxY); cBBRes.MinZ = std::min (MinZ, rcBB.MinZ); cBBRes.MaxZ = std::max (MaxZ, rcBB.MaxZ); return cBBRes; } template inline void BoundBox3::Add (const Vector3 &rclVect) { this->MinX = std::min(this->MinX, rclVect.x); this->MinY = std::min(this->MinY, rclVect.y); this->MinZ = std::min(this->MinZ, rclVect.z); this->MaxX = std::max(this->MaxX, rclVect.x); this->MaxY = std::max(this->MaxY, rclVect.y); this->MaxZ = std::max(this->MaxZ, rclVect.z); } template inline void BoundBox3::Add (const BoundBox3 &rcBB) { this->MinX = std::min (this->MinX, rcBB.MinX); this->MaxX = std::max (this->MaxX, rcBB.MaxX); this->MinY = std::min (this->MinY, rcBB.MinY); this->MaxY = std::max (this->MaxY, rcBB.MaxY); this->MinZ = std::min (this->MinZ, rcBB.MinZ); this->MaxZ = std::max (this->MaxZ, rcBB.MaxZ); } template inline bool BoundBox3::IsInBox (const Vector3 &rcVct) const { if (rcVct.x < this->MinX || rcVct.x > this->MaxX) { return false; } if (rcVct.y < this->MinY || rcVct.y > this->MaxY) { return false; } if (rcVct.z < this->MinZ || rcVct.z > this->MaxZ) { return false; } return true; } template inline bool BoundBox3::IsInBox (const BoundBox3 &rcBB) const { if (rcBB.MinX < this->MinX || rcBB.MaxX > this->MaxX) { return false; } if (rcBB.MinY < this->MinY || rcBB.MaxY > this->MaxY) { return false; } if (rcBB.MinZ < this->MinZ || rcBB.MaxZ > this->MaxZ) { return false; } return true; } template inline bool BoundBox3::IsInBox (const BoundBox2d &rcBB) const { if (rcBB.MinX < this->MinX || rcBB.MaxX > this->MaxX) { return false; } if (rcBB.MinY < this->MinY || rcBB.MaxY > this->MaxY) { return false; } return true; } template inline bool BoundBox3::IsValid () const { return ((MinX <= MaxX) && (MinY <= MaxY) && (MinZ <= MaxZ)); } template inline bool BoundBox3::GetOctantFromVector (const Vector3 &rclVct, OCTANT &rclOctant) const { if (!IsInBox (rclVct)) { return false; } unsigned short usNdx = 0; if (isOnRayS ((MinX + MaxX)/2, MaxX, rclVct.x)) { // left/RIGHT usNdx |= 1; } if (isOnRayS ((MinY + MaxY)/2, MaxY, rclVct.y)) { // down/UP usNdx |= 2; } if (isOnRayS ((MinZ + MaxZ)/2, MaxZ, rclVct.z)) { // back/FRONT usNdx |= 4; } rclOctant = static_cast(usNdx); return true; } template inline BoundBox3 BoundBox3::CalcOctant (typename BoundBox3< Precision >::OCTANT Octant) const { BoundBox3 cOct (*this); switch (Octant) { case OCT_LDB: cOct.MaxX = (cOct.MinX + cOct.MaxX)/2; cOct.MaxY = (cOct.MinY + cOct.MaxY)/2; cOct.MaxZ = (cOct.MinZ + cOct.MaxZ)/2; break; case OCT_RDB: cOct.MinX = (cOct.MinX + cOct.MaxX)/2; cOct.MaxY = (cOct.MinY + cOct.MaxY)/2; cOct.MaxZ = (cOct.MinZ + cOct.MaxZ)/2; break; case OCT_LUB: cOct.MaxX = (cOct.MinX + cOct.MaxX)/2; cOct.MinY = (cOct.MinY + cOct.MaxY)/2; cOct.MaxZ = (cOct.MinZ + cOct.MaxZ)/2; break; case OCT_RUB: cOct.MinX = (cOct.MinX + cOct.MaxX)/2; cOct.MinY = (cOct.MinY + cOct.MaxY)/2; cOct.MaxZ = (cOct.MinZ + cOct.MaxZ)/2; break; case OCT_LDF: cOct.MaxX = (cOct.MinX + cOct.MaxX)/2; cOct.MaxY = (cOct.MinY + cOct.MaxY)/2; cOct.MinZ = (cOct.MinZ + cOct.MaxZ)/2; break; case OCT_RDF: cOct.MinX = (cOct.MinX + cOct.MaxX)/2; cOct.MaxY = (cOct.MinY + cOct.MaxY)/2; cOct.MinZ = (cOct.MinZ + cOct.MaxZ)/2; break; case OCT_LUF: cOct.MaxX = (cOct.MinX + cOct.MaxX)/2; cOct.MinY = (cOct.MinY + cOct.MaxY)/2; cOct.MinZ = (cOct.MinZ + cOct.MaxZ)/2; break; case OCT_RUF: cOct.MinX = (cOct.MinX + cOct.MaxX)/2; cOct.MinY = (cOct.MinY + cOct.MaxY)/2; cOct.MinZ = (cOct.MinZ + cOct.MaxZ)/2; break; } return cOct; } template inline Vector3 BoundBox3::CalcPoint (unsigned short usPoint) const { switch (usPoint) { case TLB: return Vector3(MinX, MinY, MaxZ); case TLF: return Vector3(MaxX, MinY, MaxZ); case TRF: return Vector3(MaxX, MaxY, MaxZ); case TRB: return Vector3(MinX, MaxY, MaxZ); case BLB: return Vector3(MinX, MinY, MinZ); case BLF: return Vector3(MaxX, MinY, MinZ); case BRF: return Vector3(MaxX, MaxY, MinZ); case BRB: return Vector3(MinX, MaxY, MinZ); } return Vector3(); } // NOLINTBEGIN(bugprone-easily-swappable-parameters) template inline void BoundBox3::CalcPlane (unsigned short usPlane, Vector3& rBase, Vector3& rNormal) const // NOLINTEND(bugprone-easily-swappable-parameters) { switch (usPlane) { case LEFT: rBase.Set(MinX, MinY, MaxZ); rNormal.Set(1.0F, 0.0F, 0.0F); break; case RIGHT: rBase.Set(MaxX, MinY, MaxZ); rNormal.Set(1.0F, 0.0F, 0.0F); break; case TOP: rBase.Set(MinX, MaxY, MaxZ); rNormal.Set(0.0F, 1.0F, 0.0F); break; case BOTTOM: rBase.Set(MinX, MinY, MaxZ); rNormal.Set(0.0F, 1.0F, 0.0F); break; case FRONT: rBase.Set(MinX, MinY, MaxZ); rNormal.Set(0.0F, 0.0F, 1.0F); break; case BACK: rBase.Set(MinX, MinY, MinZ); rNormal.Set(0.0F, 0.0F, 1.0F); break; default: break; } } template inline bool BoundBox3::CalcEdge (unsigned short usEdge, Vector3& rcP0, Vector3& rcP1) const { switch (usEdge) { case TLB_TLF: rcP0 = CalcPoint(TLB); rcP1 = CalcPoint(TLF); break; case TLF_TRF: rcP0 = CalcPoint(TLF); rcP1 = CalcPoint(TRF); break; case TRF_TRB: rcP0 = CalcPoint(TRF); rcP1 = CalcPoint(TRB); break; case TRB_TLB: rcP0 = CalcPoint(TRB); rcP1 = CalcPoint(TLB); break; case BLB_BLF: rcP0 = CalcPoint(BLB); rcP1 = CalcPoint(BLF); break; case BLF_BRF: rcP0 = CalcPoint(BLF); rcP1 = CalcPoint(BRF); break; case BRF_BRB: rcP0 = CalcPoint(BRF); rcP1 = CalcPoint(BRB); break; case BRB_BLB: rcP0 = CalcPoint(BRB); rcP1 = CalcPoint(BLB); break; case TLB_BLB: rcP0 = CalcPoint(TLB); rcP1 = CalcPoint(BLB); break; case TLF_BLF: rcP0 = CalcPoint(TLF); rcP1 = CalcPoint(BLF); break; case TRF_BRF: rcP0 = CalcPoint(TRF); rcP1 = CalcPoint(BRF); break; case TRB_BRB: rcP0 = CalcPoint(TRB); rcP1 = CalcPoint(BRB); break; default: return false; // undefined } return true; } template inline bool BoundBox3::IntersectionPoint (const Vector3 &rcVct, const Vector3 &rcVctDir, Vector3& cVctRes, Precision epsilon) const { const unsigned short num = 6; bool rc=false; BoundBox3 cCmpBound(*this); // enlarge bounding box by epsilon cCmpBound.Enlarge(epsilon); // Is point inside? if (cCmpBound.IsInBox (rcVct)) { // test sides for (unsigned short i = 0; (i < num) && (!rc); i++) { rc = IntersectPlaneWithLine(i, rcVct, rcVctDir, cVctRes); if (!cCmpBound.IsInBox(cVctRes)) { rc = false; } if (rc) { // does intersection point lie in desired direction // or was found the opposing side? // -> scalar product of both direction vectors > 0 (angle < 90) rc = ((cVctRes - rcVct) * rcVctDir) >= 0.0; } } } return rc; } template inline bool BoundBox3::IsCutLine (const Vector3& rcBase, const Vector3& rcDir, Precision fTolerance) const { const unsigned short num = 6; Precision fDist; // at first only a rough and quick test by the // Distance of the line to the center of the BB is calculated // and with the maximum diagonal length + fTolerance // will be compared. // Distance between center point and line fDist = (rcDir % (GetCenter() - rcBase)).Length() / rcDir.Length(); if (fDist > (CalcDiagonalLength() + fTolerance)) { return false; } // more detailed test here Vector3 clVectRes; // intersect each face with the line for (unsigned short i = 0; i < num; i++) { if (IntersectPlaneWithLine(i, rcBase, rcDir, clVectRes)) { // Check whether the intersection point is within BB limits + Tolerance switch (i) { case LEFT : // left and right plane case RIGHT : if ((isOnRayW (MinY - fTolerance, MaxY + fTolerance, clVectRes.y) && isOnRayW (MinZ - fTolerance, MaxZ + fTolerance, clVectRes.z))) { return true; } break; case TOP : // top and bottom plane case BOTTOM : if ((isOnRayW (MinX - fTolerance, MaxX + fTolerance, clVectRes.x) && isOnRayW (MinZ - fTolerance, MaxZ + fTolerance, clVectRes.z))) { return true; } break; case FRONT : // front and back plane case BACK : if ((isOnRayW (MinX - fTolerance, MaxX + fTolerance, clVectRes.x) && isOnRayW (MinY - fTolerance, MaxY + fTolerance, clVectRes.y))) { return true; } break; } } } return false; } template inline bool BoundBox3::IsCutPlane (const Vector3 &rclBase, const Vector3 &rclNormal) const { const unsigned short num = 8; if (fabs(GetCenter().DistanceToPlane(rclBase, rclNormal)) < CalcDiagonalLength()) { Precision fD = CalcPoint(CORNER::TLB).DistanceToPlane(rclBase, rclNormal); for (unsigned short i = 1; i < num; i++) { if ((CalcPoint(i).DistanceToPlane(rclBase, rclNormal) * fD) <= 0.0F) { return true; } } } return false; } // NOLINTBEGIN(bugprone-easily-swappable-parameters) template inline bool BoundBox3::IntersectWithLine (const Vector3 & rcBase, const Vector3& rcDir, Vector3& rcP0, Vector3& rcP1) const // NOLINTEND(bugprone-easily-swappable-parameters) { const unsigned short num = 6; Vector3 clVectRes; std::array, num> clVect; unsigned short numIntersect = 0; auto checkIntersect = [&](Base::Vector3 p1, Base::Vector3 p2) { if (isOnRayS(p1.x, p1.y, p1.z) && isOnRayS(p2.x, p2.y, p2.z)) { clVect[numIntersect] = clVectRes; numIntersect++; } }; // cut each face with the line for (unsigned short i = 0; i < num; i++) { if (IntersectPlaneWithLine(i, rcBase, rcDir, clVectRes)) { // check if intersection point is inside switch (i) { case LEFT : // left and right plane case RIGHT : checkIntersect(Vector3{MinY, MaxY, clVectRes.y}, Vector3{MinZ, MaxZ, clVectRes.z}); break; case TOP : // top and bottom plane case BOTTOM : checkIntersect(Vector3{MinX, MaxX, clVectRes.x}, Vector3{MinZ, MaxZ, clVectRes.z}); break; case FRONT : // front and back plane case BACK : checkIntersect(Vector3{MinX, MaxX, clVectRes.x}, Vector3{MinY, MaxY, clVectRes.y}); break; } } } if (numIntersect == 2) { rcP0 = clVect[0]; rcP1 = clVect[1]; return true; } if (numIntersect > 2) { // search two different intersection points for (unsigned short i = 1; i < numIntersect; i++) { if (clVect[i] != clVect[0]) { rcP0 = clVect[0]; rcP1 = clVect[i]; return true; } } } return false; } // NOLINTBEGIN(bugprone-easily-swappable-parameters) template inline bool BoundBox3::IntersectPlaneWithLine (unsigned short usSide, const Vector3& rcBase, const Vector3& rcDir, Vector3& rcP0) const // NOLINTEND(bugprone-easily-swappable-parameters) { Precision value; Vector3 cBase; Vector3 cNormal; Vector3 cDir(rcDir); CalcPlane(usSide, cBase, cNormal); if ((cNormal * cDir) == 0.0F) { return false; // no point of intersection } value = (cNormal * (cBase - rcBase)) / (cNormal * cDir); cDir.Scale(value, value, value); rcP0 = rcBase + cDir; return true; } template inline typename BoundBox3::SIDE BoundBox3::GetSideFromRay (const Vector3 &rclPt, const Vector3 &rclDir) const { Vector3 cIntersection; return GetSideFromRay( rclPt, rclDir, cIntersection); } template inline typename BoundBox3::SIDE BoundBox3::GetSideFromRay (const Vector3 &rclPt, const Vector3 &rclDir, Vector3& rcInt) const { Vector3 cP0; Vector3 cP1; if (!IntersectWithLine(rclPt, rclDir, cP0, cP1)) { return INVALID; } Vector3 cOut; // same orientation if ((cP1-cP0)*rclDir > 0) { cOut = cP1; } else { cOut = cP0; } rcInt = cOut; Precision fMax = 1.0e-3F; //NOLINT SIDE tSide = INVALID; if (fabs(cOut.x - MinX) < fMax) { // left plane fMax = Precision(fabs(cOut.x - MinX)); tSide = LEFT; } if (fabs(cOut.x - MaxX) < fMax) { // right plane fMax = Precision(fabs(cOut.x - MaxX)); tSide = RIGHT; } if (fabs(cOut.y - MinY) < fMax) { // bottom plane fMax = Precision(fabs(cOut.y - MinY)); tSide = BOTTOM; } if (fabs(cOut.y - MaxY) < fMax) { // top plane fMax = Precision(fabs(cOut.y - MaxY)); tSide = TOP; } if (fabs(cOut.z - MinZ) < fMax) { // front plane fMax = Precision(fabs(cOut.z - MinZ)); tSide = FRONT; } if (fabs(cOut.z - MaxZ) < fMax) { // back plane fMax = Precision(fabs(cOut.z - MaxZ)); tSide = BACK; } return tSide; } template inline Vector3 BoundBox3::ClosestPoint (const Vector3 &rclPt) const { Vector3 closest = rclPt; Vector3 center = GetCenter(); Precision devx = closest.x - center.x; Precision devy = closest.y - center.y; Precision devz = closest.z - center.z; Precision halfwidth = (MaxX - MinX) / 2; Precision halfheight = (MaxY - MinY) / 2; Precision halfdepth = (MaxZ - MinZ) / 2; // Move point to be on the nearest plane of the box. if ((fabs(devx) > fabs(devy)) && (fabs(devx) > fabs(devz))) { closest.x = center.x + halfwidth * ((devx < 0.0) ? -1.0 : 1.0); } else if (fabs(devy) > fabs(devz)) { closest.y = center.y + halfheight * ((devy < 0.0) ? -1.0 : 1.0); } else { closest.z = center.z + halfdepth * ((devz < 0.0) ? -1.0 : 1.0); } // Clamp to be inside box. closest.x = std::min(std::max(closest.x, MinX), MaxX); closest.y = std::min(std::max(closest.y, MinY), MaxY); closest.z = std::min(std::max(closest.z, MinZ), MaxZ); return closest; } template inline BoundBox2d BoundBox3::ProjectBox(const ViewProjMethod* proj) const { const int num = 8; BoundBox2d clBB2D; clBB2D.SetVoid(); for (int i = 0; i < num; i++) { Vector3 clTrsPt = (*proj)(CalcPoint(i)); clBB2D.Add(Vector2d(clTrsPt.x, clTrsPt.y)); } return clBB2D; } template inline BoundBox3 BoundBox3::Transformed(const Matrix4D& mat) const { const int num = 8; BoundBox3 bbox; for (int i = 0; i < num; i++) { bbox.Add(mat * CalcPoint(i)); } return bbox; } template inline Vector3 BoundBox3::GetCenter () const { return Vector3((MaxX + MinX) / 2, (MaxY + MinY) / 2, (MaxZ + MinZ) / 2); } template inline Vector3 BoundBox3::GetMinimum () const { return Vector3(MinX, MinY, MinZ); } template inline Vector3 BoundBox3::GetMaximum () const { return Vector3(MaxX, MaxY, MaxZ); } template inline Precision BoundBox3::CalcDiagonalLength () const { return static_cast(sqrt (((MaxX - MinX) * (MaxX - MinX)) + ((MaxY - MinY) * (MaxY - MinY)) + ((MaxZ - MinZ) * (MaxZ - MinZ)))); } template inline void BoundBox3::SetVoid () { MinX = MinY = MinZ = std::numeric_limits::max(); MaxX = MaxY = MaxZ = -std::numeric_limits::max(); } template inline void BoundBox3::Enlarge (Precision fLen) { MinX -= fLen; MinY -= fLen; MinZ -= fLen; MaxX += fLen; MaxY += fLen; MaxZ += fLen; } template inline void BoundBox3::Shrink (Precision fLen) { MinX += fLen; MinY += fLen; MinZ += fLen; MaxX -= fLen; MaxY -= fLen; MaxZ -= fLen; } template inline Precision BoundBox3::LengthX () const { return MaxX - MinX; } template inline Precision BoundBox3::LengthY () const { return MaxY - MinY; } template inline Precision BoundBox3::LengthZ () const { return MaxZ - MinZ; } template inline Precision BoundBox3::Volume () const { if (!IsValid()) { return -1.0; } return LengthX() * LengthY() * LengthZ(); } template inline void BoundBox3::MoveX (Precision value) { MinX += value; MaxX += value; } template inline void BoundBox3::MoveY (Precision value) { MinY += value; MaxY += value; } template inline void BoundBox3::MoveZ (Precision value) { MinZ += value; MaxZ += value; } template inline void BoundBox3::ScaleX (Precision value) { MinX *= value; MaxX *= value; } template inline void BoundBox3::ScaleY (Precision value) { MinY *= value; MaxY *= value; } template inline void BoundBox3::ScaleZ (Precision value) { MinZ *= value; MaxZ *= value; } using BoundBox3f = BoundBox3; using BoundBox3d = BoundBox3; } // namespace Base #endif // BASE_BOUNDBOX_H