-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvector.go
More file actions
327 lines (268 loc) · 9.62 KB
/
vector.go
File metadata and controls
327 lines (268 loc) · 9.62 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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
package geom
import (
"fmt"
"math"
)
// Vector is a 2D Vector.
type Vector[T Number] struct {
X T `json:"x"`
Y T `json:"y"`
}
// Vec is shorthand for Vector{x, y}.
func Vec[T Number](x, y T) Vector[T] {
return Vector[T]{x, y}
}
// Transform creates a new Vector by applying the given matrix to the current vector.
// For integer T, the float64 result of each component is rounded; rotations and
// non-integer scales lose precision.
func (v Vector[T]) Transform(matrix Matrix[float64]) Vector[T] {
return Vector[T]{Cast[T](matrix.A*float64(v.X) + matrix.B*float64(v.Y)), Cast[T](matrix.D*float64(v.X) + matrix.E*float64(v.Y))}
}
// Add creates a new Vector by adding the given vector to the current vector.
func (v Vector[T]) Add(vector Vector[T]) Vector[T] {
return Vector[T]{v.X + vector.X, v.Y + vector.Y}
}
// AddXY creates a new Vector by adding the given values to the current vector.
func (v Vector[T]) AddXY(deltaX, deltaY T) Vector[T] {
return Vector[T]{v.X + deltaX, v.Y + deltaY}
}
// Subtract creates a new Vector by subtracting the given vector from the current vector.
func (v Vector[T]) Subtract(vector Vector[T]) Vector[T] {
return Vector[T]{v.X - vector.X, v.Y - vector.Y}
}
// SubtractXY creates a new Vector by subtracting the given values from the current vector.
func (v Vector[T]) SubtractXY(deltaX, deltaY T) Vector[T] {
return Vector[T]{v.X - deltaX, v.Y - deltaY}
}
// Multiply creates a new Vector by multiplying the given value to the current vector.
func (v Vector[T]) Multiply(factor float64) Vector[T] {
return Vector[T]{Multiply(v.X, factor), Multiply(v.Y, factor)}
}
// MultiplyXY creates a new Vector by multiplying the given values to the current vector.
func (v Vector[T]) MultiplyXY(factorX, factorY float64) Vector[T] {
return Vector[T]{Multiply(v.X, factorX), Multiply(v.Y, factorY)}
}
// Divide creates a new Vector by dividing the given value to the current vector.
func (v Vector[T]) Divide(factor float64) Vector[T] {
return Vector[T]{Divide(v.X, factor), Divide(v.Y, factor)}
}
// DivideXY creates a new Vector by dividing the given values to the current vector.
func (v Vector[T]) DivideXY(factorX, factorY float64) Vector[T] {
return Vector[T]{Divide(v.X, factorX), Divide(v.Y, factorY)}
}
// Negate creates a new Vector with opposite direction.
func (v Vector[T]) Negate() Vector[T] {
return Vector[T]{-v.X, -v.Y}
}
// Rotate creates a new Vector rotated by the given angle (in radians).
// For integer T, sin/cos components are rounded; only multiples of 90° give exact results.
func (v Vector[T]) Rotate(angle float64) Vector[T] {
sin, cos := math.Sincos(angle)
return Vector[T]{Cast[T](float64(v.X)*cos - float64(v.Y)*sin), Cast[T](float64(v.X)*sin + float64(v.Y)*cos)}
}
// Resize creates a new Vector resized to the given length.
// For integer T, the result is rounded and the actual length may differ from the requested value.
func (v Vector[T]) Resize(length float64) Vector[T] {
return v.Multiply(length / v.Length())
}
// Normalize creates a new Vector resized to a length of 1.
// For integer T, the result is one of the four axis-aligned unit vectors (±1,0)/(0,±1);
// the zero vector returns (1,0) by convention.
func (v Vector[T]) Normalize() Vector[T] {
if v.IsZero() {
return Vector[T]{1, 0}
}
unit := v.Resize(1)
if isIntType[T]() && unit.X == unit.Y {
if v.X > v.Y {
unit.X = 1
unit.Y = 0
} else {
unit.X = 0
unit.Y = 1
}
}
return unit
}
// Abs creates a new Vector with absolute X and Y.
func (v Vector[T]) Abs() Vector[T] {
return Vector[T]{Abs(v.X), Abs(v.Y)}
}
// Round creates a new Vector by rounding X, Y values to the nearest integer.
func (v Vector[T]) Round() Vector[T] {
return Vector[T]{Round(v.X), Round(v.Y)}
}
// Floor creates a new Vector by rounding down X, Y values to the nearest integer.
func (v Vector[T]) Floor() Vector[T] {
return Vector[T]{Floor(v.X), Floor(v.Y)}
}
// Ceil creates a new Vector by rounding up X, Y values to the nearest integer.
func (v Vector[T]) Ceil() Vector[T] {
return Vector[T]{Ceil(v.X), Ceil(v.Y)}
}
// Dot returns dot (scalar) product of two vectors.
func (v Vector[T]) Dot(vector Vector[T]) T {
return v.X*vector.X + v.Y*vector.Y
}
// Cross returns cross product of two vectors.
func (v Vector[T]) Cross(vector Vector[T]) T {
return v.X*vector.Y - v.Y*vector.X
}
// Normal creates a new Vector as normal to current vector. Faster equivalent to Rotate(math.Pi/2).
func (v Vector[T]) Normal() Vector[T] {
return Vector[T]{-v.Y, v.X}
}
// Length returns the Vector's length (magnitude).
func (v Vector[T]) Length() float64 {
return math.Hypot(float64(v.X), float64(v.Y))
}
// LengthSquared returns the Vector's length (magnitude) squared (for faster comparison).
func (v Vector[T]) LengthSquared() T {
return v.X*v.X + v.Y*v.Y
}
// Angle returns the vector's angle in radians.
func (v Vector[T]) Angle() float64 {
return math.Atan2(float64(v.Y), float64(v.X))
}
// Lerp creates a new Vector in linear interpolation towards given vector.
func (v Vector[T]) Lerp(vector Vector[T], t float64) Vector[T] {
return Vector[T]{Lerp(v.X, vector.X, t), Lerp(v.Y, vector.Y, t)}
}
// Equal checks for equal X and Y values with given vector.
func (v Vector[T]) Equal(vector Vector[T]) bool {
return Equal(v.X, vector.X) && Equal(v.Y, vector.Y)
}
// IsZero checks if X and Y values are zero.
func (v Vector[T]) IsZero() bool {
return v.Equal(Vector[T]{})
}
// IsOne checks if X and Y values are (1,1).
func (v Vector[T]) IsOne() bool {
return v.Equal(Vector[T]{1, 1})
}
// IsUp checks whether the vector points upward (-Y).
func (v Vector[T]) IsUp() bool {
return v.Y < 0
}
// IsDown checks whether the vector points downward (+Y).
func (v Vector[T]) IsDown() bool {
return v.Y > 0
}
// IsLeft checks whether the vector points leftward (-X).
func (v Vector[T]) IsLeft() bool {
return v.X < 0
}
// IsRight checks whether the vector points rightward (+X).
func (v Vector[T]) IsRight() bool {
return v.X > 0
}
// IsNormalized checks if Vector is normalized.
func (v Vector[T]) IsNormalized() bool {
return Equal(v.LengthSquared(), 1.0)
}
// Less checks if Vector length is less than given value.
func (v Vector[T]) Less(value T) bool {
return v.LengthSquared() < value*value
}
// XY returns the vector X, Y values in standard order.
func (v Vector[T]) XY() (T, T) {
return v.X, v.Y
}
// Point converts the vector to a Point.
func (v Vector[T]) Point() Point[T] {
return Point[T](v)
}
// Size converts the vector to a Size (using absolute component values).
func (v Vector[T]) Size() Size[T] {
return Size[T]{Abs(v.X), Abs(v.Y)}
}
// Int converts the vector to a [int] vector.
func (v Vector[T]) Int() Vector[int] {
return Vector[int]{Cast[int](float64(v.X)), Cast[int](float64(v.Y))}
}
// Float converts the vector to a [float64] vector.
func (v Vector[T]) Float() Vector[float64] {
return Vector[float64]{float64(v.X), float64(v.Y)}
}
// String returns a string representing the vector.
func (v Vector[T]) String() string {
return fmt.Sprintf("⟨%s,%s⟩", String(v.X), String(v.Y))
}
// VectorFromAngle returns a vector of the given length pointing in the direction of angle (in radians).
// For integer T, each component is independently rounded after multiplying by length; angles
// whose sin or cos falls near ±0.5 may round to 0 instead of ±1 due to float64 precision.
// Use float64 when directional accuracy matters.
func VectorFromAngle[T Number](angle float64, length T) Vector[T] {
sin, cos := math.Sincos(angle)
return Vector[T]{Cast[T](float64(length) * cos), Cast[T](float64(length) * sin)}
}
// ZeroVector creates a new Vector with zero values (0,0).
func ZeroVector[T Number]() Vector[T] {
return Vector[T]{}
}
// OneVector creates a new Vector with identity values (+1,+1).
func OneVector[T Number]() Vector[T] {
return Vector[T]{1, 1}
}
// UpVector returns the unit vector pointing up: (0, -1).
func UpVector[T Number]() Vector[T] {
return Vector[T]{0, -1}
}
// DownVector returns the unit vector pointing down: (0, +1).
func DownVector[T Number]() Vector[T] {
return Vector[T]{0, 1}
}
// LeftVector returns the unit vector pointing left: (-1, 0).
func LeftVector[T Number]() Vector[T] {
return Vector[T]{-1, 0}
}
// RightVector returns the unit vector pointing right: (+1, 0).
func RightVector[T Number]() Vector[T] {
return Vector[T]{1, 0}
}
// UpLeftVector creates a new unit Vector with up (-y) and left (-x) directions.
func UpLeftVector() Vector[float64] {
return Vector[float64]{-OneOverSqrt2, -OneOverSqrt2}
}
// UpRightVector creates a new unit Vector with up (-y) and right (+x) directions.
func UpRightVector() Vector[float64] {
return Vector[float64]{OneOverSqrt2, -OneOverSqrt2}
}
// DownLeftVector creates a new unit Vector with down (+y) and left (-x) directions.
func DownLeftVector() Vector[float64] {
return Vector[float64]{-OneOverSqrt2, OneOverSqrt2}
}
// DownRightVector creates a new unit Vector with down (+y) and right (+x) directions.
func DownRightVector() Vector[float64] {
return Vector[float64]{OneOverSqrt2, OneOverSqrt2}
}
// DirectionFromAxes returns the unit direction vector for the given axis inputs,
// canceling opposite directions (e.g. left+right = zero). Diagonals are normalized.
func DirectionFromAxes(up, down, left, right bool) Vector[float64] {
if left && right {
left, right = false, false
}
if up && down {
up, down = false, false
}
switch {
case up && left:
return UpLeftVector()
case up && right:
return UpRightVector()
case down && left:
return DownLeftVector()
case down && right:
return DownRightVector()
case up:
return UpVector[float64]()
case down:
return DownVector[float64]()
case left:
return LeftVector[float64]()
case right:
return RightVector[float64]()
default:
return ZeroVector[float64]()
}
}