summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-05-03 23:56:57 +0200
committerGitHub <noreply@github.com>2022-05-03 23:56:57 +0200
commitaa10ea8470763afe98d5ff558381f0a0beb0c017 (patch)
tree938f58a8e0faa4f5216920fc3e82d86301d2f094 /src/library
parentf9e115daf54c29358f890b137f50a33a781af680 (diff)
parent51d0de09c6f7e2af4db3b65c3fe9595c501b82c9 (diff)
Merge pull request #69 from typst/shapeees
Advanced shapes
Diffstat (limited to 'src/library')
-rw-r--r--src/library/graphics/shape.rs83
-rw-r--r--src/library/layout/page.rs29
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();