gonum.org/v1/gonum@v0.14.0/spatial/r2/box.go (about)

     1  // Copyright ©2022 The Gonum Authors. 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 r2
     6  
     7  import "math"
     8  
     9  // Box is a 2D bounding box. Well formed Boxes have
    10  // Min components smaller than Max components.
    11  type Box struct {
    12  	Min, Max Vec
    13  }
    14  
    15  // NewBox is shorthand for Box{Min:Vec{x0,y0}, Max:Vec{x1,y1}}.
    16  // The sides are swapped so that the resulting Box is well formed.
    17  func NewBox(x0, y0, x1, y1 float64) Box {
    18  	return Box{
    19  		Min: Vec{X: math.Min(x0, x1), Y: math.Min(y0, y1)},
    20  		Max: Vec{X: math.Max(x0, x1), Y: math.Max(y0, y1)},
    21  	}
    22  }
    23  
    24  // Size returns the size of the Box.
    25  func (a Box) Size() Vec {
    26  	return Sub(a.Max, a.Min)
    27  }
    28  
    29  // Center returns the center of the Box.
    30  func (a Box) Center() Vec {
    31  	return Scale(0.5, Add(a.Min, a.Max))
    32  }
    33  
    34  // IsEmpty returns true if a Box's volume is zero
    35  // or if a Min component is greater than its Max component.
    36  func (a Box) Empty() bool {
    37  	return a.Min.X >= a.Max.X || a.Min.Y >= a.Max.Y
    38  }
    39  
    40  // Vertices returns a slice of the 4 vertices
    41  // corresponding to each of the Box's corners.
    42  //
    43  // The order of the vertices is CCW in the XY plane starting at the box minimum.
    44  // If viewing box from +Z position the ordering is as follows:
    45  //  1. Bottom left.
    46  //  2. Bottom right.
    47  //  3. Top right.
    48  //  4. Top left.
    49  func (a Box) Vertices() []Vec {
    50  	return []Vec{
    51  		0: a.Min,
    52  		1: {a.Max.X, a.Min.Y},
    53  		2: a.Max,
    54  		3: {a.Min.X, a.Max.Y},
    55  	}
    56  }
    57  
    58  // Union returns a box enclosing both the receiver and argument Boxes.
    59  func (a Box) Union(b Box) Box {
    60  	if a.Empty() {
    61  		return b
    62  	}
    63  	if b.Empty() {
    64  		return a
    65  	}
    66  	return Box{
    67  		Min: minElem(a.Min, b.Min),
    68  		Max: maxElem(a.Max, b.Max),
    69  	}
    70  }
    71  
    72  // Add adds v to the bounding box components.
    73  // It is the equivalent of translating the Box by v.
    74  func (a Box) Add(v Vec) Box {
    75  	return Box{Add(a.Min, v), Add(a.Max, v)}
    76  }
    77  
    78  // Scale returns a new Box scaled by a size vector around its center.
    79  // The scaling is done element wise, which is to say the Box's X size is
    80  // scaled by v.X. Negative components of v are interpreted as zero.
    81  func (a Box) Scale(v Vec) Box {
    82  	v = maxElem(v, Vec{})
    83  	// TODO(soypat): Probably a better way to do this.
    84  	return centeredBox(a.Center(), mulElem(v, a.Size()))
    85  }
    86  
    87  // centeredBox creates a Box with a given center and size. Size's negative
    88  // components are interpreted as zero so that resulting box is well formed.
    89  func centeredBox(center, size Vec) Box {
    90  	size = maxElem(size, Vec{})
    91  	half := Scale(0.5, absElem(size))
    92  	return Box{Min: Sub(center, half), Max: Add(center, half)}
    93  }
    94  
    95  // Contains returns true if v is contained within the bounds of the Box.
    96  func (a Box) Contains(v Vec) bool {
    97  	if a.Empty() {
    98  		return v == a.Min && v == a.Max
    99  	}
   100  	return a.Min.X <= v.X && v.X <= a.Max.X &&
   101  		a.Min.Y <= v.Y && v.Y <= a.Max.Y
   102  }
   103  
   104  // Canon returns the canonical version of b. The returned Box has minimum
   105  // and maximum coordinates swapped if necessary so that it is well-formed.
   106  func (b Box) Canon() Box {
   107  	return Box{
   108  		Min: minElem(b.Min, b.Max),
   109  		Max: maxElem(b.Min, b.Max),
   110  	}
   111  }