diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-11-03 11:44:53 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-11-03 13:35:39 +0100 |
| commit | 37a7afddfaffd44cb9bc013c9506599267e08983 (patch) | |
| tree | 20e7d62d3c5418baff01a21d0406b91bf3096214 /library/src/layout/transform.rs | |
| parent | 56342bd972a13ffe21beaf2b87ab7eb1597704b4 (diff) | |
Split crates
Diffstat (limited to 'library/src/layout/transform.rs')
| -rw-r--r-- | library/src/layout/transform.rs | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/library/src/layout/transform.rs b/library/src/layout/transform.rs new file mode 100644 index 00000000..4e0b8ac2 --- /dev/null +++ b/library/src/layout/transform.rs @@ -0,0 +1,117 @@ +use typst::geom::Transform; + +use crate::prelude::*; + +/// Move content without affecting layout. +#[derive(Debug, Hash)] +pub struct MoveNode { + /// The offset by which to move the content. + pub delta: Axes<Rel<Length>>, + /// The content that should be moved. + pub child: Content, +} + +#[node(LayoutInline)] +impl MoveNode { + fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { + let dx = args.named("dx")?.unwrap_or_default(); + let dy = args.named("dy")?.unwrap_or_default(); + Ok(Self { + delta: Axes::new(dx, dy), + child: args.expect("body")?, + } + .pack()) + } +} + +impl LayoutInline for MoveNode { + fn layout_inline( + &self, + world: Tracked<dyn World>, + regions: &Regions, + styles: StyleChain, + ) -> SourceResult<Vec<Frame>> { + let mut frames = self.child.layout_inline(world, regions, styles)?; + + let delta = self.delta.resolve(styles); + for frame in &mut frames { + let delta = delta.zip(frame.size()).map(|(d, s)| d.relative_to(s)); + frame.translate(delta.to_point()); + } + + Ok(frames) + } +} + +/// Transform content without affecting layout. +#[derive(Debug, Hash)] +pub struct TransformNode<const T: TransformKind> { + /// Transformation to apply to the content. + pub transform: Transform, + /// The content that should be transformed. + pub child: Content, +} + +/// Rotate content without affecting layout. +pub type RotateNode = TransformNode<ROTATE>; + +/// Scale content without affecting layout. +pub type ScaleNode = TransformNode<SCALE>; + +#[node(LayoutInline)] +impl<const T: TransformKind> TransformNode<T> { + /// The origin of the transformation. + #[property(resolve)] + pub const ORIGIN: Axes<Option<GenAlign>> = Axes::default(); + + fn construct(_: &mut Vm, args: &mut Args) -> SourceResult<Content> { + let transform = match T { + ROTATE => { + let angle = args.named_or_find("angle")?.unwrap_or_default(); + Transform::rotate(angle) + } + SCALE | _ => { + let all = args.find()?; + let sx = args.named("x")?.or(all).unwrap_or(Ratio::one()); + let sy = args.named("y")?.or(all).unwrap_or(Ratio::one()); + Transform::scale(sx, sy) + } + }; + + Ok(Self { transform, child: args.expect("body")? }.pack()) + } +} + +impl<const T: TransformKind> LayoutInline for TransformNode<T> { + fn layout_inline( + &self, + world: Tracked<dyn World>, + regions: &Regions, + styles: StyleChain, + ) -> SourceResult<Vec<Frame>> { + let origin = styles.get(Self::ORIGIN).unwrap_or(Align::CENTER_HORIZON); + let mut frames = self.child.layout_inline(world, regions, styles)?; + + for frame in &mut frames { + let Axes { x, y } = origin.zip(frame.size()).map(|(o, s)| o.position(s)); + let transform = Transform::translate(x, y) + .pre_concat(self.transform) + .pre_concat(Transform::translate(-x, -y)); + + frame.transform(transform); + } + + Ok(frames) + } +} + +/// Kinds of transformations. +/// +/// The move transformation is handled separately. +pub type TransformKind = usize; + +/// A rotational transformation. +const ROTATE: TransformKind = 1; + +/// A scale transformation. +const SCALE: TransformKind = 2; |
