diff --git a/src/lib/euler.mbt b/src/lib/euler.mbt index 79c27ba..0f69ff0 100644 --- a/src/lib/euler.mbt +++ b/src/lib/euler.mbt @@ -3,7 +3,7 @@ pub fn Quaternion::from_euler_angles( x : Float, y : Float, - z : Float + z : Float, ) -> Quaternion { let two : Float = 2.0 let half_x = x / two diff --git a/src/lib/lib.mbti b/src/lib/lib.mbti new file mode 100644 index 0000000..8e664ac --- /dev/null +++ b/src/lib/lib.mbti @@ -0,0 +1,59 @@ +// Generated using `moon info`, DON'T EDIT IT +package "tiye/quaternion/lib" + +// Values +fn from_wxyz(Array[Float]) -> Quaternion raise QuatError + +fn from_xyzw_array(Array[Float]) -> Quaternion raise QuatError + +let max_value : Float + +fn q(Float, Float, Float, Float) -> Quaternion + +fn qi(Int, Int, Int, Int) -> Quaternion + +// Errors +pub(all) suberror QuatError String + +// Types and methods +pub(all) struct Quaternion { + mut w : Float + mut x : Float + mut y : Float + mut z : Float +} +fn Quaternion::conjugate(Self) -> Self +fn Quaternion::conjugate_mut(Self) -> Unit +fn Quaternion::dot(Self, Self) -> Float +fn Quaternion::from_euler_angles(Float, Float, Float) -> Self +fn Quaternion::id() -> Self +fn Quaternion::inverse(Self) -> Self +fn Quaternion::inverse_mut(Self) -> Unit +fn Quaternion::length(Self) -> Float +fn Quaternion::new(w? : Float, x? : Float, y? : Float, z? : Float) -> Self +fn Quaternion::normalize(Self) -> Self +fn Quaternion::normalize_mut(Self) -> Unit raise QuatError +fn Quaternion::op_add_assign(Self, Self) -> Unit +fn Quaternion::op_eq(Self, Self) -> Bool +fn Quaternion::op_mul_assign(Self, Self) -> Unit +fn Quaternion::op_neg(Self) -> Self +fn Quaternion::op_sub_assign(Self, Self) -> Unit +fn Quaternion::roughly_eq(Self, Self, epsilon? : Float) -> Bool +fn Quaternion::scale(Self, Float) -> Self +fn Quaternion::scale_mut(Self, Float) -> Unit +fn Quaternion::square_length(Self) -> Float +fn Quaternion::to_string(Self) -> String +fn Quaternion::to_wxyz(Self) -> Array[Float] +fn Quaternion::to_xyzw(Self) -> Array[Float] +impl Add for Quaternion +impl Default for Quaternion +impl Div for Quaternion +impl Eq for Quaternion +impl Mul for Quaternion +impl Show for Quaternion +impl Sub for Quaternion + +// Type aliases + +// Traits + diff --git a/src/lib/quaternion.mbt b/src/lib/quaternion.mbt index 2dbe7fd..d2dac62 100644 --- a/src/lib/quaternion.mbt +++ b/src/lib/quaternion.mbt @@ -7,6 +7,7 @@ pub(all) struct Quaternion { } derive(Eq) // Show + ///| /// Converts a quaternion to its string representation in the format "Quaternion /// { w: , x: , y: , z: }". @@ -20,13 +21,11 @@ pub(all) struct Quaternion { /// Example: /// /// ```moonbit -/// test "Quaternion::to_string" { -/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } -/// inspect!( -/// q.to_string(), -/// content="Quaternion { w: 1.0, x: 2.0, y: 3.0, z: 4.0 }", -/// ) -/// } +/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } +/// inspect( +/// q.to_string(), +/// content="Quaternion { w: 1.0, x: 2.0, y: 3.0, z: 4.0 }", +/// ) /// ``` pub fn Quaternion::to_string(self : Quaternion) -> String { "Quaternion { w: \{self.w}, x: \{self.x}, y: \{self.y}, z: \{self.z} }" @@ -53,13 +52,11 @@ pub impl Show for Quaternion with output(self, logger) { /// Example: /// /// ```moonbit -/// test "q" { -/// let quaternion = q(1.0, 2.0, 3.0, 4.0) -/// inspect!( -/// quaternion.to_string(), -/// content="Quaternion { w: 1.0, x: 2.0, y: 3.0, z: 4.0 }", -/// ) -/// } +/// let quaternion = q(1.0, 2.0, 3.0, 4.0) +/// inspect( +/// quaternion.to_string(), +/// content="Quaternion { w: 1.0, x: 2.0, y: 3.0, z: 4.0 }", +/// ) /// ``` pub fn q(w : Float, x : Float, y : Float, z : Float) -> Quaternion { Quaternion::{ w, x, y, z } @@ -83,10 +80,8 @@ pub fn q(w : Float, x : Float, y : Float, z : Float) -> Quaternion { /// Example: /// /// ```moonbit -/// test "qi" { -/// let quaternion = qi(1, 2, 3, 4) -/// inspect!(quaternion, content="Quaternion { w: 1.0, x: 2.0, y: 3.0, z: 4.0 }") -/// } +/// let quaternion = qi(1, 2, 3, 4) +/// inspect(quaternion, content="Quaternion { w: 1.0, x: 2.0, y: 3.0, z: 4.0 }") /// ``` pub fn qi(w : Int, x : Int, y : Int, z : Int) -> Quaternion { q(w.to_float(), x.to_float(), y.to_float(), z.to_float()) @@ -102,14 +97,12 @@ pub fn qi(w : Int, x : Int, y : Int, z : Int) -> Quaternion { /// Example: /// /// ```moonbit -/// test "Quaternion::id" { -/// let q = Quaternion::id() -/// inspect!(q, content="Quaternion { w: 1.0, x: 0.0, y: 0.0, z: 0.0 }") -/// -/// // demonstrate identity property -/// let p = Quaternion::{ w: 2.0, x: 3.0, y: 4.0, z: 5.0 } -/// inspect!(p * q, content=p.to_string()) -/// } +/// let q = Quaternion::id() +/// inspect(q, content="Quaternion { w: 1.0, x: 0.0, y: 0.0, z: 0.0 }") +/// +/// // demonstrate identity property +/// let p = Quaternion::{ w: 2.0, x: 3.0, y: 4.0, z: 5.0 } +/// inspect(p * q, content=p.to_string()) /// ``` pub fn Quaternion::id() -> Quaternion { Quaternion::{ w: 1.0, x: 0.0, y: 0.0, z: 0.0 } @@ -134,24 +127,22 @@ pub fn Quaternion::id() -> Quaternion { /// Example: /// /// ```moonbit -/// test "Quaternion::new" { -/// let q1 = Quaternion::new() -/// let q2 = Quaternion::new(w=1.0, x=2.0) -/// inspect!(q1, content="Quaternion { w: 0.0, x: 0.0, y: 0.0, z: 0.0 }") -/// inspect!(q2, content="Quaternion { w: 1.0, x: 2.0, y: 0.0, z: 0.0 }") -/// } +/// let q1 = Quaternion::new() +/// let q2 = Quaternion::new(w=1.0, x=2.0) +/// inspect(q1, content="Quaternion { w: 0.0, x: 0.0, y: 0.0, z: 0.0 }") +/// inspect(q2, content="Quaternion { w: 1.0, x: 2.0, y: 0.0, z: 0.0 }") /// ``` pub fn Quaternion::new( w? : Float, x? : Float, y? : Float, - z? : Float + z? : Float, ) -> Quaternion { Quaternion::{ - w: w.or_default(), - x: x.or_default(), - y: y.or_default(), - z: z.or_default(), + w: w.unwrap_or_default(), + x: x.unwrap_or_default(), + y: y.unwrap_or_default(), + z: z.unwrap_or_default(), } } @@ -170,11 +161,9 @@ pub fn Quaternion::new( /// Example: /// /// ```moonbit -/// test "Quaternion::scale" { -/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } -/// let scaled = q.scale(2.0) -/// inspect!(scaled, content="Quaternion { w: 2.0, x: 4.0, y: 6.0, z: 8.0 }") -/// } +/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } +/// let scaled = q.scale(2.0) +/// inspect(scaled, content="Quaternion { w: 2.0, x: 4.0, y: 6.0, z: 8.0 }") /// ``` pub fn Quaternion::scale(self : Quaternion, t : Float) -> Quaternion { Quaternion::{ w: self.w * t, x: self.x * t, y: self.y * t, z: self.z * t } @@ -193,11 +182,9 @@ pub fn Quaternion::scale(self : Quaternion, t : Float) -> Quaternion { /// Example: /// /// ```moonbit -/// test "Quaternion::scale_mut" { -/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } -/// q.scale_mut(2.0) -/// inspect!(q, content="Quaternion { w: 2.0, x: 4.0, y: 6.0, z: 8.0 }") -/// } +/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } +/// q.scale_mut(2.0) +/// inspect(q, content="Quaternion { w: 2.0, x: 4.0, y: 6.0, z: 8.0 }") /// ``` pub fn Quaternion::scale_mut(self : Quaternion, t : Float) -> Unit { self.w = self.w * t @@ -221,11 +208,9 @@ pub fn Quaternion::scale_mut(self : Quaternion, t : Float) -> Unit { /// Example: /// /// ```moonbit -/// test "Quaternion::inverse" { -/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } -/// let q_inv = q.inverse() -/// inspect!((q * q_inv).roughly_eq(Quaternion::id()), content="true") -/// } +/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } +/// let q_inv = q.inverse() +/// inspect((q * q_inv).roughly_eq(Quaternion::id()), content="true") /// ``` pub fn Quaternion::inverse(self : Quaternion) -> Quaternion { self.conjugate().scale(1.0.to_float() / self.square_length()) @@ -244,11 +229,9 @@ pub fn Quaternion::inverse(self : Quaternion) -> Quaternion { /// Example: /// /// ```moonbit -/// test "Quaternion::inverse_mut" { -/// let q = Quaternion::{ w: 2.0, x: 0.0, y: 0.0, z: 0.0 } -/// q.inverse_mut() -/// inspect!(q, content="Quaternion { w: 0.5, x: 0.0, y: 0.0, z: 0.0 }") -/// } +/// let q = Quaternion::{ w: 2.0, x: 0.0, y: 0.0, z: 0.0 } +/// q.inverse_mut() +/// inspect(q, content="Quaternion { w: 0.5, x: 0.0, y: 0.0, z: 0.0 }") /// ``` pub fn Quaternion::inverse_mut(self : Quaternion) -> Unit { self.scale_mut(1.0.to_float() / self.square_length()) @@ -269,11 +252,9 @@ pub fn Quaternion::inverse_mut(self : Quaternion) -> Unit { /// Example: /// /// ```moonbit -/// test "Quaternion::dot" { -/// let q1 = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } -/// let q2 = Quaternion::{ w: 2.0, x: 3.0, y: 4.0, z: 5.0 } -/// inspect!(q1.dot(q2), content="40.0") -/// } +/// let q1 = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } +/// let q2 = Quaternion::{ w: 2.0, x: 3.0, y: 4.0, z: 5.0 } +/// inspect(q1.dot(q2), content="40.0") /// ``` pub fn Quaternion::dot(self : Quaternion, other : Quaternion) -> Float { self.w * other.w + self.x * other.x + self.y * other.y + self.z * other.z @@ -293,13 +274,11 @@ pub fn Quaternion::dot(self : Quaternion, other : Quaternion) -> Float { /// Example: /// /// ```moonbit -/// test "Quaternion::conjugate" { -/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } -/// inspect!( -/// q.conjugate(), -/// content="Quaternion { w: 1.0, x: -2.0, y: -3.0, z: -4.0 }", -/// ) -/// } +/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } +/// inspect( +/// q.conjugate(), +/// content="Quaternion { w: 1.0, x: -2.0, y: -3.0, z: -4.0 }", +/// ) /// ``` pub fn Quaternion::conjugate(self : Quaternion) -> Quaternion { Quaternion::{ w: self.w, x: -self.x, y: -self.y, z: -self.z } @@ -318,11 +297,9 @@ pub fn Quaternion::conjugate(self : Quaternion) -> Quaternion { /// Example: /// /// ```moonbit -/// test "Quaternion::conjugate_mut" { -/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } -/// q.conjugate_mut() -/// inspect!(q, content="Quaternion { w: 1.0, x: -2.0, y: -3.0, z: -4.0 }") -/// } +/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } +/// q.conjugate_mut() +/// inspect(q, content="Quaternion { w: 1.0, x: -2.0, y: -3.0, z: -4.0 }") /// ``` pub fn Quaternion::conjugate_mut(self : Quaternion) -> Unit { self.x = -self.x @@ -343,10 +320,8 @@ pub fn Quaternion::conjugate_mut(self : Quaternion) -> Unit { /// Example: /// /// ```moonbit -/// test "Quaternion::square_length" { -/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 2.0, z: 1.0 } -/// inspect!(q.square_length(), content="10.0") -/// } +/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 2.0, z: 1.0 } +/// inspect(q.square_length(), content="10.0") /// ``` pub fn Quaternion::square_length(self : Quaternion) -> Float { self.w * self.w + self.x * self.x + self.y * self.y + self.z * self.z @@ -365,10 +340,8 @@ pub fn Quaternion::square_length(self : Quaternion) -> Float { /// Example: /// /// ```moonbit -/// test "Quaternion::length" { -/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 2.0, z: 1.0 } -/// inspect!(q.length(), content="3.316624790355399") -/// } +/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 2.0, z: 1.0 } +/// inspect(q.length(), content="3.316624790355399") /// ``` pub fn Quaternion::length(self : Quaternion) -> Float { self.square_length().sqrt() @@ -400,11 +373,9 @@ pub let max_value : Float = (0x7F7FFFFF).reinterpret_as_float() /// Example: /// /// ```moonbit -/// test "Quaternion::op_eq" { -/// let q1 = Quaternion::{ w: 1.0, x: 0.0, y: 0.0, z: 0.0 } -/// let q2 = Quaternion::{ w: 1.0, x: 0.0000000001, y: 0.0, z: 0.0 } -/// inspect!(q1.op_eq(q2), content="true") -/// } +/// let q1 = Quaternion::{ w: 1.0, x: 0.0, y: 0.0, z: 0.0 } +/// let q2 = Quaternion::{ w: 1.0, x: 0.0000000001, y: 0.0, z: 0.0 } +/// inspect(q1.op_eq(q2), content="true") /// ``` pub fn Quaternion::op_eq(self : Quaternion, other : Quaternion) -> Bool { (self - other).square_length() < 1.0.to_float() / max_value @@ -427,18 +398,16 @@ pub fn Quaternion::op_eq(self : Quaternion, other : Quaternion) -> Bool { /// Example: /// /// ```moonbit -/// test "Quaternion::roughly_eq" { -/// let q1 = Quaternion::{ w: 1.0, x: 0.0, y: 0.0, z: 0.0 } -/// let q2 = Quaternion::{ w: 0.9999999999, x: 0.0, y: 0.0, z: 0.0 } -/// inspect!(q1.roughly_eq(q2), content="true") -/// } +/// let q1 = Quaternion::{ w: 1.0, x: 0.0, y: 0.0, z: 0.0 } +/// let q2 = Quaternion::{ w: 0.9999999999, x: 0.0, y: 0.0, z: 0.0 } +/// inspect(q1.roughly_eq(q2), content="true") /// ``` pub fn Quaternion::roughly_eq( self : Quaternion, other : Quaternion, - epsilon? : Float + epsilon? : Float, ) -> Bool { - (self - other).square_length() < epsilon.or(0.00000000001) + (self - other).square_length() < epsilon.unwrap_or(0.00000000001) } ///| @@ -455,11 +424,9 @@ pub fn Quaternion::roughly_eq( /// Example: /// /// ```moonbit -/// test "Quaternion::op_add" { -/// let q1 = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } -/// let q2 = Quaternion::{ w: 5.0, x: 6.0, y: 7.0, z: 8.0 } -/// inspect!(q1 + q2, content="Quaternion { w: 6.0, x: 8.0, y: 10.0, z: 12.0 }") -/// } +/// let q1 = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } +/// let q2 = Quaternion::{ w: 5.0, x: 6.0, y: 7.0, z: 8.0 } +/// inspect(q1 + q2, content="Quaternion { w: 6.0, x: 8.0, y: 10.0, z: 12.0 }") /// ``` pub impl Add for Quaternion with op_add(self : Quaternion, other : Quaternion) -> Quaternion { Quaternion::{ @@ -482,12 +449,10 @@ pub impl Add for Quaternion with op_add(self : Quaternion, other : Quaternion) - /// Example: /// /// ```moonbit -/// test "Quaternion::op_add_assign" { -/// let q1 = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } -/// let q2 = Quaternion::{ w: 5.0, x: 6.0, y: 7.0, z: 8.0 } -/// q1.op_add_assign(q2) -/// inspect!(q1, content="Quaternion { w: 6.0, x: 8.0, y: 10.0, z: 12.0 }") -/// } +/// let q1 = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } +/// let q2 = Quaternion::{ w: 5.0, x: 6.0, y: 7.0, z: 8.0 } +/// q1.op_add_assign(q2) +/// inspect(q1, content="Quaternion { w: 6.0, x: 8.0, y: 10.0, z: 12.0 }") /// ``` pub fn Quaternion::op_add_assign(self : Quaternion, other : Quaternion) -> Unit { self.w = self.w + other.w @@ -512,12 +477,10 @@ pub fn Quaternion::op_add_assign(self : Quaternion, other : Quaternion) -> Unit /// Example: /// /// ```moonbit -/// test "Quaternion::op_mul" { -/// let q1 = Quaternion::{ w: 1.0, x: 0.0, y: 0.0, z: 0.0 } -/// let q2 = Quaternion::{ w: 0.0, x: 1.0, y: 0.0, z: 0.0 } -/// let result = q1 * q2 -/// inspect!(result, content="Quaternion { w: 0.0, x: 1.0, y: 0.0, z: 0.0 }") -/// } +/// let q1 = Quaternion::{ w: 1.0, x: 0.0, y: 0.0, z: 0.0 } +/// let q2 = Quaternion::{ w: 0.0, x: 1.0, y: 0.0, z: 0.0 } +/// let result = q1 * q2 +/// inspect(result, content="Quaternion { w: 0.0, x: 1.0, y: 0.0, z: 0.0 }") /// ``` pub impl Mul for Quaternion with op_mul(self : Quaternion, other : Quaternion) -> Quaternion { Quaternion::{ @@ -540,15 +503,13 @@ pub impl Mul for Quaternion with op_mul(self : Quaternion, other : Quaternion) - /// Example: /// /// ```moonbit -/// test "Quaternion::op_mul_assign" { -/// let q1 = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } -/// let q2 = Quaternion::{ w: 2.0, x: -1.0, y: -2.0, z: -3.0 } -/// q1.op_mul_assign(q2) -/// inspect!( -/// q1.to_string(), -/// content="Quaternion { w: 12.0, x: 1.0, y: 0.0, z: -1.0 }", -/// ) -/// } +/// let q1 = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } +/// let q2 = Quaternion::{ w: 2.0, x: -1.0, y: -2.0, z: -3.0 } +/// q1.op_mul_assign(q2) +/// inspect( +/// q1.to_string(), +/// content="Quaternion { w: 12.0, x: 1.0, y: 0.0, z: -1.0 }", +/// ) /// ``` pub fn Quaternion::op_mul_assign(self : Quaternion, other : Quaternion) -> Unit { let w = self.w * other.w - @@ -587,12 +548,10 @@ pub fn Quaternion::op_mul_assign(self : Quaternion, other : Quaternion) -> Unit /// Example: /// /// ```moonbit -/// test "Quaternion::op_div" { -/// let a = Quaternion::{ w: 1.0, x: 0.0, y: 0.0, z: 0.0 } -/// let b = Quaternion::{ w: 2.0, x: 0.0, y: 0.0, z: 0.0 } -/// let result = a / b -/// inspect!(result, content="Quaternion { w: 0.5, x: 0.0, y: 0.0, z: 0.0 }") -/// } +/// let a = Quaternion::{ w: 1.0, x: 0.0, y: 0.0, z: 0.0 } +/// let b = Quaternion::{ w: 2.0, x: 0.0, y: 0.0, z: 0.0 } +/// let result = a / b +/// inspect(result, content="Quaternion { w: 0.5, x: 0.0, y: 0.0, z: 0.0 }") /// ``` pub impl Div for Quaternion with op_div(self : Quaternion, b : Quaternion) -> Quaternion { self.op_mul(b.inverse()) @@ -612,12 +571,10 @@ pub impl Div for Quaternion with op_div(self : Quaternion, b : Quaternion) -> Qu /// Example: /// /// ```moonbit -/// test "Quaternion::op_sub" { -/// let q1 = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } -/// let q2 = Quaternion::{ w: 0.5, x: 1.0, y: 1.5, z: 2.0 } -/// let result = q1 - q2 -/// inspect!(result, content="Quaternion { w: 0.5, x: 1.0, y: 1.5, z: 2.0 }") -/// } +/// let q1 = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } +/// let q2 = Quaternion::{ w: 0.5, x: 1.0, y: 1.5, z: 2.0 } +/// let result = q1 - q2 +/// inspect(result, content="Quaternion { w: 0.5, x: 1.0, y: 1.5, z: 2.0 }") /// ``` pub impl Sub for Quaternion with op_sub(self : Quaternion, other : Quaternion) -> Quaternion { Quaternion::{ @@ -640,12 +597,10 @@ pub impl Sub for Quaternion with op_sub(self : Quaternion, other : Quaternion) - /// Example: /// /// ```moonbit -/// test "Quaternion::op_sub_assign" { -/// let q1 = Quaternion::{ w: 2.0, x: 3.0, y: 4.0, z: 5.0 } -/// let q2 = Quaternion::{ w: 1.0, x: 1.0, y: 1.0, z: 1.0 } -/// q1.op_sub_assign(q2) -/// inspect!(q1, content="Quaternion { w: 1.0, x: 2.0, y: 3.0, z: 4.0 }") -/// } +/// let q1 = Quaternion::{ w: 2.0, x: 3.0, y: 4.0, z: 5.0 } +/// let q2 = Quaternion::{ w: 1.0, x: 1.0, y: 1.0, z: 1.0 } +/// q1.op_sub_assign(q2) +/// inspect(q1, content="Quaternion { w: 1.0, x: 2.0, y: 3.0, z: 4.0 }") /// ``` pub fn Quaternion::op_sub_assign(self : Quaternion, other : Quaternion) -> Unit { self.w = self.w - other.w @@ -668,13 +623,11 @@ pub fn Quaternion::op_sub_assign(self : Quaternion, other : Quaternion) -> Unit /// Example: /// /// ```moonbit -/// test "Quaternion::op_neg" { -/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } -/// inspect!( -/// q.op_neg(), -/// content="Quaternion { w: -1.0, x: -2.0, y: -3.0, z: -4.0 }", -/// ) -/// } +/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } +/// inspect( +/// q.op_neg(), +/// content="Quaternion { w: -1.0, x: -2.0, y: -3.0, z: -4.0 }", +/// ) /// ``` pub fn Quaternion::op_neg(self : Quaternion) -> Quaternion { Quaternion::{ w: -self.w, x: -self.x, y: -self.y, z: -self.z } @@ -693,10 +646,8 @@ pub fn Quaternion::op_neg(self : Quaternion) -> Quaternion { /// Example: /// /// ```moonbit -/// test "Quaternion::to_array" { -/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } -/// inspect!(q.to_array(), content="[1.0, 2.0, 3.0, 4.0]") -/// } +/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } +/// inspect(q.to_wxyz(), content="[1.0, 2.0, 3.0, 4.0]") /// ``` pub fn Quaternion::to_wxyz(self : Quaternion) -> Array[Float] { [self.w, self.x, self.y, self.z] @@ -716,20 +667,18 @@ pub fn Quaternion::to_wxyz(self : Quaternion) -> Array[Float] { /// Example: /// /// ```moonbit -/// test "Quaternion::to_xyzw" { -/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } -/// inspect!(q.to_xyzw(), content="[2.0, 3.0, 4.0, 1.0]") -/// } +/// let q = Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } +/// inspect(q.to_xyzw(), content="[2.0, 3.0, 4.0, 1.0]") /// ``` pub fn Quaternion::to_xyzw(self : Quaternion) -> Array[Float] { [self.x, self.y, self.z, self.w] } ///| -pub(all) type! QuatError String +pub(all) suberror QuatError String ///| -pub fn from_wxyz(arr : Array[Float]) -> Quaternion!QuatError { +pub fn from_wxyz(arr : Array[Float]) -> Quaternion raise QuatError { if arr.length() != 4 { raise QuatError("Array must have 4 elements") } @@ -737,7 +686,7 @@ pub fn from_wxyz(arr : Array[Float]) -> Quaternion!QuatError { } ///| from_xyzw_array -pub fn from_xyzw_array(arr : Array[Float]) -> Quaternion!QuatError { +pub fn from_xyzw_array(arr : Array[Float]) -> Quaternion raise QuatError { if arr.length() != 4 { raise QuatError("Array must have 4 elements") } @@ -754,7 +703,7 @@ pub fn Quaternion::normalize(self : Quaternion) -> Quaternion { } ///| normalize mutable -pub fn Quaternion::normalize_mut(self : Quaternion) -> Unit!QuatError { +pub fn Quaternion::normalize_mut(self : Quaternion) -> Unit raise QuatError { let len = self.length() if len == 0.0 { raise QuatError("Cannot normalize a zero-length quaternion") diff --git a/src/lib/quaternion_test.mbt b/src/lib/quaternion_test.mbt index 0662199..fab7add 100644 --- a/src/lib/quaternion_test.mbt +++ b/src/lib/quaternion_test.mbt @@ -1,32 +1,37 @@ +///| test "q" { let q1 = @lib.q(0.0, 0.0, 0.0, 0.0) - inspect!(q1, content="Quaternion { w: 0, x: 0, y: 0, z: 0 }") + inspect(q1, content="Quaternion { w: 0, x: 0, y: 0, z: 0 }") } +///| test "q/nan" { let q1 = @lib.q(@float.not_a_number, 0.0, 0.0, 0.0) - inspect!( + inspect( q1, content="Quaternion { w: \{@double.not_a_number.to_float()}, x: 0, y: 0, z: 0 }", ) } +///| test "Quaternion::new/default_arguments" { let q = @lib.Quaternion::new() - inspect!(q.w, content="0") - inspect!(q.x, content="0") - inspect!(q.y, content="0") - inspect!(q.z, content="0") + inspect(q.w, content="0") + inspect(q.x, content="0") + inspect(q.y, content="0") + inspect(q.z, content="0") } +///| test "Quaternion::new/partial_defaults" { let q = @lib.Quaternion::new(w=1.0.to_float()) - inspect!(q.w, content="1") - inspect!(q.x, content="0") - inspect!(q.y, content="0") - inspect!(q.z, content="0") + inspect(q.w, content="1") + inspect(q.x, content="0") + inspect(q.y, content="0") + inspect(q.z, content="0") } +///| test "Quaternion::new/all_arguments" { let q = @lib.Quaternion::new( w=1.0.to_float(), @@ -34,231 +39,258 @@ test "Quaternion::new/all_arguments" { y=3.0.to_float(), z=4.0.to_float(), ) - inspect!(q.w, content="1") - inspect!(q.x, content="2") - inspect!(q.y, content="3") - inspect!(q.z, content="4") + inspect(q.w, content="1") + inspect(q.x, content="2") + inspect(q.y, content="3") + inspect(q.z, content="4") } +///| test "Quaternion::scale/large_scalar" { let q = @lib.Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } let t = 1.0e+10.to_float() // Use Float literal let result = q.scale(t) - inspect!( + inspect( result, content="Quaternion { w: 10000000000, x: 20000000000, y: 30000001024, z: 40000000000 }", // why 1024? ) } +///| test "Quaternion::inverse/identity" { let q = @lib.Quaternion::id() - inspect!(q.inverse(), content="Quaternion { w: 1, x: 0, y: 0, z: 0 }") + inspect(q.inverse(), content="Quaternion { w: 1, x: 0, y: 0, z: 0 }") } +///| test "Quaternion::inverse/zero" { let q = @lib.Quaternion::{ w: 0.0, x: 0.0, y: 0.0, z: 0.0 } - inspect!(q.inverse(), content="Quaternion { w: NaN, x: NaN, y: NaN, z: NaN }") + inspect(q.inverse(), content="Quaternion { w: NaN, x: NaN, y: NaN, z: NaN }") } +///| test "Quaternion::inverse/random" { let q = @lib.Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } - inspect!( + inspect( q.inverse(), content="Quaternion { w: 0.03333333507180214, x: -0.06666667014360428, y: -0.10000000894069672, z: -0.13333334028720856 }", ) - inspect!( + inspect( q.inverse().inverse(), content="Quaternion { w: 0.9999998807907104, x: 1.999999761581421, y: 2.999999761581421, z: 3.999999523162842 }", ) // another random quaternion let q2 = @lib.Quaternion::{ w: 0.5, x: 0.5, y: 2.5, z: 0.5 } - inspect!( + inspect( q2.inverse(), content="Quaternion { w: 0.0714285746216774, x: -0.0714285746216774, y: -0.3571428656578064, z: -0.0714285746216774 }", ) - inspect!( + inspect( q2.inverse().inverse(), content="Quaternion { w: 0.5000000596046448, x: 0.5000000596046448, y: 2.500000238418579, z: 0.5000000596046448 }", ) } +///| test "Quaternion::inverse/general" { let q = @lib.Quaternion::{ w: 1.0, x: 2.0, y: 3.0, z: 4.0 } - inspect!( + inspect( q.inverse(), content="Quaternion { w: 0.03333333507180214, x: -0.06666667014360428, y: -0.10000000894069672, z: -0.13333334028720856 }", ) } +///| test "Quaternion::dot/basic" { let q1 = @lib.q(1.0, 2.0, 3.0, 4.0) let q2 = @lib.q(5.0, 6.0, 7.0, 8.0) // dot product should be: 1*5 + 2*6 + 3*7 + 4*8 = 70 - inspect!(q1.dot(q2), content="70") + inspect(q1.dot(q2), content="70") } +///| test "Quaternion::dot/identity" { let q1 = @lib.q(2.0, 3.0, 4.0, 5.0) let id = @lib.Quaternion::id() // dot with identity should give: 2*1 + 3*0 + 4*0 + 5*0 = 2 - inspect!(q1.dot(id), content="2") + inspect(q1.dot(id), content="2") } +///| test "Quaternion::dot/zero" { let q1 = @lib.q(1.0, 2.0, 3.0, 4.0) let zero = @lib.Quaternion::default() // dot with zero should give 0 - inspect!(q1.dot(zero), content="0") + inspect(q1.dot(zero), content="0") } +///| test "Quaternion::op_eq" { // identical quaternions should be equal let q1 = @lib.q(1.0, 2.0, 3.0, 4.0) - inspect!(q1 == q1, content="true") + inspect(q1 == q1, content="true") // very close quaternions should be equal let q2 = @lib.q(1.0, 2.0, 3.0, 4.0) let q3 = @lib.q(1.0000000001, 2.0, 3.0, 4.0) - inspect!(q2 == q3, content="true") + inspect(q2 == q3, content="true") } +///| test "Quaternion::op_eq/zero" { // zero quaternions should be equal let zero1 = @lib.q(0.0, 0.0, 0.0, 0.0) let zero2 = @lib.q(0.0, 0.0, 0.0, 0.0) - inspect!(zero1 == zero2, content="true") + inspect(zero1 == zero2, content="true") } +///| test "Quaternion::op_eq/distinct" { // clearly different quaternions should not be equal let q1 = @lib.q(1.0, 0.0, 0.0, 0.0) let q2 = @lib.q(0.0, 1.0, 0.0, 0.0) - inspect!(q1 == q2, content="false") + inspect(q1 == q2, content="false") } +///| test "Quaternion::length/basic" { let q1 = @lib.Quaternion::{ w: 1.0, x: 0.0, y: 0.0, z: 0.0 } let q2 = @lib.Quaternion::{ w: 0.0, x: 1.0, y: 0.0, z: 0.0 } let q3 = @lib.Quaternion::{ w: 1.0, x: 1.0, y: 1.0, z: 1.0 } - inspect!(q1.length(), content="1") - inspect!(q2.length(), content="1") - inspect!(q3.length(), content="2") + inspect(q1.length(), content="1") + inspect(q2.length(), content="1") + inspect(q3.length(), content="2") } +///| test "Quaternion::length/zero" { let q = @lib.Quaternion::{ w: 0.0, x: 0.0, y: 0.0, z: 0.0 } - inspect!(q.length(), content="0") + inspect(q.length(), content="0") } +///| test "Quaternion::length/negative" { let q = @lib.Quaternion::{ w: -1.0, x: -1.0, y: -1.0, z: -1.0 } - inspect!(q.length(), content="2") + inspect(q.length(), content="2") } +///| test "Quaternion::conjugate/basic" { let q = @lib.Quaternion::{ w: 2.0, x: 3.0, y: -4.0, z: 5.0 } let result = q.conjugate() - inspect!(result, content="Quaternion { w: 2, x: -3, y: 4, z: -5 }") + inspect(result, content="Quaternion { w: 2, x: -3, y: 4, z: -5 }") } +///| test "Quaternion::conjugate/identity" { let q = @lib.Quaternion::id() let result = q.conjugate() - inspect!(result, content="Quaternion { w: 1, x: 0, y: 0, z: 0 }") + inspect(result, content="Quaternion { w: 1, x: 0, y: 0, z: 0 }") } +///| test "Quaternion::conjugate/zero" { let q = @lib.Quaternion::default() let result = q.conjugate() - inspect!(result, content="Quaternion { w: 0, x: 0, y: 0, z: 0 }") + inspect(result, content="Quaternion { w: 0, x: 0, y: 0, z: 0 }") } +///| test "Quaternion::op_add/basic" { let q1 = @lib.q(1.0, 2.0, 3.0, 4.0) let q2 = @lib.q(5.0, 6.0, 7.0, 8.0) let result = q1 + q2 - inspect!(result, content="Quaternion { w: 6, x: 8, y: 10, z: 12 }") + inspect(result, content="Quaternion { w: 6, x: 8, y: 10, z: 12 }") } +///| test "Quaternion::op_add/identity" { let q1 = @lib.q(1.0, 2.0, 3.0, 4.0) let zero = @lib.q(0.0, 0.0, 0.0, 0.0) let result = q1 + zero - inspect!(result, content="Quaternion { w: 1, x: 2, y: 3, z: 4 }") + inspect(result, content="Quaternion { w: 1, x: 2, y: 3, z: 4 }") } +///| test "Quaternion::op_add/large_values" { let large = @float.max_value let q1 = @lib.q(large, large, large, large) let q2 = @lib.q(large, large, large, large) let result = q1 + q2 - inspect!( + inspect( result, content="Quaternion { w: Infinity, x: Infinity, y: Infinity, z: Infinity }", ) } +///| test "Quaternion::op_mul/basic" { let q1 = @lib.qi(1, 2, 3, 4) let q2 = @lib.qi(2, 3, 4, 5) let result = q1 * q2 // mathematical result from manual calculation - inspect!(result, content="Quaternion { w: -36, x: 6, y: 12, z: 12 }") + inspect(result, content="Quaternion { w: -36, x: 6, y: 12, z: 12 }") } +///| test "Quaternion::op_mul/identity" { let id = @lib.Quaternion::id() let q = @lib.qi(1, 2, 3, 4) let result = q * id // multiplication with identity should return the same quaternion - inspect!(result, content="Quaternion { w: 1, x: 2, y: 3, z: 4 }") + inspect(result, content="Quaternion { w: 1, x: 2, y: 3, z: 4 }") } +///| test "Quaternion::op_mul/zero" { let zero = @lib.Quaternion::default() let q = @lib.qi(1, 2, 3, 4) let result = q * zero // multiplication with zero should return zero - inspect!(result, content="Quaternion { w: 0, x: 0, y: 0, z: 0 }") + inspect(result, content="Quaternion { w: 0, x: 0, y: 0, z: 0 }") } +///| test "Quaternion::op_div/basic" { let a = @lib.qi(1, 2, 3, 4) let b = @lib.qi(2, 1, 0, 1) let result = a / b // result should be close to quaternion {w: 1.0, x: 1.6, y: 1.2, z: 1.4} - inspect!( + inspect( result, content="Quaternion { w: 1.3333333730697632, x: 0, y: 0.6666666865348816, z: 1.6666667461395264 }", ) - inspect!(result * b, content="Quaternion { w: 1, x: 2, y: 3, z: 4 }") + inspect(result * b, content="Quaternion { w: 1, x: 2, y: 3, z: 4 }") } +///| test "Quaternion::to_array/basic" { let q = @lib.q(1.0, 2.0, 3.0, 4.0) let result = q.to_wxyz() - inspect!(result, content="[1, 2, 3, 4]") + inspect(result, content="[1, 2, 3, 4]") let result2 = q.to_xyzw() - inspect!(result2, content="[2, 3, 4, 1]") - let q2 = @lib.from_xyzw_array?([2.0, 3.0, 4.0, 1.0]).unwrap() - inspect!(q2, content="Quaternion { w: 1, x: 2, y: 3, z: 4 }") - let q3 = @lib.from_wxyz?([1.0, 2.0, 3.0, 4.0]).unwrap() - inspect!(q3, content="Quaternion { w: 1, x: 2, y: 3, z: 4 }") + inspect(result2, content="[2, 3, 4, 1]") + let q2 = (try? @lib.from_xyzw_array([2.0, 3.0, 4.0, 1.0])).unwrap() + inspect(q2, content="Quaternion { w: 1, x: 2, y: 3, z: 4 }") + let q3 = (try? @lib.from_wxyz([1.0, 2.0, 3.0, 4.0])).unwrap() + inspect(q3, content="Quaternion { w: 1, x: 2, y: 3, z: 4 }") } +///| test "Quaternion::to_array/fail" { - let result = @lib.from_wxyz?([1.0, 2.0, 3.0]) - inspect!(result.is_err(), content="true") - let result2 = @lib.from_xyzw_array?([1.0, 2.0, 3.0]) - inspect!(result2.is_err(), content="true") + let result = try? @lib.from_wxyz([1.0, 2.0, 3.0]) + inspect(result.is_err(), content="true") + let result2 = try? @lib.from_xyzw_array([1.0, 2.0, 3.0]) + inspect(result2.is_err(), content="true") } +///| test "Quaternion::normalize/basic" { let q = @lib.q(1.0, 2.0, 3.0, 4.0) let result = q.normalize() /// use roughly_eq - inspect!( + inspect( result.roughly_eq(@lib.Quaternion::{ w: 0.18257418203353882, x: 0.3651483657360077, @@ -267,11 +299,11 @@ test "Quaternion::normalize/basic" { }), content="true", ) - inspect!(result.length(), content="1") + inspect(result.length(), content="1") let q2 = @lib.q(1.0, 2.0, 3.0, 4.0) - q2.normalize_mut?().unwrap() - inspect!(q2.length(), content="1") + (try? q2.normalize_mut()).unwrap() + inspect(q2.length(), content="1") let q3 = @lib.q(0.0, 0.0, 0.0, 0.0) - let ret = q3.normalize_mut?() - inspect!(ret.is_err(), content="true") + let ret = try? q3.normalize_mut() + inspect(ret.is_err(), content="true") }