-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathaxis.go
More file actions
181 lines (155 loc) · 4.53 KB
/
Copy pathaxis.go
File metadata and controls
181 lines (155 loc) · 4.53 KB
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package geom
import (
"fmt"
)
// Axis is one of the two main coordinate axes.
type Axis int
const (
// AxisHorizontal is the X axis.
AxisHorizontal Axis = iota
// AxisVertical is the Y axis.
AxisVertical
// AxisNone is the absence of an axis. Every method treats it as having no extent.
AxisNone Axis = -1
)
// Axes lists both axes in order. It returns a fresh array, so a caller cannot alter the list.
func Axes() [2]Axis {
return [2]Axis{AxisHorizontal, AxisVertical}
}
// ParseAxis returns the axis with the given name, as String prints it, and an error for any
// other string. "None" parses to AxisNone.
func ParseAxis(name string) (Axis, error) {
if name == AxisNone.String() {
return AxisNone, nil
}
for _, axis := range Axes() {
if axis.String() == name {
return axis, nil
}
}
return AxisNone, fmt.Errorf("geom: unknown axis %q", name)
}
// Perpendicular returns the perpendicular axis.
func (a Axis) Perpendicular() Axis {
switch a {
case AxisHorizontal:
return AxisVertical
case AxisVertical:
return AxisHorizontal
default:
return AxisNone
}
}
// Direction returns the axis direction with the given sign: positive is DirectionRight on the
// horizontal axis and DirectionDown on the vertical one.
func (a Axis) Direction(positive bool) Direction {
direction := a.direction()
if !positive {
return direction.Opposite()
}
return direction
}
// direction returns the direction of growing coordinates on the axis.
func (a Axis) direction() Direction {
switch a {
case AxisHorizontal:
return DirectionRight
case AxisVertical:
return DirectionDown
default:
return DirectionNone
}
}
// Along returns the extent of the given size on the main axis.
func (a Axis) Along[T Number](size Size[T]) T {
switch a {
case AxisHorizontal:
return size.Width
case AxisVertical:
return size.Height
default:
return 0
}
}
// Across returns the extent of the given size on the cross axis.
func (a Axis) Across[T Number](size Size[T]) T {
return a.Perpendicular().Along(size)
}
// Project returns the signed component of the given vector on the main axis.
func (a Axis) Project[T Number](vector Vector[T]) T {
switch a {
case AxisHorizontal:
return vector.X
case AxisVertical:
return vector.Y
default:
return 0
}
}
// ScaleAlong creates a new Size scaled by the given factor on the main axis only.
// AxisNone has no axis to scale along and returns the size unchanged.
func (a Axis) ScaleAlong[T Number](size Size[T], factor float64) Size[T] {
if a.IsNone() {
return size
}
return a.Size(Multiply(a.Along(size), factor), a.Across(size))
}
// ScaleAcross creates a new Size scaled by the given factor on the cross axis only.
// AxisNone has no axis to scale across and returns the size unchanged.
func (a Axis) ScaleAcross[T Number](size Size[T], factor float64) Size[T] {
return a.Perpendicular().ScaleAlong(size, factor)
}
// IsNone reports whether the axis is neither AxisHorizontal nor AxisVertical. Unlike
// Direction, which wraps any value into the eight directions, an Axis outside the two
// constants is not normalized, so every such value counts as AxisNone.
func (a Axis) IsNone() bool {
return a != AxisHorizontal && a != AxisVertical
}
// Vector creates a new Vector displaced by along on the main axis and across on the cross axis.
func (a Axis) Vector[T Number](along, across T) Vector[T] {
switch a {
case AxisHorizontal:
return Vector[T]{along, across}
case AxisVertical:
return Vector[T]{across, along}
default:
return Vector[T]{}
}
}
// Size creates a new Size measuring along on the main axis and across on the cross axis.
// The values are stored as given, like Sz; a negative one is not made absolute.
func (a Axis) Size[T Number](along, across T) Size[T] {
switch a {
case AxisHorizontal:
return Size[T]{along, across}
case AxisVertical:
return Size[T]{across, along}
default:
return Size[T]{}
}
}
// String returns the name of the axis constant.
func (a Axis) String() string {
switch a {
case AxisHorizontal:
return "Horizontal"
case AxisVertical:
return "Vertical"
default:
return "None"
}
}
// MarshalText implements encoding.TextMarshaler with the name String prints, so an axis is
// stored as "Horizontal" in JSON and as a map key rather than as its number.
func (a Axis) MarshalText() ([]byte, error) {
return []byte(a.String()), nil
}
// UnmarshalText implements encoding.TextUnmarshaler, the inverse of MarshalText through ParseAxis.
func (a *Axis) UnmarshalText(text []byte) error {
axis, err := ParseAxis(string(text))
if err != nil {
return err
}
*a = axis
return nil
}