diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-05-03 23:56:57 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-05-03 23:56:57 +0200 |
| commit | aa10ea8470763afe98d5ff558381f0a0beb0c017 (patch) | |
| tree | 938f58a8e0faa4f5216920fc3e82d86301d2f094 /src/library | |
| parent | f9e115daf54c29358f890b137f50a33a781af680 (diff) | |
| parent | 51d0de09c6f7e2af4db3b65c3fe9595c501b82c9 (diff) | |
Merge pull request #69 from typst/shapeees
Advanced shapes
Diffstat (limited to 'src/library')
| -rw-r--r-- | src/library/graphics/shape.rs | 83 | ||||
| -rw-r--r-- | src/library/layout/page.rs | 29 |
2 files changed, 74 insertions, 38 deletions
diff --git a/src/library/graphics/shape.rs b/src/library/graphics/shape.rs index 49c74c2f..40b6e1e3 100644 --- a/src/library/graphics/shape.rs +++ b/src/library/graphics/shape.rs @@ -25,9 +25,19 @@ impl<const S: ShapeKind> ShapeNode<S> { pub const FILL: Option<Paint> = None; /// How to stroke the shape. #[property(resolve, fold)] - pub const STROKE: Smart<Option<RawStroke>> = Smart::Auto; + pub const STROKE: Smart<Sides<Option<RawStroke>>> = Smart::Auto; + /// How much to pad the shape's content. - pub const PADDING: Relative<RawLength> = Relative::zero(); + #[property(resolve, fold)] + pub const INSET: Sides<Option<Relative<RawLength>>> = Sides::splat(Relative::zero()); + + /// How much to extend the shape's dimensions beyond the allocated space. + #[property(resolve, fold)] + pub const OUTSET: Sides<Option<Relative<RawLength>>> = Sides::splat(Relative::zero()); + + /// How much to round the shape's corners. + #[property(resolve, fold)] + pub const RADIUS: Sides<Option<Relative<RawLength>>> = Sides::splat(Relative::zero()); fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> { let size = match S { @@ -50,6 +60,30 @@ impl<const S: ShapeKind> ShapeNode<S> { Self(args.find()?).pack().sized(Spec::new(width, height)), )) } + + fn set(args: &mut Args) -> TypResult<StyleMap> { + let mut styles = StyleMap::new(); + styles.set_opt(Self::FILL, args.named("fill")?); + + if is_round(S) { + styles.set_opt( + Self::STROKE, + args.named::<Smart<Option<RawStroke>>>("stroke")? + .map(|some| some.map(Sides::splat)), + ); + } else { + styles.set_opt(Self::STROKE, args.named("stroke")?); + } + + styles.set_opt(Self::INSET, args.named("inset")?); + styles.set_opt(Self::OUTSET, args.named("outset")?); + + if !is_round(S) { + styles.set_opt(Self::RADIUS, args.named("radius")?); + } + + Ok(styles) + } } impl<const S: ShapeKind> Layout for ShapeNode<S> { @@ -61,13 +95,13 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> { ) -> TypResult<Vec<Arc<Frame>>> { let mut frames; if let Some(child) = &self.0 { - let mut padding = styles.get(Self::PADDING); + let mut inset = styles.get(Self::INSET); if is_round(S) { - padding.rel += Ratio::new(0.5 - SQRT_2 / 4.0); + inset = inset.map(|side| side + Ratio::new(0.5 - SQRT_2 / 4.0)); } // Pad the child. - let child = child.clone().padded(Sides::splat(padding)); + let child = child.clone().padded(inset.map(|side| side.map(RawLength::from))); let mut pod = Regions::one(regions.first, regions.base, regions.expand); frames = child.layout(ctx, &pod, styles)?; @@ -114,19 +148,38 @@ impl<const S: ShapeKind> Layout for ShapeNode<S> { // Add fill and/or stroke. let fill = styles.get(Self::FILL); let stroke = match styles.get(Self::STROKE) { - Smart::Auto => fill.is_none().then(Stroke::default), - Smart::Custom(stroke) => stroke.map(RawStroke::unwrap_or_default), + Smart::Auto if fill.is_none() => Sides::splat(Some(Stroke::default())), + Smart::Auto => Sides::splat(None), + Smart::Custom(strokes) => { + strokes.map(|s| s.map(RawStroke::unwrap_or_default)) + } }; - if fill.is_some() || stroke.is_some() { - let geometry = if is_round(S) { - Geometry::Ellipse(frame.size) - } else { - Geometry::Rect(frame.size) - }; + let outset = styles.get(Self::OUTSET).relative_to(frame.size); + let size = frame.size + outset.sum_by_axis(); + + let radius = styles + .get(Self::RADIUS) + .map(|side| side.relative_to(size.x.min(size.y) / 2.0)); - let shape = Shape { geometry, fill, stroke }; - frame.prepend(Point::zero(), Element::Shape(shape)); + let pos = Point::new(-outset.left, -outset.top); + + if fill.is_some() || stroke.iter().any(Option::is_some) { + if is_round(S) { + let shape = Shape { + geometry: Geometry::Ellipse(size), + fill, + stroke: stroke.left, + }; + frame.prepend(pos, Element::Shape(shape)); + } else { + frame.prepend_multiple( + Rect::new(size, radius) + .shapes(fill, stroke) + .into_iter() + .map(|x| (pos, Element::Shape(x))), + ) + } } // Apply link if it exists. diff --git a/src/library/layout/page.rs b/src/library/layout/page.rs index 4307d2f9..c8495e64 100644 --- a/src/library/layout/page.rs +++ b/src/library/layout/page.rs @@ -18,14 +18,10 @@ impl PageNode { /// Whether the page is flipped into landscape orientation. pub const FLIPPED: bool = false; - /// The left margin. - pub const LEFT: Smart<Relative<RawLength>> = Smart::Auto; - /// The right margin. - pub const RIGHT: Smart<Relative<RawLength>> = Smart::Auto; - /// The top margin. - pub const TOP: Smart<Relative<RawLength>> = Smart::Auto; - /// The bottom margin. - pub const BOTTOM: Smart<Relative<RawLength>> = Smart::Auto; + /// The page margin. + #[property(fold)] + pub const MARGINS: Sides<Option<Smart<Relative<RawLength>>>> = + Sides::splat(Smart::Auto); /// How many columns the page has. pub const COLUMNS: NonZeroUsize = NonZeroUsize::new(1).unwrap(); @@ -53,15 +49,7 @@ impl PageNode { styles.set_opt(Self::WIDTH, args.named("width")?); styles.set_opt(Self::HEIGHT, args.named("height")?); - - let all = args.named("margins")?; - let hor = args.named("horizontal")?; - let ver = args.named("vertical")?; - styles.set_opt(Self::LEFT, args.named("left")?.or(hor).or(all)); - styles.set_opt(Self::TOP, args.named("top")?.or(ver).or(all)); - styles.set_opt(Self::RIGHT, args.named("right")?.or(hor).or(all)); - styles.set_opt(Self::BOTTOM, args.named("bottom")?.or(ver).or(all)); - + styles.set_opt(Self::MARGINS, args.named("margins")?); styles.set_opt(Self::FLIPPED, args.named("flipped")?); styles.set_opt(Self::FILL, args.named("fill")?); styles.set_opt(Self::COLUMNS, args.named("columns")?); @@ -96,12 +84,7 @@ impl PageNode { // Determine the margins. let default = Relative::from(0.1190 * min); - let padding = Sides { - left: styles.get(Self::LEFT).unwrap_or(default), - right: styles.get(Self::RIGHT).unwrap_or(default), - top: styles.get(Self::TOP).unwrap_or(default), - bottom: styles.get(Self::BOTTOM).unwrap_or(default), - }; + let padding = styles.get(Self::MARGINS).map(|side| side.unwrap_or(default)); let mut child = self.0.clone(); |
