You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

449 lines
14 KiB

// David Eberly, Geometric Tools, Redmond WA 98052
// Copyright (c) 1998-2021
// 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/DisjointIntervals.h>
#include <functional>
namespace gte
{
// Compute Boolean operations of disjoint sets of half-open rectangles of
// the form [xmin,xmax)x[ymin,ymax) with xmin < xmax and ymin < ymax.
template <typename Scalar>
class DisjointRectangles
{
public:
// Convenient type definition.
typedef DisjointIntervals<Scalar> ISet;
// Construction and destruction. The non-default constructor requires
// that xmin < xmax and ymin < ymax.
DisjointRectangles()
:
mNumRectangles(0)
{
}
DisjointRectangles(Scalar const& xmin, Scalar const& xmax, Scalar const& ymin, Scalar const& ymax)
{
if (xmin < xmax && ymin < ymax)
{
mNumRectangles = 1;
mStrips.push_back(Strip(ymin, ymax, ISet(xmin, xmax)));
}
else
{
mNumRectangles = 0;
}
}
~DisjointRectangles()
{
}
// Copy operations.
DisjointRectangles(DisjointRectangles const& other)
{
*this = other;
}
DisjointRectangles& operator=(DisjointRectangles const& other)
{
mNumRectangles = other.mNumRectangles;
mStrips = other.mStrips;
return *this;
}
// Move operations.
DisjointRectangles(DisjointRectangles&& other)
{
*this = std::move(other);
}
DisjointRectangles& operator=(DisjointRectangles&& other)
{
mNumRectangles = other.mNumRectangles;
mStrips = std::move(other.mStrips);
return *this;
}
// The rectangle set consists of y-strips of interval sets.
class Strip
{
public:
// Construction and destruction.
Strip()
:
ymin((Scalar)0),
ymax((Scalar)0)
{
}
Strip(Scalar const& inYMin, Scalar const& inYMax, ISet const& inIntervalSet)
:
ymin(inYMin),
ymax(inYMax),
intervalSet(inIntervalSet)
{
}
~Strip()
{
}
// Copy operations.
Strip(Strip const& other)
{
*this = other;
}
Strip& operator=(Strip const& other)
{
ymin = other.ymin;
ymax = other.ymax;
intervalSet = other.intervalSet;
return *this;
}
// Move operations.
Strip(Strip&& other)
{
*this = std::move(other);
}
Strip& operator=(Strip&& other)
{
ymin = other.ymin;
ymax = other.ymax;
intervalSet = std::move(other.intervalSet);
other.ymin = (Scalar)0;
other.ymax = (Scalar)0;
return *this;
}
// Member access.
Scalar ymin, ymax;
ISet intervalSet;
};
// The number of rectangles in the set.
inline int GetNumRectangles() const
{
return mNumRectangles;
}
// The i-th rectangle is [xmin,xmax)x[ymin,ymax). The values xmin,
// xmax, ymin and ymax are valid when 0 <= i < GetNumRectangles().
bool GetRectangle(int i, Scalar& xmin, Scalar& xmax, Scalar& ymin, Scalar& ymax) const
{
int totalQuantity = 0;
for (auto const& strip : mStrips)
{
ISet const& intervalSet = strip.intervalSet;
int xQuantity = intervalSet.GetNumIntervals();
int nextTotalQuantity = totalQuantity + xQuantity;
if (i < nextTotalQuantity)
{
i -= totalQuantity;
intervalSet.GetInterval(i, xmin, xmax);
ymin = strip.ymin;
ymax = strip.ymax;
return true;
}
totalQuantity = nextTotalQuantity;
}
return false;
}
// Make this set empty.
inline void Clear()
{
mNumRectangles = 0;
mStrips.clear();
}
// The number of y-strips in the set.
inline int GetNumStrips() const
{
return static_cast<int>(mStrips.size());
}
// The i-th strip. The returned values are valid when
// 0 <= i < GetStripQuantity().
bool GetStrip(int i, Scalar& ymin, Scalar& ymax, ISet& xIntervalSet) const
{
if (0 <= i && i < GetNumStrips())
{
Strip const& strip = mStrips[i];
ymin = strip.ymin;
ymax = strip.ymax;
xIntervalSet = strip.intervalSet;
return true;
}
return false;
}
// Insert [xmin,xmax)x[ymin,ymax) into the set. This is a Boolean
// union operation. The operation is successful only when xmin < xmax
// and ymin < ymax.
bool Insert(Scalar const& xmin, Scalar const& xmax, Scalar const& ymin, Scalar const& ymax)
{
if (xmin < xmax && ymin < ymax)
{
DisjointRectangles input(xmin, xmax, ymin, ymax);
DisjointRectangles output = *this | input;
*this = std::move(output);
return true;
}
return false;
}
// Remove [xmin,xmax)x[ymin,ymax) from the set. This is a Boolean
// difference operation. The operation is successful only when
// xmin < xmax and ymin < ymax.
bool Remove(Scalar const& xmin, Scalar const& xmax, Scalar const& ymin, Scalar const& ymax)
{
if (xmin < xmax && ymin < ymax)
{
DisjointRectangles input(xmin, xmax, ymin, ymax);
DisjointRectangles output = *this - input;
*this = std::move(output);
return true;
}
return false;
}
// Get the union of the rectangle sets sets, input0 union input1.
friend DisjointRectangles operator|(DisjointRectangles const& input0, DisjointRectangles const& input1)
{
return Execute(
[](ISet const& i0, ISet const& i1) { return i0 | i1; },
true, true, input0, input1);
}
// Get the intersection of the rectangle sets, input0 intersect is1.
friend DisjointRectangles operator&(DisjointRectangles const& input0, DisjointRectangles const& input1)
{
return Execute(
[](ISet const& i0, ISet const& i1) { return i0 & i1; },
false, false, input0, input1);
}
// Get the differences of the rectangle sets, input0 minus input1.
friend DisjointRectangles operator-(DisjointRectangles const& input0, DisjointRectangles const& input1)
{
return Execute(
[](ISet const& i0, ISet const& i1) { return i0 - i1; },
false, true, input0, input1);
}
// Get the exclusive or of the rectangle sets, input0 xor input1 =
// (input0 minus input1) or (input1 minus input0).
friend DisjointRectangles operator^(DisjointRectangles const& input0, DisjointRectangles const& input1)
{
return Execute(
[](ISet const& i0, ISet const& i1) { return i0 ^ i1; },
true, true, input0, input1);
}
private:
static DisjointRectangles Execute(
std::function<ISet(ISet const&, ISet const&)> const& operation,
bool unionExclusiveOr, bool unionExclusiveOrDifference,
DisjointRectangles const& input0, DisjointRectangles const& input1)
{
DisjointRectangles output;
size_t const numStrips0 = input0.GetNumStrips();
size_t const numStrips1 = input1.GetNumStrips();
size_t i0 = 0, i1 = 0;
bool getOriginal0 = true, getOriginal1 = true;
Scalar ymin0 = (Scalar)0;
Scalar ymax0 = (Scalar)0;
Scalar ymin1 = (Scalar)0;
Scalar ymax1 = (Scalar)0;
while (i0 < numStrips0 && i1 < numStrips1)
{
ISet const& intr0 = input0.mStrips[i0].intervalSet;
if (getOriginal0)
{
ymin0 = input0.mStrips[i0].ymin;
ymax0 = input0.mStrips[i0].ymax;
}
ISet const& intr1 = input1.mStrips[i1].intervalSet;
if (getOriginal1)
{
ymin1 = input1.mStrips[i1].ymin;
ymax1 = input1.mStrips[i1].ymax;
}
// Case 1.
if (ymax1 <= ymin0)
{
// operator(empty,strip1)
if (unionExclusiveOr)
{
output.mStrips.push_back(Strip(ymin1, ymax1, intr1));
}
++i1;
getOriginal0 = false;
getOriginal1 = true;
continue; // using next ymin1/ymax1
}
// Case 11.
if (ymin1 >= ymax0)
{
// operator(strip0,empty)
if (unionExclusiveOrDifference)
{
output.mStrips.push_back(Strip(ymin0, ymax0, intr0));
}
++i0;
getOriginal0 = true;
getOriginal1 = false;
continue; // using next ymin0/ymax0
}
// Reduce cases 2, 3, 4 to cases 5, 6, 7.
if (ymin1 < ymin0)
{
// operator(empty,[ymin1,ymin0))
if (unionExclusiveOr)
{
output.mStrips.push_back(Strip(ymin1, ymin0, intr1));
}
ymin1 = ymin0;
getOriginal1 = false;
}
// Reduce cases 8, 9, 10 to cases 5, 6, 7.
if (ymin1 > ymin0)
{
// operator([ymin0,ymin1),empty)
if (unionExclusiveOrDifference)
{
output.mStrips.push_back(Strip(ymin0, ymin1, intr0));
}
ymin0 = ymin1;
getOriginal0 = false;
}
// Case 5.
if (ymax1 < ymax0)
{
// operator(strip0,[ymin1,ymax1))
auto result = operation(intr0, intr1);
output.mStrips.push_back(Strip(ymin1, ymax1, result));
ymin0 = ymax1;
++i1;
getOriginal0 = false;
getOriginal1 = true;
continue; // using next ymin1/ymax1
}
// Case 6.
if (ymax1 == ymax0)
{
// operator(strip0,[ymin1,ymax1))
auto result = operation(intr0, intr1);
output.mStrips.push_back(Strip(ymin1, ymax1, result));
++i0;
++i1;
getOriginal0 = true;
getOriginal1 = true;
continue; // using next ymin0/ymax0 and ymin1/ymax1
}
// Case 7.
if (ymax1 > ymax0)
{
// operator(strip0,[ymin1,ymax0))
auto result = operation(intr0, intr1);
output.mStrips.push_back(Strip(ymin1, ymax0, result));
ymin1 = ymax0;
++i0;
getOriginal0 = true;
getOriginal1 = false;
// continue; using current ymin1/ymax1
}
}
if (unionExclusiveOrDifference)
{
while (i0 < numStrips0)
{
if (getOriginal0)
{
ymin0 = input0.mStrips[i0].ymin;
ymax0 = input0.mStrips[i0].ymax;
}
else
{
getOriginal0 = true;
}
// operator(strip0,empty)
output.mStrips.push_back(Strip(ymin0, ymax0,
input0.mStrips[i0].intervalSet));
++i0;
}
}
if (unionExclusiveOr)
{
while (i1 < numStrips1)
{
if (getOriginal1)
{
ymin1 = input1.mStrips[i1].ymin;
ymax1 = input1.mStrips[i1].ymax;
}
else
{
getOriginal1 = true;
}
// operator(empty,strip1)
output.mStrips.push_back(Strip(ymin1, ymax1,
input1.mStrips[i1].intervalSet));
++i1;
}
}
output.ComputeRectangleQuantity();
return output;
}
void ComputeRectangleQuantity()
{
mNumRectangles = 0;
for (auto strip : mStrips)
{
mNumRectangles += strip.intervalSet.GetNumIntervals();
}
}
// The number of rectangles in the set.
int mNumRectangles;
// The y-strips of the set, each containing an x-interval set.
std::vector<Strip> mStrips;
};
}