core: Add matrix tests (#72)
* Add matrix tests. Requires the approx crate
This commit is contained in:
parent
6650a7a924
commit
06b0d42c8b
|
@ -21,5 +21,8 @@ swf = { path = "../swf" }
|
|||
version = "0.1.16"
|
||||
default-features = false # can't use rayon on web
|
||||
|
||||
[dev-dependencies]
|
||||
approx = "0.3.2"
|
||||
|
||||
[features]
|
||||
default = ["minimp3"]
|
||||
|
|
|
@ -85,3 +85,580 @@ impl std::ops::MulAssign for Matrix {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use approx::{assert_ulps_eq, AbsDiffEq, UlpsEq};
|
||||
|
||||
macro_rules! test_invert {
|
||||
( $test: ident, $($args: expr),* ) => {
|
||||
#[test]
|
||||
fn $test() {
|
||||
$(
|
||||
let (mut input, output) = $args;
|
||||
input.invert();
|
||||
assert_ulps_eq!(input, output);
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! test_multiply {
|
||||
( $test: ident, $($args: expr),* ) => {
|
||||
#[test]
|
||||
fn $test() {
|
||||
$(
|
||||
let (input1, input2, output) = $args;
|
||||
assert_ulps_eq!(input1 * input2, output);
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! test_multiply_twips {
|
||||
( $test: ident, $($args: expr),* ) => {
|
||||
#[test]
|
||||
fn $test() {
|
||||
$(
|
||||
let (input1, input2, output) = $args;
|
||||
assert_eq!(input1 * input2, output);
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl AbsDiffEq for Matrix {
|
||||
type Epsilon = <f32 as AbsDiffEq>::Epsilon;
|
||||
fn default_epsilon() -> Self::Epsilon {
|
||||
f32::default_epsilon()
|
||||
}
|
||||
|
||||
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
|
||||
self.a.abs_diff_eq(&other.a, epsilon)
|
||||
&& self.b.abs_diff_eq(&other.b, epsilon)
|
||||
&& self.c.abs_diff_eq(&other.c, epsilon)
|
||||
&& self.d.abs_diff_eq(&other.d, epsilon)
|
||||
&& self.tx.abs_diff_eq(&other.tx, epsilon)
|
||||
&& self.ty.abs_diff_eq(&other.ty, epsilon)
|
||||
}
|
||||
}
|
||||
|
||||
impl UlpsEq for Matrix {
|
||||
fn default_max_ulps() -> u32 {
|
||||
f32::default_max_ulps()
|
||||
}
|
||||
|
||||
fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
|
||||
self.a.ulps_eq(&other.a, epsilon, max_ulps)
|
||||
&& self.b.ulps_eq(&other.b, epsilon, max_ulps)
|
||||
&& self.c.ulps_eq(&other.c, epsilon, max_ulps)
|
||||
&& self.d.ulps_eq(&other.d, epsilon, max_ulps)
|
||||
&& self.tx.ulps_eq(&other.tx, epsilon, max_ulps)
|
||||
&& self.ty.ulps_eq(&other.ty, epsilon, max_ulps)
|
||||
}
|
||||
}
|
||||
|
||||
// Identity matrix inverted should be unchanged
|
||||
test_invert!(
|
||||
invert_identity_matrix,
|
||||
(Matrix::default(), Matrix::default())
|
||||
);
|
||||
|
||||
// Standard test cases; there's nothing special about these matrices
|
||||
test_invert!(
|
||||
invert_matrices,
|
||||
(
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 4.0,
|
||||
tx: 7.0,
|
||||
b: 2.0,
|
||||
d: 5.0,
|
||||
ty: 2.0
|
||||
},
|
||||
Matrix {
|
||||
a: -1.666_666_6,
|
||||
c: 1.333_333_3,
|
||||
tx: 9.0,
|
||||
b: 0.666_666_6,
|
||||
d: -0.333_333_3,
|
||||
ty: -4.0
|
||||
}
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: -1.0,
|
||||
c: -4.0,
|
||||
tx: -7.0,
|
||||
b: -2.0,
|
||||
d: -5.0,
|
||||
ty: -2.0
|
||||
},
|
||||
Matrix {
|
||||
a: 1.666_666_6,
|
||||
c: -1.333_333_3,
|
||||
tx: 9.0,
|
||||
b: -0.666_666_6,
|
||||
d: 0.333_333_3,
|
||||
ty: -4.0
|
||||
}
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: 1.5,
|
||||
c: 1.2,
|
||||
tx: 1.0,
|
||||
b: -2.7,
|
||||
d: 3.4,
|
||||
ty: -2.4
|
||||
},
|
||||
Matrix {
|
||||
a: 0.407_673_9,
|
||||
c: -0.143_884_9,
|
||||
tx: -0.752_997_6,
|
||||
b: 0.323_741,
|
||||
d: 0.179_856_1,
|
||||
ty: 0.107_913_67
|
||||
}
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: -2.0,
|
||||
c: 0.0,
|
||||
tx: 10.0,
|
||||
b: 0.0,
|
||||
d: -1.0,
|
||||
ty: 5.0
|
||||
},
|
||||
Matrix {
|
||||
a: -0.5,
|
||||
c: 0.0,
|
||||
tx: 5.0,
|
||||
b: 0.0,
|
||||
d: -1.0,
|
||||
ty: 5.0
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Anything multiplied by the identity matrix should be unchanged
|
||||
test_multiply!(
|
||||
multiply_identity_matrix,
|
||||
(Matrix::default(), Matrix::default(), Matrix::default()),
|
||||
(
|
||||
Matrix::default(),
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 4.0,
|
||||
tx: 7.0,
|
||||
b: 2.0,
|
||||
d: 5.0,
|
||||
ty: 2.0
|
||||
},
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 4.0,
|
||||
tx: 7.0,
|
||||
b: 2.0,
|
||||
d: 5.0,
|
||||
ty: 2.0
|
||||
}
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 4.0,
|
||||
tx: 7.0,
|
||||
b: 2.0,
|
||||
d: 5.0,
|
||||
ty: 2.0
|
||||
},
|
||||
Matrix::default(),
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 4.0,
|
||||
tx: 7.0,
|
||||
b: 2.0,
|
||||
d: 5.0,
|
||||
ty: 2.0
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// General test cases for matrix multiplication
|
||||
test_multiply!(
|
||||
multiply_matrices,
|
||||
(
|
||||
Matrix {
|
||||
a: 6.0,
|
||||
c: 4.0,
|
||||
tx: 2.0,
|
||||
b: 5.0,
|
||||
d: 3.0,
|
||||
ty: 1.0
|
||||
},
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 3.0,
|
||||
tx: 5.0,
|
||||
b: 2.0,
|
||||
d: 4.0,
|
||||
ty: 6.0
|
||||
},
|
||||
Matrix {
|
||||
a: 14.0,
|
||||
c: 34.0,
|
||||
tx: 56.0,
|
||||
b: 11.0,
|
||||
d: 27.0,
|
||||
ty: 44.0
|
||||
}
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 3.0,
|
||||
tx: 5.0,
|
||||
b: 2.0,
|
||||
d: 4.0,
|
||||
ty: 6.0
|
||||
},
|
||||
Matrix {
|
||||
a: 6.0,
|
||||
c: 4.0,
|
||||
tx: 2.0,
|
||||
b: 5.0,
|
||||
d: 3.0,
|
||||
ty: 1.0
|
||||
},
|
||||
Matrix {
|
||||
a: 21.0,
|
||||
c: 13.0,
|
||||
tx: 10.0,
|
||||
b: 32.0,
|
||||
d: 20.0,
|
||||
ty: 14.0
|
||||
}
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 2.0,
|
||||
tx: 3.0,
|
||||
b: 4.0,
|
||||
d: 5.0,
|
||||
ty: 6.0
|
||||
},
|
||||
Matrix {
|
||||
a: 6.0,
|
||||
c: 5.0,
|
||||
tx: 4.0,
|
||||
b: 3.0,
|
||||
d: 2.0,
|
||||
ty: 1.0
|
||||
},
|
||||
Matrix {
|
||||
a: 12.0,
|
||||
c: 9.0,
|
||||
tx: 9.0,
|
||||
b: 39.0,
|
||||
d: 30.0,
|
||||
ty: 27.0
|
||||
}
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: 6.0,
|
||||
c: 5.0,
|
||||
tx: 4.0,
|
||||
b: 3.0,
|
||||
d: 2.0,
|
||||
ty: 1.0
|
||||
},
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 2.0,
|
||||
tx: 3.0,
|
||||
b: 4.0,
|
||||
d: 5.0,
|
||||
ty: 6.0
|
||||
},
|
||||
Matrix {
|
||||
a: 26.0,
|
||||
c: 37.0,
|
||||
tx: 52.0,
|
||||
b: 11.0,
|
||||
d: 16.0,
|
||||
ty: 22.0
|
||||
}
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 2.0,
|
||||
tx: 3.0,
|
||||
b: 4.0,
|
||||
d: 5.0,
|
||||
ty: 6.0
|
||||
},
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 2.0,
|
||||
tx: 3.0,
|
||||
b: 4.0,
|
||||
d: 5.0,
|
||||
ty: 6.0
|
||||
},
|
||||
Matrix {
|
||||
a: 9.0,
|
||||
c: 12.0,
|
||||
tx: 18.0,
|
||||
b: 24.0,
|
||||
d: 33.0,
|
||||
ty: 48.0
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Twips multiplied by the identity/default matrix should be unchanged
|
||||
test_multiply_twips!(
|
||||
multiply_twips_identity_matrix,
|
||||
(
|
||||
Matrix::default(),
|
||||
(Twips::new(0), Twips::new(0)),
|
||||
(Twips::new(0), Twips::new(0))
|
||||
),
|
||||
(
|
||||
Matrix::default(),
|
||||
(Twips::new(0), Twips::new(10)),
|
||||
(Twips::new(0), Twips::new(10))
|
||||
),
|
||||
(
|
||||
Matrix::default(),
|
||||
(Twips::new(10), Twips::new(0)),
|
||||
(Twips::new(10), Twips::new(0))
|
||||
),
|
||||
(
|
||||
Matrix::default(),
|
||||
(Twips::new(-251), Twips::new(152)),
|
||||
(Twips::new(-251), Twips::new(152))
|
||||
)
|
||||
);
|
||||
|
||||
// multiply by translate matrices; values should be shifted
|
||||
test_multiply_twips!(
|
||||
multiply_twips_translate,
|
||||
(
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 0.0,
|
||||
tx: 10.0,
|
||||
b: 0.0,
|
||||
d: 1.0,
|
||||
ty: 5.0
|
||||
},
|
||||
(Twips::new(0), Twips::new(0)),
|
||||
(Twips::new(10), Twips::new(5))
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 0.0,
|
||||
tx: -200.0,
|
||||
b: 0.0,
|
||||
d: 1.0,
|
||||
ty: 0.0
|
||||
},
|
||||
(Twips::new(50), Twips::new(20)),
|
||||
(Twips::new(-150), Twips::new(20))
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 0.0,
|
||||
tx: 1.125,
|
||||
b: 0.0,
|
||||
d: 1.0,
|
||||
ty: 1.925
|
||||
},
|
||||
(Twips::new(0), Twips::new(0)),
|
||||
(Twips::new(1), Twips::new(1))
|
||||
)
|
||||
);
|
||||
|
||||
// multiply by scalar matrices; values should be scaled up/down
|
||||
test_multiply_twips!(
|
||||
multiply_twips_scale,
|
||||
(
|
||||
Matrix {
|
||||
a: 3.0,
|
||||
c: 0.0,
|
||||
tx: 0.0,
|
||||
b: 0.0,
|
||||
d: 3.0,
|
||||
ty: 0.0
|
||||
},
|
||||
(Twips::new(0), Twips::new(0)),
|
||||
(Twips::new(0), Twips::new(0))
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: 3.0,
|
||||
c: 0.0,
|
||||
tx: 0.0,
|
||||
b: 0.0,
|
||||
d: 3.0,
|
||||
ty: 0.0
|
||||
},
|
||||
(Twips::new(10), Twips::new(10)),
|
||||
(Twips::new(30), Twips::new(30))
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: 0.6,
|
||||
c: 0.0,
|
||||
tx: 0.0,
|
||||
b: 0.0,
|
||||
d: 0.2,
|
||||
ty: 0.0
|
||||
},
|
||||
(Twips::new(5), Twips::new(10)),
|
||||
(Twips::new(3), Twips::new(2))
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: 0.5,
|
||||
c: 0.0,
|
||||
tx: 0.0,
|
||||
b: 0.0,
|
||||
d: 0.5,
|
||||
ty: 0.0
|
||||
},
|
||||
(Twips::new(5), Twips::new(5)),
|
||||
(Twips::new(2), Twips::new(2))
|
||||
)
|
||||
);
|
||||
|
||||
// multiply by rotation matrices; values should be rotated around origin
|
||||
test_multiply_twips!(
|
||||
multiply_twips_rotation,
|
||||
(
|
||||
Matrix {
|
||||
a: 0.0,
|
||||
c: -1.0,
|
||||
tx: 0.0,
|
||||
b: 1.0,
|
||||
d: 0.0,
|
||||
ty: 0.0
|
||||
},
|
||||
(Twips::new(10), Twips::new(0)),
|
||||
(Twips::new(0), Twips::new(10))
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: 0.0,
|
||||
c: -1.0,
|
||||
tx: 0.0,
|
||||
b: 1.0,
|
||||
d: 0.0,
|
||||
ty: 0.0
|
||||
},
|
||||
(Twips::new(0), Twips::new(10)),
|
||||
(Twips::new(-10), Twips::new(0))
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: 0.0,
|
||||
c: 1.0,
|
||||
tx: 0.0,
|
||||
b: -1.0,
|
||||
d: 0.0,
|
||||
ty: 0.0
|
||||
},
|
||||
(Twips::new(10), Twips::new(10)),
|
||||
(Twips::new(10), Twips::new(-10))
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: f32::cos(std::f32::consts::FRAC_PI_4),
|
||||
c: f32::sin(std::f32::consts::FRAC_PI_4),
|
||||
tx: 0.0,
|
||||
b: -f32::sin(std::f32::consts::FRAC_PI_4),
|
||||
d: f32::cos(std::f32::consts::FRAC_PI_4),
|
||||
ty: 0.0
|
||||
},
|
||||
(Twips::new(100), Twips::new(0)),
|
||||
(Twips::new(70), Twips::new(-70))
|
||||
),
|
||||
(
|
||||
Matrix {
|
||||
a: f32::cos(std::f32::consts::FRAC_PI_4),
|
||||
c: f32::sin(std::f32::consts::FRAC_PI_4),
|
||||
tx: 0.0,
|
||||
b: -f32::sin(std::f32::consts::FRAC_PI_4),
|
||||
d: f32::cos(std::f32::consts::FRAC_PI_4),
|
||||
ty: 0.0
|
||||
},
|
||||
(Twips::new(100), Twips::new(100)),
|
||||
(Twips::new(141), Twips::new(0))
|
||||
)
|
||||
);
|
||||
|
||||
// Testing transformation matrices that have more than 1 translation applied
|
||||
test_multiply_twips!(
|
||||
multiply_twips_complex,
|
||||
(
|
||||
// result of scaling by 3 * rotation by 45 degrees
|
||||
Matrix {
|
||||
a: 3.0 * f32::cos(std::f32::consts::FRAC_PI_4),
|
||||
c: 3.0 * f32::sin(std::f32::consts::FRAC_PI_4),
|
||||
tx: 0.0,
|
||||
b: 3.0 * -f32::sin(std::f32::consts::FRAC_PI_4),
|
||||
d: 3.0 * f32::cos(std::f32::consts::FRAC_PI_4),
|
||||
ty: 0.0
|
||||
},
|
||||
(Twips::new(100), Twips::new(100)),
|
||||
(Twips::new(424), Twips::new(0))
|
||||
),
|
||||
(
|
||||
// result of translating by (-5, 5) * rotation by 45 degrees
|
||||
Matrix {
|
||||
a: 3.0 * f32::cos(std::f32::consts::FRAC_PI_4),
|
||||
c: 3.0 * f32::sin(std::f32::consts::FRAC_PI_4),
|
||||
tx: -5.0,
|
||||
b: 3.0 * -f32::sin(std::f32::consts::FRAC_PI_4),
|
||||
d: 3.0 * f32::cos(std::f32::consts::FRAC_PI_4),
|
||||
ty: 5.0
|
||||
},
|
||||
(Twips::new(100), Twips::new(100)),
|
||||
(Twips::new(419), Twips::new(5))
|
||||
),
|
||||
(
|
||||
// result of rotation by 45 degrees * translating by (-5, 5)
|
||||
Matrix {
|
||||
a: f32::cos(std::f32::consts::FRAC_PI_4),
|
||||
c: f32::sin(std::f32::consts::FRAC_PI_4),
|
||||
tx: -5.0,
|
||||
b: -f32::sin(std::f32::consts::FRAC_PI_4),
|
||||
d: f32::cos(std::f32::consts::FRAC_PI_4),
|
||||
ty: 5.0
|
||||
},
|
||||
(Twips::new(100), Twips::new(100)),
|
||||
(Twips::new(136), Twips::new(5))
|
||||
),
|
||||
(
|
||||
// result of translating by (-5, 5) * rotation by 45 degrees
|
||||
Matrix {
|
||||
a: f32::cos(std::f32::consts::FRAC_PI_4),
|
||||
c: f32::sin(std::f32::consts::FRAC_PI_4),
|
||||
tx: 0.0,
|
||||
b: -f32::sin(std::f32::consts::FRAC_PI_4),
|
||||
d: f32::cos(std::f32::consts::FRAC_PI_4),
|
||||
ty: 10.0 * f32::sin(std::f32::consts::FRAC_PI_4)
|
||||
},
|
||||
(Twips::new(105), Twips::new(95)),
|
||||
(Twips::new(141), Twips::new(0))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue