From 134638525516995d5947c5b3f98ffbc13784a143 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 17 Dec 2024 10:25:27 +0100 Subject: Rename `pattern` to `tiling` (#5590) --- crates/typst-library/src/visualize/mod.rs | 11 +- crates/typst-library/src/visualize/paint.rs | 28 +-- crates/typst-library/src/visualize/pattern.rs | 285 ------------------------- crates/typst-library/src/visualize/stroke.rs | 26 +-- crates/typst-library/src/visualize/tiling.rs | 289 ++++++++++++++++++++++++++ 5 files changed, 321 insertions(+), 318 deletions(-) delete mode 100644 crates/typst-library/src/visualize/pattern.rs create mode 100644 crates/typst-library/src/visualize/tiling.rs (limited to 'crates/typst-library/src/visualize') diff --git a/crates/typst-library/src/visualize/mod.rs b/crates/typst-library/src/visualize/mod.rs index 5c8bf646..61c56a61 100644 --- a/crates/typst-library/src/visualize/mod.rs +++ b/crates/typst-library/src/visualize/mod.rs @@ -6,10 +6,10 @@ mod image; mod line; mod paint; mod path; -mod pattern; mod polygon; mod shape; mod stroke; +mod tiling; pub use self::color::*; pub use self::gradient::*; @@ -17,12 +17,12 @@ pub use self::image::*; pub use self::line::*; pub use self::paint::*; pub use self::path::*; -pub use self::pattern::*; pub use self::polygon::*; pub use self::shape::*; pub use self::stroke::*; +pub use self::tiling::*; -use crate::foundations::{category, Category, Scope}; +use crate::foundations::{category, Category, Scope, Type}; /// Drawing and data visualization. /// @@ -37,7 +37,7 @@ pub(super) fn define(global: &mut Scope) { global.category(VISUALIZE); global.define_type::(); global.define_type::(); - global.define_type::(); + global.define_type::(); global.define_type::(); global.define_elem::(); global.define_elem::(); @@ -47,4 +47,7 @@ pub(super) fn define(global: &mut Scope) { global.define_elem::(); global.define_elem::(); global.define_elem::(); + + // Compatibility. + global.define("pattern", Type::of::()); } diff --git a/crates/typst-library/src/visualize/paint.rs b/crates/typst-library/src/visualize/paint.rs index cd1006aa..a618e515 100644 --- a/crates/typst-library/src/visualize/paint.rs +++ b/crates/typst-library/src/visualize/paint.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Debug, Formatter}; use ecow::EcoString; use crate::foundations::{cast, Repr, Smart}; -use crate::visualize::{Color, Gradient, Pattern, RelativeTo}; +use crate::visualize::{Color, Gradient, RelativeTo, Tiling}; /// How a fill or stroke should be painted. #[derive(Clone, Eq, PartialEq, Hash)] @@ -12,8 +12,8 @@ pub enum Paint { Solid(Color), /// A gradient. Gradient(Gradient), - /// A pattern. - Pattern(Pattern), + /// A tiling. + Tiling(Tiling), } impl Paint { @@ -21,7 +21,7 @@ impl Paint { pub fn unwrap_solid(&self) -> Color { match self { Self::Solid(color) => *color, - Self::Gradient(_) | Self::Pattern(_) => panic!("expected solid color"), + Self::Gradient(_) | Self::Tiling(_) => panic!("expected solid color"), } } @@ -30,7 +30,7 @@ impl Paint { match self { Self::Solid(_) => Smart::Auto, Self::Gradient(gradient) => gradient.relative(), - Self::Pattern(pattern) => pattern.relative(), + Self::Tiling(tiling) => tiling.relative(), } } @@ -44,8 +44,8 @@ impl Paint { Self::Gradient(gradient) => { Self::Gradient(gradient.clone().with_relative(RelativeTo::Parent)) } - Self::Pattern(pattern) => { - Self::Pattern(pattern.clone().with_relative(RelativeTo::Parent)) + Self::Tiling(tiling) => { + Self::Tiling(tiling.clone().with_relative(RelativeTo::Parent)) } } } @@ -56,14 +56,14 @@ impl Debug for Paint { match self { Self::Solid(v) => v.fmt(f), Self::Gradient(v) => v.fmt(f), - Self::Pattern(v) => v.fmt(f), + Self::Tiling(v) => v.fmt(f), } } } -impl From for Paint { - fn from(pattern: Pattern) -> Self { - Self::Pattern(pattern) +impl From for Paint { + fn from(tiling: Tiling) -> Self { + Self::Tiling(tiling) } } @@ -72,7 +72,7 @@ impl Repr for Paint { match self { Self::Solid(color) => color.repr(), Self::Gradient(gradient) => gradient.repr(), - Self::Pattern(pattern) => pattern.repr(), + Self::Tiling(tiling) => tiling.repr(), } } } @@ -94,9 +94,9 @@ cast! { self => match self { Self::Solid(color) => color.into_value(), Self::Gradient(gradient) => gradient.into_value(), - Self::Pattern(pattern) => pattern.into_value(), + Self::Tiling(tiling) => tiling.into_value(), }, color: Color => Self::Solid(color), gradient: Gradient => Self::Gradient(gradient), - pattern: Pattern => Self::Pattern(pattern), + tiling: Tiling => Self::Tiling(tiling), } diff --git a/crates/typst-library/src/visualize/pattern.rs b/crates/typst-library/src/visualize/pattern.rs deleted file mode 100644 index 2017ea65..00000000 --- a/crates/typst-library/src/visualize/pattern.rs +++ /dev/null @@ -1,285 +0,0 @@ -use std::hash::Hash; -use std::sync::Arc; - -use ecow::{eco_format, EcoString}; -use typst_syntax::{Span, Spanned}; -use typst_utils::{LazyHash, Numeric}; - -use crate::diag::{bail, SourceResult}; -use crate::engine::Engine; -use crate::foundations::{func, repr, scope, ty, Content, Smart, StyleChain}; -use crate::introspection::Locator; -use crate::layout::{Abs, Axes, Frame, Length, Region, Size}; -use crate::visualize::RelativeTo; -use crate::World; - -/// A repeating pattern fill. -/// -/// Typst supports the most common pattern type of tiled patterns, where a -/// pattern is repeated in a grid-like fashion, covering the entire area of an -/// element that is filled or stroked. The pattern is defined by a tile size and -/// a body defining the content of each cell. You can also add horizontal or -/// vertical spacing between the cells of the pattern. -/// -/// # Examples -/// -/// ```example -/// #let pat = pattern(size: (30pt, 30pt))[ -/// #place(line(start: (0%, 0%), end: (100%, 100%))) -/// #place(line(start: (0%, 100%), end: (100%, 0%))) -/// ] -/// -/// #rect(fill: pat, width: 100%, height: 60pt, stroke: 1pt) -/// ``` -/// -/// Patterns are also supported on text, but only when setting the -/// [relativeness]($pattern.relative) to either `{auto}` (the default value) or -/// `{"parent"}`. To create word-by-word or glyph-by-glyph patterns, you can -/// wrap the words or characters of your text in [boxes]($box) manually or -/// through a [show rule]($styling/#show-rules). -/// -/// ```example -/// #let pat = pattern( -/// size: (30pt, 30pt), -/// relative: "parent", -/// square( -/// size: 30pt, -/// fill: gradient -/// .conic(..color.map.rainbow), -/// ) -/// ) -/// -/// #set text(fill: pat) -/// #lorem(10) -/// ``` -/// -/// You can also space the elements further or closer apart using the -/// [`spacing`]($pattern.spacing) feature of the pattern. If the spacing -/// is lower than the size of the pattern, the pattern will overlap. -/// If it is higher, the pattern will have gaps of the same color as the -/// background of the pattern. -/// -/// ```example -/// #let pat = pattern( -/// size: (30pt, 30pt), -/// spacing: (10pt, 10pt), -/// relative: "parent", -/// square( -/// size: 30pt, -/// fill: gradient -/// .conic(..color.map.rainbow), -/// ), -/// ) -/// -/// #rect( -/// width: 100%, -/// height: 60pt, -/// fill: pat, -/// ) -/// ``` -/// -/// # Relativeness -/// The location of the starting point of the pattern is dependent on the -/// dimensions of a container. This container can either be the shape that it is -/// being painted on, or the closest surrounding container. This is controlled -/// by the `relative` argument of a pattern constructor. By default, patterns -/// are relative to the shape they are being painted on, unless the pattern is -/// applied on text, in which case they are relative to the closest ancestor -/// container. -/// -/// Typst determines the ancestor container as follows: -/// - For shapes that are placed at the root/top level of the document, the -/// closest ancestor is the page itself. -/// - For other shapes, the ancestor is the innermost [`block`] or [`box`] that -/// contains the shape. This includes the boxes and blocks that are implicitly -/// created by show rules and elements. For example, a [`rotate`] will not -/// affect the parent of a gradient, but a [`grid`] will. -#[ty(scope, cast)] -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct Pattern(Arc); - -/// Internal representation of [`Pattern`]. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -struct Repr { - /// The pattern's rendered content. - frame: LazyHash, - /// The pattern's tile size. - size: Size, - /// The pattern's tile spacing. - spacing: Size, - /// The pattern's relative transform. - relative: Smart, -} - -#[scope] -impl Pattern { - /// Construct a new pattern. - /// - /// ```example - /// #let pat = pattern( - /// size: (20pt, 20pt), - /// relative: "parent", - /// place( - /// dx: 5pt, - /// dy: 5pt, - /// rotate(45deg, square( - /// size: 5pt, - /// fill: black, - /// )), - /// ), - /// ) - /// - /// #rect(width: 100%, height: 60pt, fill: pat) - /// ``` - #[func(constructor)] - pub fn construct( - engine: &mut Engine, - /// The callsite span. - span: Span, - /// The bounding box of each cell of the pattern. - #[named] - #[default(Spanned::new(Smart::Auto, Span::detached()))] - size: Spanned>>, - /// The spacing between cells of the pattern. - #[named] - #[default(Spanned::new(Axes::splat(Length::zero()), Span::detached()))] - spacing: Spanned>, - /// The [relative placement](#relativeness) of the pattern. - /// - /// For an element placed at the root/top level of the document, the - /// parent is the page itself. For other elements, the parent is the - /// innermost block, box, column, grid, or stack that contains the - /// element. - #[named] - #[default(Smart::Auto)] - relative: Smart, - /// The content of each cell of the pattern. - body: Content, - ) -> SourceResult { - let size_span = size.span; - if let Smart::Custom(size) = size.v { - // Ensure that sizes are absolute. - if !size.x.em.is_zero() || !size.y.em.is_zero() { - bail!(size_span, "pattern tile size must be absolute"); - } - - // Ensure that sizes are non-zero and finite. - if size.x.is_zero() - || size.y.is_zero() - || !size.x.is_finite() - || !size.y.is_finite() - { - bail!(size_span, "pattern tile size must be non-zero and non-infinite"); - } - } - - // Ensure that spacing is absolute. - if !spacing.v.x.em.is_zero() || !spacing.v.y.em.is_zero() { - bail!(spacing.span, "pattern tile spacing must be absolute"); - } - - // Ensure that spacing is finite. - if !spacing.v.x.is_finite() || !spacing.v.y.is_finite() { - bail!(spacing.span, "pattern tile spacing must be finite"); - } - - // The size of the frame - let size = size.v.map(|l| l.map(|a| a.abs)); - let region = size.unwrap_or_else(|| Axes::splat(Abs::inf())); - - // Layout the pattern. - let world = engine.world; - let library = world.library(); - let locator = Locator::root(); - let styles = StyleChain::new(&library.styles); - let pod = Region::new(region, Axes::splat(false)); - let mut frame = - (engine.routines.layout_frame)(engine, &body, locator, styles, pod)?; - - // Set the size of the frame if the size is enforced. - if let Smart::Custom(size) = size { - frame.set_size(size); - } - - // Check that the frame is non-zero. - if frame.width().is_zero() || frame.height().is_zero() { - bail!( - span, "pattern tile size must be non-zero"; - hint: "try setting the size manually" - ); - } - - Ok(Self(Arc::new(Repr { - size: frame.size(), - frame: LazyHash::new(frame), - spacing: spacing.v.map(|l| l.abs), - relative, - }))) - } -} - -impl Pattern { - /// Set the relative placement of the pattern. - pub fn with_relative(mut self, relative: RelativeTo) -> Self { - if let Some(this) = Arc::get_mut(&mut self.0) { - this.relative = Smart::Custom(relative); - } else { - self.0 = Arc::new(Repr { - relative: Smart::Custom(relative), - ..self.0.as_ref().clone() - }); - } - - self - } - - /// Return the frame of the pattern. - pub fn frame(&self) -> &Frame { - &self.0.frame - } - - /// Return the size of the pattern in absolute units. - pub fn size(&self) -> Size { - self.0.size - } - - /// Return the spacing of the pattern in absolute units. - pub fn spacing(&self) -> Size { - self.0.spacing - } - - /// Returns the relative placement of the pattern. - pub fn relative(&self) -> Smart { - self.0.relative - } - - /// Returns the relative placement of the pattern. - pub fn unwrap_relative(&self, on_text: bool) -> RelativeTo { - self.0.relative.unwrap_or_else(|| { - if on_text { - RelativeTo::Parent - } else { - RelativeTo::Self_ - } - }) - } -} - -impl repr::Repr for Pattern { - fn repr(&self) -> EcoString { - let mut out = - eco_format!("pattern(({}, {})", self.0.size.x.repr(), self.0.size.y.repr()); - - if self.0.spacing.is_zero() { - out.push_str(", spacing: ("); - out.push_str(&self.0.spacing.x.repr()); - out.push_str(", "); - out.push_str(&self.0.spacing.y.repr()); - out.push(')'); - } - - out.push_str(", ..)"); - - out - } -} diff --git a/crates/typst-library/src/visualize/stroke.rs b/crates/typst-library/src/visualize/stroke.rs index 4ca10920..2ab493a5 100644 --- a/crates/typst-library/src/visualize/stroke.rs +++ b/crates/typst-library/src/visualize/stroke.rs @@ -7,7 +7,7 @@ use crate::foundations::{ Resolve, Smart, StyleChain, Value, }; use crate::layout::{Abs, Length}; -use crate::visualize::{Color, Gradient, Paint, Pattern}; +use crate::visualize::{Color, Gradient, Paint, Tiling}; /// Defines how to draw a line. /// @@ -213,9 +213,9 @@ impl Stroke { thickness: self.thickness.map(&f), cap: self.cap, join: self.join, - dash: self.dash.map(|pattern| { - pattern.map(|pattern| DashPattern { - array: pattern + dash: self.dash.map(|dash| { + dash.map(|dash| DashPattern { + array: dash .array .into_iter() .map(|l| match l { @@ -223,7 +223,7 @@ impl Stroke { DashLength::LineWidth => DashLength::LineWidth, }) .collect(), - phase: f(pattern.phase), + phase: f(dash.phase), }) }), miter_limit: self.miter_limit, @@ -237,14 +237,10 @@ impl Stroke { let thickness = self.thickness.unwrap_or(default.thickness); let dash = self .dash - .map(|pattern| { - pattern.map(|pattern| DashPattern { - array: pattern - .array - .into_iter() - .map(|l| l.finish(thickness)) - .collect(), - phase: pattern.phase, + .map(|dash| { + dash.map(|dash| DashPattern { + array: dash.array.into_iter().map(|l| l.finish(thickness)).collect(), + phase: dash.phase, }) }) .unwrap_or(default.dash); @@ -372,8 +368,8 @@ cast! { paint: Smart::Custom(gradient.into()), ..Default::default() }, - pattern: Pattern => Self { - paint: Smart::Custom(pattern.into()), + tiling: Tiling => Self { + paint: Smart::Custom(tiling.into()), ..Default::default() }, mut dict: Dict => { diff --git a/crates/typst-library/src/visualize/tiling.rs b/crates/typst-library/src/visualize/tiling.rs new file mode 100644 index 00000000..d699d3b6 --- /dev/null +++ b/crates/typst-library/src/visualize/tiling.rs @@ -0,0 +1,289 @@ +use std::hash::Hash; +use std::sync::Arc; + +use ecow::{eco_format, EcoString}; +use typst_syntax::{Span, Spanned}; +use typst_utils::{LazyHash, Numeric}; + +use crate::diag::{bail, SourceResult}; +use crate::engine::Engine; +use crate::foundations::{func, repr, scope, ty, Content, Smart, StyleChain}; +use crate::introspection::Locator; +use crate::layout::{Abs, Axes, Frame, Length, Region, Size}; +use crate::visualize::RelativeTo; +use crate::World; + +/// A repeating tiling fill. +/// +/// Typst supports the most common type of tilings, where a pattern is repeated +/// in a grid-like fashion, covering the entire area of an element that is +/// filled or stroked. The pattern is defined by a tile size and a body defining +/// the content of each cell. You can also add horizontal or vertical spacing +/// between the cells of the tiling. +/// +/// # Examples +/// +/// ```example +/// #let pat = tiling(size: (30pt, 30pt))[ +/// #place(line(start: (0%, 0%), end: (100%, 100%))) +/// #place(line(start: (0%, 100%), end: (100%, 0%))) +/// ] +/// +/// #rect(fill: pat, width: 100%, height: 60pt, stroke: 1pt) +/// ``` +/// +/// Tilings are also supported on text, but only when setting the +/// [relativeness]($tiling.relative) to either `{auto}` (the default value) or +/// `{"parent"}`. To create word-by-word or glyph-by-glyph tilings, you can +/// wrap the words or characters of your text in [boxes]($box) manually or +/// through a [show rule]($styling/#show-rules). +/// +/// ```example +/// #let pat = tiling( +/// size: (30pt, 30pt), +/// relative: "parent", +/// square( +/// size: 30pt, +/// fill: gradient +/// .conic(..color.map.rainbow), +/// ) +/// ) +/// +/// #set text(fill: pat) +/// #lorem(10) +/// ``` +/// +/// You can also space the elements further or closer apart using the +/// [`spacing`]($tiling.spacing) feature of the tiling. If the spacing +/// is lower than the size of the tiling, the tiling will overlap. +/// If it is higher, the tiling will have gaps of the same color as the +/// background of the tiling. +/// +/// ```example +/// #let pat = tiling( +/// size: (30pt, 30pt), +/// spacing: (10pt, 10pt), +/// relative: "parent", +/// square( +/// size: 30pt, +/// fill: gradient +/// .conic(..color.map.rainbow), +/// ), +/// ) +/// +/// #rect( +/// width: 100%, +/// height: 60pt, +/// fill: pat, +/// ) +/// ``` +/// +/// # Relativeness +/// The location of the starting point of the tiling is dependent on the +/// dimensions of a container. This container can either be the shape that it is +/// being painted on, or the closest surrounding container. This is controlled +/// by the `relative` argument of a tiling constructor. By default, tilings +/// are relative to the shape they are being painted on, unless the tiling is +/// applied on text, in which case they are relative to the closest ancestor +/// container. +/// +/// Typst determines the ancestor container as follows: +/// - For shapes that are placed at the root/top level of the document, the +/// closest ancestor is the page itself. +/// - For other shapes, the ancestor is the innermost [`block`] or [`box`] that +/// contains the shape. This includes the boxes and blocks that are implicitly +/// created by show rules and elements. For example, a [`rotate`] will not +/// affect the parent of a gradient, but a [`grid`] will. +/// +/// # Compatibility +/// This type used to be called `pattern`. The name remains as an alias, but is +/// deprecated since Typst 0.13. +#[ty(scope, cast, keywords = ["pattern"])] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct Tiling(Arc); + +/// Internal representation of [`Tiling`]. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +struct Repr { + /// The tiling's rendered content. + frame: LazyHash, + /// The tiling's tile size. + size: Size, + /// The tiling's tile spacing. + spacing: Size, + /// The tiling's relative transform. + relative: Smart, +} + +#[scope] +impl Tiling { + /// Construct a new tiling. + /// + /// ```example + /// #let pat = tiling( + /// size: (20pt, 20pt), + /// relative: "parent", + /// place( + /// dx: 5pt, + /// dy: 5pt, + /// rotate(45deg, square( + /// size: 5pt, + /// fill: black, + /// )), + /// ), + /// ) + /// + /// #rect(width: 100%, height: 60pt, fill: pat) + /// ``` + #[func(constructor)] + pub fn construct( + engine: &mut Engine, + /// The callsite span. + span: Span, + /// The bounding box of each cell of the tiling. + #[named] + #[default(Spanned::new(Smart::Auto, Span::detached()))] + size: Spanned>>, + /// The spacing between cells of the tiling. + #[named] + #[default(Spanned::new(Axes::splat(Length::zero()), Span::detached()))] + spacing: Spanned>, + /// The [relative placement](#relativeness) of the tiling. + /// + /// For an element placed at the root/top level of the document, the + /// parent is the page itself. For other elements, the parent is the + /// innermost block, box, column, grid, or stack that contains the + /// element. + #[named] + #[default(Smart::Auto)] + relative: Smart, + /// The content of each cell of the tiling. + body: Content, + ) -> SourceResult { + let size_span = size.span; + if let Smart::Custom(size) = size.v { + // Ensure that sizes are absolute. + if !size.x.em.is_zero() || !size.y.em.is_zero() { + bail!(size_span, "tile size must be absolute"); + } + + // Ensure that sizes are non-zero and finite. + if size.x.is_zero() + || size.y.is_zero() + || !size.x.is_finite() + || !size.y.is_finite() + { + bail!(size_span, "tile size must be non-zero and non-infinite"); + } + } + + // Ensure that spacing is absolute. + if !spacing.v.x.em.is_zero() || !spacing.v.y.em.is_zero() { + bail!(spacing.span, "tile spacing must be absolute"); + } + + // Ensure that spacing is finite. + if !spacing.v.x.is_finite() || !spacing.v.y.is_finite() { + bail!(spacing.span, "tile spacing must be finite"); + } + + // The size of the frame + let size = size.v.map(|l| l.map(|a| a.abs)); + let region = size.unwrap_or_else(|| Axes::splat(Abs::inf())); + + // Layout the tiling. + let world = engine.world; + let library = world.library(); + let locator = Locator::root(); + let styles = StyleChain::new(&library.styles); + let pod = Region::new(region, Axes::splat(false)); + let mut frame = + (engine.routines.layout_frame)(engine, &body, locator, styles, pod)?; + + // Set the size of the frame if the size is enforced. + if let Smart::Custom(size) = size { + frame.set_size(size); + } + + // Check that the frame is non-zero. + if frame.width().is_zero() || frame.height().is_zero() { + bail!( + span, "tile size must be non-zero"; + hint: "try setting the size manually" + ); + } + + Ok(Self(Arc::new(Repr { + size: frame.size(), + frame: LazyHash::new(frame), + spacing: spacing.v.map(|l| l.abs), + relative, + }))) + } +} + +impl Tiling { + /// Set the relative placement of the tiling. + pub fn with_relative(mut self, relative: RelativeTo) -> Self { + if let Some(this) = Arc::get_mut(&mut self.0) { + this.relative = Smart::Custom(relative); + } else { + self.0 = Arc::new(Repr { + relative: Smart::Custom(relative), + ..self.0.as_ref().clone() + }); + } + + self + } + + /// Return the frame of the tiling. + pub fn frame(&self) -> &Frame { + &self.0.frame + } + + /// Return the size of the tiling in absolute units. + pub fn size(&self) -> Size { + self.0.size + } + + /// Return the spacing of the tiling in absolute units. + pub fn spacing(&self) -> Size { + self.0.spacing + } + + /// Returns the relative placement of the tiling. + pub fn relative(&self) -> Smart { + self.0.relative + } + + /// Returns the relative placement of the tiling. + pub fn unwrap_relative(&self, on_text: bool) -> RelativeTo { + self.0.relative.unwrap_or_else(|| { + if on_text { + RelativeTo::Parent + } else { + RelativeTo::Self_ + } + }) + } +} + +impl repr::Repr for Tiling { + fn repr(&self) -> EcoString { + let mut out = + eco_format!("tiling(({}, {})", self.0.size.x.repr(), self.0.size.y.repr()); + + if self.0.spacing.is_zero() { + out.push_str(", spacing: ("); + out.push_str(&self.0.spacing.x.repr()); + out.push_str(", "); + out.push_str(&self.0.spacing.y.repr()); + out.push(')'); + } + + out.push_str(", ..)"); + + out + } +} -- cgit v1.2.3