#include "Includes.h" module OrientedBoundingBox use SMConstants use Utilities use TessellationTypes implicit none integer, parameter :: TASK_THRESHOLD = 10000, LOCAL = 0, GLOBAL =1 real(kind=RP), parameter :: SAFETY_FACTOR = 0.001_RP public :: OBB, sortReal, ElementOBBintersect, LOCAL, GLOBAL ! ! ************************************************** ! Main type for the Convex Hull computations ! ************************************************** type Hull_type type(point_type), dimension(:), allocatable :: Points integer :: NumOfPoints end type ! ! ************************************************** ! Main type for a rectangle ! ************************************************** type rectangle real(kind=rp), dimension(2,4) :: Vertices real(kind=rp), dimension(2) :: Center real(kind=rp), dimension(NDIM) :: normal, t1, t2 real(kind=rp) :: Length, Width, Angle contains procedure :: ComputeVertices end type ! ! ************************************************** ! Main type for the Oriented Bounding Box computations ! ************************************************** type OBB_type type(point_type), dimension(:), allocatable :: Points, HullPoints type(rectangle) :: MBR real(kind=rp), dimension(NDIM,8) :: vertices, LocVertices real(kind=rp), dimension(NDIM,NDIM) :: R, invR real(kind=rp), dimension(NDIM) :: CloudCenter, LocFrameCenter real(kind=rp) :: nMin, nMax integer :: NumOfPoints, center, left, right character(len=LINE_LENGTH) :: filename logical :: verbose, AAB contains procedure :: construct => OBB_construct procedure :: ReadStorePoints => OBB_ReadStorePoints procedure :: ComputeAngle => OBB_ComputeAngle procedure :: SortingNodes => OBB_SortingNodes procedure :: isPointInside => OBB_isPointInside procedure :: ChangeObjsRefFrame => OBB_ChangeObjsRefFrame procedure :: STL_rotate => OBB_STL_rotate procedure :: STL_translate => OBB_STL_translate procedure :: ChangeRefFrame procedure :: ComputeRotationMatrix procedure :: plot => OBB_plot end type type(OBB_type), allocatable :: OBB(:) contains ! ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine computes the determinant of a 3x3 Matrix. ! ------------------------------------------------- function Determinant( Mat ) result( det ) implicit none !-arguments----------------------------- real(kind=rp), dimension(:,:), intent(in) :: Mat !-local-variables------------------------- real(kind=rp) :: det det = Mat(1,1)*( Mat(2,2)*Mat(3,3) - Mat(2,3)*Mat(3,2) ) - & Mat(1,2)*( Mat(2,1)*Mat(3,3) - Mat(2,3)*Mat(3,1) ) + & Mat(1,3)*( Mat(2,1)*Mat(3,2) - Mat(2,2)*Mat(3,1) ) end function Determinant ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine computes the eigenvalues & eigenvectors of a 3x3 matrix. ! For details, see https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf . ! ------------------------------------------------- subroutine ComputeEigenStructure( A, EigenVal, EigenVec1, EigenVec2, EigenVec3 ) use MappedGeometryClass implicit none !-arguments---------------------------------------------- real(kind=rp), dimension(:,:), intent(in) :: A real(kind=rp), intent(out) :: EigenVal real(kind=rp), dimension(NDIM), intent(out) :: EigenVec1, EigenVec2, EigenVec3 !-local-variables------------------------------------------ real(kind=rp), dimension(NDIM,NDIM) :: Ident, B, EigenVecs real(kind=rp), dimension(NDIM) :: EigenVals real(kind=rp) :: p1, p2, p, q, r, phi integer :: i integer, dimension(1) :: min_loc, max_loc p1 = POW2( A(1,2) ) + POW2( A(1,3) ) + POW2( A(2,3) ) ! ! Diagonal Matrix ! -------------- if( AlmostEqual(p1,0.0_RP) ) then EigenVals(1) = A(1,1) EigenVals(2) = A(2,2) EigenVals(3) = A(3,3) min_loc = minloc(EigenVals) max_loc = maxloc(EigenVals) EigenVal = EigenVals(min_loc(1)) EigenVec1 = 0.0_RP; EigenVec2 = 0.0_RP; EigenVec3 = 0.0_RP EigenVec3(min_loc(1)) = 1.0_RP EigenVec1(max_loc(1)) = 1.0_RP EigenVals(min_loc(1)) = huge(1.0_RP) min_loc = minloc(EigenVals) EigenVec2(min_loc(1)) = 1.0_RP return end if q = 0._RP; Ident = 0._RP do i = 1, NDIM q = q + A(i,i) Ident(i,i) = 1.0_RP end do q = q/3.0_RP p2 = POW2( A(1,1) - q ) + POW2( A(2,2) - q ) + POW2( A(3,3) - q ) + 2.0_RP*p1 p = sqrt(p2/6.0_RP) !sqrt{ [tr(A-qI)^2]/6 } ! ! Matrix B has the same eigenstraucture of A since it's translated by a factor = q ! ---------------------------------------------------------------------- B = ( 1.0_RP/p ) * ( A - q * Ident ) r = Determinant(B)/2.0_RP if( r <= -1.0_RP ) then phi = PI/3.0_RP elseif( r >= 1 ) then phi = 0.0_RP else phi = acos(r)/3.0_RP end if ! ! Ordered eigen values \lambda_1 > \lambda_2 > \lambda_3 ! EigenVals(1) = q + 2.0_RP * p * cos(phi) EigenVals(3) = q + 2.0_RP * p * cos(phi + 2.0_RP/3.0_RP*PI ) EigenVals(2) = 3.0_RP * q - EigenVals(1) - Eigenvals(3) ! (A - \lambda_i I)· v_i = 0 ==> v_i*· (A - \lambda_i I)e_j = 0 with e_j unit basis vector ! v_i = (A^j - \lambda_i e_j) x (A^k - \lambda_i e_k) ! v_j = (A^i - \lambda_j e_i) x (A^k - \lambda_j e_k) ! v_k = v_i x v_j call vcross( A(:,2)-EigenVals(1)*(/ 0.0_RP,1.0_RP,0.0_RP /), & A(:,3)-EigenVals(1)*(/ 0.0_RP,0.0_RP,1.0_RP /), & EigenVecs(:,1) ) call vcross( A(:,1)-EigenVals(2)*(/ 1.0_RP,0.0_RP,0.0_RP /), & A(:,3)-EigenVals(2)*(/ 0.0_RP,0.0_RP,1.0_RP /), & EigenVecs(:,2) ) call vcross( EigenVecs(:,1),EigenVecs(:,2), EigenVecs(:,3)) EigenVecs(:,1) = EigenVecs(:,1)/norm2(EigenVecs(:,1)) EigenVecs(:,2) = EigenVecs(:,2)/norm2(EigenVecs(:,2)) EigenVecs(:,3) = EigenVecs(:,3)/norm2(EigenVecs(:,3)) ! ! Saving the last eigenvalue ! ------------------------ EigenVal = EigenVals(3) ! ! Saving the Eigenvectors ! ------------------------ EigenVec1 = EigenVecs(:,1); EigenVec2 = EigenVecs(:,2); EigenVec3 = EigenVecs(:,3) end subroutine ComputeEigenStructure ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------------------------------------------------- ! Subroutine that computes the vertices of a rectangle ! ------------------------------------------------------------------------------------------- subroutine ComputeVertices( this ) implicit none !-arguments---------------------- class(rectangle), intent(inout) :: this this% vertices(:,1) = 0.5_RP*(/ -this% Length, -this% Width /) this% vertices(:,2) = 0.5_RP*(/ this% Length, -this% Width /) this% vertices(:,3) = 0.5_RP*(/ this% Length, this% Width /) this% vertices(:,4) = 0.5_RP*(/ -this% Length, this% Width /) end subroutine ComputeVertices ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------------------------------------------------- ! Subroutine for reading the nodes coming from a .dat file. In the future it will a .stl/.obj file ! ------------------------------------------------------------------------------------------- subroutine OBB_ReadStorePoints( this, stl ) implicit none !-arguments---------------------- class(OBB_type), intent(inout) :: this type(STLfile), intent(in) :: stl !-local.variables------------------- integer :: i, j, n this% NumOfPoints = NumOfVertices*stl% NumOfObjs allocate(this% Points(this% NumOfPoints)) n = 0 associate( Objs => stl% ObjectsList ) do i = 1, stl% NumOfObjs do j = 1, NumOfVertices n = n + 1 this% Points(n)% coords = Objs(i)% vertices(j)% coords this% Points(n)% index = n end do end do end associate end subroutine OBB_ReadStorePoints ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------------------------------------------------- ! Subroutine for plotting the OBB ! ------------------------------------------------------------------------------------------- subroutine OBB_plot( this ) use PhysicsStorage use MPI_Process_Info implicit none !-arguments-------------------------- class(OBB_type), intent(inout) :: this !-local-variables---------------------- integer :: i, funit if( .not. MPI_Process% isRoot ) return funit = UnusedUnit() open(funit,file='IBM/OrientedBoundingBox_'//trim(this% filename)//'.tec', status='unknown') write(funit,"(a28)") 'TITLE = "OrientedBoudingBox"' write(funit,"(a25)") 'VARIABLES = "x", "y", "z"' write(funit,"(a69)") 'ZONE NODES=8, ELEMENTS = 6, DATAPACKING=POINT, ZONETYPE=FETETRAHEDRON' do i = 1, 8 write(funit,'(3E13.5)') Lref*this% vertices(1,i), Lref*this% vertices(2,i), Lref*this% vertices(3,i) end do write(funit,'(4i2)') 1, 2, 3, 4 write(funit,'(4i2)') 1, 5, 8, 4 write(funit,'(4i2)') 5, 6, 7, 8 write(funit,'(4i2)') 2, 3, 7, 6 write(funit,'(4i2)') 4, 8, 7, 3 write(funit,'(4i2)') 1, 2, 6, 5 close(unit=funit) end subroutine OBB_plot subroutine ChangeRefFrame(this, v, FRAME, vNew) implicit none !-arguments---------------------------------------------------- class(OBB_type), intent(inout) :: this real(kind=rp), dimension(:), intent(in) :: v integer, intent(in) :: FRAME real(kind=rp), dimension(NDIM), intent(out) :: vNew !-local-variables---------------------------------------------- real(kind=rp), dimension(NDIM) :: b real(kind=rp), dimension(NDIM,NDIM) :: T, invT T = 0.0_RP T(NDIM,NDIM) = 1.0_RP T(1,1) = cos(this% MBR% Angle); T(2,2) = T(1,1) T(1,2) = sin(this% MBR% Angle); T(2,1) = -T(1,2) invT(:,1) = T(1,:) invT(:,2) = T(2,:) invT(:,3) = T(3,:) select case( FRAME ) case(LOCAL) !~ b = matmul( this% invR,(v - this% CloudCenter)) !~ b(1:2) = b(1:2) - this% MBR% Center !~ vNew = matmul(invR,b) b = matmul( this% R,(v - this% CloudCenter)) b(1:2) = b(1:2) - this% MBR% Center vNew = matmul(T,b) case(GLOBAL) !~ b = matmul(R,v) !~ b(1:2) = b(1:2) + this% MBR% center !~ vNew = this% CloudCenter + matmul(this% R,b) b = matmul(invT,v) b(1:2) = b(1:2) + this% MBR% center vNew = this% CloudCenter + matmul(this% invR,b) end select end subroutine ChangeRefFrame ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine computes a rotation Matrix assuming a starting orthonormal ! base (1,0,0);(0,1,0);(0,0,1) and the final orthonormal one u, v, w ! ------------------------------------------------- subroutine ComputeRotationMatrix( this, u, v, w ) implicit none !-arguments------------------------------------------------ class(OBB_type), intent(inout) :: this real(kind=rp), dimension(NDIM), intent(in) :: u, v, w this% R(1,:) = (/ u(1), u(2), u(3) /) this% R(2,:) = (/ v(1), v(2), v(3) /) this% R(3,:) = (/ w(1), w(2), w(3) /) this% invR(:,1) = this% R(1,:) this% invR(:,2) = this% R(2,:) this% invR(:,3) = this% R(3,:) end subroutine ComputeRotationMatrix ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine computes the Oriented Bounding Box. ! ------------------------------------------------- subroutine OBB_construct( this, stl, isPlot, AAB ) implicit none !-arguments----------------------------------- class(OBB_type), intent(inout) :: this type(STLfile), intent(in) :: stl logical, intent(in) :: isPlot, AAB !-local-variables----------------------------- type(Hull_type) :: Hull real(kind=rp) :: EigenVal integer :: i this% AAB = AAB ! ! Reading the data ! --------------- call this% ReadStorePoints( stl ) ! ! Computing center of the points cloud ! --------------------------------- this% CloudCenter = ComputeCentroid( this ) if( this% AAB ) then this% MBR% t1 = (/ 1.0_RP, 0.0_RP, 0.0_RP /) this% MBR% t2 = (/ 0.0_RP, 1.0_RP, 0.0_RP /) this% MBR% normal = (/ 0.0_RP, 0.0_RP, 1.0_RP /) else ! ! Diagonalization of the cloud ! ------------------------- call PointsCloudDiagonalization( this, EigenVal, this% MBR% t1, this% MBR% t2, this% MBR% normal ) end if ! ! Computing rotation matrix ! ------------------------ call this% ComputeRotationMatrix( this% MBR% t1, this% MBR% t2, this% MBR% normal ) ! ! Point projection ! --------------- call ProjectPointsOnPlane( this ) ! ! Computing convex hull ! --------------------- call ConvexHull( Hull, this ) ! ! Minimum Bounding Rectangle ! --------------------------- call RotatingCalipers( Hull, this% MBR% Width, this% MBR% Length, this% MBR% Angle, this% MBR% Center, this% AAB ) ! ! Setting vertices of the MBR ! ----------------------------- call this% MBR% ComputeVertices() ! ! Extrusion ! --------- call ExtrudeMBR( this ) if( isPlot ) call this% plot() if(allocated(this% HullPoints)) deallocate(this% HullPoints) allocate( this% HullPoints(Hull% NumOfPoints) ) do i = 1, Hull% NumOfPoints this% HullPoints(i) = Hull% Points(i) end do deallocate(this% Points, Hull% Points) end subroutine OBB_construct ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This function computes the orientation of the rotation of 2D points. ! Rotation := 1 -> Clockwise ! Rotation := 2 -> Counterclockwise ! Rotation := 0 -> Aligned ! ------------------------------------------------- integer function RotationType( Point1, Point2, Point3 ) result( Rotation ) implicit none !-arguments----------------------------------- type(point_type), intent(in) :: Point1, Point2, Point3 !-local-variables------------------------------- real(kind=rp), dimension(NDIM-1) :: b, c real(kind=rp) :: angle b = Point3% coords(1:2) - Point2% coords(1:2); c = Point2% coords(1:2) - Point1% coords(1:2) angle = b(1)*c(2) - b(2)*c(1) if( AlmostEqual(angle,0.0_RP) ) then Rotation = 0 elseif( angle .gt. 0.0_RP ) then Rotation = 1 else Rotation = 2 end if end function RotationType ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------------------------- ! This function returns the index of the left most point of a cloud. ! ------------------------------------------------------------------- subroutine OBB_ComputeAngle( this, LowestIndex ) implicit none !-arguments------------------------ class(OBB_type), intent(inout) :: this integer, intent(in) :: LowestIndex !-local-variables-------------------- real(kind=rp), dimension(NDIM) :: v integer :: i !$omp parallel shared(this, LowestIndex,i) !$omp do schedule(runtime) private(v) do i = 1, this% NumOfPoints if( i .eq. LowestIndex ) then this% Points(i)% theta = -2.0_RP cycle end if v = this% Points(i)% coords-this% Points(LowestIndex)% coords if( almostEqual(norm2(v(1:2)),0.0_RP) ) then this% Points(i)% theta = -1.0_RP cycle end if this% Points(i)% theta = acos(v(1)/norm2(v(1:2))) end do !$omp end do !$omp end parallel end subroutine OBB_ComputeAngle ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------------------------ ! This function returns the index of the left most point of a cloud. ! ------------------------------------------------------------------ subroutine ComputeHullExtremePoint( OBB, LowestIndex ) implicit none !-arguments------------------------ class(OBB_type), intent(inout) :: OBB integer, intent(out) :: LowestIndex !-local-variables-------------------- type(point_type) :: LowestPoint integer :: i LowestPoint = OBB% Points(1) do i = 2, OBB% NumOfPoints if( OBB% Points(i)% coords(2) .lt. LowestPoint% coords(2) ) then LowestPoint = OBB% Points(i) elseif( AlmostEqual( OBB% Points(i)% coords(2), LowestPoint% coords(2)) ) then if( OBB% Points(i)% coords(1) .lt. LowestPoint% coords(1) ) then LowestPoint = OBB% Points(i) end if end if end do LowestIndex = LowestPoint% index end subroutine ComputeHullExtremePoint ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine orders the nodes according to the angle theta ! ------------------------------------------------ subroutine OBB_SortingNodes( this, left, right ) use StopwatchClass implicit none !-arguments-------------------------------------------------------------- class(OBB_type), intent(inout) :: this integer, intent(in) :: left, right !-local-variables-------------------------------------------------------- integer :: i call sort( this% Points(:)% theta, this% Points(:)% index, this% Points(:)% coords(1), & this% Points(:)% coords(2), this% Points(:)% coords(3), left, right ) do i = 1, this% NumOfPoints-1 if( almostEqual(this% Points(i+1)% theta, this% Points(i)% theta) ) this% Points(i+1)% delete = .true. end do end subroutine OBB_SortingNodes ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine sort the elements of a from the lowest to the grates. b saves the old indeces. ! ------------------------------------------------ recursive subroutine sort( a, b, coordx, coordy, coordz, left, right ) implicit none !-arguments------------------------------------------------- real(kind=rp), dimension(:), intent(inout) :: a integer, dimension(:), intent(inout) :: b real(kind=rp), dimension(:), intent(inout) :: coordx, coordy, coordz integer, intent(in) :: left, right !-local-variables------------------------------------------- real(kind=rp) :: x, t integer :: i, j, ind, left_cycle, & right_cycle if( left .ge. right ) return x = a( (left + right)/2 ) i = left; j = right do do while( a(i) .lt. x ) i = i+1 end do do while( x .lt. a(j) ) j = j-1 end do if( i .ge. j ) exit t = a(i); a(i) = a(j); a(j) = t ind = b(i); b(i) = b(j); b(j) = ind t = coordx(i); coordx(i) = coordx(j); coordx(j) = t t = coordy(i); coordy(i) = coordy(j); coordy(j) = t t = coordz(i); coordz(i) = coordz(j); coordz(j) = t i = i+1 j = j-1 end do call sort( a, b, coordx, coordy, coordz, left, i-1 ) call sort( a, b, coordx, coordy, coordz, j+1, right ) end subroutine sort ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine sort the elements of a from the lowest to the grates. b saves the old indeces. ! ------------------------------------------------ recursive subroutine sortReal(a, left, right) implicit none !-arguments------------------------------------------------- real(kind=rp), dimension(:), intent(inout) :: a integer, intent(in) :: left, right !-local-variables------------------------------------------- real(kind=rp) :: x, t integer :: i, j, left_cycle, right_cycle if( left .ge. right ) return x = a( (left + right)/2 ) i = left; j = right do do while( a(i) .lt. x ) i = i+1 end do do while( x .lt. a(j) ) j = j-1 end do if( i .ge. j ) exit t = a(i); a(i) = a(j); a(j) = t i = i+1 j = j-1 end do left_cycle = (i-1)-left right_cycle = right-(j+1) !$omp task shared(a,left,i) if(left_cycle > TASK_THRESHOLD) call sortReal( a, left, i-1 ) !$omp end task !$omp task shared(a,j,right) if(right_cycle > TASK_THRESHOLD) call sortReal( a, j+1, right ) !$omp end task !$omp taskwait end subroutine sortReal ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This function computes the Centroid of the points cloud. ! ------------------------------------------------- function ComputeCentroid( OBB ) result( CloudCenter ) implicit none !-arguments----------------------------------- type(OBB_type), intent(in) :: OBB !-local-variables------------------------------- real(kind=rp), dimension(NDIM) :: CloudCenter integer :: i CloudCenter = 0.0_RP do i = 1, OBB% NumOfPoints CloudCenter = CloudCenter + OBB% Points(i)% coords end do CloudCenter = CloudCenter/OBB% NumOfPoints end function ComputeCentroid ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine computes the covariance matrix of a clouds of points given its center. ! ------------------------------------------------- subroutine CovarianceMatrix( OBB, CovMat ) implicit none !-arguments---------------------------------------------- type(OBB_type), intent(in) :: OBB real(kind=rp), dimension(NDIM,NDIM), intent(out) :: CovMat !-local-variables---------------------------------------------- real(kind=rp), dimension(NDIM) :: d integer :: i, j, k CovMat = 0.0_RP do i = 1, OBB% NumOfPoints d = OBB% Points(i)% coords - OBB% CloudCenter do k = 1, NDIM; do j = 1, NDIM CovMat(k,j) = CovMat(k,j) + d(k) * d(j) end do; end do end do CovMat = CovMat/OBB% NumOfPoints end subroutine CovarianceMatrix ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine computes the projection of the points cloud on a plane defined by a normal vector. ! ------------------------------------------------- subroutine ProjectPointsOnPlane( OBB ) use MappedGeometryClass implicit none !-arguments---------------------------------------------------------- type(OBB_type), intent(inout) :: OBB !-local-variables------------------------------------------------------ real(kind=rp), dimension(NDIM) :: d real(kind=rp) :: N_point integer :: i ! ! Initialization of nMin and nMax, i.e. of the min and max normal distance of the cloud w.r.t ! the projection plane ! ------------------------------------------------------------------------------- OBB% nMax = -huge(1.0_RP); OBB% nMin = huge(1.0_RP) do i = 1, OBB% NumOfPoints d = OBB% Points(i)% coords - OBB% CloudCenter N_Point = vdot( d, OBB% MBR% normal ) OBB% Points(i)% coords = (/ vdot(d, OBB% MBR% t1), vdot(d, OBB% MBR% t2), 0.0_RP /) OBB% nMax = max(OBB% nMax,N_point) OBB% nMin = min(OBB% nMin,N_point) end do OBB% nMax = (1.0_RP+SAFETY_FACTOR)*OBB% nMax OBB% nMin = (1.0_RP+SAFETY_FACTOR)*OBB% nMin end subroutine ProjectPointsOnPlane ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine computes the eigenstructure of the OBB. ! ------------------------------------------------- subroutine PointsCloudDiagonalization( OBB, EigenVal, EigenVec1, EigenVec2, EigenVec3 ) implicit none !-arguments--------------------------------------------- type(OBB_type), intent(inout) :: OBB real(kind=rp), intent(out) :: EigenVal real(kind=rp), dimension(NDIM), intent(out) :: EigenVec1, EigenVec2, EigenVec3 !-local-variables---------------------------- real(kind=rp), dimension(NDIM,NDIM) :: CovMat call CovarianceMatrix( OBB, CovMat ) call ComputeEigenStructure( CovMat, EigenVal, EigenVec1, EigenVec2, EigenVec3 ) if( any(isNan(EigenVec1)) .or. any(isNan(EigenVec2)) .or. any(isNan(EigenVec3)) ) then EigenVec1 = (/ 1.0_RP, 0.0_RP, 0.0_RP /) EigenVec2 = (/ 0.0_RP, 1.0_RP, 0.0_RP /) EigenVec3 = (/ 0.0_RP, 0.0_RP, 1.0_RP /) OBB% AAB = .true. end if end subroutine PointsCloudDiagonalization ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine computes the Convex Hull of a set of points. ! The points are ordered according to the value of the x-coord. ! ------------------------------------------------- subroutine ConvexHull( Hull, OBB ) implicit none !-arguments------------------------------------------------ type(Hull_type), intent(inout) :: Hull type(OBB_type), intent(inout) :: OBB !-local-variables-------------------------------------------- type(point_type) ,pointer :: p, p1 integer :: i, LowestIndex, start type(PointLinkedList) :: convPoints ! ! Compute Left most point & set Hull's head ! ----------------------------------------- call ComputeHullExtremePoint( OBB, LowestIndex ) ! ! Compute the angles ! ------------------ call OBB% ComputeAngle( LowestIndex ) ! ! Sorting points according to their angle ! --------------------------------------- call OBB% SortingNodes(1, OBB% NumOfPoints ) convPoints = PointLinkedList() call convPoints% add( OBB% Points(1) ) !Check duplicate if( almostEqual(OBB% Points(1)% coords(1), OBB% Points(2)% coords(1)) .and. & almostEqual(OBB% Points(1)% coords(2), OBB% Points(2)% coords(2)) ) OBB% Points(2)% delete = .true. do i = 2, OBB% NumOfPoints if( .not. OBB% Points(i)% delete ) then call convPoints% add( OBB% Points(i) ) start = i+1 exit end if end do Hull% NumOfPoints = 2 p => convPoints% head p1 => convPoints% head% next do i = start, OBB% NumOfPoints if( OBB% Points(i)% delete ) cycle do while( RotationType(p, p1, OBB% Points(i) ) .eq. 1 ) p => p% prev; p1 => p1% prev call convPoints% RemoveLast() Hull% NumOfPoints = Hull% NumOfPoints - 1 end do call convPoints% add( OBB% Points(i) ) p1 => convPoints% head% prev; p => p1% prev Hull% NumOfPoints = Hull% NumOfPoints + 1 end do allocate(Hull% Points(Hull% NumOfPoints)) p => convPoints% head do i = 1, Hull% NumOfPoints Hull% Points(i) = p p => p% next end do call convPoints% destruct() end subroutine ConvexHull ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine computes the 2-pi normalized-angle between 2 vectors. ! ------------------------------------------------- real(kind=rp) function ComputingAngle( a, b ) result( Angle ) implicit none !-arguments------------------------------------ real(kind=rp), dimension(:), intent(in) :: a, b !-local-variables------------------------------ real(kind=rp) :: theta1, theta2 theta1 = atan2( a(2), a(1) ) theta2 = atan2( b(2), b(1) ) Angle = theta2 - theta1 Angle = mod(Angle+2.0_RP*PI, 2.0_RP*PI) end function ComputingAngle ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine counterclockwise-rotates 2D vector. ! ------------------------------------------------- subroutine RotateVector( RotVec, theta ) implicit none !-arguments------------------------------------------ real(kind=rp), dimension(:), intent(inout) :: RotVec real(kind=rp), intent(in) :: theta !-local-variables------------------------------------ real(kind=rp), dimension(size(RotVec)) :: TempVec ! ! Temporary vector ! ---------------- TempVec = RotVec RotVec(1) = TempVec(1)*cos(theta) - TempVec(2)*sin(theta) RotVec(2) = TempVec(1)*sin(theta) + TempVec(2)*cos(theta) end subroutine RotateVector ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine the parameter t_s use for defining the position of the point S on a line. ! x_s = x_p + t_s(x_q-x_p) ! y_s = y_p + t_s(y_q-y_p) ! ------------------------------------------------- function CurvAbscissa( p1, p2, p3 ) result( t ) implicit none !-arguments-------------------------------- type(point_type), intent(in) :: p1, p2, p3 !-local-variables-------------------------- real(kind=rp) :: t t = ( p3% coords(1) - p1% coords(1) )*( p2% coords(1) - p1% coords(1) ) + & ( p3% coords(2) - p1% coords(2) )*( p2% coords(2) - p1% coords(2) ) t = t/( POW2(p2% coords(1) - p1% coords(1)) + POW2(p2% coords(2) - p1% coords(2)) ) end function CurvAbscissa ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine computes the distance of a point (p3) from a line defined by ! the points p1 and p2 ! ------------------------------------------------- real(kind=rp) function ComputeWidth( p1, p2, p3 ) result( width ) implicit none !-arguments-------------------------------- type(point_type), intent(in) :: p1, p2, p3 !-local-variables-------------------------- real(kind=rp), dimension(NDIM-1) :: vec real(kind=rp) :: t ! ! Computing t-values for the point p3 ! -------------------------------- t = CurvAbscissa( p1, p2, p3 ) ! ! Position of the projection of p3 point on the line p1-p2 ! ------------------------------------------------ vec = p1% coords(1:2) + t *( p2% coords(1:2)- p1% coords(1:2) ) ! ! Computing the distance ! --------------------- width = norm2( p3% coords(1:2) - vec(1:2) ) end function ComputeWidth ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine computes the rotating calipers method. ! ------------------------------------------------- subroutine RotatingCalipers( Hull, rectWidth, rectLength, rectAngle, rectCenter, AAB ) implicit none !-arguments----------------------------------------------------------------- type(Hull_type), intent(inout) :: Hull real(kind=rp), dimension(:), intent(out) :: rectCenter real(kind=rp), intent(out) :: rectWidth, rectLength, rectAngle logical, intent(in) :: AAB !-local-variables------------------------------------------------------------- real(kind=rp), dimension(NDIM-1) :: Caliper1, Caliper2, v1, v2, PointMin, PointMax integer, dimension(1) :: loc, MinIndex, MaxIndex real(kind=rp) :: RotAngle, width, minwidth, theta1, theta2, dtheta real(kind=rp), dimension(Hull% NumOfPoints) :: x, y integer :: indexMin1, indexMin2, indexMax1, indexMax2 if( AAB ) then rectAngle = 0.0_RP else minwidth = huge(1.0_RP); rectAngle = 0.0_RP; RotAngle = 0.0_RP Caliper1 = (/ 1.0_RP, 0.0_RP/) Caliper2 = (/ -1.0_RP, 0.0_RP/) MinIndex = minloc(Hull% Points(:)% coords(2)) MaxIndex = maxloc(Hull% Points(:)% coords(2)) indexMin1 = MinIndex(1); indexMax1 = MaxIndex(1) do while( RotAngle .lt. PI ) indexMin2 = mod(indexMin1, Hull% NumOfPoints) + 1 indexMax2 = mod(indexMax1, Hull% NumOfPoints) + 1 ! ! Looping Counterclockwise from y-min point (p1) ! and from y-max point (p2). ! ------------------------------------------- v1 = Hull% Points(indexMin2)% coords(1:2) - Hull% Points(indexMin1)% coords(1:2) v2 = Hull% Points(indexMax2)% coords(1:2) - Hull% Points(indexMax1)% coords(1:2) ! ! Computing the angles between calipers and vectors v1,v2 ! --------------------------------------------------- theta1 = ComputingAngle( Caliper1, v1 ) theta2 = ComputingAngle( Caliper2, v2 ) dtheta = min(theta1,theta2) loc = minloc((/ theta1,theta2 /)) call RotateVector(Caliper1, dtheta) call RotateVector(Caliper2, dtheta) RotAngle = RotAngle + dtheta if( loc(1) .eq. 1 ) then width = ComputeWidth( Hull% Points(indexMin1), Hull% Points(indexMin2), Hull% Points(indexMax1) ) indexMin1 = mod(indexMin1, Hull% NumOfPoints) + 1 else width = ComputeWidth( Hull% Points(indexMax1), Hull% Points(indexMax2), Hull% Points(indexMin1) ) indexMax1 = mod(indexMax1, Hull% NumOfPoints) + 1 end if if( width .lt. minwidth ) then minwidth = width rectAngle = RotAngle end if end do end if rectCenter(1) = sum(Hull% Points(:)% coords(1))/Hull% NumOfPoints rectCenter(2) = sum(Hull% Points(:)% coords(2))/Hull% NumOfPoints ! ! Initialization of the extreme points ! --------------------------------------- PointMax = -huge(1.0_RP); PointMin = huge(1.0_RP) ! ! ClockWise rotation looking for extreme points ! --------------------------------------------- x = (Hull% Points(:)% coords(1)-rectCenter(1))*cos(rectAngle) + & (Hull% Points(:)% coords(2)-rectCenter(2))*sin(rectAngle) y = -(Hull% Points(:)% coords(1)-rectCenter(1))*sin(rectAngle) + & (Hull% Points(:)% coords(2)-rectCenter(2))*cos(rectAngle) PointMax(1) = maxval(x) PointMax(2) = maxval(y) PointMin(1) = minval(x) PointMin(2) = minval(y) ! ! Minimum Bounding Rectangle properties ! ------------------------------------- rectLength = PointMax(1) - PointMin(1); rectWidth = PointMax(2) - PointMin(2); !Safety factor rectLength = (1.0_RP+SAFETY_FACTOR)*rectLength rectWidth = (1.0_RP+SAFETY_FACTOR)*rectWidth rectCenter = 0.5_RP* (/ PointMax(1) + PointMin(1),PointMax(2) + PointMin(2) /) call RotateVector(rectCenter,rectAngle) rectCenter(1) = rectCenter(1) + sum(Hull% Points(:)% coords(1))/Hull% NumOfPoints rectCenter(2) = rectCenter(2) + sum(Hull% Points(:)% coords(2))/Hull% NumOfPoints end subroutine RotatingCalipers ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This subroutine extrude the minimum bounding rectangle to get the full OBB. ! MBR -> Minimum Bounding Rectangle ! MRB := ( rectWidth, rectLength, rectAngle, rectCenter ) ! ------------------------------------------------- subroutine ExtrudeMBR( OBB ) implicit none !-arguments------------------------------------------------- type(OBB_type), intent(inout) :: OBB !-local-variables------------------------------------------- integer :: i ! ! Extrusion ! --------- do i = 1, 4 call OBB% ChangeRefFrame((/ OBB% MBR% vertices(1,i), OBB% MBR% vertices(2,i), OBB% nMin /), GLOBAL, OBB% vertices(:,i) ) call OBB% ChangeRefFrame((/ OBB% MBR% vertices(1,i), OBB% MBR% vertices(2,i), OBB% nMax /), GLOBAL, OBB% vertices(:,i+4) ) OBB% LocVertices(:,i) = (/ OBB% MBR% vertices(:,i), OBB% nMin /) OBB% LocVertices(:,i+4) = (/ OBB% MBR% vertices(:,i), OBB% nMax /) end do OBB% LocFrameCenter = OBB% CloudCenter + matmul(OBB% R, (/ OBB% MBR% Center,0.0_RP/)) end subroutine ExtrudeMBR ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This function check if a point is inside the OBB or not ! ------------------------------------------------- function OBB_isPointInside( this, coords, coeff ) result( isInsideOBB ) implicit none !-arguments------------------------------------ class(OBB_type), intent(inout) :: this real(kind=rp), dimension(:), intent(in) :: coords real(kind=rp), intent(in) :: coeff logical :: isInsideOBB !-local-variables-------------------------------- real(kind=rp), dimension(NDIM) :: Point real(kind=rp) :: Multcoeff optional :: coeff if( present(coeff) ) then Multcoeff = coeff else Multcoeff = 1.0_RP end if call this% ChangeRefFrame(coords, LOCAL, Point) isInsideOBB = .false. !check x y z if( isInsideBox(Point, Multcoeff*this% LocVertices) ) isInsideOBB = .true. end function OBB_isPointInside subroutine OBB_ChangeObjsRefFrame( this, objs, FRAME ) implicit none !-arguments--------------------------------------------- class(OBB_type), intent(inout) :: this type(Object_type), intent(inout) :: objs(:) integer, intent(in) :: FRAME !-local-variables--------------------------------------- integer :: i, j !$omp parallel !$omp do schedule(runtime) private(j) do i = 1, size(objs) do j = 1, size(objs(i)% vertices) call this% ChangeRefFrame( objs(i)% vertices(j)% coords, FRAME, objs(i)% vertices(j)% coords ) end do end do !$omp end do !$omp end parallel end subroutine OBB_ChangeObjsRefFrame subroutine OBB_STL_rotate( this, stl, surface ) implicit none !-arguments----------------------------- class(OBB_type), intent(inout) :: this type(STLfile), intent(inout) :: stl logical, intent(in) :: surface !-local-variables----------------------- real(kind=RP) :: meanCoords(NDIM), meanCoordsNew(NDIM), shiftVec(NDIM) integer :: i, j, NumOfPoints, Vertices NumOfPoints = 3*stl% NumOfObjs; meanCoordsNew = 0.0_RP; meanCoords = 0.0_RP if( surface ) then Vertices = NumOfVertices + 4 else Vertices = NumOfVertices end if !$omp parallel !$omp do schedule(runtime) private(j) do i = 1, stl% NumOfObjs do j = 1, Vertices stl% ObjectsList(i)% vertices(j)% coords = matmul( stl% rotationMatrix, stl% ObjectsList(i)% vertices(j)% coords - stl% rotationCenter ) end do end do !$omp end do !$omp end parallel !$omp parallel !$omp do schedule(runtime) private(j) do i = 1, stl% NumOfObjs do j = 1, Vertices ! going back to the original reference frame stl% ObjectsList(i)% vertices(j)% coords = stl% ObjectsList(i)% vertices(j)% coords + stl% rotationCenter end do end do !$omp end do !$omp end parallel end subroutine OBB_STL_rotate subroutine OBB_STL_translate( this, stl, surface ) implicit none !-arguments----------------------------- class(OBB_type), intent(inout) :: this type(STLfile), intent(inout) :: stl logical, intent(in) :: surface !-local-variables----------------------- real(kind=RP) :: meanCoords(NDIM) integer :: i, j, NumOfPoints, Vertices meanCoords = 0.0_RP; NumOfPoints = 0 if( surface ) then Vertices = NumOfVertices + 4 else Vertices = NumOfVertices end if !$omp parallel !$omp do schedule(runtime) private(j) do i = 1, stl% NumOfObjs do j = 1, Vertices stl% ObjectsList(i)% vertices(j)% coords(stl% motionAxis) = stl% ObjectsList(i)% vertices(j)% coords(stl% motionAxis) + stl% ds end do end do !$omp end do !$omp end parallel end subroutine OBB_STL_translate ! !///////////////////////////////////////////////////////////////////////////////////////////// ! ! ------------------------------------------------- ! This function check if a point is inside a generic box ! ------------------------------------------------- logical function isInsideBox( Point, vertices, equal ) result( isInside ) implicit none real(kind=rp), dimension(:), intent(in) :: Point real(kind=rp), dimension(:,:), intent(in) :: vertices logical, intent(in) :: equal optional :: equal isInside = .false. if( present(equal) ) then if( .not. equal ) then if( (Point(1) > vertices(1,1) .and. Point(1) < vertices(1,7)) .and. & (Point(2) > vertices(2,1) .and. Point(2) < vertices(2,7)) .and. & (Point(3) > vertices(3,1) .and. Point(3) < vertices(3,7)) ) then isInside = .true. end if return end if end if if( (Point(1) >= vertices(1,1) .and. Point(1) <= vertices(1,7)) .and. & (Point(2) >= vertices(2,1) .and. Point(2) <= vertices(2,7)) .and. & (Point(3) >= vertices(3,1) .and. Point(3) <= vertices(3,7)) ) isInside = .true. end function isInsideBox logical function ElementOBBintersect( corners, coeff, STLNum ) result( intersect ) implicit none !-arguments---------------------------------------------- real(kind=RP), intent(in) :: corners(NDIM,8), coeff integer, intent(in) :: STLNum !-local-variables---------------------------------- real(kind=RP) :: Loccorners(NDIM,8), LocVertices(NDIM,8) integer :: i intersect = .false. do i = 1, 8 call OBB(STLNum)% ChangeRefFrame(corners(:,i),LOCAL,Loccorners(:,i)) end do LocVertices = coeff*OBB(STLNum)% LocVertices if( (maxval(Loccorners(1,:)) >= minval(LocVertices(1,:)) .and. maxval(LocVertices(1,:)) >= minval(Loccorners(1,:)) ) .and. & (maxval(Loccorners(2,:)) >= minval(LocVertices(2,:)) .and. maxval(LocVertices(2,:)) >= minval(Loccorners(2,:)) ) .and. & (maxval(Loccorners(3,:)) >= minval(LocVertices(3,:)) .and. maxval(LocVertices(3,:)) >= minval(Loccorners(3,:)) ) ) then intersect = .true. end if end function ElementOBBintersect logical function AABTriangleIntersection( v0, v1, v2 ) result( Intersect ) use MappedGeometryClass implicit none real(kind=RP), intent(in) :: v0(NDIM), v1(NDIM), v2(NDIM) !-local-variables---------------------------------------------------------------------- real(kind=RP) :: f0(NDIM), f1(NDIM), f2(NDIM), p0, p1, p2, & r, d, normal(NDIM), box_extents(NDIM), & a00(NDIM), a01(NDIM), a02(NDIM), & a10(NDIM), a11(NDIM), a12(NDIM), & a20(NDIM), a21(NDIM), a22(NDIM) Intersect = .false. f0 = v1 - v0; f1 = v2 - v1; f2 = v0 - v2; box_extents =(/1.0_RP,1.0_RP,1.0_RP/) a00 = (/0.0_RP, -f0(IZ), f0(IY)/) p0 = dot_product(v0, a00) p1 = dot_product(v1, a00) p2 = dot_product(v2, a00) r = box_extents(IY) * abs(f0(IZ)) + box_extents(IZ) * abs(f0(IY)) if (max(-max(p0, p1, p2), min(p0, p1, p2)) > r ) return a01 = (/0.0_RP, -f1(IZ), f1(IY)/) p0 = dot_product(v0, a01) p1 = dot_product(v1, a01) p2 = dot_product(v2, a01) r = box_extents(IY) * abs(f1(IZ)) + box_extents(IZ) * abs(f1(IY)) if (max(-max(p0, p1, p2), min(p0, p1, p2)) > r ) return a02 = (/0.0_RP, -f2(IZ), f2(IY)/) p0 = dot_product(v0, a02) p1 = dot_product(v1, a02) p2 = dot_product(v2, a02) r = box_extents(IY) * abs(f2(IZ)) + box_extents(IZ) * abs(f2(IY)) if (max(-max(p0, p1, p2), min(p0, p1, p2)) > r ) return a10 = (/f0(IZ), 0.0_RP, -f0(IX)/) p0 = dot_product(v0, a10) p1 = dot_product(v1, a10) p2 = dot_product(v2, a10) r = box_extents(IX) * abs(f0(IZ)) + box_extents(IZ) * abs(f0(IX)) if (max(-max(p0, p1, p2), min(p0, p1, p2)) > r ) return a11 = (/f1(IZ), 0.0_RP, -f1(IX)/) p0 = dot_product(v0, a11) p1 = dot_product(v1, a11) p2 = dot_product(v2, a11) r = box_extents(IX) * abs(f1(IZ)) + box_extents(IZ) * abs(f1(IX)) if (max(-max(p0, p1, p2), min(p0, p1, p2)) > r )return a12 = (/f2(IZ), 0.0_RP, -f2(IX)/) p0 = dot_product(v0, a12) p1 = dot_product(v1, a12) p2 = dot_product(v2, a12) r = box_extents(IX) * abs(f2(IZ)) + box_extents(IZ) * abs(f2(IX)) if (max(-max(p0, p1, p2), min(p0, p1, p2)) > r ) return a20 = (/-f0(IY), f0(IX), 0.0_RP/) p0 = dot_product(v0, a20) p1 = dot_product(v1, a20) p2 = dot_product(v2, a20) r = box_extents(IX) * abs(f0(IY)) + box_extents(IY) * abs(f0(IX)) if (max(-max(p0, p1, p2), min(p0, p1, p2)) > r ) return a21 = (/-f1(IY), f1(IX), 0.0_RP/) p0 = dot_product(v0, a21) p1 = dot_product(v1, a21) p2 = dot_product(v2, a21) r = box_extents(IX) * abs(f1(IY)) + box_extents(IY) * abs(f1(IX)) if (max(-max(p0, p1, p2), min(p0, p1, p2)) > r) return a22 = (/-f2(IY), f2(IX), 0.0_RP/) p0 = dot_product(v0, a22) p1 = dot_product(v1, a22) p2 = dot_product(v2, a22) r = box_extents(IX) * abs(f2(IY)) + box_extents(IY) * abs(f2(IX)) if (max(-max(p0, p1, p2), min(p0, p1, p2)) > r ) return if (max(v0(IX), v1(IX), v2(IX)) < -box_extents(IX) .or. min(v0(IX), v1(IX), v2(IX)) > box_extents(IX) ) return if (max(v0(IY), v1(IY), v2(IY)) < -box_extents(IY) .or. min(v0(IY), v1(IY), v2(IY)) > box_extents(IY) ) return if (max(v0(IZ), v1(IZ), v2(IZ)) < -box_extents(IZ) .or. min(v0(IZ), v1(IZ), v2(IZ)) > box_extents(IZ) ) return call vcross(f0, f1, normal) !normal = normal/norm2(normal) d = dot_product(normal, v0) r = box_extents(IX) * abs(normal(IX)) + box_extents(IY) * abs(normal(IY)) + box_extents(IZ) * abs(normal(IZ)) if ( abs(d) > r ) return Intersect = .true. end function AABTriangleIntersection subroutine RayAABIntersection( P, direction, corners, intersect, t ) implicit none real(kind=RP), intent(in) :: P(NDIM), direction(NDIM), corners(:,:) logical, intent(out) :: intersect real(kind=RP), intent(out) :: t integer :: i real(kind=RP) :: a_min(NDIM), a_max(NDIM), tmin, tmax, & t1, t2, tTemp, ood !v_max(IX) = maxval(corners(IX,:)) !v_max(IY) = maxval(corners(IY,:)) !v_max(IZ) = maxval(corners(IZ,:)) !v_min(IX) = minval(corners(IX,:)) !_min(IY) = minval(corners(IY,:)) !v_min(IZ) = minval(corners(IZ,:)) !den(IX) = 1.0_RP/direction(IX) !den(IY) = 1.0_RP/direction(IY) !den(IZ) = 1.0_RP/direction(IZ) !center = (v_max + v_min)/2.0_RP !t1 = (v_min(IX) - center(IX))*den(IX) !t2 = (v_max(IX) - center(IX))*den(IX) !t3 = (v_min(IY) - center(IY))*den(IY) !t4 = (v_max(IY) - center(IY))*den(IY) !t5 = (v_min(IZ) - center(IZ))*den(IZ) !t6 = (v_max(IZ) - center(IZ))*den(IZ) !t_min = max(max(min(t1,t2),min(t3,t4),min(t5,t6))) !t_max = min(main(max(t1,t2),max(t3,t4),max(t5,t6))) !if( t_max < 0.0_RP ) then ! t = t_max ! intersect = .false. !elseif( t_min > t_max ) then ! t = t_max ! intersect = .false. !else ! t = t_min ! intersect = .true. !end if !a_min = -1.0_RP !a_max = 1.0_RP !tmin = -huge(1.0) !tmax = huge(1.0) intersect = .false. !do i = 1, NDIM ! if( almostEqual(abs(direction(i)), 0.0_RP) ) then ! if( P(i) .lt. a_min(i) .or. P(i) .gt. a_max(i) ) return ! else ! ood = 1.0_RP/direction(i) ! t1 = (a_min(i) - p(i))*ood ! t2 = (a_max(i) - p(i))*ood ! if( t1 .gt. t2 ) then ! tTemp = t1 ! t1 = t2 ! t2 = tTemp ! end if ! if (t1 .gt. tmin) tmin = t1 ! if (t2 .gt. tmax) tmax = t2 ! if (tmin .gt. tmax) return ! end if !end do intersect = .true. t = tmin end subroutine RayAABIntersection end module OrientedBoundingBox