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