-
Notifications
You must be signed in to change notification settings - Fork 2
/
geom.go
152 lines (116 loc) · 3.8 KB
/
geom.go
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package plot
import "math"
// Length defines canvas space size.
type Length = float64
// Point describes a canvas position or offset.
type Point struct{ X, Y Length }
// nanPoint describes a missing or invalid point.
var nanPoint = Point{
X: math.NaN(),
Y: math.NaN(),
}
// P is a convenience func for creating a point.
func P(x, y Length) Point { return Point{x, y} }
// Empty returns whether the point is (0, 0)
func (a Point) Empty() bool { return a == Point{} }
// Neg negates the point coordinates.
func (a Point) Neg() Point { return Point{-a.X, -a.Y} }
// Add calculates a + b.
func (a Point) Add(b Point) Point { return Point{a.X + b.X, a.Y + b.Y} }
// Sub calculates a - b.
func (a Point) Sub(b Point) Point { return Point{a.X - b.X, a.Y - b.Y} }
// Min returns the min coordinates of a and b.
func (a Point) Min(b Point) Point { return Point{math.Min(a.X, b.X), math.Min(a.Y, b.Y)} }
// Max returns the max coordinates of a and b.
func (a Point) Max(b Point) Point { return Point{math.Max(a.X, b.X), math.Max(a.Y, b.Y)} }
// Scale scales the point by v.
func (a Point) Scale(v float64) Point { return Point{a.X * v, a.Y * v} }
// XY returns x and y coordinates.
func (a Point) XY() (x, y Length) { return a.X, a.Y }
// Rect defines a position.
type Rect struct{ Min, Max Point }
// R is a convenience func for creating a rectangle.
func R(x0, y0, x1, y1 Length) Rect { return Rect{Point{x0, y0}, Point{x1, y1}} }
// Zero returns rect such that the top-left corner is at (0, 0)
func (r Rect) Zero() Rect { return Rect{Point{0, 0}, r.Max.Sub(r.Min)} }
// Empty returns whether the rectangle is empty.
func (r Rect) Empty() bool { return r.Min.Empty() && r.Max.Empty() }
// Size returns the size of the rect.
func (r Rect) Size() Point { return r.Max.Sub(r.Min) }
// Offset moves the given rectangle.
func (r Rect) Offset(by Point) Rect { return Rect{r.Min.Add(by), r.Max.Add(by)} }
// Shrink shrinks the rect by the sides.
func (r Rect) Shrink(radius Point) Rect { return Rect{r.Min.Add(radius), r.Max.Sub(radius)} }
// UnitLocation converts relative position to rect bounds.
func (r Rect) UnitLocation(u Point) Point {
return Point{
X: lerpUnit(u.X, r.Min.X, r.Max.X),
Y: lerpUnit(u.Y, r.Min.Y, r.Max.Y),
}
}
// Inset shrinks the rect by the given rect.
func (r Rect) Inset(by Rect) Rect { return Rect{r.Min.Add(by.Min), r.Max.Sub(by.Max)} }
// Points returns corners of the rectangle.
func (r Rect) Points() []Point {
return []Point{
r.Min,
Point{r.Min.X, r.Max.Y},
r.Max,
Point{r.Max.X, r.Min.Y},
r.Min,
}
}
// Column returns a i-th column when the rect is split into count columns.
func (r Rect) Column(i, count int) Rect {
if count == 0 {
return r
}
x0 := r.Min.X + float64(i)*(r.Max.X-r.Min.X)/float64(count)
x1 := r.Min.X + float64(i+1)*(r.Max.X-r.Min.X)/float64(count)
return R(x0, r.Min.Y, x1, r.Max.Y)
}
// Row returns a i-th row when the rect is split into count rows.
func (r Rect) Row(i, count int) Rect {
if count == 0 {
return r
}
y0 := r.Min.Y + float64(i)*(r.Max.Y-r.Min.Y)/float64(count)
y1 := r.Min.Y + float64(i+1)*(r.Max.Y-r.Min.Y)/float64(count)
return R(r.Min.X, y0, r.Max.X, y1)
}
// Convenience functions
// Ps creates points from pairs.
func Ps(cs ...Length) []Point {
if len(cs)%2 != 0 {
panic("must give x, y pairs")
}
ps := make([]Point, len(cs)/2)
for i := range ps {
ps[i] = Point{cs[i*2], cs[i*2+1]}
}
return ps
}
// Points creates a slice of points by combining two slices.
// If x or y is nil, then it will enumerate them with indices.
func Points(x, y []float64) []Point {
n := len(x)
if n < len(y) {
n = len(y)
}
points := make([]Point, 0, n)
for i := 0; i < n; i++ {
var p Point
if i < len(x) {
p.X = x[i]
} else {
p.X = float64(i)
}
if i < len(y) {
p.Y = y[i]
} else {
p.Y = float64(i)
}
points = append(points, p)
}
return points
}