///|
fn transform_ops_with_head(
head : TransformOp,
tail : ArrayView[TransformOp],
) -> Array[TransformOp] {
let output = [head]
for item in tail {
output.push(item)
}
output
}
///|
/// Adds one operation to the front of an operation list, merging with the first
/// operation when the two adjacent operations are compatible.
///
/// Compatible adjacent translations, scales about the same origin, rotations
/// about the same origin, and shears about the same origin are combined to keep
/// operation lists compact.
pub fn transform_compose_op(
op : TransformOp,
ops : ArrayView[TransformOp],
) -> Array[TransformOp] {
if ops.length() == 0 {
[op]
} else {
let head = ops[0]
let rest = ops[1:]
match (head, op) {
(Translate(dx, dy), Translate(dx2, dy2)) =>
transform_ops_with_head(Translate(dx + dx2, dy + dy2), rest)
(Scale(origin, sx, sy), Scale(origin2, sx2, sy2)) if origin == origin2 =>
transform_ops_with_head(Scale(origin, sx * sx2, sy * sy2), rest)
(Rotate(origin, angle), Rotate(origin2, angle2)) if origin == origin2 =>
transform_ops_with_head(Rotate(origin, angle + angle2), rest)
(ShearX(origin, factor), ShearX(origin2, factor2)) if origin == origin2 =>
transform_ops_with_head(ShearX(origin, factor + factor2), rest)
(ShearY(origin, factor), ShearY(origin2, factor2)) if origin == origin2 =>
transform_ops_with_head(ShearY(origin, factor + factor2), rest)
_ => transform_ops_with_head(op, ops)
}
}
}
///|
/// Concatenates two transform operation lists.
///
/// The `after` operations are placed before the `before` operations to preserve
/// the same ordering used by CamlPDF transform lists.
pub fn transform_append(
after : ArrayView[TransformOp],
before : ArrayView[TransformOp],
) -> Array[TransformOp] {
[..after, ..before]
}
///|
/// Applies this matrix to a point.
pub fn TransformMatrix::apply(self : TransformMatrix, point : Point2) -> Point2 {
{
x: point.x * self.a + point.y * self.c + self.e,
y: point.x * self.b + point.y * self.d + self.f,
}
}
///|
/// Applies a matrix to a point.
pub fn transform_apply_matrix(
matrix : TransformMatrix,
point : Point2,
) -> Point2 {
matrix.apply(point)
}
///|
/// Lowers one high-level transform operation to a matrix.
pub fn transform_matrix_of_op(op : TransformOp) -> TransformMatrix {
match op {
Scale(origin, sx, sy) => transform_scale(origin, sx, sy)
Rotate(origin, angle) => transform_rotate(origin, angle)
Translate(dx, dy) => transform_translate(dx, dy)
ShearX(origin, factor) => transform_shear_x(origin, factor)
ShearY(origin, factor) => transform_shear_y(origin, factor)
}
}
///|
/// Lowers an operation list to one composed matrix.
pub fn transform_matrix_of_ops(ops : ArrayView[TransformOp]) -> TransformMatrix {
let mut matrix = transform_identity_matrix
for op in ops {
matrix = transform_matrix_compose(matrix, transform_matrix_of_op(op))
}
matrix
}
///|
/// Compatibility alias for lowering a transform operation list to a matrix.
pub fn transform_matrix_of_transform(
ops : ArrayView[TransformOp],
) -> TransformMatrix {
transform_matrix_of_ops(ops)
}
///|
/// Applies a transform operation list to a point.
pub fn transform_apply_ops(
ops : ArrayView[TransformOp],
point : Point2,
) -> Point2 {
transform_matrix_of_ops(ops).apply(point)
}
///|
/// Compatibility alias for applying a transform operation list to a point.
pub fn transform_apply(ops : ArrayView[TransformOp], point : Point2) -> Point2 {
transform_apply_ops(ops, point)
}