| 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 |
| 3 | module vec |
| 4 | |
| 5 | import math |
| 6 | |
| 7 | // Vec4[T] is a generic struct representing a vector in 4D space. |
| 8 | pub struct Vec4[T] { |
| 9 | pub 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. |
| 17 | pub 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. |
| 27 | pub 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. |
| 35 | pub 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. |
| 43 | pub 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`. |
| 48 | pub 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`. |
| 56 | pub 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`. |
| 62 | pub 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`. |
| 67 | pub 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`. |
| 74 | pub 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] |
| 84 | pub 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`. |
| 89 | pub 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. |
| 95 | pub 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. |
| 101 | pub 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. |
| 106 | pub 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. |
| 111 | pub 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. |
| 119 | pub 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] |
| 132 | pub 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`. |
| 137 | pub 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. |
| 142 | pub 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. |
| 147 | pub 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. |
| 155 | pub 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] |
| 168 | pub 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`. |
| 173 | pub 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. |
| 178 | pub 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`. |
| 183 | pub 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`. |
| 191 | pub 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] |
| 204 | pub 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`. |
| 209 | pub 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. |
| 214 | pub 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`. |
| 219 | pub 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`. |
| 227 | pub 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. |
| 239 | pub 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`. |
| 247 | pub 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. |
| 252 | pub 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. |
| 263 | pub 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. |
| 274 | pub 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 |
| 287 | pub 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] |
| 294 | pub 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. |
| 299 | pub 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`. |
| 304 | pub 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`. |
| 325 | pub 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`. |
| 334 | pub 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`. |
| 339 | pub 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`. |
| 345 | pub 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. |
| 350 | pub 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`. |
| 373 | pub 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`. |
| 391 | pub 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. |
| 407 | pub 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. |
| 417 | pub 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. |
| 431 | pub fn (v Vec4[T]) sum() T { |
| 432 | return v.x + v.y + v.z + v.w |
| 433 | } |
| 434 | |