C#矩形分割矩形,使用任意个矩形来占据一个大的矩形,并求得未占用区域的所有不重叠矩形块
1 public enum TanglecySide 2 { 3 None = 1, 4 LeftToRight = 1 >> 1, 5 RightToLeft = 1 >> 2, 6 TopBottom = 1 >> 3, 7 BottomToTop = 1 >> 4, 8 LeftToLeft = 1 >> 5, 9 RightToRight = 1 >> 6, 10 TopToTop = 1 >> 7, 11 BottomToBottom = 1 >> 8, 12 } 13 14 public class Rect2DCompare : IComparer<Rect2D> 15 { 16 public int Compare(Rect2D? x, Rect2D? y) 17 { 18 if(x is null && y is null) return 0; 19 if(x is null && y is not null) return -1; 20 if (x is not null && y is null) return 1; 21 return x.CompareTo(y); 22 } 23 } 24 25 public class Rect2D : IComparable<Rect2D> 26 { 27 public int X { get; set; } 28 public int Y { get; set; } 29 public int Width { get; set; } 30 public int Height { get; set; } 31 public bool CanSplit { get; set; } 32 public int Right { get => Right; } 33 public int Bottom { get => Bottom; } 34 35 public override string ToString() 36 { 37 return $"{X},{Y},{Width},{Height}"; 38 } 39 public Rect2D() { } 40 public Rect2D(int x, int y, int width, int height, bool canSplit = true) 41 { 42 X = x; 43 Y = y; 44 Width = width; Height = height; 45 CanSplit = canSplit; 46 } 47 48 public int CompareTo(Rect2D? y) 49 { 50 if(y == null) return 1; 51 return (X * Y).CompareTo(y.X * y.Y); 52 } 53 54 /// <summary> 55 /// 不相切但相交 56 /// </summary> 57 /// <param name="rect"></param> 58 /// <returns></returns> 59 public bool IsIntersect(Rect2D rect) 60 { 61 return !(Right <= rect.X || Bottom <= rect.Y || X >= rect.Right || Y >= rect.Bottom); 62 } 63 64 /// <summary> 65 /// 获取本矩形于目标矩形相切的边 66 /// </summary> 67 /// <param name="rect">目标矩形</param> 68 /// <param name="intersect">是否包括同侧相切的情况,例如本矩形左侧与目标矩形左侧相切</param> 69 /// <returns>返回本矩形相交的边</returns> 70 public TanglecySide TangencySides(Rect2D rect, bool includeSameSide = false) 71 { 72 TanglecySide sideToSide = TanglecySide.None; 73 if (!IsIntersect(rect)) return sideToSide; 74 if (this.X == rect.Right) sideToSide |= TanglecySide.LeftToRight; 75 if (this.Y == rect.Bottom) sideToSide |= TanglecySide.TopBottom; 76 if (this.Right == rect.X) sideToSide |= TanglecySide.RightToLeft; 77 if (this.Bottom == rect.Y) sideToSide |= TanglecySide.BottomToTop; 78 if (includeSameSide) 79 { 80 if (X == rect.X) sideToSide |= TanglecySide.LeftToLeft; 81 if (Y == rect.Y) sideToSide |= TanglecySide.TopToTop; 82 if (Right == rect.Right) sideToSide |= TanglecySide.RightToRight; 83 if (X + Height == rect.Bottom) sideToSide |= TanglecySide.BottomToBottom; 84 } 85 return sideToSide; 86 } 87 88 /// <summary> 89 /// 判断点是否在矩形上,包括在边上 90 /// </summary> 91 /// <param name="x"></param> 92 /// <param name="y"></param> 93 /// <returns></returns> 94 public bool Contains(int x, int y) => x >= X && y >= Y && x <= Right && y <= Bottom; 95 /// <summary> 96 /// 判断rect整个区域是否在矩形上,包括相切 97 /// </summary> 98 /// <param name="rect"></param> 99 /// <returns></returns> 100 public bool Contains(Rect2D rect) => X <= rect.X && rect.Right <= Right && Y <= rect.Y && rect.Bottom <= Bottom; 101 102 /// <summary> 103 /// 传入矩形按四个顶点进行拆分 104 /// </summary> 105 /// <param name="rect">不可拆分的矩形</param> 106 /// <returns>返回拆分后的矩形列表</returns> 107 public List<Rect2D> GetUnIntersectAreas(Rect2D rect) 108 { 109 if (this.CanSplit == rect.CanSplit) return null; 110 if (!this.CanSplit) return rect.GetUnIntersectAreas(this); 111 if (!IsIntersect(rect)) return null; 112 if (rect.Contains(this)) return null; 113 List<Rect2D> result = new List<Rect2D>(); 114 115 //当被拆分的矩形在左上侧时才有左上角矩形 116 Rect2D leftTop = new Rect2D 117 { 118 X = Math.Min(X, rect.X), 119 Y = Math.Min(Y, rect.Y), 120 Width = Math.Max(rect.X - X, 0), 121 Height = Math.Max(rect.Y - Y, 0), 122 CanSplit = true, 123 }; 124 if (leftTop.Width > 0 && leftTop.Height > 0) 125 result.Add(leftTop); 126 127 //当被拆分的矩形在右上侧时才有右上角矩形 128 Rect2D rightTop = new Rect2D 129 { 130 X = Math.Min(Right, rect.Right), 131 Y = Math.Min(Y, rect.Y), 132 Width = Math.Max(0, Right - rect.Right), 133 Height = Math.Max(0, rect.Y - Y), 134 CanSplit = true, 135 }; 136 if (rightTop.Width > 0 && rightTop.Height > 0) 137 result.Add(rightTop); 138 139 //当被拆分的矩形在上侧时才有上侧矩形 140 Rect2D top = new Rect2D 141 { 142 X = Math.Max(X, rect.X), 143 Y = Math.Min(Y, rect.Y), 144 Width = Math.Max(0, Width - leftTop.Width - rightTop.Width), 145 Height = Math.Max(0, leftTop.Height), 146 CanSplit = true, 147 }; 148 if(top.Width > 0 && top.Height > 0) result.Add(top); 149 150 //当被拆分的矩形在左下侧时才有左下角矩形 151 Rect2D leftBottom = new Rect2D 152 { 153 X = Math.Min(X, rect.X), 154 Y = Math.Min(rect.Bottom, Bottom), 155 Width = Math.Max(rect.X - X, 0), 156 Height = Math.Max(0, Bottom - rect.Bottom), 157 CanSplit = true, 158 }; 159 if (leftBottom.Width > 0 && leftBottom.Height > 0) result.Add(leftBottom); 160 161 //当被拆分的矩形在右下侧时才有右下角矩形 162 Rect2D rightBottom = new Rect2D 163 { 164 X = Math.Min(Right, rect.Right), 165 Y = Math.Min(rect.Bottom, Bottom), 166 Width = Math.Max(0, Right - rect.Right), 167 Height = Math.Max(0, Bottom - rect.Bottom), 168 CanSplit = true, 169 }; 170 if (rightBottom.Width > 0 && rightBottom.Height > 0) result.Add(rightBottom); 171 172 //当被拆分的矩形在下侧时才有下侧矩形 173 Rect2D bottom = new Rect2D 174 { 175 X = Math.Max(X, rect.X), 176 Y = Math.Min(rect.Bottom, Bottom), 177 Width = Math.Max(0, Width - leftTop.Width - rightTop.Width), 178 Height = Math.Max(0, Bottom - rect.Bottom), 179 CanSplit = true, 180 }; 181 if (bottom.Width > 0 && bottom.Height > 0) result.Add(bottom); 182 183 //当被拆分的矩形在左侧时才有左侧矩形 184 Rect2D left = new Rect2D 185 { 186 X = Math.Min(X, rect.X), 187 Y = Math.Max(Y, rect.Y), 188 Width = Math.Max(rect.X - X, 0), 189 Height = Height - leftTop.Height - leftBottom.Height, 190 CanSplit = true, 191 }; 192 if (left.Width > 0 && left.Height > 0) result.Add(left); 193 194 195 //当被拆分的矩形在右侧时才有右侧矩形 196 Rect2D right = new Rect2D 197 { 198 X = Math.Min(Right, rect.Right), 199 Y = Math.Max(Y, rect.Y), 200 Width = Math.Max(0, Right - rect.Right), 201 Height = Height - rightTop.Height - rightBottom.Height, 202 CanSplit = true, 203 }; 204 if (right.Width > 0 && right.Height > 0) result.Add(right); 205 return result; 206 } 207 208 /// <summary> 209 /// 传入被拆分的矩形和填充的不可拆分的矩形列表 210 /// </summary> 211 /// <param name="container">被拆分的矩形</param> 212 /// <param name="noSplitRects">填充的矩形列表</param> 213 /// <returns></returns> 214 public static List<Rect2D> GetUnIntersectAreas(Rect2D container, List<Rect2D> noSplitRects) 215 { 216 var rects = new List<Rect2D> { container }; 217 foreach (var item in noSplitRects) 218 { 219 List<Rect2D> items = new List<Rect2D>(); 220 int i = 0; 221 while (i < rects.Count) 222 { 223 var list = rects[i].GetUnIntersectAreas(item); 224 if (list != null && list.Count > 0) 225 { 226 items.AddRange(list); 227 rects.RemoveAt(i); 228 } 229 else if (item.Contains(rects[i])) 230 rects.RemoveAt(i); 231 else 232 { 233 i++; 234 } 235 } 236 if (items.Count > 0) 237 { 238 rects.AddRange(items); 239 } 240 } 241 return rects; 242 } 243 }