forked from grow-graphics/xy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
vector3.go
469 lines (420 loc) · 18.4 KB
/
vector3.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
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
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
package xy
import "math"
// Vector3 is a 3-element structure that can be used to represent 3D coordinates or any other triplet of numeric values.
//
// It uses floating-point coordinates. By default, these floating-point values use 32-bit precision, unlike [Float] which
// is always 64-bit. If double precision is needed, compile the engine with the option precision=double.
//
// See Vector3i for its integer counterpart.
//
// Note: In a boolean context, a Vector3 will evaluate to false if it's equal to Vector3{0, 0, 0}. Otherwise, a [Vector3]
// will always evaluate to true.
type Vector3 [3]float
// NewVector3 constructs a new Vector3 from the given x, y, and z.
func NewVector3(x, y, z float64) Vector3 { return Vector3{float(x), float(y), float(z)} } //Vector3(float,float,float)
func (v Vector3) X() float64 { return float64(v[X]) }
func (v Vector3) Y() float64 { return float64(v[Y]) }
func (v Vector3) Z() float64 { return float64(v[Z]) }
func (v *Vector3) SetX(x float64) { v[X] = float(x) }
func (v *Vector3) SetY(y float64) { v[Y] = float(y) }
func (v *Vector3) SetZ(z float64) { v[Z] = float(z) }
// "Constants"
func (Vector3) ZERO() Vector3 { return Vector3{0, 0, 0} }
func (Vector3) ONE() Vector3 { return Vector3{1, 1, 1} }
func (Vector3) INF() Vector3 {
return Vector3{float(math.Inf(1)), float(math.Inf(1)), float(math.Inf(1))}
}
func (Vector3) LEFT() Vector3 { return Vector3{-1, 0, 0} }
func (Vector3) RIGHT() Vector3 { return Vector3{1, 0, 0} }
func (Vector3) UP() Vector3 { return Vector3{0, 1, 0} }
func (Vector3) DOWN() Vector3 { return Vector3{0, -1, 0} }
func (Vector3) FORWARD() Vector3 { return Vector3{0, 0, -1} }
func (Vector3) BACK() Vector3 { return Vector3{0, 0, 1} }
func (Vector3) MODEL_LEFT() Vector3 { return Vector3{1, 0, 0} }
func (Vector3) MODEL_RIGHT() Vector3 { return Vector3{-1, 0, 0} }
func (Vector3) MODEL_TOP() Vector3 { return Vector3{0, 1, 0} }
func (Vector3) MODEL_BOTTOM() Vector3 { return Vector3{0, -1, 0} }
func (Vector3) MODEL_FRONT() Vector3 { return Vector3{0, 0, -1} }
func (Vector3) MODEL_REAR() Vector3 { return Vector3{0, 0, 1} }
func (v Vector3) Vector3i() Vector3i { return Vector3i{int32(v[X]), int32(v[Y]), int32(v[Z])} } //Vector3i(Vector3)
// Abs returns a new vector with all components in absolute values (i.e. positive).
func (v Vector3) Abs() Vector3 { return v.abs() } //Vector3.abs
// AngleTo returns the unsigned minimum angle to the given vector, in radians.
func (v Vector3) AngleTo(to Vector3) Radians { //Vector3.angle_to
return Atan2(v.Cross(to).Length(), v.Dot(to))
}
// BezierDerivative returns the derivative at the given t on the Bézier curve
// defined by this vector and the given control_1, control_2, and end points.
func (v Vector3) BezierDerivative(control1, control2, end Vector3, t float64) Vector3 { //Vector3.bezier_derivative
return Vector3{
BezierDerivative(v[X], control1[X], control2[X], end[X], float(t)),
BezierDerivative(v[Y], control1[Y], control2[Y], end[Y], float(t)),
BezierDerivative(v[Z], control1[Z], control2[Z], end[Z], float(t)),
}
}
// BezierInterpolate returns the point at the given t on the Bézier curve defined by
// this vector and the given control_1, control_2, and end points.
func (v Vector3) BezierInterpolate(control1, control2, end Vector3, t float64) Vector3 { //Vector3.bezier_interpolate
return Vector3{
BezierInterpolate(v[X], control1[X], control2[X], end[X], float(t)),
BezierInterpolate(v[Y], control1[Y], control2[Y], end[Y], float(t)),
BezierInterpolate(v[Z], control1[Z], control2[Z], end[Z], float(t)),
}
}
// Bounce returns the vector "bounced off" from a plane defined by the given normal.
func (v Vector3) Bounce(n Vector3) Vector3 { //Vector3.bounce
return Vector3.Sub(Vector3{}, v.Reflect(n))
}
// Ceil returns a new vector with all components rounded up (towards positive infinity).
func (v Vector3) Ceil() Vector3 { return v.ceil() } //Vector3.ceil
// Clamp returns a new vector with all components clamped between the components of min and max,
// by running [Clampf] on each component.
func (v Vector3) Clamp(min, max Vector3) Vector3 { //Vector3.clamp
return Vector3{
Clampf(v[X], min[X], max[X]),
Clampf(v[Y], min[Y], max[Y]),
Clampf(v[Z], min[Z], max[Z]),
}
}
// Cross returns the cross product of this vector and with.
func (v Vector3) Cross(with Vector3) Vector3 { //Vector3.cross
return Vector3{
v[Y]*with[Z] - v[Z]*with[Y],
v[Z]*with[X] - v[X]*with[Z],
v[X]*with[Y] - v[Y]*with[X],
}
}
// CubicInterpolate performs a cubic interpolation between this vector and b using pre_a and post_b as handles
// and returns the result at position weight. weight is on the range of 0.0 to 1.0, representing the amount of
// interpolation.
func (v Vector3) CubicInterpolate(b, preA, postB Vector3, weight float64) Vector3 { //Vector3.cubic_interpolate
return Vector3{
CubicInterpolate(v[X], b[X], preA[X], postB[X], float(weight)),
CubicInterpolate(v[Y], b[Y], preA[Y], postB[Y], float(weight)),
CubicInterpolate(v[Z], b[Z], preA[Z], postB[Z], float(weight)),
}
}
// CubicInterpolateInTime performs a cubic interpolation between this vector and b using pre_a and post_b as handles
// and returns the result at position weight. weight is on the range of 0.0 to 1.0, representing the amount of interpolation.
//
// It can perform smoother interpolation than [Vector3.CubicInterpolate] by the time values.
func (v Vector3) CubicInterpolateInTime(b, preA, postB Vector3, weight, b_t, pre_a_t, post_b_t float64) Vector3 { //Vector3.cubic_interpolate_in_time
return Vector3{
CubicInterpolateInTime(v[X], b[X], preA[X], postB[X], float(weight), float(b_t), float(pre_a_t), float(post_b_t)),
CubicInterpolateInTime(v[Y], b[Y], preA[Y], postB[Y], float(weight), float(b_t), float(pre_a_t), float(post_b_t)),
CubicInterpolateInTime(v[Z], b[Z], preA[Z], postB[Z], float(weight), float(b_t), float(pre_a_t), float(post_b_t)),
}
}
// DirectionTo returns the normalized vector pointing from this vector to to. This is equivalent to using (b - a).Normalized().
func (v Vector3) DirectionTo(to Vector3) Vector3 { //Vector3.direction_to
return Vector3.Sub(to, v).Normalized()
}
// DistanceSquaredTo returns the squared distance between this vector and to.
//
// This method runs faster than [Vector3.DistanceTo], so prefer it if you need to compare vectors or need the squared distance for
// some formula.
func (v Vector3) DistanceSquaredTo(to Vector3) float64 { //Vector3.distance_squared_to
return float64(v.Sub(to).LengthSquared())
}
// DistanceTo returns the distance between this vector and to.
func (v Vector3) DistanceTo(to Vector3) float64 { //Vector3.distance_to
return float64(v.Sub(to).Length())
}
// Dot returns the dot product of this vector and with. This can be used to compare the angle between two vectors. For example, this
// can be used to determine whether an enemy is facing the player.
//
// The dot product will be 0 for a straight angle (90 degrees), greater than 0 for angles narrower than 90 degrees and lower than 0
// for angles wider than 90 degrees.
//
// When using unit (normalized) vectors, the result will always be between -1.0 (180 degree angle) when the vectors are facing opposite
// directions, and 1.0 (0 degree angle) when the vectors are aligned.
//
// Note: a.Dot(b) is equivalent to b.Dot(a).
func (v Vector3) Dot(with Vector3) float64 { //Vector3.dot
return float64(v[X]*with[X] + v[Y]*with[Y] + v[Z]*with[Z])
}
// Floor returns a new vector with all components rounded down (towards negative infinity).
func (v Vector3) Floor() Vector3 { return v.floor() } //Vector3.floor
// Inverse returns the inverse of the vector. This is the same as Vector3{1/v[X],1/v[Y],1/v[Z]}.
func (v Vector3) Inverse() Vector3 { //Vector3.inverse
return Vector3{1 / v[X], 1 / v[Y], 1 / v[Z]}
}
// IsApproximatelyEqual returns true if this vector and other are approximately equal, by running [IsApproximatelyEqual] on each component.
func (v Vector3) IsApproximatelyEqual(other Vector3) bool { //Vector3.is_equal_approx
if !IsApproximatelyEqual(v[X], other[X]) {
return false
}
if !IsApproximatelyEqual(v[Y], other[Y]) {
return false
}
if !IsApproximatelyEqual(v[Z], other[Z]) {
return false
}
return true
}
// IsFinite returns true if this vector's values are finite, by running [IsFinite] on each component.
func (v Vector3) IsFinite() bool { //Vector3.is_finite
if !IsFinite(v[X]) {
return false
}
if !IsFinite(v[Y]) {
return false
}
if !IsFinite(v[Z]) {
return false
}
return true
}
// IsNormalized Returns true if the vector is normalized, i.e. its length is approximately equal to 1.
func (v Vector3) IsNormalized() bool { return IsApproximatelyEqual(v.LengthSquared(), 1) } //Vector3.is_normalized
// IsApproximatelyZero returns true if this vector is approximately equal to zero, by running
// [IsApproximatelyZero] on each component.
func (v Vector3) IsApproximatelyZero() bool { //Vector3.is_zero_approx
if !IsApproximatelyZero(v[X]) {
return false
}
if !IsApproximatelyZero(v[Y]) {
return false
}
if !IsApproximatelyZero(v[Z]) {
return false
}
return true
}
// Length the length (magnitude) of this vector.
func (v Vector3) Length() float64 { //Vector3.length
return float64(math.Sqrt(float64(v.LengthSquared())))
}
// LengthSquared returns the squared length (squared magnitude) of this vector.
//
// This method runs faster than length, so prefer it if you need to compare vectors
// or need the squared distance for some formula.
func (v Vector3) LengthSquared() float64 { //Vector3.length_squared
return float64(float64(v[x])*float64(v[x]) + float64(v[y])*float64(v[y]) + float64(v[z])*float64(v[z]))
}
// Lerp returns the result of the linear interpolation between this vector and to by amount weight.
// weight is on the range of 0.0 to 1.0, representing the amount of interpolation.
func (v Vector3) Lerp(to Vector3, weight float64) Vector3 { return v.lerp(to, weight) } //Vector3.lerp
// LimitLength returns the vector with a maximum length by limiting its length to length.
func (v Vector3) LimitLength(length float64) Vector3 { //Vector3.limit_length
var l = v.Length()
if l > 0 && length < l {
v = v.Divf(l)
v = v.Mulf(length)
}
return v
}
// MaxAxis returns the axis of the vector's highest value. See [Axis] constants. If all components are
// equal, this method returns [X].
func (v Vector3) MaxAxis() Axis { //Vector3.max_axis_index
if v[X] < v[Y] {
if v[Y] < v[Z] {
return Z
}
return Y
}
if v[X] < v[Z] {
return Z
}
return X
}
// MinAxis returns the axis of the vector's lowest value. See [Axis] constants. If all components are
// equal, this method returns [Z].
func (v Vector3) MinAxis() Axis { //Vector3.min_axis_index
if v[X] < v[Y] {
if v[Y] < v[Z] {
return X
}
return Z
}
if v[X] < v[Z] {
return Y
}
return Z
}
// MoveToward returns a new vector moved toward to by the fixed delta amount. Will not go past the final value.
func (v Vector3) MoveToward(to Vector3, delta float64) Vector3 { //Vector3.move_toward
var vd = to.Sub(v)
var l = vd.Length()
if l <= delta || l < cmpEpsilon {
return to
}
return v.Add(vd.Divf(l).Mulf(delta))
}
// Normalized returns the result of scaling the vector to unit length. Equivalent to v / v.Length().
// See also [Vector3.IsNormalized].
//
// Note: This function may return incorrect values if the input vector length is near zero.
func (v Vector3) Normalized() Vector3 { //Vector3.normalized
var l = v.Length()
if l == 0 {
return Vector3{}
}
return v.Divf(l)
}
// OctahedronDecode returns the [Vector3] from an octahedral-compressed form created using
// OctahedronEncode (stored as a [Vector2]).
func (v Vector3) OctahedronDecode(uv Vector2) Vector3 { //Vector3.octahedron_decode
var f = Vector3{uv[X]*2.0 - 1.0, uv[Y]*2.0 - 1.0}
var n = Vector3{f[X], f[Y], 1.0 - Absf(f[X]) - Absf(f[Y])}
var t = Clampf(-n[Z], 0.0, 1.0)
if uv[X] >= 0 {
n[X] += -t
} else {
n[X] += t
}
if uv[Y] >= 0 {
n[Y] += -t
} else {
n[Y] += t
}
return n.Normalized()
}
// OctahedronEncode returns the octahedral-encoded (oct32) form of this [Vector3] as a [Vector2].
// Since a [Vector2] occupies 1/3 less memory compared to [Vector3], this form of compression can
// be used to pass greater amounts of normalized [Vector3]s without increasing storage or memory
// requirements. See also [Vector3.OctahedronDecode].
//
// Note: OctahedronEncode can only be used for normalized vectors. OctahedronEncode does not check
// whether this [Vector3] is normalized, and will return a value that does not decompress to the
// original value if the [Vector3] is not normalized.
//
// Note: Octahedral compression is lossy, although visual differences are rarely perceptible in
// real world scenarios.
func (v Vector3) OctahedronEncode() Vector2 { //Vector3.octahedron_encode
v = v.Divf(float64(Absf(v[X]) + Absf(v[Y]) + Absf(v[Z])))
var o Vector2
if v[Z] >= 0.0 {
o[X] = v[X]
o[Y] = v[Y]
} else {
if v[X] > 0 {
o[X] = 1.0 - Absf(v[Y])
} else {
o[X] = -(1.0 - Absf(v[Y]))
}
if v[Y] > 0 {
o[Y] = Absf(v[X])
} else {
o[Y] = -(1.0 - Absf(v[X]))
}
}
o[X] = o[X]*0.5 + 0.5
o[Y] = o[Y]*0.5 + 0.5
return o
}
// Outer returns the outer product with with.
func (v Vector3) Outer(with Vector3) Basis { //Vector3.outer
return Basis{
Vector3{v[X] * with[X], v[X] * with[Y], v[X] * with[Z]},
Vector3{y * with[X], y * with[Y], y * with[Z]},
Vector3{z * with[X], z * with[Y], z * with[Z]},
}
}
// Posmodf returns a vector composed of the Fposmod of this vector's components and mod.
func (v Vector3) Posmodf(mod float64) Vector3 { //Vector3.posmod
return Vector3{Fposmod(v[X], float(mod)), Fposmod(v[Y], float(mod)), Fposmod(v[Z], float(mod))}
}
// Posmodv returns a vector composed of the Fposmod of this vector's components and mod.
func (v Vector3) Posmodv(mod Vector3) Vector3 { //Vector3.posmodv
return Vector3{Fposmod(v[X], mod[X]), Fposmod(v[Y], mod[Y]), Fposmod(v[Z], mod[Z])}
}
// Project returns the result of projecting the vector onto the given vector b.
func (v Vector3) Project(b Vector3) Vector3 { //Vector3.project
return b.Mulf(v.Dot(b) / b.LengthSquared())
}
// Rotated returns the result of rotating this vector around a given axis by angle (in radians).
// The axis must be a normalized vector.
func (v Vector3) Rotated(axis Vector3, angle Radians) Vector3 { //Vector3.rotated
return NewBasisRotatedAround(axis, angle).Transform(v)
}
// Round returns a new vector with all components rounded to the nearest integer, with halfway cases
// rounded away from zero.
func (v Vector3) Round() Vector3 { return v.round() } //Vector3.round
// SignedAngleTo returns the signed angle to the given vector, in radians. The sign of the angle is
// positive in a counter-clockwise direction and negative in a clockwise direction when viewed from
// the side specified by the axis.
func (v Vector3) SignedAngleTo(to Vector3, axis Vector3) Radians { //Vector3.signed_angle_to
var cross_to = v.Cross(to)
var unsigned_angle = Atan2(cross_to.Length(), v.Dot(to))
var sign = cross_to.Dot(axis)
if sign < 0 {
return -unsigned_angle
}
return unsigned_angle
}
// Slerp returns the result of spherical linear interpolation between this vector and to, by amount weight.
// weight is on the range of 0.0 to 1.0, representing the amount of interpolation.
//
// This method also handles interpolating the lengths if the input vectors have different lengths. For the
// special case of one or both input vectors having zero length, this method behaves like lerp.
func (v Vector3) Slerp(to Vector3, weight float64) Vector3 { //Vector3.slerp
// This method seems more complicated than it really is, since we write out
// the internals of some methods for efficiency (mainly, checking length).
var start_length_sq = v.LengthSquared()
var end_length_sq = to.LengthSquared()
if start_length_sq == 0.0 || end_length_sq == 0.0 {
// Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
return v.lerp(to, weight)
}
var axis Vector3 = v.Cross(to)
var axis_length_sq = axis.LengthSquared()
if axis_length_sq == 0.0 {
// Colinear vectors have no rotation axis or angle between them, so the best we can do is lerp.
return v.lerp(to, weight)
}
axis = axis.Divf(Sqrt(axis_length_sq))
var start_length = Sqrt(start_length_sq)
var result_length = Lerpf(start_length, Sqrt(end_length_sq), weight)
var angle = v.AngleTo(to)
return v.Rotated(axis, angle*Radians(weight)).Mulf(result_length / start_length)
}
// Slide returns a new vector slid along a plane defined by the given normal.
func (v Vector3) Slide(n Vector3) Vector3 { //Vector3.slide
return Vector3.Sub(v, n.Mulf(v.Dot(n)))
}
// Snapped returns a new vector with each component snapped to the nearest multiple of the corresponding component
// in step. This can also be used to round the components to an arbitrary number of decimals.
func (v Vector3) Snapped(step Vector3) Vector3 { //Vector3.snapped
return Vector3{
Snappedf(v[X], step[X]),
Snappedf(v[Y], step[Y]),
Snappedf(v[Z], step[Z]),
}
}
// Reflect returns the result of reflecting the vector from a plane defined by the given normal n.
func (v Vector3) Reflect(n Vector3) Vector3 { //Vector3.reflect
return Vector3.Sub(n.Mulf(2.0*v.Dot(n)), v)
}
func (v Vector3) Add(other Vector3) Vector3 { //Vector3 + Vector3
return Vector3{v[x] + other[x], v[y] + other[y], v[z] + other[z]}
}
func (v Vector3) Sub(other Vector3) Vector3 { //Vector3 - Vector3
return Vector3{v[x] - other[x], v[y] - other[y], v[z] - other[z]}
}
func (v Vector3) Mul(other Vector3) Vector3 { //Vector3 * Vector3
return Vector3{v[x] * other[x], v[y] * other[y], v[z] * other[z]}
}
func (v Vector3) Div(other Vector3) Vector3 { //Vector3 / Vector3
return Vector3{v[x] / other[x], v[y] / other[y], v[z] / other[z]}
}
func (v Vector3) Addf(other float64) Vector3 { //Vector3 + float64
return Vector3{v[x] + float(other), v[y] + float(other), v[z] + float(other)}
}
func (v Vector3) Subf(other float64) Vector3 { //Vector3 - float64
return Vector3{v[x] - float(other), v[y] - float(other), v[z] - float(other)}
}
func (v Vector3) Mulf(other float64) Vector3 { //Vector3 * float64
return Vector3{v[x] * float(other), v[y] * float(other), v[z] * float(other)}
}
func (v Vector3) Divf(other float64) Vector3 { //Vector3 / float64
return Vector3{v[x] / float(other), v[y] / float(other), v[z] / float(other)}
}
func (v Vector3) Neg() Vector3 { return Vector3{-v[x], -v[y], -v[z]} } //-Vector3
func (v Vector3) Transform(t Transform3D) Vector3 { //Transform3D * Vector3
return Vector3{
float(t.Basis[0].Dot(v)) + t.Origin[X],
float(t.Basis[1].Dot(v)) + t.Origin[Y],
float(t.Basis[2].Dot(v)) + t.Origin[Z],
}
}