| 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 | pub const vec_epsilon = 10e-7 |
| 8 | |
| 9 | // Vec2[T] is a generic struct representing a vector in 2D space. |
| 10 | pub struct Vec2[T] { |
| 11 | pub mut: |
| 12 | x T |
| 13 | y T |
| 14 | } |
| 15 | |
| 16 | // vec2[T] returns a `Vec2` of type `T`, with `x` and `y` fields set. |
| 17 | pub fn vec2[T](x T, y T) Vec2[T] { |
| 18 | return Vec2[T]{ |
| 19 | x: x |
| 20 | y: y |
| 21 | } |
| 22 | } |
| 23 | |
| 24 | // zero sets the `x` and `y` fields to 0. |
| 25 | pub fn (mut v Vec2[T]) zero() { |
| 26 | v.x = 0 |
| 27 | v.y = 0 |
| 28 | } |
| 29 | |
| 30 | // one sets the `x` and `y` fields to 1. |
| 31 | pub fn (mut v Vec2[T]) one() { |
| 32 | v.x = 1 |
| 33 | v.y = 1 |
| 34 | } |
| 35 | |
| 36 | // copy returns a copy of this vector. |
| 37 | pub fn (v Vec2[T]) copy() Vec2[T] { |
| 38 | return Vec2[T]{v.x, v.y} |
| 39 | } |
| 40 | |
| 41 | // from sets the `x` and `y` fields from `u`. |
| 42 | pub fn (mut v Vec2[T]) from(u Vec2[T]) { |
| 43 | v.x = u.x |
| 44 | v.y = u.y |
| 45 | } |
| 46 | |
| 47 | // from_vec3 sets the `x` and `y` fields from `u`. |
| 48 | pub fn (mut v Vec2[T]) from_vec3[U](u Vec3[U]) { |
| 49 | v.x = T(u.x) |
| 50 | v.y = T(u.y) |
| 51 | } |
| 52 | |
| 53 | // as_vec3 returns a Vec3 with `x` and `y` fields set from `v`, `z` is set to 0. |
| 54 | pub fn (v Vec2[T]) as_vec3[T]() Vec3[T] { |
| 55 | return Vec3[T]{v.x, v.y, 0} |
| 56 | } |
| 57 | |
| 58 | // from_vec4 sets the `x` and `y` fields from `u`. |
| 59 | pub fn (mut v Vec2[T]) from_vec4[U](u Vec4[U]) { |
| 60 | v.x = T(u.x) |
| 61 | v.y = T(u.y) |
| 62 | } |
| 63 | |
| 64 | // as_vec4 returns a Vec4 with `x` and `y` fields set from `v`, `z` and `w` is set to 0. |
| 65 | pub fn (v Vec2[T]) as_vec4[T]() Vec4[T] { |
| 66 | return Vec4[T]{v.x, v.y, 0, 0} |
| 67 | } |
| 68 | |
| 69 | // |
| 70 | // Addition |
| 71 | // |
| 72 | |
| 73 | // + returns the resulting vector of the addition of `v` and `u`. |
| 74 | @[inline] |
| 75 | pub fn (v Vec2[T]) + (u Vec2[T]) Vec2[T] { |
| 76 | return Vec2[T]{v.x + u.x, v.y + u.y} |
| 77 | } |
| 78 | |
| 79 | // add returns the resulting vector of the addition of `v` + `u`. |
| 80 | pub fn (v Vec2[T]) add(u Vec2[T]) Vec2[T] { |
| 81 | return v + u |
| 82 | } |
| 83 | |
| 84 | // add_scalar returns the resulting vector of the addition of the `scalar` value. |
| 85 | pub fn (v Vec2[T]) add_scalar[U](scalar U) Vec2[T] { |
| 86 | return Vec2[T]{v.x + T(scalar), v.y + T(scalar)} |
| 87 | } |
| 88 | |
| 89 | // plus adds vector `u` to the vector. |
| 90 | pub fn (mut v Vec2[T]) plus(u Vec2[T]) { |
| 91 | v.x += u.x |
| 92 | v.y += u.y |
| 93 | } |
| 94 | |
| 95 | // plus_scalar adds the scalar `scalar` to the vector. |
| 96 | pub fn (mut v Vec2[T]) plus_scalar[U](scalar U) { |
| 97 | v.x += T(scalar) |
| 98 | v.y += T(scalar) |
| 99 | } |
| 100 | |
| 101 | // |
| 102 | // Subtraction |
| 103 | // |
| 104 | |
| 105 | // - returns the resulting vector of the subtraction of `v` and `u`. |
| 106 | @[inline] |
| 107 | pub fn (v Vec2[T]) - (u Vec2[T]) Vec2[T] { |
| 108 | return Vec2[T]{v.x - u.x, v.y - u.y} |
| 109 | } |
| 110 | |
| 111 | // sub returns the resulting vector of the subtraction of `v` - `u`. |
| 112 | pub fn (v Vec2[T]) sub(u Vec2[T]) Vec2[T] { |
| 113 | return v - u |
| 114 | } |
| 115 | |
| 116 | // sub_scalar returns the resulting vector of the subtraction of the `scalar` value. |
| 117 | pub fn (v Vec2[T]) sub_scalar[U](scalar U) Vec2[T] { |
| 118 | return Vec2[T]{v.x - T(scalar), v.y - T(scalar)} |
| 119 | } |
| 120 | |
| 121 | // subtract subtracts vector `u` from the vector. |
| 122 | pub fn (mut v Vec2[T]) subtract(u Vec2[T]) { |
| 123 | v.x -= u.x |
| 124 | v.y -= u.y |
| 125 | } |
| 126 | |
| 127 | // subtract_scalar subtracts the scalar `scalar` from the vector. |
| 128 | pub fn (mut v Vec2[T]) subtract_scalar[U](scalar U) { |
| 129 | v.x -= T(scalar) |
| 130 | v.y -= T(scalar) |
| 131 | } |
| 132 | |
| 133 | // |
| 134 | // Multiplication |
| 135 | // |
| 136 | |
| 137 | // * returns the resulting vector of the multiplication of `v` and `u`. |
| 138 | @[inline] |
| 139 | pub fn (v Vec2[T]) * (u Vec2[T]) Vec2[T] { |
| 140 | return Vec2[T]{v.x * u.x, v.y * u.y} |
| 141 | } |
| 142 | |
| 143 | // mul returns the resulting vector of the multiplication of `v` * `u`. |
| 144 | pub fn (v Vec2[T]) mul(u Vec2[T]) Vec2[T] { |
| 145 | return v * u |
| 146 | } |
| 147 | |
| 148 | // mul_scalar returns the resulting vector of the multiplication of the `scalar` value. |
| 149 | pub fn (v Vec2[T]) mul_scalar[U](scalar U) Vec2[T] { |
| 150 | return Vec2[T]{v.x * T(scalar), v.y * T(scalar)} |
| 151 | } |
| 152 | |
| 153 | // multiply multiplies the vector with `u`. |
| 154 | pub fn (mut v Vec2[T]) multiply(u Vec2[T]) { |
| 155 | v.x *= u.x |
| 156 | v.y *= u.y |
| 157 | } |
| 158 | |
| 159 | // multiply_scalar multiplies the vector with `scalar`. |
| 160 | pub fn (mut v Vec2[T]) multiply_scalar[U](scalar U) { |
| 161 | v.x *= T(scalar) |
| 162 | v.y *= T(scalar) |
| 163 | } |
| 164 | |
| 165 | // |
| 166 | // Division |
| 167 | // |
| 168 | |
| 169 | // / returns the resulting vector of the division of `v` and `u`. |
| 170 | @[inline] |
| 171 | pub fn (v Vec2[T]) / (u Vec2[T]) Vec2[T] { |
| 172 | return Vec2[T]{v.x / u.x, v.y / u.y} |
| 173 | } |
| 174 | |
| 175 | // div returns the resulting vector of the division of `v` / `u`. |
| 176 | pub fn (v Vec2[T]) div(u Vec2[T]) Vec2[T] { |
| 177 | return v / u |
| 178 | } |
| 179 | |
| 180 | // div_scalar returns the resulting vector of the division by the `scalar` value. |
| 181 | pub fn (v Vec2[T]) div_scalar[U](scalar U) Vec2[T] { |
| 182 | return Vec2[T]{v.x / T(scalar), v.y / T(scalar)} |
| 183 | } |
| 184 | |
| 185 | // divide divides the vector by `u`. |
| 186 | pub fn (mut v Vec2[T]) divide(u Vec2[T]) { |
| 187 | v.x /= u.x |
| 188 | v.y /= u.y |
| 189 | } |
| 190 | |
| 191 | // divide_scalar divides the vector by `scalar`. |
| 192 | pub fn (mut v Vec2[T]) divide_scalar[U](scalar U) { |
| 193 | v.x /= T(scalar) |
| 194 | v.y /= T(scalar) |
| 195 | } |
| 196 | |
| 197 | // |
| 198 | // Utility |
| 199 | // |
| 200 | |
| 201 | // magnitude returns the magnitude, also known as the length, of the vector. |
| 202 | pub fn (v Vec2[T]) magnitude() T { |
| 203 | if v.x == 0 && v.y == 0 { |
| 204 | return T(0) |
| 205 | } |
| 206 | $if T is f64 { |
| 207 | return math.sqrt((v.x * v.x) + (v.y * v.y)) |
| 208 | } $else { |
| 209 | return T(math.sqrt(f64(v.x * v.x) + f64(v.y * v.y))) |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | // magnitude_x returns the magnitude, also known as the length, of the 1D vector field x, y is ignored. |
| 214 | pub fn (v Vec2[T]) magnitude_x() T { |
| 215 | return T(math.sqrt(v.x * v.x)) |
| 216 | } |
| 217 | |
| 218 | // magnitude_x returns the magnitude, also known as the length, of the 1D vector field y, x is ignored. |
| 219 | pub fn (v Vec2[T]) magnitude_y() T { |
| 220 | return T(math.sqrt(v.y * v.y)) |
| 221 | } |
| 222 | |
| 223 | // dot returns the dot product of `v` and `u`. |
| 224 | // The dot product is a scalar value that represents the magnitude of one vector |
| 225 | // projected onto another vector. |
| 226 | // It is calculated by multiplying the corresponding components of the vectors |
| 227 | // and summing the results. |
| 228 | // example: |
| 229 | // ```v |
| 230 | // v := vec2[f32](3, 4) //magnitude = 5 |
| 231 | // u := vec2[f32](5, 6) //magnitude = 7.81 |
| 232 | // dot := v.dot(u) // 3*5 + 4*6 = 15 + 24 = 39 |
| 233 | // (dot) // Output: 39 |
| 234 | // ``` |
| 235 | pub fn (v Vec2[T]) dot(u Vec2[T]) T { |
| 236 | return (v.x * u.x) + (v.y * u.y) |
| 237 | } |
| 238 | |
| 239 | // cross returns the cross product of `v` and `u`. |
| 240 | pub fn (v Vec2[T]) cross(u Vec2[T]) T { |
| 241 | return (v.x * u.y) - (v.y * u.x) |
| 242 | } |
| 243 | |
| 244 | // unit returns the unit vector. |
| 245 | // unit vectors always have a magnitude, or length, of exactly 1. |
| 246 | pub fn (v Vec2[T]) unit() Vec2[T] { |
| 247 | m := v.magnitude() |
| 248 | return Vec2[T]{v.x / m, v.y / m} |
| 249 | } |
| 250 | |
| 251 | // perp_cw returns the clockwise, or "left-hand", perpendicular vector of this vector. |
| 252 | pub fn (v Vec2[T]) perp_cw() Vec2[T] { |
| 253 | return Vec2[T]{v.y, -v.x} |
| 254 | } |
| 255 | |
| 256 | // perp_ccw returns the counter-clockwise, or "right-hand", perpendicular vector of this vector. |
| 257 | pub fn (v Vec2[T]) perp_ccw() Vec2[T] { |
| 258 | return Vec2[T]{-v.y, v.x} |
| 259 | } |
| 260 | |
| 261 | // perpendicular returns the `v` projected perpendicular vector to the 'u' vector. |
| 262 | pub fn (v Vec2[T]) perpendicular(u Vec2[T]) Vec2[T] { |
| 263 | return v - v.project(u) |
| 264 | } |
| 265 | |
| 266 | // project returns the projected vector. |
| 267 | // The projection of vector `v` onto vector `u` is the orthogonal projection |
| 268 | // of `v` onto a straight line parallel to `u` that passes through the origin. |
| 269 | // This is equivalent to the vector projection of `v` onto the unit vector in the direction of `u`. |
| 270 | // and is given by the formula: proj_v(u) = (v · u / |u|^2) * u |
| 271 | // where "·" denotes the dot product and |u| is the magnitude of vector `u`. |
| 272 | // If `v` is a zero vector, the result will also be a zero vector. |
| 273 | // example: |
| 274 | // ```v |
| 275 | // v := vec2[f32](3, 4) |
| 276 | // u := vec2[f32](5, 6) |
| 277 | // proj := v.project(u) |
| 278 | // println(proj) // Output: vec2[f32](3.1967213, 3.8360658) |
| 279 | // ``` |
| 280 | pub fn (v Vec2[T]) project(u Vec2[T]) Vec2[T] { |
| 281 | scale := T(v.dot(u) / u.dot(u)) |
| 282 | return u.mul_scalar(scale) |
| 283 | } |
| 284 | |
| 285 | // rotate_around_cw returns the vector `v` rotated *clockwise* `radians` around an origin vector `o` in Cartesian space. |
| 286 | pub fn (v Vec2[T]) rotate_around_cw(o Vec2[T], radians f64) Vec2[T] { |
| 287 | return v.rotate_around_ccw(o, -radians) |
| 288 | } |
| 289 | |
| 290 | // rotate_around_ccw returns the vector `v` rotated *counter-clockwise* `radians` around an origin vector `o` in Cartesian space. |
| 291 | pub fn (v Vec2[T]) rotate_around_ccw(o Vec2[T], radians f64) Vec2[T] { |
| 292 | s := math.sin(radians) |
| 293 | c := math.cos(radians) |
| 294 | dx := v.x - o.x |
| 295 | dy := v.y - o.y |
| 296 | return Vec2[T]{ |
| 297 | x: T(c * dx - s * dy + o.x) |
| 298 | y: T(s * dx + c * dy + o.y) |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | // eq returns a bool indicating if the two vectors are equal. |
| 303 | @[inline] |
| 304 | pub fn (v Vec2[T]) eq(u Vec2[T]) bool { |
| 305 | return v.x == u.x && v.y == u.y |
| 306 | } |
| 307 | |
| 308 | // eq_epsilon returns a bool indicating if the two vectors are equal within the module `vec_epsilon` const. |
| 309 | pub fn (v Vec2[T]) eq_epsilon(u Vec2[T]) bool { |
| 310 | return v.eq_approx[T, T](u, T(vec_epsilon)) |
| 311 | } |
| 312 | |
| 313 | // eq_approx returns whether these vectors are approximately equal within `tolerance`. |
| 314 | pub fn (v Vec2[T]) eq_approx[T, U](u Vec2[T], tolerance U) bool { |
| 315 | diff_x := math.abs(v.x - u.x) |
| 316 | diff_y := math.abs(v.y - u.y) |
| 317 | if diff_x <= tolerance && diff_y <= tolerance { |
| 318 | return true |
| 319 | } |
| 320 | |
| 321 | max_x := math.max(math.abs(v.x), math.abs(u.x)) |
| 322 | max_y := math.max(math.abs(v.y), math.abs(u.y)) |
| 323 | if diff_x < max_x * tolerance && diff_y < max_y * tolerance { |
| 324 | return true |
| 325 | } |
| 326 | |
| 327 | return false |
| 328 | } |
| 329 | |
| 330 | // is_approx_zero returns whether this vector is equal to zero within `tolerance`. |
| 331 | pub fn (v Vec2[T]) is_approx_zero(tolerance T) bool { |
| 332 | if math.abs(v.x) <= tolerance && math.abs(v.y) <= tolerance { |
| 333 | return true |
| 334 | } |
| 335 | return false |
| 336 | } |
| 337 | |
| 338 | // eq_scalar returns a bool indicating if the `x` and `y` fields both equals `scalar`. |
| 339 | pub fn (v Vec2[T]) eq_scalar[U](scalar U) bool { |
| 340 | return v.x == T(scalar) && v.y == scalar |
| 341 | } |
| 342 | |
| 343 | // distance returns the distance to the vector `u`. |
| 344 | pub fn (v Vec2[T]) distance(u Vec2[T]) T { |
| 345 | $if T is f64 { |
| 346 | return math.sqrt((v.x - u.x) * (v.x - u.x) + (v.y - u.y) * (v.y - u.y)) |
| 347 | } $else { |
| 348 | return T(math.sqrt(f64(v.x - u.x) * f64(v.x - u.x) + f64(v.y - u.y) * f64(v.y - u.y))) |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | // manhattan_distance returns the Manhattan Distance to the vector `u`. |
| 353 | pub fn (v Vec2[T]) manhattan_distance(u Vec2[T]) T { |
| 354 | return math.abs(v.x - u.x) + math.abs(v.y - u.y) |
| 355 | } |
| 356 | |
| 357 | // angle_between returns the angle in radians to the vector `u`. |
| 358 | pub fn (v Vec2[T]) angle_between(u Vec2[T]) T { |
| 359 | $if T is f64 { |
| 360 | return math.atan2(v.cross(u), v.dot(u)) |
| 361 | } $else { |
| 362 | return T(math.atan2(f64(v.cross(u)), f64(v.dot(u)))) |
| 363 | } |
| 364 | } |
| 365 | |
| 366 | // angle_towards returns the angle in radians between the horizontal axis, |
| 367 | // and a line passing through the first and second point, as if the first point |
| 368 | // was at the center of the coordinate system. |
| 369 | pub fn (p1 Vec2[T]) angle_towards(p2 Vec2[T]) T { |
| 370 | $if T is f64 { |
| 371 | return math.atan2(p2.y - p1.y, p2.x - p1.x) |
| 372 | } $else { |
| 373 | return T(math.atan2(f64(p2.y) - f64(p1.y), f64(p2.x) - f64(p1.x))) |
| 374 | } |
| 375 | } |
| 376 | |
| 377 | // angle returns the angle in radians of the vector. |
| 378 | // example: |
| 379 | // ```v |
| 380 | // v := vec2[f32](3.0, 4.0) |
| 381 | // a := v.angle() |
| 382 | // assert a == 0.64 (approximate value in radians) |
| 383 | // w := vec2[f32](0.0, 1.0) |
| 384 | // b := w.angle() |
| 385 | // assert b == 1.57 (approximate value in radians) |
| 386 | // ``` |
| 387 | pub fn (v Vec2[T]) angle() T { |
| 388 | $if T is f64 { |
| 389 | return math.atan2(v.y, v.x) |
| 390 | } $else { |
| 391 | return T(math.atan2(f64(v.y), f64(v.x))) |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | // abs sets `x` and `y` field values to their absolute values. |
| 396 | pub fn (mut v Vec2[T]) abs() { |
| 397 | v.x = math.abs(v.x) |
| 398 | v.y = math.abs(v.y) |
| 399 | } |
| 400 | |
| 401 | // clean returns a vector with all fields of this vector set to zero (0) if they fall within `tolerance`. |
| 402 | pub fn (v Vec2[T]) clean[U](tolerance U) Vec2[T] { |
| 403 | mut r := v.copy() |
| 404 | if math.abs(v.x) < tolerance { |
| 405 | r.x = 0 |
| 406 | } |
| 407 | if math.abs(v.y) < tolerance { |
| 408 | r.y = 0 |
| 409 | } |
| 410 | return r |
| 411 | } |
| 412 | |
| 413 | // clean_tolerance sets all fields to zero (0) if they fall within `tolerance`. |
| 414 | pub fn (mut v Vec2[T]) clean_tolerance[U](tolerance U) { |
| 415 | if math.abs(v.x) < tolerance { |
| 416 | v.x = 0 |
| 417 | } |
| 418 | if math.abs(v.y) < tolerance { |
| 419 | v.y = 0 |
| 420 | } |
| 421 | } |
| 422 | |
| 423 | // inv returns the inverse, or reciprocal, of the vector. |
| 424 | // If a field is zero, its inverse is also set to zero to avoid division by zero. |
| 425 | // the direction the vector points is generally not preserved, but |
| 426 | // the magnitude of each field is inverted. |
| 427 | // example: |
| 428 | // ```v |
| 429 | // v := vec2[f32](2.0, 4.0) |
| 430 | // inv_v := v.inv() // inv_v == vec2[f32](0.5, 0.25) |
| 431 | // ``` |
| 432 | pub fn (v Vec2[T]) inv() Vec2[T] { |
| 433 | return Vec2[T]{ |
| 434 | x: if v.x != 0 { T(1) / v.x } else { 0 } |
| 435 | y: if v.y != 0 { T(1) / v.y } else { 0 } |
| 436 | } |
| 437 | } |
| 438 | |
| 439 | // normalize normalizes the vector. |
| 440 | // A normalized vector has the same direction as the original vector but a magnitude of 1. |
| 441 | // If the vector has a magnitude of 0, a zero vector is returned since we cannot find the direction of a zero-length vector. |
| 442 | // example: |
| 443 | // ```v |
| 444 | // v := vec2[f32](3.0, 4.0)//magnitude = 5.0 |
| 445 | // n := v.normalize() // n == vec2[f32](0.6, 0.8) // magnitude = 1.0 |
| 446 | // ``` |
| 447 | pub fn (v Vec2[T]) normalize() Vec2[T] { |
| 448 | m := v.magnitude() |
| 449 | if m == 0 { |
| 450 | return vec2[T](0, 0) |
| 451 | } |
| 452 | return Vec2[T]{ |
| 453 | x: v.x * (1 / m) |
| 454 | y: v.y * (1 / m) |
| 455 | } |
| 456 | } |
| 457 | |
| 458 | // sum returns a sum of all the fields. |
| 459 | // example: |
| 460 | // ```v |
| 461 | // v := vec2[f32](3.0, 4.0) |
| 462 | // s := v.sum() |
| 463 | // assert s == 7.0 |
| 464 | // ``` |
| 465 | pub fn (v Vec2[T]) sum() T { |
| 466 | return v.x + v.y |
| 467 | } |
| 468 | |