File: box.go

package info (click to toggle)
golang-gonum-v1-gonum 0.15.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 18,792 kB
  • sloc: asm: 6,252; fortran: 5,271; sh: 377; ruby: 211; makefile: 98
file content (111 lines) | stat: -rw-r--r-- 3,067 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// 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))
}

// Empty 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),
	}
}