// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * 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::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