From 231b96e5cff47a78b94ffa95d04f0f5955aefab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20d=27Herbais=20de=20Thun?= Date: Wed, 13 Dec 2023 12:44:28 +0100 Subject: [PATCH] Make layout account for transforms (#2555) --- crates/typst/src/layout/frame.rs | 2 +- crates/typst/src/layout/point.rs | 17 +++ crates/typst/src/layout/transform.rs | 165 ++++++++++++++++++++++---- tests/ref/layout/transform-layout.png | Bin 0 -> 42107 bytes tests/typ/layout/transform-layout.typ | 58 +++++++++ 5 files changed, 215 insertions(+), 27 deletions(-) create mode 100644 tests/ref/layout/transform-layout.png create mode 100644 tests/typ/layout/transform-layout.typ diff --git a/crates/typst/src/layout/frame.rs b/crates/typst/src/layout/frame.rs index 3abc8623e..d06cd23da 100644 --- a/crates/typst/src/layout/frame.rs +++ b/crates/typst/src/layout/frame.rs @@ -50,7 +50,7 @@ impl Frame { /// Create a new, empty soft frame. /// - /// Panics the size is not finite. + /// Panics if the size is not finite. #[track_caller] pub fn soft(size: Size) -> Self { Self::new(size, FrameKind::Soft) diff --git a/crates/typst/src/layout/point.rs b/crates/typst/src/layout/point.rs index 239a6fe79..bcc05c2b2 100644 --- a/crates/typst/src/layout/point.rs +++ b/crates/typst/src/layout/point.rs @@ -49,12 +49,20 @@ impl Point { Self { x: self.x.max(other.x), y: self.y.max(other.y) } } + /// Maps the point with the given function. + pub fn map(self, f: impl Fn(Abs) -> Abs) -> Self { + Self { x: f(self.x), y: f(self.y) } + } + /// The distance between this point and the origin. pub fn hypot(self) -> Abs { Abs::raw(self.x.to_raw().hypot(self.y.to_raw())) } /// Transform the point with the given transformation. + /// + /// In the event that one of the coordinates is infinite, the result will + /// be zero. pub fn transform(self, ts: Transform) -> Self { Self::new( ts.sx.of(self.x) + ts.kx.of(self.y) + ts.tx, @@ -62,6 +70,15 @@ impl Point { ) } + /// Transforms the point with the given transformation, without accounting + /// for infinite values. + pub fn transform_inf(self, ts: Transform) -> Self { + Self::new( + ts.sx.get() * self.x + ts.kx.get() * self.y + ts.tx, + ts.ky.get() * self.x + ts.sy.get() * self.y + ts.ty, + ) + } + /// Convert to a size. pub fn to_size(self) -> Size { Size::new(self.x, self.y) diff --git a/crates/typst/src/layout/transform.rs b/crates/typst/src/layout/transform.rs index bfc35825c..92cba120c 100644 --- a/crates/typst/src/layout/transform.rs +++ b/crates/typst/src/layout/transform.rs @@ -2,13 +2,13 @@ use crate::diag::SourceResult; use crate::engine::Engine; use crate::foundations::{elem, Content, Resolve, StyleChain}; use crate::layout::{ - Abs, Align, Angle, Axes, FixedAlign, Fragment, HAlign, Layout, Length, Ratio, - Regions, Rel, VAlign, + Abs, Align, Angle, Axes, FixedAlign, Fragment, Frame, HAlign, Layout, Length, Point, + Ratio, Regions, Rel, Size, VAlign, }; /// Moves content without affecting layout. /// -/// The `move` function allows you to move content while the layout still 'sees' +/// The `move` function allows you to move content while th layout still 'sees' /// it at the original positions. Containers will still be sized as if the /// content was not moved. /// @@ -57,7 +57,7 @@ impl Layout for MoveElem { /// Rotates content without affecting layout. /// /// Rotates an element by a given angle. The layout will act as if the element -/// was not rotated. +/// was not rotated unless you specify `{reflow: true}`. /// /// # Example /// ```example @@ -98,6 +98,18 @@ pub struct RotateElem { #[default(HAlign::Center + VAlign::Horizon)] pub origin: Align, + /// Whether the rotation impacts the layout. + /// + /// If set to `{false}`, the rotated content will retain the bounding box of + /// the original content. If set to `{true}`, the bounding box will take the + /// rotation of the content into account and adjust the layout accordingly. + /// + /// ```example + /// Hello #rotate(90deg, reflow: true)[World]! + /// ``` + #[default(false)] + pub reflow: bool, + /// The content to rotate. #[required] pub body: Content, @@ -111,17 +123,27 @@ impl Layout for RotateElem { styles: StyleChain, regions: Regions, ) -> SourceResult { - let pod = Regions::one(regions.base(), Axes::splat(false)); - let mut frame = self.body().layout(engine, styles, pod)?.into_frame(); - let Axes { x, y } = self - .origin(styles) - .resolve(styles) - .zip_map(frame.size(), FixedAlign::position); - let ts = Transform::translate(x, y) - .pre_concat(Transform::rotate(self.angle(styles))) - .pre_concat(Transform::translate(-x, -y)); - frame.transform(ts); - Ok(Fragment::frame(frame)) + let angle = self.angle(styles); + let align = self.origin(styles).resolve(styles); + + // Compute the new region's approximate size. + let size = regions + .base() + .to_point() + .transform_inf(Transform::rotate(angle)) + .map(Abs::abs) + .to_size(); + + measure_and_layout( + engine, + regions.base(), + size, + styles, + self.body(), + Transform::rotate(angle), + align, + self.reflow(styles), + ) } } @@ -133,6 +155,7 @@ impl Layout for RotateElem { /// ```example /// #set align(center) /// #scale(x: -100%)[This is mirrored.] +/// #scale(x: -100%, reflow: true)[This is mirrored.] /// ``` #[elem(Layout)] pub struct ScaleElem { @@ -163,6 +186,18 @@ pub struct ScaleElem { #[default(HAlign::Center + VAlign::Horizon)] pub origin: Align, + /// Whether the scaling impacts the layout. + /// + /// If set to `{false}`, the scaled content will be allowed to overlap + /// other content. If set to `{true}`, it will compute the new size of + /// the scaled content and adjust the layout accordingly. + /// + /// ```example + /// Hello #scale(x: 20%, y: 40%, reflow: true)[World]! + /// ``` + #[default(false)] + pub reflow: bool, + /// The content to scale. #[required] pub body: Content, @@ -176,17 +211,26 @@ impl Layout for ScaleElem { styles: StyleChain, regions: Regions, ) -> SourceResult { - let pod = Regions::one(regions.base(), Axes::splat(false)); - let mut frame = self.body().layout(engine, styles, pod)?.into_frame(); - let Axes { x, y } = self - .origin(styles) - .resolve(styles) - .zip_map(frame.size(), FixedAlign::position); - let transform = Transform::translate(x, y) - .pre_concat(Transform::scale(self.x(styles), self.y(styles))) - .pre_concat(Transform::translate(-x, -y)); - frame.transform(transform); - Ok(Fragment::frame(frame)) + let sx = self.x(styles); + let sy = self.y(styles); + let align = self.origin(styles).resolve(styles); + + // Compute the new region's approximate size. + let size = regions + .base() + .zip_map(Axes::new(sx, sy), |r, s| s.of(r)) + .map(Abs::abs); + + measure_and_layout( + engine, + regions.base(), + size, + styles, + self.body(), + Transform::scale(sx, sy), + align, + self.reflow(styles), + ) } } @@ -314,3 +358,72 @@ impl Default for Transform { Self::identity() } } + +/// Applies a transformation to a frame, reflowing the layout if necessary. +#[allow(clippy::too_many_arguments)] +fn measure_and_layout( + engine: &mut Engine, + base_size: Size, + size: Size, + styles: StyleChain, + body: &Content, + transform: Transform, + align: Axes, + reflow: bool, +) -> SourceResult { + if !reflow { + // Layout the body. + let pod = Regions::one(base_size, Axes::splat(false)); + let mut frame = body.layout(engine, styles, pod)?.into_frame(); + let Axes { x, y } = align.zip_map(frame.size(), FixedAlign::position); + + // Apply the transform. + let ts = Transform::translate(x, y) + .pre_concat(transform) + .pre_concat(Transform::translate(-x, -y)); + frame.transform(ts); + + return Ok(Fragment::frame(frame)); + } + + // Measure the size of the body. + let pod = Regions::one(size, Axes::splat(false)); + let frame = body.measure(engine, styles, pod)?.into_frame(); + + // Actually perform the layout. + let pod = Regions::one(frame.size(), Axes::splat(true)); + let mut frame = body.layout(engine, styles, pod)?.into_frame(); + let Axes { x, y } = align.zip_map(frame.size(), FixedAlign::position); + + // Apply the transform. + let ts = Transform::translate(x, y) + .pre_concat(transform) + .pre_concat(Transform::translate(-x, -y)); + + // Compute the bounding box and offset and wrap in a new frame. + let (offset, size) = compute_bounding_box(&frame, ts); + frame.transform(ts); + frame.translate(offset); + frame.set_size(size); + Ok(Fragment::frame(frame)) +} + +/// Computes the bounding box and offset of a transformed frame. +fn compute_bounding_box(frame: &Frame, ts: Transform) -> (Point, Size) { + let top_left = Point::zero().transform_inf(ts); + let top_right = Point::new(frame.width(), Abs::zero()).transform_inf(ts); + let bottom_left = Point::new(Abs::zero(), frame.height()).transform_inf(ts); + let bottom_right = Point::new(frame.width(), frame.height()).transform_inf(ts); + + // We first compute the new bounding box of the rotated frame. + let min_x = top_left.x.min(top_right.x).min(bottom_left.x).min(bottom_right.x); + let min_y = top_left.y.min(top_right.y).min(bottom_left.y).min(bottom_right.y); + let max_x = top_left.x.max(top_right.x).max(bottom_left.x).max(bottom_right.x); + let max_y = top_left.y.max(top_right.y).max(bottom_left.y).max(bottom_right.y); + + // Then we compute the new size of the frame. + let width = max_x - min_x; + let height = max_y - min_y; + + (Point::new(-min_x, -min_y), Size::new(width.abs(), height.abs())) +} diff --git a/tests/ref/layout/transform-layout.png b/tests/ref/layout/transform-layout.png new file mode 100644 index 0000000000000000000000000000000000000000..576824f065c4c63e2347c67123d9d63d7b611241 GIT binary patch literal 42107 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 literal 0 HcmV?d00001 diff --git a/tests/typ/layout/transform-layout.typ b/tests/typ/layout/transform-layout.typ new file mode 100644 index 000000000..ce6dc9301 --- /dev/null +++ b/tests/typ/layout/transform-layout.typ @@ -0,0 +1,58 @@ +// Test layout transformations + +--- +// Test that rotation impact layout. +#set page(width: 200pt) +#set rotate(reflow: true) + +#let one(angle) = box(fill: aqua, rotate(angle)[Test Text]) +#for angle in range(0, 360, step: 15) { + one(angle * 1deg) +} + +--- +// Test relative sizing in rotated boxes. +#set page(width: 200pt, height: 200pt) +#set text(size: 32pt) +#let rotated(body) = box(rotate( + 90deg, + box(stroke: 0.5pt, height: 20%, clip: true, body) +)) + +#set rotate(reflow: false) +Hello #rotated[World]!\ + +#set rotate(reflow: true) +Hello #rotated[World]! + +--- +// Test that scaling impact layout. +#set page(width: 200pt) +#set text(size: 32pt) +#let scaled(body) = box(scale( + x: 20%, + y: 40%, + body +)) + +#set scale(reflow: false) +Hello #scaled[World]! + +#set scale(reflow: true) +Hello #scaled[World]! + +--- +// Test relative sizing in scaled boxes. +#set page(width: 200pt, height: 200pt) +#set text(size: 32pt) +#let scaled(body) = box(scale( + x: 60%, + y: 40%, + box(stroke: 0.5pt, width: 30%, clip: true, body) +)) + +#set scale(reflow: false) +Hello #scaled[World]!\ + +#set scale(reflow: true) +Hello #scaled[World]!