github.com/searKing/golang/go@v1.2.117/exp/image/geom.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  	"math"
     9  
    10  	constraints_ "github.com/searKing/golang/go/exp/constraints"
    11  	"golang.org/x/exp/constraints"
    12  )
    13  
    14  // UnionPoints returns the smallest rectangle that contains all points.
    15  // an empty rectangle is a empty set, Not a point
    16  func UnionPoints[E constraints_.Number](pts ...Point[E]) Rectangle[E] {
    17  	if len(pts) == 0 {
    18  		return Rectangle[E]{}
    19  	}
    20  
    21  	r := Rectangle[E]{
    22  		Min: pts[0],
    23  		Max: pts[0],
    24  	}
    25  	for _, p := range pts[1:] {
    26  		if p.X < r.Min.X {
    27  			r.Min.X = p.X
    28  		}
    29  		if p.Y < r.Min.Y {
    30  			r.Min.Y = p.Y
    31  		}
    32  		if p.X > r.Max.X {
    33  			r.Max.X = p.X
    34  		}
    35  		if p.Y > r.Max.Y {
    36  			r.Max.Y = p.Y
    37  		}
    38  	}
    39  	return r
    40  }
    41  
    42  // UnionRectangles returns the smallest rectangle that contains all rectangles, empty rectangles excluded.
    43  func UnionRectangles[E constraints_.Number](rs ...Rectangle[E]) Rectangle[E] {
    44  	var ur Rectangle[E]
    45  	for _, r := range rs {
    46  		ur = ur.Union(r)
    47  	}
    48  	return ur
    49  }
    50  
    51  // ScaleLineSegment segment's size to length flexible in limit
    52  func ScaleLineSegment[E constraints_.Number](segment Point[E], length E, limit Point[E]) Point[E] {
    53  	var swapped = segment.X > segment.Y
    54  	if swapped { // swap (X,Y) -> (Y,X)
    55  		segment.X, segment.Y = segment.Y, segment.X
    56  		limit.X, limit.Y = limit.Y, limit.X
    57  	}
    58  
    59  	dx := length - (segment.Y - segment.X)
    60  	segment.X -= E(math.Round(float64(dx) / 2.0))
    61  	if segment.X < limit.X {
    62  		segment.X = limit.X
    63  	}
    64  	segment.Y = segment.X + length
    65  	if segment.Y > limit.Y {
    66  		segment.Y = limit.Y
    67  		segment.X = segment.Y - length
    68  		if segment.X < limit.X {
    69  			segment.X = limit.X
    70  		}
    71  	}
    72  
    73  	if swapped {
    74  		segment.X, segment.Y = segment.Y, segment.X
    75  	}
    76  	return segment
    77  }
    78  
    79  // ScaleRectangleBySize scale rect to size flexible in limit
    80  func ScaleRectangleBySize[E constraints.Integer](rect Rectangle[E], size Point[E], limit Rectangle[E]) Rectangle[E] {
    81  	// padding in x direction
    82  	x := ScaleLineSegment(Pt(rect.Min.X, rect.Max.X), size.X, Pt(limit.Min.X, limit.Max.X))
    83  	// padding in y direction
    84  	y := ScaleLineSegment(Pt(rect.Min.Y, rect.Max.Y), size.Y, Pt(limit.Min.Y, limit.Max.Y))
    85  
    86  	return limit.Intersect(Rect(x.X, y.X, x.Y, y.Y))
    87  }