v2 / vlib / math / vec / vec4.v
433 lines · 374 sloc · 11.0 KB · 7690cbfbaf547f6875dbc20550409b943c5a776a
Raw
1// Copyright(C) 2020-2024 Lars Pontoppidan. All rights reserved.
2// Use of this source code is governed by an MIT license file distributed with this software package
3module vec
4
5import math
6
7// Vec4[T] is a generic struct representing a vector in 4D space.
8pub struct Vec4[T] {
9pub mut:
10 x T
11 y T
12 z T
13 w T
14}
15
16// vec4[T] returns a `Vec4` of type `T`, with `x`,`y`,`z` and `w` fields set.
17pub fn vec4[T](x T, y T, z T, w T) Vec4[T] {
18 return Vec4[T]{
19 x: x
20 y: y
21 z: z
22 w: w
23 }
24}
25
26// zero sets the `x`,`y`,`z` and `w` fields to 0.
27pub fn (mut v Vec4[T]) zero() {
28 v.x = 0
29 v.y = 0
30 v.z = 0
31 v.w = 0
32}
33
34// one sets the `x`,`y`,`z` and `w` fields to 1.
35pub fn (mut v Vec4[T]) one() {
36 v.x = 1
37 v.y = 1
38 v.z = 1
39 v.w = 1
40}
41
42// copy returns a copy of this vector.
43pub fn (v Vec4[T]) copy() Vec4[T] {
44 return Vec4[T]{v.x, v.y, v.z, v.w}
45}
46
47// from sets the `x`,`y`,`z` and `w` fields from `u`.
48pub fn (mut v Vec4[T]) from(u Vec4[T]) {
49 v.x = u.x
50 v.y = u.y
51 v.z = u.z
52 v.w = u.w
53}
54
55// from_vec2 sets the `x` and `y` fields from `u`.
56pub fn (mut v Vec4[T]) from_vec2(u Vec2[T]) {
57 v.x = u.x
58 v.y = u.y
59}
60
61// as_vec2 returns a Vec2 with `x` and `y` fields set from `v`.
62pub fn (v Vec4[T]) as_vec2[U]() Vec2[U] {
63 return Vec2[U]{v.x, v.y}
64}
65
66// from_vec3 sets the `x`,`y` and `z` fields from `u`.
67pub fn (mut v Vec4[T]) from_vec3[U](u Vec3[U]) {
68 v.x = T(u.x)
69 v.y = T(u.y)
70 v.z = T(u.z)
71}
72
73// as_vec3 returns a Vec3 with `x`,`y` and `z` fields set from `v`.
74pub fn (v Vec4[T]) as_vec3[U]() Vec3[U] {
75 return Vec3[U]{v.x, v.y, v.z}
76}
77
78//
79// Addition
80//
81
82// + returns the resulting vector of the addition of `v` and `u`.
83@[inline]
84pub fn (v Vec4[T]) + (u Vec4[T]) Vec4[T] {
85 return Vec4[T]{v.x + u.x, v.y + u.y, v.z + u.z, v.w + u.w}
86}
87
88// add returns the resulting vector of the addition of `v` + `u`.
89pub fn (v Vec4[T]) add(u Vec4[T]) Vec4[T] {
90 return v + u
91}
92
93// add_vec2 returns the resulting vector of the addition of the
94// `x` and `y` fields of `u`, `z` is left untouched.
95pub fn (v Vec4[T]) add_vec2[U](u Vec2[U]) Vec4[T] {
96 return Vec4[T]{v.x + u.x, v.y + u.y, 0, 0}
97}
98
99// add_vec3 returns the resulting vector of the addition of the
100// `x`,`y` and `z` fields of `u`, `w` is left untouched.
101pub fn (v Vec4[T]) add_vec3[U](u Vec3[U]) Vec4[T] {
102 return Vec4[T]{v.x + u.x, v.y + u.y, v.z + u.z, 0}
103}
104
105// add_scalar returns the resulting vector of the addition of the `scalar` value.
106pub fn (v Vec4[T]) add_scalar[U](scalar U) Vec4[T] {
107 return Vec4[T]{v.x + T(scalar), v.y + T(scalar), v.z + T(scalar), v.w + T(scalar)}
108}
109
110// plus adds vector `u` to the vector.
111pub fn (mut v Vec4[T]) plus(u Vec4[T]) {
112 v.x += u.x
113 v.y += u.y
114 v.z += u.z
115 v.w += u.w
116}
117
118// plus_scalar adds the scalar `scalar` to the vector.
119pub fn (mut v Vec4[T]) plus_scalar[U](scalar U) {
120 v.x += T(scalar)
121 v.y += T(scalar)
122 v.z += T(scalar)
123 v.w += T(scalar)
124}
125
126//
127// Subtraction
128//
129
130// - returns the resulting vector of the subtraction of `v` and `u`.
131@[inline]
132pub fn (v Vec4[T]) - (u Vec4[T]) Vec4[T] {
133 return Vec4[T]{v.x - u.x, v.y - u.y, v.z - u.z, v.w - u.w}
134}
135
136// sub returns the resulting vector of the subtraction of `v` - `u`.
137pub fn (v Vec4[T]) sub(u Vec4[T]) Vec4[T] {
138 return v - u
139}
140
141// sub_scalar returns the resulting vector of the subtraction of the `scalar` value.
142pub fn (v Vec4[T]) sub_scalar[U](scalar U) Vec4[T] {
143 return Vec4[T]{v.x - T(scalar), v.y - T(scalar), v.z - T(scalar), v.w - T(scalar)}
144}
145
146// subtract subtracts vector `u` from the vector.
147pub fn (mut v Vec4[T]) subtract(u Vec4[T]) {
148 v.x -= u.x
149 v.y -= u.y
150 v.z -= u.z
151 v.w -= u.w
152}
153
154// subtract_scalar subtracts the scalar `scalar` from the vector.
155pub fn (mut v Vec4[T]) subtract_scalar[U](scalar U) {
156 v.x -= T(scalar)
157 v.y -= T(scalar)
158 v.z -= T(scalar)
159 v.w -= T(scalar)
160}
161
162//
163// Multiplication
164//
165
166// * returns the resulting vector of the multiplication of `v` and `u`.
167@[inline]
168pub fn (v Vec4[T]) * (u Vec4[T]) Vec4[T] {
169 return Vec4[T]{v.x * u.x, v.y * u.y, v.z * u.z, v.w * u.w}
170}
171
172// mul returns the resulting vector of the multiplication of `v` * `u`.
173pub fn (v Vec4[T]) mul(u Vec4[T]) Vec4[T] {
174 return v * u
175}
176
177// mul_scalar returns the resulting vector of the multiplication of the `scalar` value.
178pub fn (v Vec4[T]) mul_scalar[U](scalar U) Vec4[T] {
179 return Vec4[T]{v.x * T(scalar), v.y * T(scalar), v.z * T(scalar), v.w * T(scalar)}
180}
181
182// multiply multiplies the vector with `u`.
183pub fn (mut v Vec4[T]) multiply(u Vec4[T]) {
184 v.x *= u.x
185 v.y *= u.y
186 v.z *= u.z
187 v.w *= u.w
188}
189
190// multiply_scalar multiplies the vector with `scalar`.
191pub fn (mut v Vec4[T]) multiply_scalar[U](scalar U) {
192 v.x *= T(scalar)
193 v.y *= T(scalar)
194 v.z *= T(scalar)
195 v.w *= T(scalar)
196}
197
198//
199// Division
200//
201
202// / returns the resulting vector of the division of `v` and `u`.
203@[inline]
204pub fn (v Vec4[T]) / (u Vec4[T]) Vec4[T] {
205 return Vec4[T]{v.x / u.x, v.y / u.y, v.z / u.z, v.w / u.w}
206}
207
208// div returns the resulting vector of the division of `v` / `u`.
209pub fn (v Vec4[T]) div(u Vec4[T]) Vec4[T] {
210 return v / u
211}
212
213// div_scalar returns the resulting vector of the division by the `scalar` value.
214pub fn (v Vec4[T]) div_scalar[U](scalar U) Vec4[T] {
215 return Vec4[T]{v.x / T(scalar), v.y / T(scalar), v.z / T(scalar), v.w / T(scalar)}
216}
217
218// divide divides the vector by `u`.
219pub fn (mut v Vec4[T]) divide(u Vec4[T]) {
220 v.x /= u.x
221 v.y /= u.y
222 v.z /= u.z
223 v.w /= u.w
224}
225
226// divide_scalar divides the vector by `scalar`.
227pub fn (mut v Vec4[T]) divide_scalar[U](scalar U) {
228 v.x /= T(scalar)
229 v.y /= T(scalar)
230 v.z /= T(scalar)
231 v.w /= T(scalar)
232}
233
234//
235// Utility
236//
237
238// magnitude returns the magnitude, also known as the length, of the vector.
239pub fn (v Vec4[T]) magnitude() T {
240 if v.x == 0 && v.y == 0 && v.z == 0 && v.w == 0 {
241 return T(0)
242 }
243 return T(math.sqrt((v.x * v.x) + (v.y * v.y) + (v.z * v.z) + (v.w * v.w)))
244}
245
246// dot returns the dot product of `v` and `u`.
247pub fn (v Vec4[T]) dot(u Vec4[T]) T {
248 return T((v.x * u.x) + (v.y * u.y) + (v.z * u.z) + (v.w * u.w))
249}
250
251// cross_xyz returns the cross product of `v` and `u`'s `x`,`y` and `z` fields.
252pub fn (v Vec4[T]) cross_xyz(u Vec4[T]) Vec4[T] {
253 return Vec4[T]{
254 x: (v.y * u.z) - (v.z * u.y)
255 y: (v.z * u.x) - (v.x * u.z)
256 z: (v.x * u.y) - (v.y * u.x)
257 w: 0
258 }
259}
260
261// unit returns the unit vector.
262// unit vectors always have a magnitude, or length, of exactly 1.
263pub fn (v Vec4[T]) unit() Vec4[T] {
264 m := v.magnitude()
265 return Vec4[T]{
266 x: v.x / m
267 y: v.y / m
268 z: v.z / m
269 w: v.w / m
270 }
271}
272
273// perpendicular returns the `v` projected perpendicular vector to the 'u' vector.
274pub fn (v Vec4[T]) perpendicular(u Vec4[T]) Vec4[T] {
275 return v - v.project(u)
276}
277
278// project returns the projected vector.
279// The projection of vector `v` onto vector `u` is the orthogonal projection
280// of `v` onto a straight line parallel to `u` that passes through the origin.
281// This is equivalent to the vector projection of `v` onto the unit vector in the direction of `u`.
282// and is given by the formula: proj_v(u) = (v · u / |u|^2) * u
283// where "·" denotes the dot product and |u| is the magnitude of vector `u`.
284// If `v` is a zero vector, the result will also be a zero vector.
285// example:
286// TODO: add examples
287pub fn (v Vec4[T]) project(u Vec4[T]) Vec4[T] {
288 scale := T(v.dot(u) / u.dot(u))
289 return u.mul_scalar(scale)
290}
291
292// eq returns a bool indicating if the two vectors are equal.
293@[inline]
294pub fn (v Vec4[T]) eq(u Vec4[T]) bool {
295 return v.x == u.x && v.y == u.y && v.z == u.z && v.w == u.w
296}
297
298// eq_epsilon returns a bool indicating if the two vectors are equal within the module `vec_epsilon` const.
299pub fn (v Vec4[T]) eq_epsilon(u Vec4[T]) bool {
300 return v.eq_approx[T, T](u, T(vec_epsilon))
301}
302
303// eq_approx returns whether these vectors are approximately equal within `tolerance`.
304pub fn (v Vec4[T]) eq_approx[T, U](u Vec4[T], tolerance U) bool {
305 diff_x := math.abs(v.x - u.x)
306 diff_y := math.abs(v.y - u.y)
307 diff_z := math.abs(v.z - u.z)
308 diff_w := math.abs(v.w - u.w)
309 if diff_x <= tolerance && diff_y <= tolerance && diff_z <= tolerance && diff_w <= tolerance {
310 return true
311 }
312
313 max_x := math.max(math.abs(v.x), math.abs(u.x))
314 max_y := math.max(math.abs(v.y), math.abs(u.y))
315 max_z := math.max(math.abs(v.z), math.abs(u.z))
316 max_w := math.max(math.abs(v.w), math.abs(u.w))
317 if diff_x < max_x * tolerance && diff_y < max_y * tolerance && diff_z < max_z * tolerance
318 && diff_w < max_w * tolerance {
319 return true
320 }
321 return false
322}
323
324// is_approx_zero returns whether this vector is equal to zero within `tolerance`.
325pub fn (v Vec4[T]) is_approx_zero(tolerance f64) bool {
326 if math.abs(v.x) <= tolerance && math.abs(v.y) <= tolerance && math.abs(v.z) <= tolerance
327 && math.abs(v.w) <= tolerance {
328 return true
329 }
330 return false
331}
332
333// eq_scalar returns a bool indicating if the `x`,`y`,`z` and `w` fields all equals `scalar`.
334pub fn (v Vec4[T]) eq_scalar[U](scalar U) bool {
335 return v.x == scalar && v.y == T(scalar) && v.z == T(scalar) && v.w == T(scalar)
336}
337
338// distance returns the distance to the vector `u`.
339pub fn (v Vec4[T]) distance(u Vec4[T]) f64 {
340 return math.sqrt((v.x - u.x) * (v.x - u.x) + (v.y - u.y) * (v.y - u.y) +
341 (v.z - u.z) * (v.z - u.z) + (v.w - u.w) * (v.w - u.w))
342}
343
344// manhattan_distance returns the Manhattan distance to the vector `u`.
345pub fn (v Vec4[T]) manhattan_distance(u Vec4[T]) f64 {
346 return math.abs(v.x - u.x) + math.abs(v.y - u.y) + math.abs(v.z - u.z) + math.abs(v.w - u.w)
347}
348
349// abs sets `x`, `y`, `z` and `w` field values to their absolute values.
350pub fn (mut v Vec4[T]) abs() {
351 if v.x < 0 {
352 v.x = math.abs(v.x)
353 }
354 if v.y < 0 {
355 v.y = math.abs(v.y)
356 }
357 if v.z < 0 {
358 v.z = math.abs(v.z)
359 }
360 if v.w < 0 {
361 v.w = math.abs(v.w)
362 }
363}
364
365// NOTE a few of the following functions was adapted and/or inspired from Dario Deleddas excellent
366// work on the `gg.m4` vlib module. Here's the Copyright/license text covering that code:
367//
368// Copyright (c) 2021 Dario Deledda. All rights reserved.
369// Use of this source code is governed by an MIT license
370// that can be found in the LICENSE file.
371
372// clean returns a vector with all fields of this vector set to zero (0) if they fall within `tolerance`.
373pub fn (v Vec4[T]) clean[U](tolerance U) Vec4[T] {
374 mut r := v.copy()
375 if math.abs(v.x) < tolerance {
376 r.x = 0
377 }
378 if math.abs(v.y) < tolerance {
379 r.y = 0
380 }
381 if math.abs(v.z) < tolerance {
382 r.z = 0
383 }
384 if math.abs(v.w) < tolerance {
385 r.w = 0
386 }
387 return r
388}
389
390// clean_tolerance sets all fields to zero (0) if they fall within `tolerance`.
391pub fn (mut v Vec4[T]) clean_tolerance[U](tolerance U) {
392 if math.abs(v.x) < tolerance {
393 v.x = 0
394 }
395 if math.abs(v.y) < tolerance {
396 v.y = 0
397 }
398 if math.abs(v.z) < tolerance {
399 v.z = 0
400 }
401 if math.abs(v.w) < tolerance {
402 v.w = 0
403 }
404}
405
406// inv returns the inverse, or reciprocal, of the vector.
407pub fn (v Vec4[T]) inv() Vec4[T] {
408 return Vec4[T]{
409 x: if v.x != 0 { T(1) / v.x } else { 0 }
410 y: if v.y != 0 { T(1) / v.y } else { 0 }
411 z: if v.z != 0 { T(1) / v.z } else { 0 }
412 w: if v.w != 0 { T(1) / v.w } else { 0 }
413 }
414}
415
416// normalize normalizes the vector.
417pub fn (v Vec4[T]) normalize() Vec4[T] {
418 m := v.magnitude()
419 if m == 0 {
420 return vec4[T](0, 0, 0, 0)
421 }
422 return Vec4[T]{
423 x: v.x * (1 / m)
424 y: v.y * (1 / m)
425 z: v.z * (1 / m)
426 w: v.w * (1 / m)
427 }
428}
429
430// sum returns a sum of all the fields.
431pub fn (v Vec4[T]) sum() T {
432 return v.x + v.y + v.z + v.w
433}
434