Pattern improvements

This commit is contained in:
Laurenz 2023-11-29 16:28:30 +01:00
parent aa23198ad9
commit 3c22c9f319
4 changed files with 72 additions and 82 deletions

View File

@ -24,11 +24,11 @@ pub(crate) fn write_patterns(ctx: &mut PdfContext) {
.bbox(Rect::new(
0.0,
0.0,
pattern.size_abs().x.to_pt() as _,
pattern.size_abs().y.to_pt() as _,
pattern.size().x.to_pt() as _,
pattern.size().y.to_pt() as _,
))
.x_step((pattern.size_abs().x + pattern.spacing_abs().x).to_pt() as _)
.y_step((pattern.size_abs().y + pattern.spacing_abs().y).to_pt() as _);
.x_step((pattern.size().x + pattern.spacing().x).to_pt() as _)
.y_step((pattern.size().y + pattern.spacing().y).to_pt() as _);
let mut resources_map = tiling_pattern.resources();

View File

@ -855,8 +855,7 @@ impl<'a> PatternSampler<'a> {
Self {
pixmap,
size: (pattern.size_abs() + pattern.spacing_abs())
* state.pixel_per_pt as f64,
size: (pattern.size() + pattern.spacing()) * state.pixel_per_pt as f64,
transform_to_parent: fill_transform,
pixel_per_pt: state.pixel_per_pt,
}
@ -994,7 +993,7 @@ fn to_sk_paint<'a>(
}
fn render_pattern_frame(state: &State, pattern: &Pattern) -> sk::Pixmap {
let size = pattern.size_abs() + pattern.spacing_abs();
let size = pattern.size() + pattern.spacing();
let mut canvas = sk::Pixmap::new(
(size.x.to_f32() * state.pixel_per_pt).round() as u32,
(size.y.to_f32() * state.pixel_per_pt).round() as u32,
@ -1003,7 +1002,7 @@ fn render_pattern_frame(state: &State, pattern: &Pattern) -> sk::Pixmap {
// Render the pattern into a new canvas.
let ts = sk::Transform::from_scale(state.pixel_per_pt, state.pixel_per_pt);
let temp_state = State::new(pattern.size_abs(), ts, state.pixel_per_pt);
let temp_state = State::new(pattern.size(), ts, state.pixel_per_pt);
render_frame(&mut canvas, temp_state, pattern.frame());
canvas
}

View File

@ -606,7 +606,7 @@ impl SVGRenderer {
}
fn push_pattern(&mut self, pattern: &Pattern, size: Size, ts: Transform) -> Id {
let pattern_size = pattern.size_abs() + pattern.spacing_abs();
let pattern_size = pattern.size() + pattern.spacing();
// Unfortunately due to a limitation of `xmlwriter`, we need to
// render the frame twice: once to allocate all of the resources
// that it needs and once to actually render it.
@ -1031,7 +1031,7 @@ impl SVGRenderer {
for (id, pattern) in
self.patterns.iter().map(|(i, p)| (i, p.clone())).collect::<Vec<_>>()
{
let size = pattern.size_abs() + pattern.spacing_abs();
let size = pattern.size() + pattern.spacing();
self.xml.start_element("pattern");
self.xml.write_attribute("id", &id);
self.xml.write_attribute("width", &size.x.to_pt());

View File

@ -6,8 +6,8 @@ use ecow::{eco_format, EcoString};
use crate::diag::{bail, SourceResult};
use crate::engine::Engine;
use crate::foundations::{func, scope, ty, Content, Repr, Smart, StyleChain};
use crate::layout::{Abs, Axes, Em, Frame, Layout, Length, Regions, Size};
use crate::foundations::{func, repr, scope, ty, Content, Smart, StyleChain};
use crate::layout::{Abs, Axes, Frame, Layout, Length, Regions, Size};
use crate::syntax::{Span, Spanned};
use crate::util::Numeric;
use crate::visualize::RelativeTo;
@ -16,24 +16,20 @@ 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. The pattern is defined by a
/// body and a tile size. The tile size is the size of each cell of the pattern.
/// The body is the content of each cell of the pattern. The pattern is
/// repeated in a grid-like fashion covering the entire area of the element
/// being filled. You can also specify a spacing between the cells of the
/// pattern, which is defined by a horizontal and vertical spacing. The spacing
/// is the distance between the edges of adjacent cells of the pattern. The default
/// spacing is zero.
/// 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 patterng.
///
/// # Examples
///
/// ```example
/// #let pat = pattern(size: (30pt, 30pt))[
/// #place(top + left, line(start: (0%, 0%), end: (100%, 100%), stroke: 1pt))
/// #place(top + left, line(start: (0%, 100%), end: (100%, 0%), stroke: 1pt))
/// #place(line(start: (0%, 0%), end: (100%, 100%)))
/// #place(line(start: (0%, 100%), end: (100%, 0%)))
/// ]
///
/// #rect(fill: pat, width: 100%, height: 100%, stroke: 1pt)
/// #rect(fill: pat, width: 100%, height: 60pt, stroke: 1pt)
/// ```
///
/// Patterns are also supported on text, but only when setting the
@ -46,7 +42,11 @@ use crate::World;
/// #let pat = pattern(
/// size: (30pt, 30pt),
/// relative: "parent",
/// square(size: 30pt, fill: gradient.conic(..color.map.rainbow))
/// square(
/// size: 30pt,
/// fill: gradient
/// .conic(..color.map.rainbow),
/// )
/// )
///
/// #set text(fill: pat)
@ -64,14 +64,22 @@ use crate::World;
/// size: (30pt, 30pt),
/// spacing: (10pt, 10pt),
/// relative: "parent",
/// square(size: 30pt, fill: gradient.conic(..color.map.rainbow))
/// square(
/// size: 30pt,
/// fill: gradient
/// .conic(..color.map.rainbow),
/// ),
/// )
///
/// #rect(width: 100%, height: 100%, fill: pat)
/// #rect(
/// width: 100%,
/// height: 60pt,
/// fill: pat,
/// )
/// ```
///
/// # Relativeness
/// The location of the starting point of the pattern is dependant on the
/// The location of the starting point of the pattern is dependent on the
/// dimensions of a container. This container can either be the shape they
/// are painted on, or the closest surrounding container. This is controlled by
/// the `relative` argument of a pattern constructor. By default, patterns are
@ -87,14 +95,12 @@ use crate::World;
/// [`rotate`]($rotate) will not affect the parent of a gradient, but a
/// [`grid`]($grid) will.
#[ty(scope)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Pattern(Arc<PatternRepr>);
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct Pattern(Arc<Repr>);
/// Internal representation of [`Pattern`].
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct PatternRepr {
/// The body of the pattern
body: Prehashed<Content>,
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
struct Repr {
/// The pattern's rendered content.
frame: Prehashed<Frame>,
/// The pattern's tile size.
@ -113,10 +119,17 @@ impl Pattern {
/// #let pat = pattern(
/// size: (20pt, 20pt),
/// relative: "parent",
/// place(dx: 5pt, dy: 5pt, rotate(45deg, square(size: 5pt, fill: black)))
/// place(
/// dx: 5pt,
/// dy: 5pt,
/// rotate(45deg, square(
/// size: 5pt,
/// fill: black,
/// )),
/// ),
/// )
///
/// #rect(width: 100%, height: 100%, fill: pat)
/// #rect(width: 100%, height: 60pt, fill: pat)
/// ```
#[func(constructor)]
pub fn construct(
@ -192,38 +205,13 @@ impl Pattern {
frame.set_size(size);
}
Ok(Self(Arc::new(PatternRepr {
Ok(Self(Arc::new(Repr {
size: frame.size(),
body: Prehashed::new(body),
frame: Prehashed::new(frame),
spacing: spacing.v.map(|l| l.abs),
relative,
})))
}
/// Returns the content of an individual tile of the pattern.
#[func]
pub fn body(&self) -> Content {
self.0.body.clone().into_inner()
}
/// Returns the size of an individual tile of the pattern.
#[func]
pub fn size(&self) -> Axes<Length> {
self.0.size.map(|l| Length { abs: l, em: Em::zero() })
}
/// Returns the spacing between tiles of the pattern.
#[func]
pub fn spacing(&self) -> Axes<Length> {
self.0.spacing.map(|l| Length { abs: l, em: Em::zero() })
}
/// Returns the relative placement of the pattern.
#[func]
pub fn relative(&self) -> Smart<RelativeTo> {
self.0.relative
}
}
impl Pattern {
@ -232,7 +220,7 @@ impl Pattern {
if let Some(this) = Arc::get_mut(&mut self.0) {
this.relative = Smart::Custom(relative);
} else {
self.0 = Arc::new(PatternRepr {
self.0 = Arc::new(Repr {
relative: Smart::Custom(relative),
..self.0.as_ref().clone()
});
@ -241,6 +229,26 @@ impl Pattern {
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<RelativeTo> {
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(|| {
@ -251,29 +259,14 @@ impl Pattern {
}
})
}
/// Return the size of the pattern in absolute units.
pub fn size_abs(&self) -> Size {
self.0.size
}
/// Return the spacing of the pattern in absolute units.
pub fn spacing_abs(&self) -> Size {
self.0.spacing
}
/// Return the frame of the pattern.
pub fn frame(&self) -> &Frame {
&self.0.frame
}
}
impl Repr for Pattern {
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.spacing() != Axes::splat(Length::zero()) {
if self.0.spacing.is_zero() {
out.push_str(", spacing: (");
out.push_str(&self.0.spacing.x.repr());
out.push_str(", ");
@ -281,9 +274,7 @@ impl Repr for Pattern {
out.push(')');
}
out.push_str(", ");
out.push_str(&self.0.body.repr());
out.push(')');
out.push_str(", ..)");
out
}