// Matrix2D affine transform engine for 2D graphics kernel.
///|
pub struct Matrix2D {
a : Double
b : Double
c : Double
d : Double
tx : Double
ty : Double
} derive(Eq, Debug)
///|
pub fn Matrix2D::identity() -> Matrix2D {
{ a: 1.0, b: 0.0, c: 0.0, d: 1.0, tx: 0.0, ty: 0.0 }
}
///|
pub fn Matrix2D::new(
a : Double,
b : Double,
c : Double,
d : Double,
tx : Double,
ty : Double,
) -> Matrix2D {
{ a, b, c, d, tx, ty }
}
///|
pub fn Matrix2D::translation(x : Double, y : Double) -> Matrix2D {
{ a: 1.0, b: 0.0, c: 0.0, d: 1.0, tx: x, ty: y }
}
///|
pub fn Matrix2D::scaling(sx : Double, sy : Double) -> Matrix2D {
{ a: sx, b: 0.0, c: 0.0, d: sy, tx: 0.0, ty: 0.0 }
}
///|
pub fn Matrix2D::scaling_at(
sx : Double,
sy : Double,
center : Vec2,
) -> Matrix2D {
Matrix2D::translation(center.x, center.y)
.multiply(Matrix2D::scaling(sx, sy))
.multiply(Matrix2D::translation(-center.x, -center.y))
}
///|
pub fn Matrix2D::rotation(angle_radians : Double) -> Matrix2D {
let cos_a = @cmath.cos(angle_radians)
let sin_a = @cmath.sin(angle_radians)
{ a: cos_a, b: sin_a, c: -sin_a, d: cos_a, tx: 0.0, ty: 0.0 }
}
///|
pub fn Matrix2D::rotation_at(angle_radians : Double, center : Vec2) -> Matrix2D {
Matrix2D::translation(center.x, center.y)
.multiply(Matrix2D::rotation(angle_radians))
.multiply(Matrix2D::translation(-center.x, -center.y))
}
///|
pub fn Matrix2D::shearing(kx : Double, ky : Double) -> Matrix2D {
{ a: 1.0, b: ky, c: kx, d: 1.0, tx: 0.0, ty: 0.0 }
}
///|
pub fn Matrix2D::multiply(self : Matrix2D, other : Matrix2D) -> Matrix2D {
{
a: self.a * other.a + self.c * other.b,
b: self.b * other.a + self.d * other.b,
c: self.a * other.c + self.c * other.d,
d: self.b * other.c + self.d * other.d,
tx: self.a * other.tx + self.c * other.ty + self.tx,
ty: self.b * other.tx + self.d * other.ty + self.ty,
}
}
///|
pub fn Matrix2D::determinant(self : Matrix2D) -> Double {
self.a * self.d - self.b * self.c
}
///|
pub fn Matrix2D::invert(self : Matrix2D) -> Matrix2D? {
let det = self.determinant()
if det.abs() < 0.000000000001 {
None
} else {
let inv_det = 1.0 / det
Some({
a: self.d * inv_det,
b: -self.b * inv_det,
c: -self.c * inv_det,
d: self.a * inv_det,
tx: (self.c * self.ty - self.d * self.tx) * inv_det,
ty: (self.b * self.tx - self.a * self.ty) * inv_det,
})
}
}
///|
pub fn Matrix2D::transform_point(self : Matrix2D, p : Vec2) -> Vec2 {
{
x: self.a * p.x + self.c * p.y + self.tx,
y: self.b * p.x + self.d * p.y + self.ty,
}
}
///|
pub fn Matrix2D::transform_vector(self : Matrix2D, v : Vec2) -> Vec2 {
{ x: self.a * v.x + self.c * v.y, y: self.b * v.x + self.d * v.y }
}
///|
pub fn Matrix2D::transform_bounds(self : Matrix2D, rect : Rect2D) -> Rect2D {
if rect.is_empty() {
rect
} else {
let c1 = self.transform_point(Vec2::new(rect.min_x, rect.min_y))
let c2 = self.transform_point(Vec2::new(rect.max_x, rect.min_y))
let c3 = self.transform_point(Vec2::new(rect.max_x, rect.max_y))
let c4 = self.transform_point(Vec2::new(rect.min_x, rect.max_y))
Rect2D::from_points([c1, c2, c3, c4])
}
}
///|
pub struct MatrixDecomposition {
translation : Vec2
scale : Vec2
rotation : Double
skew : Vec2
} derive(Eq, Debug)
///|
pub fn Matrix2D::decompose(self : Matrix2D) -> MatrixDecomposition {
let tx = self.tx
let ty = self.ty
let scale_x = (self.a * self.a + self.b * self.b).sqrt()
let scale_y = (self.c * self.c + self.d * self.d).sqrt()
let rotation = @cmath.atan2(self.b, self.a)
let skew_x = @cmath.atan2(
self.a * self.c + self.b * self.d,
scale_x * scale_x,
)
{
translation: Vec2::new(tx, ty),
scale: Vec2::new(scale_x, scale_y),
rotation,
skew: Vec2::new(skew_x, 0.0),
}
}