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  }