github.com/searKing/golang/go@v1.2.117/exp/image/rectangle.go (about) 1 // Copyright 2023 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package image 6 7 import ( 8 "image" 9 "image/color" 10 "math" 11 "reflect" 12 13 "github.com/searKing/golang/go/exp/constraints" 14 ) 15 16 type Rectangle[E constraints.Number] struct { 17 Min, Max Point[E] 18 } 19 20 // String returns a string representation of r like "(3,4)-(6,5)". 21 func (r Rectangle[E]) String() string { 22 return r.Min.String() + "-" + r.Max.String() 23 } 24 25 // Dx returns r's width. 26 func (r Rectangle[E]) Dx() E { 27 return r.Max.X - r.Min.X 28 } 29 30 // Dy returns r's height. 31 func (r Rectangle[E]) Dy() E { 32 return r.Max.Y - r.Min.Y 33 } 34 35 // Size returns r's width and height. 36 func (r Rectangle[E]) Size() Point[E] { 37 return Point[E]{ 38 r.Max.X - r.Min.X, 39 r.Max.Y - r.Min.Y, 40 } 41 } 42 43 // Area returns r's area, as width * height. 44 func (r Rectangle[E]) Area() E { 45 dx, dy := r.Dx(), r.Dy() 46 if dx <= 0 || dy <= 0 { 47 return 0 48 } 49 return dx * dy 50 } 51 52 // Add returns the rectangle r translated by p. 53 func (r Rectangle[E]) Add(p Point[E]) Rectangle[E] { 54 return Rectangle[E]{ 55 Point[E]{r.Min.X + p.X, r.Min.Y + p.Y}, 56 Point[E]{r.Max.X + p.X, r.Max.Y + p.Y}, 57 } 58 } 59 60 // Sub returns the rectangle r translated by -p. 61 func (r Rectangle[E]) Sub(p Point[E]) Rectangle[E] { 62 return Rectangle[E]{ 63 Point[E]{r.Min.X - p.X, r.Min.Y - p.Y}, 64 Point[E]{r.Max.X - p.X, r.Max.Y - p.Y}, 65 } 66 } 67 68 // Mul returns the rectangle r translated by r*k. 69 func (r Rectangle[E]) Mul(k E) Rectangle[E] { 70 return Rectangle[E]{r.Min.Mul(k), r.Max.Mul(k)} 71 } 72 73 // MulPoint returns the rectangle r translated by r.*p. 74 func (r Rectangle[E]) MulPoint(p Point[E]) Rectangle[E] { 75 return Rectangle[E]{r.Min.MulPoint(p), r.Max.MulPoint(p)} 76 } 77 78 // MulRectangle returns the rectangle r translated by r.*p. 79 func (r Rectangle[E]) MulRectangle(p Rectangle[E]) Rectangle[E] { 80 return Rectangle[E]{r.Min.MulPoint(p.Min), r.Max.MulPoint(p.Max)} 81 } 82 83 // Div returns the rectangle r translated by r/k. 84 func (r Rectangle[E]) Div(k E) Rectangle[E] { 85 return Rectangle[E]{r.Min.Div(k), r.Max.Div(k)} 86 } 87 88 // DivPoint returns the rectangle r translated by r./p. 89 func (r Rectangle[E]) DivPoint(p Point[E]) Rectangle[E] { 90 return Rectangle[E]{r.Min.DivPoint(p), r.Max.DivPoint(p)} 91 } 92 93 // DivRectangle returns the rectangle r translated by r./p. 94 func (r Rectangle[E]) DivRectangle(p Rectangle[E]) Rectangle[E] { 95 return Rectangle[E]{r.Min.DivPoint(p.Min), r.Max.DivPoint(p.Max)} 96 } 97 98 // Inset returns the rectangle r inset by n, which may be negative. If either 99 // of r's dimensions is less than 2*n then an empty rectangle near the center 100 // of r will be returned. 101 func (r Rectangle[E]) Inset(n E) Rectangle[E] { 102 return r.InsetPoint(Point[E]{X: n, Y: n}) 103 } 104 105 // InsetPoint returns the rectangle r inset by n, which may be negative. If either 106 // of r's dimensions is less than n.X+n.Y then an empty rectangle near the center 107 // of r will be returned. 108 func (r Rectangle[E]) InsetPoint(n Point[E]) Rectangle[E] { 109 return r.InsetRectangle(Rectangle[E]{Min: n, Max: n}) 110 } 111 112 // InsetRectangle returns the rectangle r inset by n, which may be negative. If either 113 // of r's dimensions is less than (n.Min.X+n.Max.X, n.Min.Y+n.Max.Y), then an empty rectangle near the center 114 // of r will be returned. 115 func (r Rectangle[E]) InsetRectangle(n Rectangle[E]) Rectangle[E] { 116 if r.Dx() < n.Min.X+n.Max.X { 117 r.Min.X = (r.Min.X + r.Max.X) / 2 118 r.Max.X = r.Min.X 119 } else { 120 r.Min.X += n.Min.X 121 r.Max.X -= n.Max.X 122 } 123 if r.Dy() < n.Min.Y+n.Max.Y { 124 r.Min.Y = (r.Min.Y + r.Max.Y) / 2 125 r.Max.Y = r.Min.Y 126 } else { 127 r.Min.Y += n.Min.Y 128 r.Max.Y -= n.Max.Y 129 } 130 return r 131 } 132 133 // Intersect returns the largest rectangle contained by both r and s. If the 134 // two rectangles do not overlap then the zero rectangle will be returned. 135 func (r Rectangle[E]) Intersect(s Rectangle[E]) Rectangle[E] { 136 if r.Min.X < s.Min.X { 137 r.Min.X = s.Min.X 138 } 139 if r.Min.Y < s.Min.Y { 140 r.Min.Y = s.Min.Y 141 } 142 if r.Max.X > s.Max.X { 143 r.Max.X = s.Max.X 144 } 145 if r.Max.Y > s.Max.Y { 146 r.Max.Y = s.Max.Y 147 } 148 // Letting r0 and s0 be the values of r and s at the time that the method 149 // is called, this next line is equivalent to: 150 // 151 // if max(r0.Min.X, s0.Min.X) >= min(r0.Max.X, s0.Max.X) || likewiseForY { etc } 152 if r.Empty() { 153 var zero Rectangle[E] 154 return zero 155 } 156 return r 157 } 158 159 // Border returns four rectangles that together contain those points between r 160 // and r.Inset(inset). Visually: 161 // 162 // 00000000 163 // 00000000 164 // 11....22 165 // 11....22 166 // 11....22 167 // 33333333 168 // 33333333 169 // 170 // The inset may be negative, in which case the points will be outside r. 171 // 172 // Some of the returned rectangles may be empty. None of the returned 173 // rectangles will overlap. 174 func (r Rectangle[E]) Border(inset E) [4]Rectangle[E] { 175 return r.BorderPoint(Point[E]{ 176 X: inset, 177 Y: inset, 178 }) 179 } 180 181 // BorderPoint returns four rectangles that together contain those points between r 182 // and r.Inset(inset). Visually: 183 // 184 // 00000000 185 // 00000000 186 // 11....22 187 // 11....22 188 // 11....22 189 // 33333333 190 // 33333333 191 // 192 // The inset may be negative, in which case the points will be outside r. 193 // 194 // Some of the returned rectangles may be empty. None of the returned 195 // rectangles will overlap. 196 func (r Rectangle[E]) BorderPoint(inset Point[E]) [4]Rectangle[E] { 197 return r.BorderRectangle(Rectangle[E]{ 198 Min: inset, 199 Max: inset, 200 }) 201 } 202 203 // BorderRectangle returns four rectangles that together contain those points between r 204 // and r.Inset(inset). Visually: 205 // 206 // 00000000 207 // 00000000 208 // 11....22 209 // 11....22 210 // 11....22 211 // 33333333 212 // 33333333 213 // 214 // The inset may be negative, in which case the points will be outside r. 215 // 216 // Some of the returned rectangles may be empty. None of the returned 217 // rectangles will overlap. 218 func (r Rectangle[E]) BorderRectangle(inset Rectangle[E]) [4]Rectangle[E] { 219 if inset.Min.X == 0 && inset.Min.Y == 0 && inset.Max.X == 0 && inset.Max.Y == 0 { 220 return [4]Rectangle[E]{} 221 } 222 if r.Dx() <= inset.Min.X+inset.Max.X || r.Dy() <= inset.Min.Y+inset.Max.Y { 223 return [4]Rectangle[E]{r} 224 } 225 226 x := [4]E{ 227 r.Min.X, 228 r.Min.X + inset.Min.X, 229 r.Max.X - inset.Max.X, 230 r.Max.X, 231 } 232 y := [4]E{ 233 r.Min.Y, 234 r.Min.Y + inset.Min.Y, 235 r.Max.Y - inset.Max.Y, 236 r.Max.Y, 237 } 238 if inset.Min.X < 0 { 239 x[0], x[1] = x[1], x[0] 240 } 241 if inset.Max.X < 0 { 242 x[2], x[3] = x[3], x[2] 243 } 244 if inset.Min.Y < 0 { 245 y[0], y[1] = y[1], y[0] 246 } 247 if inset.Max.Y < 0 { 248 y[2], y[3] = y[3], y[2] 249 } 250 251 // The top and bottom sections are responsible for filling the corners. 252 // The top and bottom sections go from x[0] to x[3], across the y's. 253 // The left and right sections go from y[1] to y[2], across the x's. 254 255 return [4]Rectangle[E]{{ 256 // Top section. 257 Min: Point[E]{ 258 X: x[0], 259 Y: y[0], 260 }, 261 Max: Point[E]{ 262 X: x[3], 263 Y: y[1], 264 }, 265 }, { 266 // Left section. 267 Min: Point[E]{ 268 X: x[0], 269 Y: y[1], 270 }, 271 Max: Point[E]{ 272 X: x[1], 273 Y: y[2], 274 }, 275 }, { 276 // Right section. 277 Min: Point[E]{ 278 X: x[2], 279 Y: y[1], 280 }, 281 Max: Point[E]{ 282 X: x[3], 283 Y: y[2], 284 }, 285 }, { 286 // Bottom section. 287 Min: Point[E]{ 288 X: x[0], 289 Y: y[2], 290 }, 291 Max: Point[E]{ 292 X: x[3], 293 Y: y[3], 294 }, 295 }} 296 } 297 298 // Union returns the smallest rectangle that contains both r and s. 299 func (r Rectangle[E]) Union(s Rectangle[E]) Rectangle[E] { 300 if r.Empty() { 301 return s 302 } 303 if s.Empty() { 304 return r 305 } 306 if r.Min.X > s.Min.X { 307 r.Min.X = s.Min.X 308 } 309 if r.Min.Y > s.Min.Y { 310 r.Min.Y = s.Min.Y 311 } 312 if r.Max.X < s.Max.X { 313 r.Max.X = s.Max.X 314 } 315 if r.Max.Y < s.Max.Y { 316 r.Max.Y = s.Max.Y 317 } 318 return r 319 } 320 321 // Empty reports whether the rectangle contains no points. 322 func (r Rectangle[E]) Empty() bool { 323 return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y 324 } 325 326 // Eq reports whether r and s contain the same set of points. All empty 327 // rectangles are considered equal. 328 func (r Rectangle[E]) Eq(s Rectangle[E]) bool { 329 return r == s || r.Empty() && s.Empty() 330 } 331 332 // Overlaps reports whether r and s have a non-empty intersection. 333 func (r Rectangle[E]) Overlaps(s Rectangle[E]) bool { 334 return !r.Empty() && !s.Empty() && 335 r.Min.X < s.Max.X && s.Min.X < r.Max.X && 336 r.Min.Y < s.Max.Y && s.Min.Y < r.Max.Y 337 } 338 339 // In reports whether every point in r is in s. 340 func (r Rectangle[E]) In(s Rectangle[E]) bool { 341 if r.Empty() { 342 return true 343 } 344 // Note that r.Max is an exclusive bound for r, so that r.In(s) 345 // does not require that r.Max.In(s). 346 return s.Min.X <= r.Min.X && r.Max.X <= s.Max.X && 347 s.Min.Y <= r.Min.Y && r.Max.Y <= s.Max.Y 348 } 349 350 // Canon returns the canonical version of r. The returned rectangle has minimum 351 // and maximum coordinates swapped if necessary so that it is well-formed. 352 func (r Rectangle[E]) Canon() Rectangle[E] { 353 if r.Max.X < r.Min.X { 354 r.Min.X, r.Max.X = r.Max.X, r.Min.X 355 } 356 if r.Max.Y < r.Min.Y { 357 r.Min.Y, r.Max.Y = r.Max.Y, r.Min.Y 358 } 359 return r 360 } 361 362 // At implements the Image interface. 363 func (r Rectangle[E]) At(x, y E) color.Color { 364 if (Point[E]{x, y}).In(r) { 365 return color.Opaque 366 } 367 return color.Transparent 368 } 369 370 // RGBA64At implements the RGBA64Image interface. 371 func (r Rectangle[E]) RGBA64At(x, y E) color.RGBA64 { 372 if (Point[E]{x, y}).In(r) { 373 return color.RGBA64{R: 0xffff, G: 0xffff, B: 0xffff, A: 0xffff} 374 } 375 return color.RGBA64{} 376 } 377 378 // Bounds implements the Image interface. 379 func (r Rectangle[E]) Bounds() Rectangle[E] { 380 return r 381 } 382 383 // ColorModel implements the Image interface. 384 func (r Rectangle[E]) ColorModel() color.Model { 385 return color.Alpha16Model 386 } 387 388 func (r Rectangle[E]) RoundRectangle() image.Rectangle { 389 return image.Rect(round(r.Min.X), round(r.Min.Y), round(r.Max.X), round(r.Max.Y)) 390 } 391 392 // UnionPoints returns the smallest rectangle that contains all points. 393 func (r Rectangle[E]) UnionPoints(pts ...Point[E]) Rectangle[E] { 394 if len(pts) == 0 { 395 return r 396 } 397 var pos int 398 if r.Empty() { // an empty rectangle is an empty set, Not a point 399 r.Min = pts[0] 400 r.Max = pts[0] 401 pos = 1 402 } 403 for _, p := range pts[pos:] { 404 if p.X < r.Min.X { 405 r.Min.X = p.X 406 } 407 if p.Y < r.Min.Y { 408 r.Min.Y = p.Y 409 } 410 if p.X > r.Max.X { 411 r.Max.X = p.X 412 } 413 if p.Y > r.Max.Y { 414 r.Max.Y = p.Y 415 } 416 } 417 return r 418 } 419 420 // Scale scale rectangle's size to size, by expand from the mid-point of rect. 421 func (r Rectangle[E]) Scale(size Point[E]) Rectangle[E] { 422 size = size.Sub(r.Size()) 423 minOffset := size.Div(2) 424 maxOffset := size.Sub(minOffset) 425 426 return Rectangle[E]{ 427 Min: Point[E]{X: r.Min.X - minOffset.X, Y: r.Min.Y - minOffset.Y}, 428 Max: Point[E]{X: r.Max.X + maxOffset.X, Y: r.Max.Y + maxOffset.Y}, 429 } 430 } 431 432 // ScaleByFactor scale rectangle's size to factor * size, by expand from the mid-point of rect. 433 func (r Rectangle[E]) ScaleByFactor(factor Point[E]) Rectangle[E] { 434 if r.Empty() { 435 return r 436 } 437 return r.Scale(r.Size().MulPoint(factor)) 438 } 439 440 // FlexIn flex rect into box, shrink but not grow to fit the space available in its flex container 441 func (r Rectangle[E]) FlexIn(container Rectangle[E]) Rectangle[E] { 442 r2 := r 443 r2.Min = container.Min 444 r2.Max = container.Min.Add(r.Size()) 445 if r.Min.X > r2.Min.X { 446 r2.Max.X += r.Min.X - r2.Min.X 447 r2.Min.X = r.Min.X 448 } 449 if r.Min.Y > r2.Min.Y { 450 r2.Max.Y += r.Min.Y - r2.Min.Y 451 r2.Min.Y = r.Min.Y 452 } 453 if r2.Max.X > container.Max.X { 454 r2.Min.X -= r2.Max.X - container.Max.X 455 r2.Max.X = container.Max.X 456 } 457 if r2.Max.Y > container.Max.Y { 458 r2.Min.Y -= r2.Max.Y - container.Max.Y 459 r2.Max.Y = container.Max.Y 460 } 461 if r2.Min.X < container.Min.X { 462 r2.Min.X = container.Min.X 463 } 464 if r2.Min.Y < container.Min.Y { 465 r2.Min.Y = container.Min.Y 466 } 467 return r2 468 } 469 470 // Rect is shorthand for Rectangle[E]{Pt(x0, y0), Pt(x1, y1)}. The returned 471 // rectangle has minimum and maximum coordinates swapped if necessary so that 472 // it is well-formed. 473 func Rect[E constraints.Number](x0, y0, x1, y1 E) Rectangle[E] { 474 if x0 > x1 { 475 x0, x1 = x1, x0 476 } 477 if y0 > y1 { 478 y0, y1 = y1, y0 479 } 480 return Rectangle[E]{Point[E]{x0, y0}, Point[E]{x1, y1}} 481 } 482 483 func FromRectInt[E constraints.Number](rect image.Rectangle) Rectangle[E] { 484 return Rect(E(rect.Min.X), E(rect.Min.Y), E(rect.Max.X), E(rect.Max.Y)) 485 } 486 487 func round[E constraints.Number](x E) int { 488 kind := reflect.TypeOf(x).Kind() 489 switch kind { 490 case reflect.Float32, reflect.Float64: 491 return int(math.Round(float64(x))) 492 } 493 return int(x) 494 }