pub struct Transform {
pub translate_x: f32,
pub translate_y: f32,
pub scale_x: f32,
pub scale_y: f32,
pub rotate: f32,
pub shear_x: f32,
pub shear_y: f32,
pub origin_x: f32,
pub origin_y: f32,
pub perspective: [f32; 3],
}Fields§
§translate_x: f32§translate_y: f32§scale_x: f32§scale_y: f32§rotate: f32§shear_x: f32Horizontal shear factor (x += shear_x * y), e.g. ASS \fax.
shear_y: f32Vertical shear factor (y += shear_y * x), e.g. ASS \fay.
origin_x: f32§origin_y: f32§perspective: [f32; 3]Projective row of the homogeneous 3x3 map: w = px * x + py * y + pw
and the rendered point is the affine result divided by w. Identity
(pure affine) is [0.0, 0.0, 1.0], e.g. a 3D tilt of a planar layer.
Only the scene-graph translation consumes this (perspective subtrees
are flattened into offscreen layers and composited projectively, CSS
style). linear, apply_to_point
and combine stay affine-only by design.
Implementations§
Source§impl Transform
impl Transform
pub fn identity() -> Self
pub fn translate(x: f32, y: f32) -> Self
Sourcepub fn linear(&self) -> [f32; 4]
pub fn linear(&self) -> [f32; 4]
Forward 2x2 linear part (row-major [m00, m01, m10, m11]):
M = R(rotate) * H(shear) * S(scale) applied before translation.
Sourcepub fn linear_is_identity(&self) -> bool
pub fn linear_is_identity(&self) -> bool
Whether scale/rotation/shear are all identity (translation may apply).
Sourcepub fn has_perspective(&self) -> bool
pub fn has_perspective(&self) -> bool
Whether the projective row is non-trivial (true perspective).
Sourcepub fn projective_matrix(&self) -> [f32; 9]
pub fn projective_matrix(&self) -> [f32; 9]
Full homogeneous 3x3 map (row-major, 9 elements): the affine
linear() + translation in rows 0-1, perspective
in row 2. world_h = M * [x, y, 1], world = world_h.xy / world_h.z.
Origin pivots are intentionally not folded in: the renderer consumes
the affine parts origin-free (see rect_to_instance_ndc), so pivots
must be baked into the rows by the producer (see
from_projective_rows).
Sourcepub fn from_projective_rows(
row0: [f32; 3],
row1: [f32; 3],
perspective: [f32; 3],
) -> Self
pub fn from_projective_rows( row0: [f32; 3], row1: [f32; 3], perspective: [f32; 3], ) -> Self
Build a transform from explicit homogeneous rows: row0/row1 are the
affine [a, b, t] rows, perspective the projective [px, py, pw]
row. The affine 2x2 is decomposed into scale/rotate/shear parts (via
the same decomposition combine uses), so this
round-trips any 3D-rotation-of-a-plane + perspective map exactly —
e.g. ASS \frx/\fry about an \org pivot, where the pivot lives in
the rows because the renderer ignores origin_*.
Sourcepub fn apply_projective(&self, p: Vec2) -> Vec2
pub fn apply_projective(&self, p: Vec2) -> Vec2
Apply the full projective map to a point, with perspective divide.
Degenerate w (≈ 0, at/behind the viewer) clamps to a tiny epsilon of
the original sign so output stays finite; callers culling
behind-camera content should test w via projective_w.
Sourcepub fn projective_w(&self, p: Vec2) -> f32
pub fn projective_w(&self, p: Vec2) -> f32
The homogeneous w of a point under this transform’s projective row.
Sourcepub fn compose_projective(outer: &[f32; 9], inner: &[f32; 9]) -> [f32; 9]
pub fn compose_projective(outer: &[f32; 9], inner: &[f32; 9]) -> [f32; 9]
Compose two homogeneous 3x3 maps (row-major 9-element arrays, as from
projective_matrix): world = outer × inner × local. Used at scene-translation time to fold affine
ancestors over a perspective node; the result feeds layer-flattening,
never the part-based combine.
Sourcepub fn project_rect(&self, r: &Rect) -> Rect
pub fn project_rect(&self, r: &Rect) -> Rect
Axis-aligned bounds of a projectively mapped rect (projects all four corners and bounds them; the correct cull rect for flattened layers).
pub fn apply_to_point(&self, p: Vec2) -> Vec2
pub fn apply_to_rect(&self, r: Rect) -> Rect
Sourcepub fn combine(&self, other: &Transform) -> Transform
pub fn combine(&self, other: &Transform) -> Transform
Compose two transforms (self outer, other inner) for a
transform stack: current.combine(pushed) where pushed is the
newly pushed (inner) node.
Returns a transform such that combined.apply_to_point(p) == self.apply_to_point(other.apply_to_point(p)).
The linear part is composed exactly (via polar decomposition of the
2x2 product); origins are inherited from self, matching the
previous behaviour for the shear-free cases.
Affine-only by design: a part-based transform cannot represent a
composed projective map, so perspective rows do NOT compose here
(debug-asserted). Perspective folds at scene-translation time into a
full 3x3 via projective_matrix and
compose_projective, which is what
flattens perspective subtrees into projectively composited layers.