Skip to content
Snippets Groups Projects
treeBoundBox.C 13.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*---------------------------------------------------------------------------*\
      =========                 |
      \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
       \\    /   O peration     |
    
        \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
    
         \\/     M anipulation  | Copyright (C) 2017-2019 OpenCFD Ltd.
    
    -------------------------------------------------------------------------------
    License
        This file is part of OpenFOAM.
    
    
        OpenFOAM is free software: you can redistribute it and/or modify it
        under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.
    
    
        OpenFOAM 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 General Public License
        for more details.
    
        You should have received a copy of the GNU General Public License
    
        along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
    
    
    \*---------------------------------------------------------------------------*/
    
    #include "treeBoundBox.H"
    
    // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
    
    
    Mark Olesen's avatar
    Mark Olesen committed
    const Foam::faceList Foam::treeBoundBox::faces
    
        face({0, 4, 6, 2}),  // 0: x-min, left
        face({1, 3, 7, 5}),  // 1: x-max, right
        face({0, 1, 5, 4}),  // 2: y-min, bottom
        face({2, 6, 7, 3}),  // 3: y-max, top
        face({0, 2, 3, 1}),  // 4: z-min, back
        face({4, 5, 7, 6})   // 5: z-max, front
    
    mattijs's avatar
    mattijs committed
    
    
    const Foam::edgeList Foam::treeBoundBox::edges
    ({
    
    Mark Olesen's avatar
    Mark Olesen committed
        {0, 1}, // 0
    
        {1, 3},
    
    Mark Olesen's avatar
    Mark Olesen committed
        {2, 3}, // 2
    
        {0, 2},
    
    Mark Olesen's avatar
    Mark Olesen committed
        {4, 5}, // 4
    
        {5, 7},
    
    Mark Olesen's avatar
    Mark Olesen committed
        {6, 7}, // 6
    
        {4, 6},
    
    Mark Olesen's avatar
    Mark Olesen committed
        {0, 4}, // 8
    
        {1, 5},
    
    Mark Olesen's avatar
    Mark Olesen committed
        {3, 7}, // 10
    
    Mattijs Janssens's avatar
    Mattijs Janssens committed
    
    
    // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
    
    
    Mark Olesen's avatar
    Mark Olesen committed
    Foam::treeBoundBox::treeBoundBox(const UList<point>& points)
    
        boundBox(points, false)
    
                << "No bounding box for zero-sized pointField" << nl;
    
    Mark Olesen's avatar
    Mark Olesen committed
    Foam::treeBoundBox::treeBoundBox
    
    mattijs's avatar
    mattijs committed
    (
        const UList<point>& points,
    
        const labelUList& indices
    
    mattijs's avatar
    mattijs committed
    )
    
        boundBox(points, indices, false)
    
        if (points.empty() || indices.empty())
    
    mattijs's avatar
    mattijs committed
        {
    
                << "No bounding box for zero-sized pointField" << nl;
    
    mattijs's avatar
    mattijs committed
        }
    
    mattijs's avatar
    mattijs committed
    // * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //
    
    Foam::tmp<Foam::pointField> Foam::treeBoundBox::points() const
    
        auto tpts = tmp<pointField>::New(8);
        auto& pts = tpts.ref();
    
    mattijs's avatar
    mattijs committed
        {
    
            pts[octant] = corner(octant);
    
    mattijs's avatar
    mattijs committed
        }
    
    Mark Olesen's avatar
    Mark Olesen committed
    Foam::treeBoundBox Foam::treeBoundBox::subBbox(const direction octant) const
    
        return subBbox(centre(), octant);
    
    Mark Olesen's avatar
    Mark Olesen committed
    Foam::treeBoundBox Foam::treeBoundBox::subBbox
    (
        const point& mid,
        const direction octant
    ) const
    
            FatalErrorInFunction
                << "octant should be [0..7]"
    
    Mark Olesen's avatar
    Mark Olesen committed
        // start with a copy of this bounding box and adjust limits accordingly
        treeBoundBox subBb(*this);
        point& bbMin = subBb.min();
        point& bbMax = subBb.max();
    
    
        if (octant & treeBoundBox::RIGHTHALF)
        {
    
    Mark Olesen's avatar
    Mark Olesen committed
            bbMin.x() = mid.x();    // mid -> max
    
    Mark Olesen's avatar
    Mark Olesen committed
            bbMax.x() = mid.x();    // min -> mid
    
        if (octant & treeBoundBox::TOPHALF)
        {
    
    Mark Olesen's avatar
    Mark Olesen committed
            bbMin.y() = mid.y();    // mid -> max
    
    Mark Olesen's avatar
    Mark Olesen committed
            bbMax.y() = mid.y();    // min -> mid
    
        if (octant & treeBoundBox::FRONTHALF)
        {
    
    Mark Olesen's avatar
    Mark Olesen committed
            bbMin.z() = mid.z();    // mid -> max
    
    Mark Olesen's avatar
    Mark Olesen committed
            bbMax.z() = mid.z();    // min -> mid
    
    Mark Olesen's avatar
    Mark Olesen committed
    bool Foam::treeBoundBox::intersects
    
        const point& overallStart,
        const vector& overallVec,
    
        const point& start,
        const point& end,
    
        point& pt,
        direction& ptOnFaces
    
        // Sutherlands algorithm:
        //   loop
        //     - start = intersection of line with one of the planes bounding
        //       the bounding box
        //     - stop if start inside bb (return true)
        //     - stop if start and end in same 'half' (e.g. both above bb)
        //       (return false)
        //
        // Uses posBits to efficiently determine 'half' in which start and end
        // point are.
        //
        // Note:
        //   - sets coordinate to exact position: e.g. pt.x() = min().x()
        //     since plane intersect routine might have truncation error.
        //     This makes sure that posBits tests 'inside'
    
    
        const direction endBits = posBits(end);
    
    Mark Olesen's avatar
    Mark Olesen committed
        pt = start;
    
        // Allow maximum of 3 clips.
        for (label i = 0; i < 4; ++i)
    
        {
            direction ptBits = posBits(pt);
    
            if (ptBits == 0)
            {
                // pt inside bb
    
                ptOnFaces = faceBits(pt);
    
                return true;
            }
    
            if ((ptBits & endBits) != 0)
            {
                // pt and end in same block outside of bb
    
                ptOnFaces = faceBits(pt);
    
                return false;
            }
    
            if (ptBits & LEFTBIT)
            {
                // Intersect with plane V=min, n=-1,0,0
    
                if (Foam::mag(overallVec.x()) > VSMALL)
                {
                    scalar s = (min().x() - overallStart.x())/overallVec.x();
                    pt.x() = min().x();
                    pt.y() = overallStart.y() + overallVec.y()*s;
                    pt.z() = overallStart.z() + overallVec.z()*s;
                }
                else
    
                    // Vector not in x-direction. But still intersecting bb planes.
                    // So must be close - just snap to bb.
    
            else if (ptBits & RIGHTBIT)
    
            {
                // Intersect with plane V=max, n=1,0,0
    
                if (Foam::mag(overallVec.x()) > VSMALL)
                {
                    scalar s = (max().x() - overallStart.x())/overallVec.x();
                    pt.x() = max().x();
                    pt.y() = overallStart.y() + overallVec.y()*s;
                    pt.z() = overallStart.z() + overallVec.z()*s;
                }
                else
    
            else if (ptBits & BOTTOMBIT)
    
            {
                // Intersect with plane V=min, n=0,-1,0
    
                if (Foam::mag(overallVec.y()) > VSMALL)
    
                    scalar s = (min().y() - overallStart.y())/overallVec.y();
                    pt.x() = overallStart.x() + overallVec.x()*s;
    
                    pt.y() = min().y();
    
                    pt.z() = overallStart.z() + overallVec.z()*s;
                }
                else
                {
                    pt.x() = min().y();
    
            else if (ptBits & TOPBIT)
    
            {
                // Intersect with plane V=max, n=0,1,0
    
                if (Foam::mag(overallVec.y()) > VSMALL)
                {
                    scalar s = (max().y() - overallStart.y())/overallVec.y();
                    pt.x() = overallStart.x() + overallVec.x()*s;
                    pt.y() = max().y();
                    pt.z() = overallStart.z() + overallVec.z()*s;
                }
                else
    
            else if (ptBits & BACKBIT)
    
            {
                // Intersect with plane V=min, n=0,0,-1
    
                if (Foam::mag(overallVec.z()) > VSMALL)
                {
                    scalar s = (min().z() - overallStart.z())/overallVec.z();
                    pt.x() = overallStart.x() + overallVec.x()*s;
                    pt.y() = overallStart.y() + overallVec.y()*s;
                    pt.z() = min().z();
                }
                else
    
            else if (ptBits & FRONTBIT)
    
            {
                // Intersect with plane V=max, n=0,0,1
    
                if (Foam::mag(overallVec.z()) > VSMALL)
                {
                    scalar s = (max().z() - overallStart.z())/overallVec.z();
                    pt.x() = overallStart.x() + overallVec.x()*s;
                    pt.y() = overallStart.y() + overallVec.y()*s;
                    pt.z() = max().z();
                }
                else
    
    
        // Can end up here if the end point is on the edge of the boundBox
        return true;
    
    bool Foam::treeBoundBox::intersects
    (
        const point& start,
        const point& end,
        point& pt
    ) const
    {
        direction ptBits;
        return intersects(start, end-start, start, end, pt, ptBits);
    }
    
    
    
    Mark Olesen's avatar
    Mark Olesen committed
    bool Foam::treeBoundBox::contains(const vector& dir, const point& pt) const
    
    {
        // Compare all components against min and max of bb
    
        for (direction cmpt=0; cmpt<3; ++cmpt)
    
    Mark Olesen's avatar
    Mark Olesen committed
            if (pt[cmpt] < min()[cmpt])
    
    Mark Olesen's avatar
    Mark Olesen committed
            else if (pt[cmpt] == min()[cmpt])
    
            {
                // On edge. Outside if direction points outwards.
                if (dir[cmpt] < 0)
                {
                    return false;
                }
            }
    
    
    Mark Olesen's avatar
    Mark Olesen committed
            if (pt[cmpt] > max()[cmpt])
    
    Mark Olesen's avatar
    Mark Olesen committed
            else if (pt[cmpt] == max()[cmpt])
    
            {
                // On edge. Outside if direction points outwards.
                if (dir[cmpt] > 0)
                {
                    return false;
                }
            }
        }
    
        // All components inside bb
        return true;
    }
    
    
    
    Foam::direction Foam::treeBoundBox::faceBits(const point& pt) const
    {
        direction faceBits = 0;
        if (pt.x() == min().x())
        {
            faceBits |= LEFTBIT;
        }
        else if (pt.x() == max().x())
        {
            faceBits |= RIGHTBIT;
        }
    
        if (pt.y() == min().y())
        {
            faceBits |= BOTTOMBIT;
        }
        else if (pt.y() == max().y())
        {
            faceBits |= TOPBIT;
        }
    
        if (pt.z() == min().z())
        {
            faceBits |= BACKBIT;
        }
        else if (pt.z() == max().z())
        {
            faceBits |= FRONTBIT;
        }
        return faceBits;
    }
    
    
    
    Mark Olesen's avatar
    Mark Olesen committed
    Foam::direction Foam::treeBoundBox::posBits(const point& pt) const
    
    {
        direction posBits = 0;
    
        if (pt.x() < min().x())
        {
            posBits |= LEFTBIT;
        }
    
        else if (pt.x() > max().x())
    
        {
            posBits |= RIGHTBIT;
        }
    
        if (pt.y() < min().y())
        {
    
    mattijs's avatar
    mattijs committed
            posBits |= BOTTOMBIT;
    
        else if (pt.y() > max().y())
    
    mattijs's avatar
    mattijs committed
            posBits |= TOPBIT;
    
    mattijs's avatar
    mattijs committed
            posBits |= BACKBIT;
    
        else if (pt.z() > max().z())
    
    mattijs's avatar
    mattijs committed
            posBits |= FRONTBIT;
    
    Mark Olesen's avatar
    Mark Olesen committed
    void Foam::treeBoundBox::calcExtremities
    
    Mark Olesen's avatar
    Mark Olesen committed
        const point& pt,
    
        point& nearest,
        point& furthest
    ) const
    {
        scalar nearX, nearY, nearZ;
        scalar farX, farY, farZ;
    
    
    Mark Olesen's avatar
    Mark Olesen committed
        if (Foam::mag(min().x() - pt.x()) < Foam::mag(max().x() - pt.x()))
    
        {
            nearX = min().x();
            farX = max().x();
        }
        else
        {
            nearX = max().x();
            farX = min().x();
        }
    
    
    Mark Olesen's avatar
    Mark Olesen committed
        if (Foam::mag(min().y() - pt.y()) < Foam::mag(max().y() - pt.y()))
    
        {
            nearY = min().y();
            farY = max().y();
        }
        else
        {
            nearY = max().y();
            farY = min().y();
        }
    
    
    Mark Olesen's avatar
    Mark Olesen committed
        if (Foam::mag(min().z() - pt.z()) < Foam::mag(max().z() - pt.z()))
    
        {
            nearZ = min().z();
            farZ = max().z();
        }
        else
        {
            nearZ = max().z();
            farZ = min().z();
        }
    
        nearest = point(nearX, nearY, nearZ);
        furthest = point(farX, farY, farZ);
    }
    
    
    
    Mark Olesen's avatar
    Mark Olesen committed
    Foam::scalar Foam::treeBoundBox::maxDist(const point& pt) const
    
    Mark Olesen's avatar
    Mark Olesen committed
        calcExtremities(pt, near, far);
    
    Mark Olesen's avatar
    Mark Olesen committed
        return Foam::mag(far - pt);
    
    Mark Olesen's avatar
    Mark Olesen committed
    Foam::label Foam::treeBoundBox::distanceCmp
    
    Mark Olesen's avatar
    Mark Olesen committed
        const point& pt,
    
        const treeBoundBox& other
    ) const
    {
        //
    
    Mark Olesen's avatar
    Mark Olesen committed
        // Distance point <-> nearest and furthest away vertex of this
    
        //
    
        point nearThis, farThis;
    
        // get nearest and furthest away vertex
    
    Mark Olesen's avatar
    Mark Olesen committed
        calcExtremities(pt, nearThis, farThis);
    
    henry's avatar
    henry committed
        const scalar minDistThis =
    
    Mark Olesen's avatar
    Mark Olesen committed
            sqr(nearThis.x() - pt.x())
         +  sqr(nearThis.y() - pt.y())
         +  sqr(nearThis.z() - pt.z());
    
    henry's avatar
    henry committed
        const scalar maxDistThis =
    
    Mark Olesen's avatar
    Mark Olesen committed
            sqr(farThis.x() - pt.x())
         +  sqr(farThis.y() - pt.y())
         +  sqr(farThis.z() - pt.z());
    
    Mark Olesen's avatar
    Mark Olesen committed
        // Distance point <-> other
    
        //
    
        point nearOther, farOther;
    
        // get nearest and furthest away vertex
    
    Mark Olesen's avatar
    Mark Olesen committed
        other.calcExtremities(pt, nearOther, farOther);
    
    henry's avatar
    henry committed
        const scalar minDistOther =
    
    Mark Olesen's avatar
    Mark Olesen committed
            sqr(nearOther.x() - pt.x())
         +  sqr(nearOther.y() - pt.y())
         +  sqr(nearOther.z() - pt.z());
    
    henry's avatar
    henry committed
        const scalar maxDistOther =
    
    Mark Olesen's avatar
    Mark Olesen committed
            sqr(farOther.x() - pt.x())
         +  sqr(farOther.y() - pt.y())
         +  sqr(farOther.z() - pt.z());
    
    
        //
        // Categorize
        //
        if (maxDistThis < minDistOther)
        {
    
    Mark Olesen's avatar
    Mark Olesen committed
            // All vertices of this are nearer to point than any vertex of other
    
            return -1;
        }
        else if (minDistThis > maxDistOther)
        {
    
    Mark Olesen's avatar
    Mark Olesen committed
            // All vertices of this are further from point than any vertex of other
    
    mattijs's avatar
    mattijs committed
    // * * * * * * * * * * * * * * * Ostream Operator  * * * * * * * * * * * * * //
    
    Foam::Ostream& Foam::operator<<(Ostream& os, const treeBoundBox& bb)
    {
        return os << static_cast<const boundBox&>(bb);
    }
    
    
    Mark Olesen's avatar
    Mark Olesen committed
    Foam::Istream& Foam::operator>>(Istream& is, treeBoundBox& bb)
    
    mattijs's avatar
    mattijs committed
        return is >> static_cast<boundBox&>(bb);
    
    }
    
    
    // ************************************************************************* //