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 }