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  }