mirror of
https://github.com/gonum/gonum.git
synced 2025-10-05 23:26:52 +08:00
112 lines
3.0 KiB
Go
112 lines
3.0 KiB
Go
// Copyright ©2022 The Gonum Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package r2
|
|
|
|
import "math"
|
|
|
|
// Box is a 2D bounding box. Well formed Boxes have
|
|
// Min components smaller than Max components.
|
|
type Box struct {
|
|
Min, Max Vec
|
|
}
|
|
|
|
// NewBox is shorthand for Box{Min:Vec{x0,y0}, Max:Vec{x1,y1}}.
|
|
// The sides are swapped so that the resulting Box is well formed.
|
|
func NewBox(x0, y0, x1, y1 float64) Box {
|
|
return Box{
|
|
Min: Vec{X: math.Min(x0, x1), Y: math.Min(y0, y1)},
|
|
Max: Vec{X: math.Max(x0, x1), Y: math.Max(y0, y1)},
|
|
}
|
|
}
|
|
|
|
// Size returns the size of the Box.
|
|
func (a Box) Size() Vec {
|
|
return Sub(a.Max, a.Min)
|
|
}
|
|
|
|
// Center returns the center of the Box.
|
|
func (a Box) Center() Vec {
|
|
return Scale(0.5, Add(a.Min, a.Max))
|
|
}
|
|
|
|
// IsEmpty returns true if a Box's volume is zero
|
|
// or if a Min component is greater than its Max component.
|
|
func (a Box) Empty() bool {
|
|
return a.Min.X >= a.Max.X || a.Min.Y >= a.Max.Y
|
|
}
|
|
|
|
// Vertices returns a slice of the 4 vertices
|
|
// corresponding to each of the Box's corners.
|
|
//
|
|
// The order of the vertices is CCW in the XY plane starting at the box minimum.
|
|
// If viewing box from +Z position the ordering is as follows:
|
|
// 1. Bottom left.
|
|
// 2. Bottom right.
|
|
// 3. Top right.
|
|
// 4. Top left.
|
|
func (a Box) Vertices() []Vec {
|
|
return []Vec{
|
|
0: a.Min,
|
|
1: {a.Max.X, a.Min.Y},
|
|
2: a.Max,
|
|
3: {a.Min.X, a.Max.Y},
|
|
}
|
|
}
|
|
|
|
// Union returns a box enclosing both the receiver and argument Boxes.
|
|
func (a Box) Union(b Box) Box {
|
|
if a.Empty() {
|
|
return b
|
|
}
|
|
if b.Empty() {
|
|
return a
|
|
}
|
|
return Box{
|
|
Min: minElem(a.Min, b.Min),
|
|
Max: maxElem(a.Max, b.Max),
|
|
}
|
|
}
|
|
|
|
// Add adds v to the bounding box components.
|
|
// It is the equivalent of translating the Box by v.
|
|
func (a Box) Add(v Vec) Box {
|
|
return Box{Add(a.Min, v), Add(a.Max, v)}
|
|
}
|
|
|
|
// Scale returns a new Box scaled by a size vector around its center.
|
|
// The scaling is done element wise, which is to say the Box's X size is
|
|
// scaled by v.X. Negative components of v are interpreted as zero.
|
|
func (a Box) Scale(v Vec) Box {
|
|
v = maxElem(v, Vec{})
|
|
// TODO(soypat): Probably a better way to do this.
|
|
return centeredBox(a.Center(), mulElem(v, a.Size()))
|
|
}
|
|
|
|
// centeredBox creates a Box with a given center and size. Size's negative
|
|
// components are interpreted as zero so that resulting box is well formed.
|
|
func centeredBox(center, size Vec) Box {
|
|
size = maxElem(size, Vec{})
|
|
half := Scale(0.5, absElem(size))
|
|
return Box{Min: Sub(center, half), Max: Add(center, half)}
|
|
}
|
|
|
|
// Contains returns true if v is contained within the bounds of the Box.
|
|
func (a Box) Contains(v Vec) bool {
|
|
if a.Empty() {
|
|
return v == a.Min && v == a.Max
|
|
}
|
|
return a.Min.X <= v.X && v.X <= a.Max.X &&
|
|
a.Min.Y <= v.Y && v.Y <= a.Max.Y
|
|
}
|
|
|
|
// Canon returns the canonical version of b. The returned Box has minimum
|
|
// and maximum coordinates swapped if necessary so that it is well-formed.
|
|
func (b Box) Canon() Box {
|
|
return Box{
|
|
Min: minElem(b.Min, b.Max),
|
|
Max: maxElem(b.Min, b.Max),
|
|
}
|
|
}
|