X3

RedSky

导航

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;
    }
}

 

posted on 2023-12-05 15:00  HotSky  阅读(32)  评论(0编辑  收藏  举报