github.com/searKing/golang/go@v1.2.74/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 // Add returns the rectangle r translated by p. 44 func (r Rectangle[E]) Add(p Point[E]) Rectangle[E] { 45 return Rectangle[E]{ 46 Point[E]{r.Min.X + p.X, r.Min.Y + p.Y}, 47 Point[E]{r.Max.X + p.X, r.Max.Y + p.Y}, 48 } 49 } 50 51 // Sub returns the rectangle r translated by -p. 52 func (r Rectangle[E]) Sub(p Point[E]) Rectangle[E] { 53 return Rectangle[E]{ 54 Point[E]{r.Min.X - p.X, r.Min.Y - p.Y}, 55 Point[E]{r.Max.X - p.X, r.Max.Y - p.Y}, 56 } 57 } 58 59 // Mul returns the rectangle r*k. 60 func (r Rectangle[E]) Mul(k E) Rectangle[E] { 61 return Rectangle[E]{r.Min.Mul(k), r.Max.Mul(k)} 62 } 63 64 // Div returns the rectangle p/k. 65 func (r Rectangle[E]) Div(k E) Rectangle[E] { 66 return Rectangle[E]{r.Min.Div(k), r.Max.Div(k)} 67 } 68 69 // Inset returns the rectangle r inset by n, which may be negative. If either 70 // of r's dimensions is less than 2*n then an empty rectangle near the center 71 // of r will be returned. 72 func (r Rectangle[E]) Inset(n E) Rectangle[E] { 73 if r.Dx() < 2*n { 74 r.Min.X = (r.Min.X + r.Max.X) / 2 75 r.Max.X = r.Min.X 76 } else { 77 r.Min.X += n 78 r.Max.X -= n 79 } 80 if r.Dy() < 2*n { 81 r.Min.Y = (r.Min.Y + r.Max.Y) / 2 82 r.Max.Y = r.Min.Y 83 } else { 84 r.Min.Y += n 85 r.Max.Y -= n 86 } 87 return r 88 } 89 90 // Intersect returns the largest rectangle contained by both r and s. If the 91 // two rectangles do not overlap then the zero rectangle will be returned. 92 func (r Rectangle[E]) Intersect(s Rectangle[E]) Rectangle[E] { 93 if r.Min.X < s.Min.X { 94 r.Min.X = s.Min.X 95 } 96 if r.Min.Y < s.Min.Y { 97 r.Min.Y = s.Min.Y 98 } 99 if r.Max.X > s.Max.X { 100 r.Max.X = s.Max.X 101 } 102 if r.Max.Y > s.Max.Y { 103 r.Max.Y = s.Max.Y 104 } 105 // Letting r0 and s0 be the values of r and s at the time that the method 106 // is called, this next line is equivalent to: 107 // 108 // if max(r0.Min.X, s0.Min.X) >= min(r0.Max.X, s0.Max.X) || likewiseForY { etc } 109 if r.Empty() { 110 var zero Rectangle[E] 111 return zero 112 } 113 return r 114 } 115 116 // Union returns the smallest rectangle that contains both r and s. 117 func (r Rectangle[E]) Union(s Rectangle[E]) Rectangle[E] { 118 if r.Empty() { 119 return s 120 } 121 if s.Empty() { 122 return r 123 } 124 if r.Min.X > s.Min.X { 125 r.Min.X = s.Min.X 126 } 127 if r.Min.Y > s.Min.Y { 128 r.Min.Y = s.Min.Y 129 } 130 if r.Max.X < s.Max.X { 131 r.Max.X = s.Max.X 132 } 133 if r.Max.Y < s.Max.Y { 134 r.Max.Y = s.Max.Y 135 } 136 return r 137 } 138 139 // Empty reports whether the rectangle contains no points. 140 func (r Rectangle[E]) Empty() bool { 141 return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y 142 } 143 144 // Eq reports whether r and s contain the same set of points. All empty 145 // rectangles are considered equal. 146 func (r Rectangle[E]) Eq(s Rectangle[E]) bool { 147 return r == s || r.Empty() && s.Empty() 148 } 149 150 // Overlaps reports whether r and s have a non-empty intersection. 151 func (r Rectangle[E]) Overlaps(s Rectangle[E]) bool { 152 return !r.Empty() && !s.Empty() && 153 r.Min.X < s.Max.X && s.Min.X < r.Max.X && 154 r.Min.Y < s.Max.Y && s.Min.Y < r.Max.Y 155 } 156 157 // In reports whether every point in r is in s. 158 func (r Rectangle[E]) In(s Rectangle[E]) bool { 159 if r.Empty() { 160 return true 161 } 162 // Note that r.Max is an exclusive bound for r, so that r.In(s) 163 // does not require that r.Max.In(s). 164 return s.Min.X <= r.Min.X && r.Max.X <= s.Max.X && 165 s.Min.Y <= r.Min.Y && r.Max.Y <= s.Max.Y 166 } 167 168 // Canon returns the canonical version of r. The returned rectangle has minimum 169 // and maximum coordinates swapped if necessary so that it is well-formed. 170 func (r Rectangle[E]) Canon() Rectangle[E] { 171 if r.Max.X < r.Min.X { 172 r.Min.X, r.Max.X = r.Max.X, r.Min.X 173 } 174 if r.Max.Y < r.Min.Y { 175 r.Min.Y, r.Max.Y = r.Max.Y, r.Min.Y 176 } 177 return r 178 } 179 180 // At implements the Image interface. 181 func (r Rectangle[E]) At(x, y E) color.Color { 182 if (Point[E]{x, y}).In(r) { 183 return color.Opaque 184 } 185 return color.Transparent 186 } 187 188 // RGBA64At implements the RGBA64Image interface. 189 func (r Rectangle[E]) RGBA64At(x, y E) color.RGBA64 { 190 if (Point[E]{x, y}).In(r) { 191 return color.RGBA64{R: 0xffff, G: 0xffff, B: 0xffff, A: 0xffff} 192 } 193 return color.RGBA64{} 194 } 195 196 // Bounds implements the Image interface. 197 func (r Rectangle[E]) Bounds() Rectangle[E] { 198 return r 199 } 200 201 // ColorModel implements the Image interface. 202 func (r Rectangle[E]) ColorModel() color.Model { 203 return color.Alpha16Model 204 } 205 206 func (r Rectangle[E]) RoundRectangle() image.Rectangle { 207 return image.Rect(round(r.Min.X), round(r.Min.Y), round(r.Max.X), round(r.Max.Y)) 208 } 209 210 // UnionPoints returns the smallest rectangle that contains all points. 211 func (r Rectangle[E]) UnionPoints(pts ...Point[E]) Rectangle[E] { 212 if len(pts) == 0 { 213 return r 214 } 215 var pos int 216 if r.Empty() { // an empty rectangle is an empty set, Not a point 217 r.Min = pts[0] 218 r.Max = pts[0] 219 pos = 1 220 } 221 for _, p := range pts[pos:] { 222 if p.X < r.Min.X { 223 r.Min.X = p.X 224 } 225 if p.Y < r.Min.Y { 226 r.Min.Y = p.Y 227 } 228 if p.X > r.Max.X { 229 r.Max.X = p.X 230 } 231 if p.Y > r.Max.Y { 232 r.Max.Y = p.Y 233 } 234 } 235 return r 236 } 237 238 // ScaleByFactor scale rect to factor*size 239 func (r Rectangle[E]) ScaleByFactor(factor Point[E]) Rectangle[E] { 240 if r.Empty() { 241 return r 242 } 243 factor = factor.Sub(Pt[E](1, 1)) 244 minOffset := Point[E]{ 245 X: r.Dx() * factor.X / 2, 246 Y: r.Dy() * factor.Y / 2, 247 } 248 maxOffset := Point[E]{ 249 X: r.Dx() * factor.X, 250 Y: r.Dy() * factor.Y, 251 }.Sub(minOffset) 252 253 return Rectangle[E]{ 254 Min: Point[E]{X: r.Min.X - minOffset.X, Y: r.Min.Y - minOffset.Y}, 255 Max: Point[E]{X: r.Max.X + maxOffset.X, Y: r.Max.Y + maxOffset.Y}, 256 } 257 } 258 259 // Rect is shorthand for Rectangle[E]{Pt(x0, y0), Pt(x1, y1)}. The returned 260 // rectangle has minimum and maximum coordinates swapped if necessary so that 261 // it is well-formed. 262 func Rect[E constraints.Number](x0, y0, x1, y1 E) Rectangle[E] { 263 if x0 > x1 { 264 x0, x1 = x1, x0 265 } 266 if y0 > y1 { 267 y0, y1 = y1, y0 268 } 269 return Rectangle[E]{Point[E]{x0, y0}, Point[E]{x1, y1}} 270 } 271 272 func FromRectInt[E constraints.Number](rect image.Rectangle) Rectangle[E] { 273 return Rect(E(rect.Min.X), E(rect.Min.Y), E(rect.Max.X), E(rect.Max.Y)) 274 } 275 276 func round[E constraints.Number](x E) int { 277 kind := reflect.TypeOf(x).Kind() 278 switch kind { 279 case reflect.Float32, reflect.Float64: 280 return int(math.Round(float64(x))) 281 } 282 return int(x) 283 }