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  }