123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- // David Eberly, Geometric Tools, Redmond WA 98052
- // Copyright (c) 1998-2020
- // Distributed under the Boost Software License, Version 1.0.
- // https://www.boost.org/LICENSE_1_0.txt
- // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
- // Version: 4.0.2019.08.13
- #pragma once
- #include <Mathematics/IntrAlignedBox3AlignedBox3.h>
- #include <Mathematics/EdgeKey.h>
- #include <set>
- #include <vector>
- namespace WwiseGTE
- {
- template <typename Real>
- class BoxManager
- {
- public:
- // Construction.
- BoxManager(std::vector<AlignedBox3<Real>>& boxes)
- :
- mBoxes(boxes)
- {
- Initialize();
- }
- // No default construction, copy construction, or assignment are
- // allowed.
- BoxManager() = delete;
- BoxManager(BoxManager const&) = delete;
- BoxManager& operator=(BoxManager const&) = delete;
- // This function is called by the constructor and does the
- // sort-and-sweep to initialize the update system. However, if you
- // add or remove items from the array of boxes after the constructor
- // call, you will need to call this function once before you start the
- // multiple calls of the update function.
- void Initialize()
- {
- // Get the box endpoints.
- int intrSize = static_cast<int>(mBoxes.size()), endpSize = 2 * intrSize;
- mXEndpoints.resize(endpSize);
- mYEndpoints.resize(endpSize);
- mZEndpoints.resize(endpSize);
- for (int i = 0, j = 0; i < intrSize; ++i)
- {
- mXEndpoints[j].type = 0;
- mXEndpoints[j].value = mBoxes[i].min[0];
- mXEndpoints[j].index = i;
- mYEndpoints[j].type = 0;
- mYEndpoints[j].value = mBoxes[i].min[1];
- mYEndpoints[j].index = i;
- mZEndpoints[j].type = 0;
- mZEndpoints[j].value = mBoxes[i].min[2];
- mZEndpoints[j].index = i;
- ++j;
- mXEndpoints[j].type = 1;
- mXEndpoints[j].value = mBoxes[i].max[0];
- mXEndpoints[j].index = i;
- mYEndpoints[j].type = 1;
- mYEndpoints[j].value = mBoxes[i].max[1];
- mYEndpoints[j].index = i;
- mZEndpoints[j].type = 1;
- mZEndpoints[j].value = mBoxes[i].max[2];
- mZEndpoints[j].index = i;
- ++j;
- }
- // Sort the rectangle endpoints.
- std::sort(mXEndpoints.begin(), mXEndpoints.end());
- std::sort(mYEndpoints.begin(), mYEndpoints.end());
- std::sort(mZEndpoints.begin(), mZEndpoints.end());
- // Create the interval-to-endpoint lookup tables.
- mXLookup.resize(endpSize);
- mYLookup.resize(endpSize);
- mZLookup.resize(endpSize);
- for (int j = 0; j < endpSize; ++j)
- {
- mXLookup[2 * mXEndpoints[j].index + mXEndpoints[j].type] = j;
- mYLookup[2 * mYEndpoints[j].index + mYEndpoints[j].type] = j;
- mZLookup[2 * mZEndpoints[j].index + mZEndpoints[j].type] = j;
- }
- // Active set of rectangles (stored by index in array).
- std::set<int> active;
- // Set of overlapping rectangles (stored by pairs of indices in
- // array).
- mOverlap.clear();
- // Sweep through the endpoints to determine overlapping
- // x-intervals.
- for (int i = 0; i < endpSize; ++i)
- {
- Endpoint& endpoint = mXEndpoints[i];
- int index = endpoint.index;
- if (endpoint.type == 0) // an interval 'begin' value
- {
- // In the 1D problem, the current interval overlaps with
- // all the active intervals. In 3D we also need to check
- // for y-overlap and z-overlap.
- for (auto activeIndex : active)
- {
- // Rectangles activeIndex and index overlap in the
- // x-dimension. Test for overlap in the y-dimension
- // and z-dimension.
- AlignedBox3<Real> const& b0 = mBoxes[activeIndex];
- AlignedBox3<Real> const& b1 = mBoxes[index];
- if (b0.max[1] >= b1.min[1] && b0.min[1] <= b1.max[1]
- && b0.max[2] >= b1.min[2] && b0.min[2] <= b1.max[2])
- {
- if (activeIndex < index)
- {
- mOverlap.insert(EdgeKey<false>(activeIndex, index));
- }
- else
- {
- mOverlap.insert(EdgeKey<false>(index, activeIndex));
- }
- }
- }
- active.insert(index);
- }
- else // an interval 'end' value
- {
- active.erase(index);
- }
- }
- }
- // After the system is initialized, you can move the boxes using this
- // function. It is not enough to modify the input array of boxes
- // because the endpoint values stored internally by this class must
- // also change. You can also retrieve the current rectangles
- // information.
- void SetBox(int i, AlignedBox3<Real> const& box)
- {
- mBoxes[i] = box;
- mXEndpoints[mXLookup[2 * i]].value = box.min[0];
- mXEndpoints[mXLookup[2 * i + 1]].value = box.max[0];
- mYEndpoints[mYLookup[2 * i]].value = box.min[1];
- mYEndpoints[mYLookup[2 * i + 1]].value = box.max[1];
- mZEndpoints[mZLookup[2 * i]].value = box.min[2];
- mZEndpoints[mZLookup[2 * i + 1]].value = box.max[2];
- }
- inline void GetBox(int i, AlignedBox3<Real>& box) const
- {
- box = mBoxes[i];
- }
- // When you are finished moving boxes, call this function to determine
- // the overlapping boxes. An incremental update is applied to
- // determine the new set of overlapping boxes.
- void Update()
- {
- InsertionSort(mXEndpoints, mXLookup);
- InsertionSort(mYEndpoints, mYLookup);
- InsertionSort(mZEndpoints, mZLookup);
- }
- // If (i,j) is in the overlap set, then box i and box j are
- // overlapping. The indices are those for the the input array. The
- // set elements (i,j) are stored so that i < j.
- inline std::set<EdgeKey<false>> const& GetOverlap() const
- {
- return mOverlap;
- }
- private:
- class Endpoint
- {
- public:
- Real value; // endpoint value
- int type; // '0' if interval min, '1' if interval max.
- int index; // index of interval containing this endpoint
- // Support for sorting of endpoints.
- bool operator<(Endpoint const& endpoint) const
- {
- if (value < endpoint.value)
- {
- return true;
- }
- if (value > endpoint.value)
- {
- return false;
- }
- return type < endpoint.type;
- }
- };
- void InsertionSort(std::vector<Endpoint>& endpoint, std::vector<int>& lookup)
- {
- // Apply an insertion sort. Under the assumption that the
- // rectangles have not changed much since the last call, the
- // endpoints are nearly sorted. The insertion sort should be very
- // fast in this case.
- TIQuery<Real, AlignedBox3<Real>, AlignedBox3<Real>> query;
- int endpSize = static_cast<int>(endpoint.size());
- for (int j = 1; j < endpSize; ++j)
- {
- Endpoint key = endpoint[j];
- int i = j - 1;
- while (i >= 0 && key < endpoint[i])
- {
- Endpoint e0 = endpoint[i];
- Endpoint e1 = endpoint[i + 1];
- // Update the overlap status.
- if (e0.type == 0)
- {
- if (e1.type == 1)
- {
- // The 'b' of interval E0.mIndex was smaller than
- // the 'e' of interval E1.mIndex, and the
- // intervals *might have been* overlapping. Now
- // 'b' and 'e' are swapped, and the intervals
- // cannot overlap. Remove the pair from the
- // overlap set. The removal operation needs to
- // find the pair and erase it if it exists.
- // Finding the pair is the expensive part of the
- // operation, so there is no real time savings in
- // testing for existence first, then deleting if
- // it does.
- mOverlap.erase(EdgeKey<false>(e0.index, e1.index));
- }
- }
- else
- {
- if (e1.type == 0)
- {
- // The 'b' of interval E1.index was larger than
- // the 'e' of interval E0.index, and the intervals
- // were not overlapping. Now 'b' and 'e' are
- // swapped, and the intervals *might be*
- // overlapping. Determine if they are overlapping
- // and then insert.
- if (query(mBoxes[e0.index], mBoxes[e1.index]).intersect)
- {
- mOverlap.insert(EdgeKey<false>(e0.index, e1.index));
- }
- }
- }
- // Reorder the items to maintain the sorted list.
- endpoint[i] = e1;
- endpoint[i + 1] = e0;
- lookup[2 * e1.index + e1.type] = i;
- lookup[2 * e0.index + e0.type] = i + 1;
- --i;
- }
- endpoint[i + 1] = key;
- lookup[2 * key.index + key.type] = i + 1;
- }
- }
- std::vector<AlignedBox3<Real>>& mBoxes;
- std::vector<Endpoint> mXEndpoints, mYEndpoints, mZEndpoints;
- std::set<EdgeKey<false>> mOverlap;
- // The intervals are indexed 0 <= i < n. The endpoint array has 2*n
- // entries. The original 2*n interval values are ordered as
- // b[0], e[0], b[1], e[1], ..., b[n-1], e[n-1]
- // When the endpoint array is sorted, the mapping between interval
- // values and endpoints is lost. In order to modify interval values
- // that are stored in the endpoint array, we need to maintain the
- // mapping. This is done by the following lookup table of 2*n
- // entries. The value mLookup[2*i] is the index of b[i] in the
- // endpoint array. The value mLookup[2*i+1] is the index of e[i]
- // in the endpoint array.
- std::vector<int> mXLookup, mYLookup, mZLookup;
- };
- }
|