///
/// Represents a region composed of one or more rectangles, providing methods for union, intersection, exclusion, and
/// complement operations.
///
public class Region : IDisposable
{
private List _rectangles;
///
/// Initializes a new instance of the class.
///
public Region () { _rectangles = new (); }
///
/// Initializes a new instance of the class with the specified rectangle.
///
/// The initial rectangle for the region.
public Region (Rectangle rectangle) { _rectangles = new() { rectangle }; }
///
/// Adds the specified rectangle to the region.
///
/// The rectangle to add to the region.
public void Union (Rectangle rectangle)
{
_rectangles.Add (rectangle);
_rectangles = MergeRectangles (_rectangles);
}
///
/// Adds the specified region to this region.
///
/// The region to add to this region.
public void Union (Region region)
{
_rectangles.AddRange (region._rectangles);
_rectangles = MergeRectangles (_rectangles);
}
///
/// Updates the region to be the intersection of itself with the specified rectangle.
///
/// The rectangle to intersect with the region.
public void Intersect (Rectangle rectangle)
{
_rectangles = _rectangles.Select (r => Rectangle.Intersect (r, rectangle)).Where (r => !r.IsEmpty).ToList ();
}
///
/// Updates the region to be the intersection of itself with the specified region.
///
/// The region to intersect with this region.
public void Intersect (Region region)
{
List intersections = new List ();
foreach (Rectangle rect1 in _rectangles)
{
foreach (Rectangle rect2 in region._rectangles)
{
Rectangle intersected = Rectangle.Intersect (rect1, rect2);
if (!intersected.IsEmpty)
{
intersections.Add (intersected);
}
}
}
_rectangles = intersections;
}
///
/// Removes the portion of the specified rectangle from the region.
///
/// The rectangle to exclude from the region.
public void Exclude (Rectangle rectangle) { _rectangles = _rectangles.SelectMany (r => SubtractRectangle (r, rectangle)).ToList (); }
///
/// Removes the portion of the specified region from this region.
///
/// The region to exclude from this region.
public void Exclude (Region region)
{
foreach (Rectangle rect in region._rectangles)
{
_rectangles = _rectangles.SelectMany (r => SubtractRectangle (r, rect)).ToList ();
}
}
///
/// Updates the region to be the complement of itself within the specified bounds.
///
/// The bounding rectangle to use for complementing the region.
public void Complement (Rectangle bounds)
{
if (bounds.IsEmpty || _rectangles.Count == 0)
{
_rectangles.Clear ();
return;
}
List complementRectangles = new List { bounds };
foreach (Rectangle rect in _rectangles)
{
complementRectangles = complementRectangles.SelectMany (r => SubtractRectangle (r, rect)).ToList ();
}
_rectangles = complementRectangles;
}
///
/// Creates an exact copy of the region.
///
/// A new that is a copy of this instance.
public Region Clone ()
{
var clone = new Region ();
clone._rectangles = new (_rectangles);
return clone;
}
///
/// Gets a bounding rectangle for the entire region.
///
/// A that bounds the region.
public Rectangle GetBounds ()
{
if (_rectangles.Count == 0)
{
return Rectangle.Empty;
}
int left = _rectangles.Min (r => r.Left);
int top = _rectangles.Min (r => r.Top);
int right = _rectangles.Max (r => r.Right);
int bottom = _rectangles.Max (r => r.Bottom);
return new (left, top, right - left, bottom - top);
}
///
/// Determines whether the region is empty.
///
/// true if the region is empty; otherwise, false.
public bool IsEmpty () { return !_rectangles.Any (); }
///
/// Determines whether the specified point is contained within the region.
///
/// The x-coordinate of the point.
/// The y-coordinate of the point.
/// true if the point is contained within the region; otherwise, false.
public bool Contains (int x, int y) { return _rectangles.Any (r => r.Contains (x, y)); }
///
/// Determines whether the specified rectangle is contained within the region.
///
/// The rectangle to check for containment.
/// true if the rectangle is contained within the region; otherwise, false.
public bool Contains (Rectangle rectangle) { return _rectangles.Any (r => r.Contains (rectangle)); }
///
/// Returns an array of rectangles that represent the region.
///
/// An array of objects that make up the region.
public Rectangle [] GetRegionScans () { return _rectangles.ToArray (); }
///
/// Merges overlapping rectangles into a minimal set of non-overlapping rectangles.
///
/// The list of rectangles to merge.
/// A list of merged rectangles.
private List MergeRectangles (List rectangles)
{
// Simplified merging logic: this does not handle all edge cases for merging overlapping rectangles.
// For a full implementation, a plane sweep algorithm or similar would be needed.
List merged = new List (rectangles);
bool mergedAny;
do
{
mergedAny = false;
for (var i = 0; i < merged.Count; i++)
{
for (int j = i + 1; j < merged.Count; j++)
{
if (merged [i].IntersectsWith (merged [j]))
{
merged [i] = Rectangle.Union (merged [i], merged [j]);
merged.RemoveAt (j);
mergedAny = true;
break;
}
}
if (mergedAny)
{
break;
}
}
}
while (mergedAny);
return merged;
}
///
/// Subtracts the specified rectangle from the original rectangle, returning the resulting rectangles.
///
/// The original rectangle.
/// The rectangle to subtract from the original.
/// An enumerable collection of resulting rectangles after subtraction.
private IEnumerable SubtractRectangle (Rectangle original, Rectangle subtract)
{
if (!original.IntersectsWith (subtract))
{
yield return original;
yield break;
}
// Top segment
if (original.Top < subtract.Top)
{
yield return new (original.Left, original.Top, original.Width, subtract.Top - original.Top);
}
// Bottom segment
if (original.Bottom > subtract.Bottom)
{
yield return new (original.Left, subtract.Bottom, original.Width, original.Bottom - subtract.Bottom);
}
// Left segment
if (original.Left < subtract.Left)
{
int top = Math.Max (original.Top, subtract.Top);
int bottom = Math.Min (original.Bottom, subtract.Bottom);
if (bottom > top)
{
yield return new (original.Left, top, subtract.Left - original.Left, bottom - top);
}
}
// Right segment
if (original.Right > subtract.Right)
{
int top = Math.Max (original.Top, subtract.Top);
int bottom = Math.Min (original.Bottom, subtract.Bottom);
if (bottom > top)
{
yield return new (subtract.Right, top, original.Right - subtract.Right, bottom - top);
}
}
}
///
/// Releases all resources used by the .
///
public void Dispose () { _rectangles.Clear (); }
}