From 7e4dc24f1b1cc57b7f08fed0c8652463e62b5e0a Mon Sep 17 00:00:00 2001 From: Larpon Date: Mon, 19 Dec 2022 17:10:48 +0100 Subject: [PATCH] math: add `math.vec` module with generic 2D, 3D and 4D vector operations (#16710) --- CHANGELOG.md | 1 + vlib/math/vec/vec2.v | 389 ++++++++++++++++++++++++++++++++++ vlib/math/vec/vec2_test.v | 84 ++++++++ vlib/math/vec/vec3.v | 396 +++++++++++++++++++++++++++++++++++ vlib/math/vec/vec3_test.v | 90 ++++++++ vlib/math/vec/vec4.v | 425 ++++++++++++++++++++++++++++++++++++++ vlib/math/vec/vec4_test.v | 99 +++++++++ 7 files changed, 1484 insertions(+) create mode 100644 vlib/math/vec/vec2.v create mode 100644 vlib/math/vec/vec2_test.v create mode 100644 vlib/math/vec/vec3.v create mode 100644 vlib/math/vec/vec3_test.v create mode 100644 vlib/math/vec/vec4.v create mode 100644 vlib/math/vec/vec4_test.v diff --git a/CHANGELOG.md b/CHANGELOG.md index 8be633e31..95ff86886 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## V 0.3.3 *Not yet released* +- add `math.vec` module for generic vector math. - `go foo()` has been replaced with `spawn foo()` (launches an OS thread, `go` will be used for upcoming coroutines instead). - vfmt now supports `// vfmt off` and `// vfmt on` for turning off the formatting locally for *short* snippets of code. Useful for keeping your carefully arranged matrices intact. diff --git a/vlib/math/vec/vec2.v b/vlib/math/vec/vec2.v new file mode 100644 index 000000000..575aba3e2 --- /dev/null +++ b/vlib/math/vec/vec2.v @@ -0,0 +1,389 @@ +// Copyright(C) 2020-2022 Lars Pontoppidan. All rights reserved. +// Use of this source code is governed by an MIT license file distributed with this software package +module vec + +import math + +pub const vec_epsilon = f32(10e-7) + +// Vec2[T] is a generic struct representing a vector in 2D space. +pub struct Vec2[T] { +pub mut: + x T + y T +} + +// vec2[T] returns a `Vec2` of type `T`, with `x` and `y` fields set. +pub fn vec2[T](x T, y T) Vec2[T] { + return Vec2[T]{ + x: x + y: y + } +} + +// zero sets the `x` and `y` fields to 0. +pub fn (mut v Vec2[T]) zero() { + v.x = 0 + v.y = 0 +} + +// one sets the `x` and `y` fields to 1. +pub fn (mut v Vec2[T]) one() { + v.x = 1 + v.y = 1 +} + +// copy returns a copy of this vector. +pub fn (v Vec2[T]) copy() Vec2[T] { + return Vec2[T]{v.x, v.y} +} + +// from sets the `x` and `y` fields from `u`. +pub fn (mut v Vec2[T]) from(u Vec2[T]) { + v.x = u.x + v.y = u.y +} + +// from_vec3 sets the `x` and `y` fields from `u`. +pub fn (mut v Vec2[T]) from_vec3[U](u Vec3[U]) { + v.x = T(u.x) + v.y = T(u.y) +} + +// as_vec3 returns a Vec3 with `x` and `y` fields set from `v`, `z` is set to 0. +pub fn (v Vec2[T]) as_vec3[T]() Vec3[T] { + return Vec3[T]{v.x, v.y, 0} +} + +// from_vec4 sets the `x` and `y` fields from `u`. +pub fn (mut v Vec2[T]) from_vec4[U](u Vec4[U]) { + v.x = T(u.x) + v.y = T(u.y) +} + +// as_vec4 returns a Vec4 with `x` and `y` fields set from `v`, `z` and `w` is set to 0. +pub fn (v Vec2[T]) as_vec4[T]() Vec4[T] { + return Vec4[T]{v.x, v.y, 0, 0} +} + +// +// Addition +// + +// + returns the resulting vector of the addition of `v` and `u`. +[inline] +pub fn (v Vec2[T]) + (u Vec2[T]) Vec2[T] { + return Vec2[T]{v.x + u.x, v.y + u.y} +} + +// add returns the resulting vector of the addition of `v` + `u`. +pub fn (v Vec2[T]) add(u Vec2[T]) Vec2[T] { + return v + u +} + +// add_scalar returns the resulting vector of the addition of the `scalar` value. +pub fn (v Vec2[T]) add_scalar[U](scalar U) Vec2[T] { + return Vec2[T]{v.x + T(scalar), v.y + T(scalar)} +} + +// plus adds vector `u` to the vector. +pub fn (mut v Vec2[T]) plus(u Vec2[T]) { + v.x += u.x + v.y += u.y +} + +// plus_scalar adds the scalar `scalar` to the vector. +pub fn (mut v Vec2[T]) plus_scalar[U](scalar U) { + v.x += T(scalar) + v.y += T(scalar) +} + +// +// Subtraction +// + +// - returns the resulting vector of the subtraction of `v` and `u`. +[inline] +pub fn (v Vec2[T]) - (u Vec2[T]) Vec2[T] { + return Vec2[T]{v.x - u.x, v.y - u.y} +} + +// sub returns the resulting vector of the subtraction of `v` - `u`. +pub fn (v Vec2[T]) sub(u Vec2[T]) Vec2[T] { + return v - u +} + +// sub_scalar returns the resulting vector of the subtraction of the `scalar` value. +pub fn (v Vec2[T]) sub_scalar[U](scalar U) Vec2[T] { + return Vec2[T]{v.x - T(scalar), v.y - T(scalar)} +} + +// subtract subtracts vector `u` from the vector. +pub fn (mut v Vec2[T]) subtract(u Vec2[T]) { + v.x -= u.x + v.y -= u.y +} + +// subtract_scalar subtracts the scalar `scalar` from the vector. +pub fn (mut v Vec2[T]) subtract_scalar[U](scalar U) { + v.x -= T(scalar) + v.y -= T(scalar) +} + +// +// Multiplication +// + +// * returns the resulting vector of the multiplication of `v` and `u`. +[inline] +pub fn (v Vec2[T]) * (u Vec2[T]) Vec2[T] { + return Vec2[T]{v.x * u.x, v.y * u.y} +} + +// mul returns the resulting vector of the multiplication of `v` * `u`. +pub fn (v Vec2[T]) mul(u Vec2[T]) Vec2[T] { + return v * u +} + +// mul_scalar returns the resulting vector of the multiplication of the `scalar` value. +pub fn (v Vec2[T]) mul_scalar[U](scalar U) Vec2[T] { + return Vec2[T]{v.x * T(scalar), v.y * T(scalar)} +} + +// multiply multiplies the vector with `u`. +pub fn (mut v Vec2[T]) multiply(u Vec2[T]) { + v.x *= u.x + v.y *= u.y +} + +// multiply_scalar multiplies the vector with `scalar`. +pub fn (mut v Vec2[T]) multiply_scalar[U](scalar U) { + v.x *= T(scalar) + v.y *= T(scalar) +} + +// +// Division +// + +// / returns the resulting vector of the division of `v` and `u`. +[inline] +pub fn (v Vec2[T]) / (u Vec2[T]) Vec2[T] { + return Vec2[T]{v.x / u.x, v.y / u.y} +} + +// div returns the resulting vector of the division of `v` / `u`. +pub fn (v Vec2[T]) div(u Vec2[T]) Vec2[T] { + return v / u +} + +// div_scalar returns the resulting vector of the division by the `scalar` value. +pub fn (v Vec2[T]) div_scalar[U](scalar U) Vec2[T] { + return Vec2[T]{v.x / T(scalar), v.y / T(scalar)} +} + +// divide divides the vector by `u`. +pub fn (mut v Vec2[T]) divide(u Vec2[T]) { + v.x /= u.x + v.y /= u.y +} + +// divide_scalar divides the vector by `scalar`. +pub fn (mut v Vec2[T]) divide_scalar[U](scalar U) { + v.x /= T(scalar) + v.y /= T(scalar) +} + +// +// Utility +// + +// magnitude returns the magnitude, also known as the length, of the vector. +pub fn (v Vec2[T]) magnitude() T { + if v.x == 0 && v.y == 0 { + return T(0) + } + $if T is f64 { + return math.sqrt((v.x * v.x) + (v.y * v.y)) + } $else { + return T(math.sqrt(f64(v.x * v.x) + f64(v.y * v.y))) + } +} + +// magnitude_x returns the magnitude, also known as the length, of the 1D vector field x, y is ignored. +pub fn (v Vec2[T]) magnitude_x() T { + return T(math.sqrt(v.x * v.x)) +} + +// magnitude_x returns the magnitude, also known as the length, of the 1D vector field y, x is ignored. +pub fn (v Vec2[T]) magnitude_y() T { + return T(math.sqrt(v.y * v.y)) +} + +// dot returns the dot product of `v` and `u`. +pub fn (v Vec2[T]) dot(u Vec2[T]) T { + return (v.x * u.x) + (v.y * u.y) +} + +// cross returns the cross product of `v` and `u`. +pub fn (v Vec2[T]) cross(u Vec2[T]) T { + return (v.x * u.y) - (v.y * u.x) +} + +// unit returns the unit vector. +// unit vectors always have a magnitude, or length, of exactly 1. +pub fn (v Vec2[T]) unit() Vec2[T] { + m := v.magnitude() + return Vec2[T]{v.x / m, v.y / m} +} + +// perp_cw returns the clockwise, or "left-hand", perpendicular vector of this vector. +pub fn (v Vec2[T]) perp_cw() Vec2[T] { + return Vec2[T]{v.y, -v.x} +} + +// perp_ccw returns the counter-clockwise, or "right-hand", perpendicular vector of this vector. +pub fn (v Vec2[T]) perp_ccw() Vec2[T] { + return Vec2[T]{-v.y, v.x} +} + +// perpendicular returns the `u` projected perpendicular vector to this vector. +pub fn (v Vec2[T]) perpendicular(u Vec2[T]) Vec2[T] { + return v - v.project(u) +} + +// project returns the projected vector. +pub fn (v Vec2[T]) project(u Vec2[T]) Vec2[T] { + percent := v.dot(u) / u.dot(v) + return u.mul_scalar(percent) +} + +// eq returns a bool indicating if the two vectors are equal. +[inline] +pub fn (v Vec2[T]) eq(u Vec2[T]) bool { + return v.x == u.x && v.y == u.y +} + +// eq_epsilon returns a bool indicating if the two vectors are equal within the module `vec_epsilon` const. +pub fn (v Vec2[T]) eq_epsilon(u Vec2[T]) bool { + return v.eq_approx[T, f32](u, vec.vec_epsilon) +} + +// eq_approx returns whether these vectors are approximately equal within `tolerance`. +pub fn (v Vec2[T]) eq_approx[T, U](u Vec2[T], tolerance U) bool { + diff_x := math.abs(v.x - u.x) + diff_y := math.abs(v.y - u.y) + if diff_x <= tolerance && diff_y <= tolerance { + return true + } + + max_x := math.max(math.abs(v.x), math.abs(u.x)) + max_y := math.max(math.abs(v.y), math.abs(u.y)) + if diff_x < max_x * tolerance && diff_y < max_y * tolerance { + return true + } + + return false +} + +// is_approx_zero returns whether this vector is equal to zero within `tolerance`. +pub fn (v Vec2[T]) is_approx_zero(tolerance T) bool { + if math.abs(v.x) <= tolerance && math.abs(v.y) <= tolerance { + return true + } + return false +} + +// eq_scalar returns a bool indicating if the `x` and `y` fields both equals `scalar`. +pub fn (v Vec2[T]) eq_scalar[U](scalar U) bool { + return v.x == T(scalar) && v.y == scalar +} + +// distance returns the distance to the vector `u`. +pub fn (v Vec2[T]) distance(u Vec2[T]) T { + $if T is f64 { + return math.sqrt((v.x - u.x) * (v.x - u.x) + (v.y - u.y) * (v.y - u.y)) + } $else { + return T(math.sqrt(f64(v.x - u.x) * f64(v.x - u.x) + f64(v.y - u.y) * f64(v.y - u.y))) + } +} + +// manhattan_distance returns the Manhattan Distance to the vector `u`. +pub fn (v Vec2[T]) manhattan_distance(u Vec2[T]) T { + return math.abs(v.x - u.x) + math.abs(v.y - u.y) +} + +// angle_between returns the angle in radians to the vector `u`. +pub fn (v Vec2[T]) angle_between(u Vec2[T]) T { + $if T is f64 { + return math.atan2((v.y - u.y), (v.x - u.x)) + } $else { + return T(math.atan2(f64(v.y - u.y), f64(v.x - u.x))) + } +} + +// angle returns the angle in radians of the vector. +pub fn (v Vec2[T]) angle() T { + $if T is f64 { + return math.atan2(v.y, v.x) + } $else { + return T(math.atan2(f64(v.y), f64(v.x))) + } +} + +// abs sets `x` and `y` field values to their absolute values. +pub fn (mut v Vec2[T]) abs() { + if v.x < 0 { + v.x = math.abs(v.x) + } + if v.y < 0 { + v.y = math.abs(v.y) + } +} + +// clean returns a vector with all fields of this vector set to zero (0) if they fall within `tolerance`. +pub fn (v Vec2[T]) clean[U](tolerance U) Vec2[T] { + mut r := v.copy() + if math.abs(v.x) < tolerance { + r.x = 0 + } + if math.abs(v.y) < tolerance { + r.y = 0 + } + return r +} + +// clean_tolerance sets all fields to zero (0) if they fall within `tolerance`. +pub fn (mut v Vec2[T]) clean_tolerance[U](tolerance U) { + if math.abs(v.x) < tolerance { + v.x = 0 + } + if math.abs(v.y) < tolerance { + v.y = 0 + } +} + +// inv returns the inverse, or reciprocal, of the vector. +pub fn (v Vec2[T]) inv() Vec2[T] { + return Vec2[T]{ + x: if v.x != 0 { T(1) / v.x } else { 0 } + y: if v.y != 0 { T(1) / v.y } else { 0 } + } +} + +// normalize normalizes the vector. +pub fn (v Vec2[T]) normalize() Vec2[T] { + m := v.magnitude() + if m == 0 { + return vec2[T](0, 0) + } + return Vec2[T]{ + x: v.x * (1 / m) + y: v.y * (1 / m) + } +} + +// sum returns a sum of all the fields. +pub fn (v Vec2[T]) sum() T { + return v.x + v.y +} diff --git a/vlib/math/vec/vec2_test.v b/vlib/math/vec/vec2_test.v new file mode 100644 index 000000000..f11d790b1 --- /dev/null +++ b/vlib/math/vec/vec2_test.v @@ -0,0 +1,84 @@ +import math.vec + +fn test_vec2_int() { + mut v1 := vec.vec2(0, 0) + mut v2 := vec.vec2(0, 0) + assert v1 == v2 + v1.one() + v2.one() + assert v1.x == 1 + assert v1.y == 1 + assert v1 == v2 + + v3 := v1 + v2 + assert typeof(v3).name == 'vec.Vec2[int]' + assert v3.x == 2 + assert v3.y == 2 +} + +fn test_vec2_f32() { + mut v1 := vec.vec2(f32(0), 0) + mut v2 := vec.vec2(f32(0), 0) + assert v1 == v2 + v1.one() + v2.one() + assert v1.x == 1 + assert v1.y == 1 + assert v1 == v2 + + v3 := v1 + v2 + assert typeof(v3).name == 'vec.Vec2[f32]' + assert v3.x == 2 + assert v3.y == 2 +} + +fn test_vec2_f64() { + mut v1 := vec.vec2(0.0, 0) + mut v2 := vec.vec2(0.0, 0) + assert v1 == v2 + v1.one() + v2.one() + assert v1.x == 1 + assert v1.y == 1 + assert v1 == v2 + + v3 := v1 + v2 + assert typeof(v3).name == 'vec.Vec2[f64]' + assert v3.x == 2 + assert v3.y == 2 +} + +fn test_vec2_f64_utils_1() { + mut v1 := vec.vec2(2.0, 3.0) + mut v2 := vec.vec2(1.0, 4.0) + + mut zv := vec.vec2(5.0, 5.0) + zv.zero() + + v3 := v1 + v2 + assert v3.x == 3 + assert v3.y == 7 + + assert v1.dot(v2) == 14 + assert v1.cross(v2) == 5 + + v1l := vec.vec2(40.0, 9.0) + assert v1l.magnitude() == 41 + + mut ctv1 := vec.vec2(0.000001, 0.000001) + ctv1.clean_tolerance(0.00001) + assert ctv1 == zv +} + +fn test_vec2_f64_utils_2() { + mut v1 := vec.vec2(4.0, 4.0) + assert v1.unit().magnitude() == 1 + v2 := v1.mul_scalar(0.5) + assert v2.x == 2 + assert v2.y == 2 + assert v2.unit().magnitude() == 1 + + invv2 := v2.inv() + assert invv2.x == 0.5 + assert invv2.y == 0.5 +} diff --git a/vlib/math/vec/vec3.v b/vlib/math/vec/vec3.v new file mode 100644 index 000000000..dee3eb2fd --- /dev/null +++ b/vlib/math/vec/vec3.v @@ -0,0 +1,396 @@ +// Copyright(C) 2020-2022 Lars Pontoppidan. All rights reserved. +// Use of this source code is governed by an MIT license file distributed with this software package +module vec + +import math + +// Vec3[T] is a generic struct representing a vector in 3D space. +pub struct Vec3[T] { +pub mut: + x T + y T + z T +} + +// vec3[T] returns a `Vec3` of type `T`, with `x`,`y` and `z` fields set. +pub fn vec3[T](x T, y T, z T) Vec3[T] { + return Vec3[T]{ + x: x + y: y + z: z + } +} + +// zero sets the `x`,`y` and `z` fields to 0. +pub fn (mut v Vec3[T]) zero() { + v.x = 0 + v.y = 0 + v.z = 0 +} + +// one sets the `x`,`y` and `z` fields to 1. +pub fn (mut v Vec3[T]) one() { + v.x = 1 + v.y = 1 + v.z = 1 +} + +// copy returns a copy of this vector. +pub fn (mut v Vec3[T]) copy() Vec3[T] { + return Vec3[T]{v.x, v.y, v.z} +} + +// from sets the `x`,`y` and `z` fields from `u`. +pub fn (mut v Vec3[T]) from(u Vec3[T]) { + v.x = u.x + v.y = u.y + v.z = u.z +} + +// from_vec2 sets the `x` and `y` fields from `u`. +pub fn (mut v Vec3[T]) from_vec2[U](u Vec2[U]) { + v.x = T(u.x) + v.y = T(u.y) +} + +// as_vec2 returns a Vec2 with `x` and `y` fields set from `v`. +pub fn (mut v Vec3[T]) as_vec2[T]() Vec2[T] { + return Vec2[T]{v.x, v.y} +} + +// from_vec4 sets the `x`,`y` and `z` fields from `u`. +pub fn (mut v Vec3[T]) from_vec4[U](u Vec4[U]) { + v.x = T(u.x) + v.y = T(u.y) + v.z = T(u.z) +} + +// as_vec4 returns a Vec4 with `x`,`y` and `z` fields set from `v`, `w` is set to 0. +pub fn (mut v Vec3[T]) as_vec4[T]() Vec4[T] { + return Vec4[T]{v.x, v.y, v.z, 0} +} + +// +// Addition +// + +// + returns the resulting vector of the addition of `v` and `u`. +[inline] +pub fn (v Vec3[T]) + (u Vec3[T]) Vec3[T] { + return Vec3[T]{v.x + u.x, v.y + u.y, v.z + u.z} +} + +// add returns the resulting vector of the addition of `v` + `u`. +pub fn (v Vec3[T]) add(u Vec3[T]) Vec3[T] { + return v + u +} + +// add_vec2 returns the resulting vector of the addition of the +// `x` and `y` fields of `u`, `z` is left untouched. +pub fn (v Vec3[T]) add_vec2[U](u Vec2[U]) Vec3[T] { + return Vec3[T]{v.x + T(u.x), v.y + T(u.y), v.z} +} + +// add_scalar returns the resulting vector of the addition of the `scalar` value. +pub fn (v Vec3[T]) add_scalar[U](scalar U) Vec3[T] { + return Vec3[T]{v.x + T(scalar), v.y + T(scalar), v.z + T(scalar)} +} + +// plus adds vector `u` to the vector. +pub fn (mut v Vec3[T]) plus(u Vec3[T]) { + v.x += u.x + v.y += u.y + v.z += u.z +} + +// plus_vec2 adds `x` and `y` fields of vector `u` to the vector, `z` is left untouched. +pub fn (mut v Vec3[T]) plus_vec2[U](u Vec2[U]) { + v.x += T(u.x) + v.y += T(u.y) +} + +// plus_scalar adds the scalar `scalar` to the vector. +pub fn (mut v Vec3[T]) plus_scalar[U](scalar U) { + v.x += T(scalar) + v.y += T(scalar) + v.z += T(scalar) +} + +// +// Subtraction +// + +// - returns the resulting vector of the subtraction of `v` and `u`. +[inline] +pub fn (v Vec3[T]) - (u Vec3[T]) Vec3[T] { + return Vec3[T]{v.x - u.x, v.y - u.y, v.z - u.z} +} + +// sub returns the resulting vector of the subtraction of `v` - `u`. +pub fn (v Vec3[T]) sub(u Vec3[T]) Vec3[T] { + return v - u +} + +// sub_scalar returns the resulting vector of the subtraction of the `scalar` value. +pub fn (v Vec3[T]) sub_scalar[U](scalar U) Vec3[T] { + return Vec3[T]{v.x - T(scalar), v.y - T(scalar), v.z - T(scalar)} +} + +// subtract subtracts vector `u` from the vector. +pub fn (mut v Vec3[T]) subtract(u Vec3[T]) { + v.x -= u.x + v.y -= u.y + v.z -= u.z +} + +// subtract_scalar subtracts the scalar `scalar` from the vector. +pub fn (mut v Vec3[T]) subtract_scalar[U](scalar U) { + v.x -= T(scalar) + v.y -= T(scalar) + v.z -= T(scalar) +} + +// +// Multiplication +// + +// * returns the resulting vector of the multiplication of `v` and `u`. +[inline] +pub fn (v Vec3[T]) * (u Vec3[T]) Vec3[T] { + return Vec3[T]{v.x * u.x, v.y * u.y, v.z * u.z} +} + +// mul returns the resulting vector of the multiplication of `v` * `u`. +pub fn (v Vec3[T]) mul(u Vec3[T]) Vec3[T] { + return v * u +} + +// mul_scalar returns the resulting vector of the multiplication of the `scalar` value. +pub fn (v Vec3[T]) mul_scalar[U](scalar U) Vec3[T] { + return Vec3[T]{v.x * T(scalar), v.y * T(scalar), v.z * T(scalar)} +} + +// multiply multiplies the vector with `u`. +pub fn (mut v Vec3[T]) multiply(u Vec3[T]) { + v.x *= u.x + v.y *= u.y + v.z *= u.z +} + +// multiply_scalar multiplies the vector with `scalar`. +pub fn (mut v Vec3[T]) multiply_scalar[U](scalar U) { + v.x *= T(scalar) + v.y *= T(scalar) + v.z *= T(scalar) +} + +// +// Division +// + +// / returns the resulting vector of the division of `v` and `u`. +[inline] +pub fn (v Vec3[T]) / (u Vec3[T]) Vec3[T] { + return Vec3[T]{v.x / u.x, v.y / u.y, v.z / u.z} +} + +// div returns the resulting vector of the division of `v` / `u`. +pub fn (v Vec3[T]) div(u Vec3[T]) Vec3[T] { + return v / u +} + +// div_scalar returns the resulting vector of the division by the `scalar` value. +pub fn (v Vec3[T]) div_scalar[U](scalar U) Vec3[T] { + return Vec3[T]{v.x / T(scalar), v.y / T(scalar), v.z / T(scalar)} +} + +// divide divides the vector by `u`. +pub fn (mut v Vec3[T]) divide(u Vec3[T]) { + v.x /= u.x + v.y /= u.y + v.z /= u.z +} + +// divide_scalar divides the vector by `scalar`. +pub fn (mut v Vec3[T]) divide_scalar[U](scalar U) { + v.x /= T(scalar) + v.y /= T(scalar) + v.z /= T(scalar) +} + +// +// Utility +// + +// magnitude returns the magnitude, also known as the length, of the vector. +pub fn (v Vec3[T]) magnitude() T { + if v.x == 0 && v.y == 0 && v.z == 0 { + return T(0) + } + return T(math.sqrt((v.x * v.x) + (v.y * v.y) + (v.z * v.z))) +} + +// dot returns the dot product of `v` and `u`. +pub fn (v Vec3[T]) dot(u Vec3[T]) T { + return T((v.x * u.x) + (v.y * u.y) + (v.z * u.z)) +} + +// cross returns the cross product of `v` and `u`. +pub fn (v Vec3[T]) cross(u Vec3[T]) Vec3[T] { + return Vec3[T]{ + x: (v.y * u.z) - (v.z * u.y) + y: (v.z * u.x) - (v.x * u.z) + z: (v.x * u.y) - (v.y * u.x) + } +} + +// unit returns the unit vector. +// unit vectors always have a magnitude, or length, of exactly 1. +pub fn (v Vec3[T]) unit() Vec3[T] { + m := v.magnitude() + return Vec3[T]{v.x / m, v.y / m, v.z / m} +} + +// perpendicular returns the `u` projected perpendicular vector to this vector. +pub fn (v Vec3[T]) perpendicular(u Vec3[T]) Vec3[T] { + return v - v.project(u) +} + +// project returns the projected vector. +pub fn (v Vec3[T]) project(u Vec3[T]) Vec3[T] { + percent := v.dot(u) / u.dot(v) + return u.mul_scalar(percent) +} + +// eq returns a bool indicating if the two vectors are equal. +[inline] +pub fn (v Vec3[T]) eq(u Vec3[T]) bool { + return v.x == u.x && v.y == u.y && v.z == u.z +} + +// eq_epsilon returns a bool indicating if the two vectors are equal within the module `vec_epsilon` const. +pub fn (v Vec3[T]) eq_epsilon(u Vec3[T]) bool { + return v.eq_approx[T, f32](u, vec_epsilon) +} + +// eq_approx returns whether these vectors are approximately equal within `tolerance`. +pub fn (v Vec3[T]) eq_approx[T, U](u Vec3[T], tolerance U) bool { + diff_x := math.abs(v.x - u.x) + diff_y := math.abs(v.y - u.y) + diff_z := math.abs(v.z - u.z) + if diff_x <= tolerance && diff_y <= tolerance && diff_z <= tolerance { + return true + } + + max_x := math.max(math.abs(v.x), math.abs(u.x)) + max_y := math.max(math.abs(v.y), math.abs(u.y)) + max_z := math.max(math.abs(v.z), math.abs(u.z)) + if diff_x < max_x * tolerance && diff_y < max_y * tolerance && diff_z < max_z * tolerance { + return true + } + return false +} + +// is_approx_zero returns whether this vector is equal to zero within `tolerance`. +pub fn (v Vec3[T]) is_approx_zero(tolerance f64) bool { + if math.abs(v.x) <= tolerance && math.abs(v.y) <= tolerance && math.abs(v.z) <= tolerance { + return true + } + return false +} + +// eq_scalar returns a bool indicating if the `x`,`y` and `z` fields all equals `scalar`. +pub fn (v Vec3[T]) eq_scalar[U](scalar U) bool { + return v.x == T(scalar) && v.y == T(scalar) && v.z == T(scalar) +} + +// distance returns the distance to the vector `u`. +pub fn (v Vec3[T]) distance(u Vec3[T]) f64 { + return math.sqrt((v.x - u.x) * (v.x - u.x) + (v.y - u.y) * (v.y - u.y) + + (v.z - u.z) * (v.z - u.z)) +} + +// manhattan_distance returns the Manhattan distance to the vector `u`. +pub fn (v Vec3[T]) manhattan_distance(u Vec3[T]) f64 { + return math.abs(v.x - u.x) + math.abs(v.y - u.y) + math.abs(v.z - u.z) +} + +// angle_between returns the angle in radians to the vector `u`. +pub fn (v Vec3[T]) angle_between(u Vec3[T]) T { + $if T is f64 { + return math.acos(((v.x * u.x) + (v.y * u.y) + (v.z * u.z)) / math.sqrt((v.x * v.x) + + (v.y * v.y) + (v.z * v.z)) * math.sqrt((u.x * u.x) + (u.y * u.y) + (u.z * u.z))) + } $else { + return T(math.acos(f64((v.x * u.x) + (v.y * u.y) + (v.z * u.z)) / math.sqrt( + f64(v.x * v.x) + (v.y * v.y) + (v.z * v.z)) * math.sqrt(f64(u.x * u.x) + (u.y * u.y) + + (u.z * u.z)))) + } +} + +// abs sets `x`, `y` and `z` field values to their absolute values. +pub fn (mut v Vec3[T]) abs() { + if v.x < 0 { + v.x = math.abs(v.x) + } + if v.y < 0 { + v.y = math.abs(v.y) + } + if v.z < 0 { + v.z = math.abs(v.z) + } +} + +// clean returns a vector with all fields of this vector set to zero (0) if they fall within `tolerance`. +pub fn (v Vec3[T]) clean[U](tolerance U) Vec3[T] { + mut r := v.copy() + if math.abs(v.x) < tolerance { + r.x = 0 + } + if math.abs(v.y) < tolerance { + r.y = 0 + } + if math.abs(v.z) < tolerance { + r.z = 0 + } + return r +} + +// clean_tolerance sets all fields to zero (0) if they fall within `tolerance`. +pub fn (mut v Vec3[T]) clean_tolerance[U](tolerance U) { + if math.abs(v.x) < tolerance { + v.x = 0 + } + if math.abs(v.y) < tolerance { + v.y = 0 + } + if math.abs(v.z) < tolerance { + v.z = 0 + } +} + +// inv returns the inverse, or reciprocal, of the vector. +pub fn (v Vec3[T]) inv() Vec3[T] { + return Vec3[T]{ + x: if v.x != 0 { T(1) / v.x } else { 0 } + y: if v.y != 0 { T(1) / v.y } else { 0 } + z: if v.z != 0 { T(1) / v.z } else { 0 } + } +} + +// normalize normalizes the vector. +pub fn (v Vec3[T]) normalize() Vec3[T] { + m := v.magnitude() + if m == 0 { + return vec3[T](0, 0, 0) + } + return Vec3[T]{ + x: v.x * (1 / m) + y: v.y * (1 / m) + z: v.z * (1 / m) + } +} + +// sum returns a sum of all the fields. +pub fn (v Vec3[T]) sum() T { + return v.x + v.y + v.z +} diff --git a/vlib/math/vec/vec3_test.v b/vlib/math/vec/vec3_test.v new file mode 100644 index 000000000..1014b943d --- /dev/null +++ b/vlib/math/vec/vec3_test.v @@ -0,0 +1,90 @@ +import math.vec + +fn test_vec3_int() { + mut v1 := vec.vec3(0, 0, 0) + mut v2 := vec.vec3(0, 0, 0) + assert v1 == v2 + v1.one() + v2.one() + assert v1.x == 1 + assert v1.y == 1 + assert v1.z == 1 + assert v1 == v2 + + v3 := v1 + v2 + assert typeof(v3).name == 'vec.Vec3[int]' + assert v3.x == 2 + assert v3.y == 2 + assert v3.z == 2 +} + +fn test_vec3_f32() { + mut v1 := vec.vec3(f32(0), 0, 0) + mut v2 := vec.vec3(f32(0), 0, 0) + assert v1 == v2 + v1.one() + v2.one() + assert v1.x == 1 + assert v1.y == 1 + assert v1.z == 1 + assert v1 == v2 + + v3 := v1 + v2 + assert typeof(v3).name == 'vec.Vec3[f32]' + assert v3.x == 2 + assert v3.y == 2 + assert v3.z == 2 +} + +fn test_vec3_f64() { + mut v1 := vec.vec3(0.0, 0, 0) + mut v2 := vec.vec3(0.0, 0, 0) + assert v1 == v2 + v1.one() + v2.one() + assert v1.x == 1 + assert v1.y == 1 + assert v1.z == 1 + assert v1 == v2 + + v3 := v1 + v2 + assert typeof(v3).name == 'vec.Vec3[f64]' + assert v3.x == 2 + assert v3.y == 2 + assert v3.z == 2 +} + +fn test_vec3_f64_utils_1() { + mut v1 := vec.vec3(2.0, 3.0, 1.5) + mut v2 := vec.vec3(1.0, 4.0, 1.5) + + mut zv := vec.vec3(5.0, 5.0, 5.0) + zv.zero() + + v3 := v1 + v2 + assert v3.x == 3 + assert v3.y == 7 + assert v3.z == 3 + + v1l := vec.vec3(6.0, 2.0, -3.0) + assert v1l.magnitude() == 7 + + mut ctv1 := vec.vec3(0.000001, 0.000001, 0.000001) + ctv1.clean_tolerance(0.00001) + assert ctv1 == zv +} + +fn test_vec3_f64_utils_2() { + mut v1 := vec.vec3(4.0, 4.0, 8.0) + assert v1.unit().magnitude() == 1 + v2 := v1.mul_scalar(0.5) + assert v2.x == 2 + assert v2.y == 2 + assert v2.z == 4 + assert v2.unit().magnitude() == 1 + + invv2 := v2.inv() + assert invv2.x == 0.5 + assert invv2.y == 0.5 + assert invv2.z == 0.25 +} diff --git a/vlib/math/vec/vec4.v b/vlib/math/vec/vec4.v new file mode 100644 index 000000000..011d5bda7 --- /dev/null +++ b/vlib/math/vec/vec4.v @@ -0,0 +1,425 @@ +// Copyright(C) 2020-2022 Lars Pontoppidan. All rights reserved. +// Use of this source code is governed by an MIT license file distributed with this software package +module vec + +import math + +// Vec4[T] is a generic struct representing a vector in 4D space. +pub struct Vec4[T] { +pub mut: + x T + y T + z T + w T +} + +// vec4[T] returns a `Vec4` of type `T`, with `x`,`y`,`z` and `w` fields set. +pub fn vec4[T](x T, y T, z T, w T) Vec4[T] { + return Vec4[T]{ + x: x + y: y + z: z + w: w + } +} + +// zero sets the `x`,`y`,`z` and `w` fields to 0. +pub fn (mut v Vec4[T]) zero() { + v.x = 0 + v.y = 0 + v.z = 0 + v.w = 0 +} + +// one sets the `x`,`y`,`z` and `w` fields to 1. +pub fn (mut v Vec4[T]) one() { + v.x = 1 + v.y = 1 + v.z = 1 + v.w = 1 +} + +// copy returns a copy of this vector. +pub fn (v Vec4[T]) copy() Vec4[T] { + return Vec4[T]{v.x, v.y, v.z, v.w} +} + +// from sets the `x`,`y`,`z` and `w` fields from `u`. +pub fn (mut v Vec4[T]) from(u Vec4[T]) { + v.x = u.x + v.y = u.y + v.z = u.z + v.w = u.w +} + +// from_vec2 sets the `x` and `y` fields from `u`. +pub fn (mut v Vec4[T]) from_vec2(u Vec2[T]) { + v.x = u.x + v.y = u.y +} + +// as_vec2 returns a Vec2 with `x` and `y` fields set from `v`. +pub fn (v Vec4[T]) as_vec2[U]() Vec2[U] { + return Vec2[U]{v.x, v.y} +} + +// from_vec3 sets the `x`,`y` and `z` fields from `u`. +pub fn (mut v Vec4[T]) from_vec3[U](u Vec3[U]) { + v.x = T(u.x) + v.y = T(u.y) + v.z = T(u.z) +} + +// as_vec3 returns a Vec3 with `x`,`y` and `z` fields set from `v`. +pub fn (v Vec4[T]) as_vec3[U]() Vec3[U] { + return Vec3[U]{v.x, v.y, v.z} +} + +// +// Addition +// + +// + returns the resulting vector of the addition of `v` and `u`. +[inline] +pub fn (v Vec4[T]) + (u Vec4[T]) Vec4[T] { + return Vec4[T]{v.x + u.x, v.y + u.y, v.z + u.z, v.w + u.w} +} + +// add returns the resulting vector of the addition of `v` + `u`. +pub fn (v Vec4[T]) add(u Vec4[T]) Vec4[T] { + return v + u +} + +// add_vec2 returns the resulting vector of the addition of the +// `x` and `y` fields of `u`, `z` is left untouched. +pub fn (v Vec4[T]) add_vec2[U](u Vec2[U]) Vec4[T] { + return Vec4[T]{v.x + u.x, v.y + u.y, 0, 0} +} + +// add_vec3 returns the resulting vector of the addition of the +// `x`,`y` and `z` fields of `u`, `w` is left untouched. +pub fn (v Vec4[T]) add_vec3[U](u Vec3[U]) Vec4[T] { + return Vec4[T]{v.x + u.x, v.y + u.y, v.z + u.z, 0} +} + +// add_scalar returns the resulting vector of the addition of the `scalar` value. +pub fn (v Vec4[T]) add_scalar[U](scalar U) Vec4[T] { + return Vec4[T]{v.x + T(scalar), v.y + T(scalar), v.z + T(scalar), v.w + T(scalar)} +} + +// plus adds vector `u` to the vector. +pub fn (mut v Vec4[T]) plus(u Vec4[T]) { + v.x += u.x + v.y += u.y + v.z += u.z + v.w += u.w +} + +// plus_scalar adds the scalar `scalar` to the vector. +pub fn (mut v Vec4[T]) plus_scalar[U](scalar U) { + v.x += T(scalar) + v.y += T(scalar) + v.z += T(scalar) + v.w += T(scalar) +} + +// +// Subtraction +// + +// - returns the resulting vector of the subtraction of `v` and `u`. +[inline] +pub fn (v Vec4[T]) - (u Vec4[T]) Vec4[T] { + return Vec4[T]{v.x - u.x, v.y - u.y, v.z - u.z, v.w - u.w} +} + +// sub returns the resulting vector of the subtraction of `v` - `u`. +pub fn (v Vec4[T]) sub(u Vec4[T]) Vec4[T] { + return v - u +} + +// sub_scalar returns the resulting vector of the subtraction of the `scalar` value. +pub fn (v Vec4[T]) sub_scalar[U](scalar U) Vec4[T] { + return Vec4[T]{v.x - T(scalar), v.y - T(scalar), v.z - T(scalar), v.w - T(scalar)} +} + +// subtract subtracts vector `u` from the vector. +pub fn (mut v Vec4[T]) subtract(u Vec4[T]) { + v.x -= u.x + v.y -= u.y + v.z -= u.z + v.w -= u.w +} + +// subtract_scalar subtracts the scalar `scalar` from the vector. +pub fn (mut v Vec4[T]) subtract_scalar[U](scalar U) { + v.x -= T(scalar) + v.y -= T(scalar) + v.z -= T(scalar) + v.w -= T(scalar) +} + +// +// Multiplication +// + +// * returns the resulting vector of the multiplication of `v` and `u`. +[inline] +pub fn (v Vec4[T]) * (u Vec4[T]) Vec4[T] { + return Vec4[T]{v.x * u.x, v.y * u.y, v.z * u.z, v.w * u.w} +} + +// mul returns the resulting vector of the multiplication of `v` * `u`. +pub fn (v Vec4[T]) mul(u Vec4[T]) Vec4[T] { + return v * u +} + +// mul_scalar returns the resulting vector of the multiplication of the `scalar` value. +pub fn (v Vec4[T]) mul_scalar[U](scalar U) Vec4[T] { + return Vec4[T]{v.x * T(scalar), v.y * T(scalar), v.z * T(scalar), v.w * T(scalar)} +} + +// multiply multiplies the vector with `u`. +pub fn (mut v Vec4[T]) multiply(u Vec4[T]) { + v.x *= u.x + v.y *= u.y + v.z *= u.z + v.w *= u.w +} + +// multiply_scalar multiplies the vector with `scalar`. +pub fn (mut v Vec4[T]) multiply_scalar[U](scalar U) { + v.x *= T(scalar) + v.y *= T(scalar) + v.z *= T(scalar) + v.w *= T(scalar) +} + +// +// Division +// + +// / returns the resulting vector of the division of `v` and `u`. +[inline] +pub fn (v Vec4[T]) / (u Vec4[T]) Vec4[T] { + return Vec4[T]{v.x / u.x, v.y / u.y, v.z / u.z, v.w / u.w} +} + +// div returns the resulting vector of the division of `v` / `u`. +pub fn (v Vec4[T]) div(u Vec4[T]) Vec4[T] { + return v / u +} + +// div_scalar returns the resulting vector of the division by the `scalar` value. +pub fn (v Vec4[T]) div_scalar[U](scalar U) Vec4[T] { + return Vec4[T]{v.x / T(scalar), v.y / T(scalar), v.z / T(scalar), v.w / T(scalar)} +} + +// divide divides the vector by `u`. +pub fn (mut v Vec4[T]) divide(u Vec4[T]) { + v.x /= u.x + v.y /= u.y + v.z /= u.z + v.w /= u.w +} + +// divide_scalar divides the vector by `scalar`. +pub fn (mut v Vec4[T]) divide_scalar[U](scalar U) { + v.x /= T(scalar) + v.y /= T(scalar) + v.z /= T(scalar) + v.w /= T(scalar) +} + +// +// Utility +// + +// magnitude returns the magnitude, also known as the length, of the vector. +pub fn (v Vec4[T]) magnitude() T { + if v.x == 0 && v.y == 0 && v.z == 0 && v.w == 0 { + return T(0) + } + return T(math.sqrt((v.x * v.x) + (v.y * v.y) + (v.z * v.z) + (v.w * v.w))) +} + +// dot returns the dot product of `v` and `u`. +pub fn (v Vec4[T]) dot(u Vec4[T]) T { + return T((v.x * u.x) + (v.y * u.y) + (v.z * u.z) + (v.w * u.w)) +} + +// cross_xyz returns the cross product of `v` and `u`'s `x`,`y` and `z` fields. +pub fn (v Vec4[T]) cross_xyz(u Vec4[T]) Vec4[T] { + return Vec4[T]{ + x: (v.y * u.z) - (v.z * u.y) + y: (v.z * u.x) - (v.x * u.z) + z: (v.x * u.y) - (v.y * u.x) + w: 0 + } +} + +// unit returns the unit vector. +// unit vectors always have a magnitude, or length, of exactly 1. +pub fn (v Vec4[T]) unit() Vec4[T] { + m := v.magnitude() + return Vec4[T]{ + x: v.x / m + y: v.y / m + z: v.z / m + w: v.w / m + } +} + +// perpendicular returns the `u` projected perpendicular vector to this vector. +pub fn (v Vec4[T]) perpendicular(u Vec4[T]) Vec4[T] { + return v - v.project(u) +} + +// project returns the projected vector. +pub fn (v Vec4[T]) project(u Vec4[T]) Vec4[T] { + percent := v.dot(u) / u.dot(v) + return u.mul_scalar(percent) +} + +// eq returns a bool indicating if the two vectors are equal. +[inline] +pub fn (v Vec4[T]) eq(u Vec4[T]) bool { + return v.x == u.x && v.y == u.y && v.z == u.z && v.w == u.w +} + +// eq_epsilon returns a bool indicating if the two vectors are equal within the module `vec_epsilon` const. +pub fn (v Vec4[T]) eq_epsilon(u Vec4[T]) bool { + return v.eq_approx[T, f32](u, vec_epsilon) +} + +// eq_approx returns whether these vectors are approximately equal within `tolerance`. +pub fn (v Vec4[T]) eq_approx[T, U](u Vec4[T], tolerance U) bool { + diff_x := math.abs(v.x - u.x) + diff_y := math.abs(v.y - u.y) + diff_z := math.abs(v.z - u.z) + diff_w := math.abs(v.w - u.w) + if diff_x <= tolerance && diff_y <= tolerance && diff_z <= tolerance && diff_w <= tolerance { + return true + } + + max_x := math.max(math.abs(v.x), math.abs(u.x)) + max_y := math.max(math.abs(v.y), math.abs(u.y)) + max_z := math.max(math.abs(v.z), math.abs(u.z)) + max_w := math.max(math.abs(v.w), math.abs(u.w)) + if diff_x < max_x * tolerance && diff_y < max_y * tolerance && diff_z < max_z * tolerance + && diff_w < max_w * tolerance { + return true + } + return false +} + +// is_approx_zero returns whether this vector is equal to zero within `tolerance`. +pub fn (v Vec4[T]) is_approx_zero(tolerance f64) bool { + if math.abs(v.x) <= tolerance && math.abs(v.y) <= tolerance && math.abs(v.z) <= tolerance + && math.abs(v.w) <= tolerance { + return true + } + return false +} + +// eq_scalar returns a bool indicating if the `x`,`y`,`z` and `w` fields all equals `scalar`. +pub fn (v Vec4[T]) eq_scalar[U](scalar U) bool { + return v.x == scalar && v.y == T(scalar) && v.z == T(scalar) && v.w == T(scalar) +} + +// distance returns the distance to the vector `u`. +pub fn (v Vec4[T]) distance(u Vec4[T]) f64 { + return math.sqrt((v.x - u.x) * (v.x - u.x) + (v.y - u.y) * (v.y - u.y) + + (v.z - u.z) * (v.z - u.z) + (v.w - u.w) * (v.w - u.w)) +} + +// manhattan_distance returns the Manhattan distance to the vector `u`. +pub fn (v Vec4[T]) manhattan_distance(u Vec4[T]) f64 { + 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) +} + +// abs sets `x`, `y`, `z` and `w` field values to their absolute values. +pub fn (mut v Vec4[T]) abs() { + if v.x < 0 { + v.x = math.abs(v.x) + } + if v.y < 0 { + v.y = math.abs(v.y) + } + if v.z < 0 { + v.z = math.abs(v.z) + } + if v.w < 0 { + v.w = math.abs(v.w) + } +} + +// NOTE a few of the following functions was adapted and/or inspired from Dario Deleddas excellent +// work on the `gg.m4` vlib module. Here's the Copyright/license text covering that code: +// +// Copyright (c) 2021 Dario Deledda. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +// clean returns a vector with all fields of this vector set to zero (0) if they fall within `tolerance`. +pub fn (v Vec4[T]) clean[U](tolerance U) Vec4[T] { + mut r := v.copy() + if math.abs(v.x) < tolerance { + r.x = 0 + } + if math.abs(v.y) < tolerance { + r.y = 0 + } + if math.abs(v.z) < tolerance { + r.z = 0 + } + if math.abs(v.w) < tolerance { + r.w = 0 + } + return r +} + +// clean_tolerance sets all fields to zero (0) if they fall within `tolerance`. +pub fn (mut v Vec4[T]) clean_tolerance[U](tolerance U) { + if math.abs(v.x) < tolerance { + v.x = 0 + } + if math.abs(v.y) < tolerance { + v.y = 0 + } + if math.abs(v.z) < tolerance { + v.z = 0 + } + if math.abs(v.w) < tolerance { + v.w = 0 + } +} + +// inv returns the inverse, or reciprocal, of the vector. +pub fn (v Vec4[T]) inv() Vec4[T] { + return Vec4[T]{ + x: if v.x != 0 { T(1) / v.x } else { 0 } + y: if v.y != 0 { T(1) / v.y } else { 0 } + z: if v.z != 0 { T(1) / v.z } else { 0 } + w: if v.w != 0 { T(1) / v.w } else { 0 } + } +} + +// normalize normalizes the vector. +pub fn (v Vec4[T]) normalize() Vec4[T] { + m := v.magnitude() + if m == 0 { + return vec4[T](0, 0, 0, 0) + } + return Vec4[T]{ + x: v.x * (1 / m) + y: v.y * (1 / m) + z: v.z * (1 / m) + w: v.w * (1 / m) + } +} + +// sum returns a sum of all the fields. +pub fn (v Vec4[T]) sum() T { + return v.x + v.y + v.z + v.w +} diff --git a/vlib/math/vec/vec4_test.v b/vlib/math/vec/vec4_test.v new file mode 100644 index 000000000..ec7340605 --- /dev/null +++ b/vlib/math/vec/vec4_test.v @@ -0,0 +1,99 @@ +import math.vec + +fn test_vec4_int() { + mut v1 := vec.vec4(0, 0, 0, 0) + mut v2 := vec.vec4(0, 0, 0, 0) + assert v1 == v2 + v1.one() + v2.one() + assert v1.x == 1 + assert v1.y == 1 + assert v1.z == 1 + assert v1.w == 1 + assert v1 == v2 + + v3 := v1 + v2 + assert typeof(v3).name == 'vec.Vec4[int]' + assert v3.x == 2 + assert v3.y == 2 + assert v3.z == 2 + assert v3.w == 2 +} + +fn test_vec4_f32() { + mut v1 := vec.vec4(f32(0), 0, 0, 0) + mut v2 := vec.vec4(f32(0), 0, 0, 0) + assert v1 == v2 + v1.one() + v2.one() + assert v1.x == 1 + assert v1.y == 1 + assert v1.z == 1 + assert v1.w == 1 + assert v1 == v2 + + v3 := v1 + v2 + assert typeof(v3).name == 'vec.Vec4[f32]' + assert v3.x == 2 + assert v3.y == 2 + assert v3.z == 2 + assert v3.w == 2 +} + +fn test_vec4_f64() { + mut v1 := vec.vec4(0.0, 0, 0, 0) + mut v2 := vec.vec4(0.0, 0, 0, 0) + assert v1 == v2 + v1.one() + v2.one() + assert v1.x == 1 + assert v1.y == 1 + assert v1.z == 1 + assert v1.w == 1 + assert v1 == v2 + + v3 := v1 + v2 + assert typeof(v3).name == 'vec.Vec4[f64]' + assert v3.x == 2 + assert v3.y == 2 + assert v3.z == 2 + assert v3.w == 2 +} + +fn test_vec4_f64_utils_1() { + mut v1 := vec.vec4(2.0, 3.0, 1.5, 3.0) + mut v2 := vec.vec4(1.0, 4.0, 1.5, 3.0) + + mut zv := vec.vec4(5.0, 5.0, 5.0, 5.0) + zv.zero() + + v3 := v1 + v2 + assert v3.x == 3 + assert v3.y == 7 + assert v3.z == 3 + assert v3.w == 6 + + assert v3.unit().magnitude() == 1 + + mut ctv1 := vec.vec4(0.000001, 0.000001, 0.000001, 0.000001) + ctv1.clean_tolerance(0.00001) + assert ctv1 == zv +} + +fn test_vec4_f64_utils_2() { + mut v1 := vec.vec4(4.0, 4.0, 8.0, 2.0) + assert v1.unit().magnitude() == 1 + + v2 := v1.mul_scalar(0.5) + assert v2.x == 2 + assert v2.y == 2 + assert v2.z == 4 + assert v2.w == 1 + assert v2.unit().magnitude() == 1 + + invv2 := v2.inv() + assert invv2.x == 0.5 + assert invv2.y == 0.5 + assert invv2.z == 0.25 + assert invv2.w == 1.0 +} -- 2.30.2