C#矩形分割矩形,使用任意个矩形来占据一个大的矩形,并求得未占用区域的所有不重叠矩形块
public enum TanglecySide
{
None = 1,
LeftToRight = 1 >> 1,
RightToLeft = 1 >> 2,
TopBottom = 1 >> 3,
BottomToTop = 1 >> 4,
LeftToLeft = 1 >> 5,
RightToRight = 1 >> 6,
TopToTop = 1 >> 7,
BottomToBottom = 1 >> 8,
}
public class Rect2DCompare : IComparer<Rect2D>
{
public int Compare(Rect2D? x, Rect2D? y)
{
if(x is null && y is null) return 0;
if(x is null && y is not null) return -1;
if (x is not null && y is null) return 1;
return x.CompareTo(y);
}
}
public class Rect2D : IComparable<Rect2D>
{
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public bool CanSplit { get; set; }
public int Right { get => Right; }
public int Bottom { get => Bottom; }
public override string ToString()
{
return $"{X},{Y},{Width},{Height}";
}
public Rect2D() { }
public Rect2D(int x, int y, int width, int height, bool canSplit = true)
{
X = x;
Y = y;
Width = width; Height = height;
CanSplit = canSplit;
}
public int CompareTo(Rect2D? y)
{
if(y == null) return 1;
return (X * Y).CompareTo(y.X * y.Y);
}
/// <summary>
/// 不相切但相交
/// </summary>
/// <param name="rect"></param>
/// <returns></returns>
public bool IsIntersect(Rect2D rect)
{
return !(Right <= rect.X || Bottom <= rect.Y || X >= rect.Right || Y >= rect.Bottom);
}
/// <summary>
/// 获取本矩形于目标矩形相切的边
/// </summary>
/// <param name="rect">目标矩形</param>
/// <param name="intersect">是否包括同侧相切的情况,例如本矩形左侧与目标矩形左侧相切</param>
/// <returns>返回本矩形相交的边</returns>
public TanglecySide TangencySides(Rect2D rect, bool includeSameSide = false)
{
TanglecySide sideToSide = TanglecySide.None;
if (!IsIntersect(rect)) return sideToSide;
if (this.X == rect.Right) sideToSide |= TanglecySide.LeftToRight;
if (this.Y == rect.Bottom) sideToSide |= TanglecySide.TopBottom;
if (this.Right == rect.X) sideToSide |= TanglecySide.RightToLeft;
if (this.Bottom == rect.Y) sideToSide |= TanglecySide.BottomToTop;
if (includeSameSide)
{
if (X == rect.X) sideToSide |= TanglecySide.LeftToLeft;
if (Y == rect.Y) sideToSide |= TanglecySide.TopToTop;
if (Right == rect.Right) sideToSide |= TanglecySide.RightToRight;
if (X + Height == rect.Bottom) sideToSide |= TanglecySide.BottomToBottom;
}
return sideToSide;
}
/// <summary>
/// 判断点是否在矩形上,包括在边上
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public bool Contains(int x, int y) => x >= X && y >= Y && x <= Right && y <= Bottom;
/// <summary>
/// 判断rect整个区域是否在矩形上,包括相切
/// </summary>
/// <param name="rect"></param>
/// <returns></returns>
public bool Contains(Rect2D rect) => X <= rect.X && rect.Right <= Right && Y <= rect.Y && rect.Bottom <= Bottom;
/// <summary>
/// 传入矩形按四个顶点进行拆分
/// </summary>
/// <param name="rect">不可拆分的矩形</param>
/// <returns>返回拆分后的矩形列表</returns>
public List<Rect2D> GetUnIntersectAreas(Rect2D rect)
{
if (this.CanSplit == rect.CanSplit) return null;
if (!this.CanSplit) return rect.GetUnIntersectAreas(this);
if (!IsIntersect(rect)) return null;
if (rect.Contains(this)) return null;
List<Rect2D> result = new List<Rect2D>();
//当被拆分的矩形在左上侧时才有左上角矩形
Rect2D leftTop = new Rect2D
{
X = Math.Min(X, rect.X),
Y = Math.Min(Y, rect.Y),
Width = Math.Max(rect.X - X, 0),
Height = Math.Max(rect.Y - Y, 0),
CanSplit = true,
};
if (leftTop.Width > 0 && leftTop.Height > 0)
result.Add(leftTop);
//当被拆分的矩形在右上侧时才有右上角矩形
Rect2D rightTop = new Rect2D
{
X = Math.Min(Right, rect.Right),
Y = Math.Min(Y, rect.Y),
Width = Math.Max(0, Right - rect.Right),
Height = Math.Max(0, rect.Y - Y),
CanSplit = true,
};
if (rightTop.Width > 0 && rightTop.Height > 0)
result.Add(rightTop);
//当被拆分的矩形在上侧时才有上侧矩形
Rect2D top = new Rect2D
{
X = Math.Max(X, rect.X),
Y = Math.Min(Y, rect.Y),
Width = Math.Max(0, Width - leftTop.Width - rightTop.Width),
Height = Math.Max(0, leftTop.Height),
CanSplit = true,
};
if(top.Width > 0 && top.Height > 0) result.Add(top);
//当被拆分的矩形在左下侧时才有左下角矩形
Rect2D leftBottom = new Rect2D
{
X = Math.Min(X, rect.X),
Y = Math.Min(rect.Bottom, Bottom),
Width = Math.Max(rect.X - X, 0),
Height = Math.Max(0, Bottom - rect.Bottom),
CanSplit = true,
};
if (leftBottom.Width > 0 && leftBottom.Height > 0) result.Add(leftBottom);
//当被拆分的矩形在右下侧时才有右下角矩形
Rect2D rightBottom = new Rect2D
{
X = Math.Min(Right, rect.Right),
Y = Math.Min(rect.Bottom, Bottom),
Width = Math.Max(0, Right - rect.Right),
Height = Math.Max(0, Bottom - rect.Bottom),
CanSplit = true,
};
if (rightBottom.Width > 0 && rightBottom.Height > 0) result.Add(rightBottom);
//当被拆分的矩形在下侧时才有下侧矩形
Rect2D bottom = new Rect2D
{
X = Math.Max(X, rect.X),
Y = Math.Min(rect.Bottom, Bottom),
Width = Math.Max(0, Width - leftTop.Width - rightTop.Width),
Height = Math.Max(0, Bottom - rect.Bottom),
CanSplit = true,
};
if (bottom.Width > 0 && bottom.Height > 0) result.Add(bottom);
//当被拆分的矩形在左侧时才有左侧矩形
Rect2D left = new Rect2D
{
X = Math.Min(X, rect.X),
Y = Math.Max(Y, rect.Y),
Width = Math.Max(rect.X - X, 0),
Height = Height - leftTop.Height - leftBottom.Height,
CanSplit = true,
};
if (left.Width > 0 && left.Height > 0) result.Add(left);
//当被拆分的矩形在右侧时才有右侧矩形
Rect2D right = new Rect2D
{
X = Math.Min(Right, rect.Right),
Y = Math.Max(Y, rect.Y),
Width = Math.Max(0, Right - rect.Right),
Height = Height - rightTop.Height - rightBottom.Height,
CanSplit = true,
};
if (right.Width > 0 && right.Height > 0) result.Add(right);
return result;
}
/// <summary>
/// 传入被拆分的矩形和填充的不可拆分的矩形列表
/// </summary>
/// <param name="container">被拆分的矩形</param>
/// <param name="noSplitRects">填充的矩形列表</param>
/// <returns></returns>
public static List<Rect2D> GetUnIntersectAreas(Rect2D container, List<Rect2D> noSplitRects)
{
var rects = new List<Rect2D> { container };
foreach (var item in noSplitRects)
{
List<Rect2D> items = new List<Rect2D>();
int i = 0;
while (i < rects.Count)
{
var list = rects[i].GetUnIntersectAreas(item);
if (list != null && list.Count > 0)
{
items.AddRange(list);
rects.RemoveAt(i);
}
else if (item.Contains(rects[i]))
rects.RemoveAt(i);
else
{
i++;
}
}
if (items.Count > 0)
{
rects.AddRange(items);
}
}
return rects;
}
}