summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/visualize
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-09-11 14:40:22 +0200
committerLaurenz <laurmaedje@gmail.com>2023-09-11 14:40:22 +0200
commitb471ac7d590abd2398ce25193b4e4df373bf2e9c (patch)
treeb5f7a6fdc807ee3340a4f42b0ad3cc563fe45429 /crates/typst-library/src/visualize
parent8f36fca68447a5d42a3d54b5fac7e5546ee244be (diff)
First-class types
Makes types first-class values.
Diffstat (limited to 'crates/typst-library/src/visualize')
-rw-r--r--crates/typst-library/src/visualize/image.rs119
-rw-r--r--crates/typst-library/src/visualize/line.rs52
-rw-r--r--crates/typst-library/src/visualize/mod.rs37
-rw-r--r--crates/typst-library/src/visualize/path.rs39
-rw-r--r--crates/typst-library/src/visualize/polygon.rs176
-rw-r--r--crates/typst-library/src/visualize/shape.rs106
6 files changed, 217 insertions, 312 deletions
diff --git a/crates/typst-library/src/visualize/image.rs b/crates/typst-library/src/visualize/image.rs
index a06509dd..e6269198 100644
--- a/crates/typst-library/src/visualize/image.rs
+++ b/crates/typst-library/src/visualize/image.rs
@@ -18,7 +18,7 @@ use crate::text::families;
/// in the resulting PDF. Make sure to double-check embedded SVG images. If you
/// have an issue, also feel free to report it on [GitHub][gh-svg].
///
-/// ## Example { #example }
+/// # Example
/// ```example
/// #figure(
/// image("molecular.jpg", width: 80%),
@@ -30,14 +30,7 @@ use crate::text::families;
/// ```
///
/// [gh-svg]: https://github.com/typst/typst/issues?q=is%3Aopen+is%3Aissue+label%3Asvg
-///
-/// Display: Image
-/// Category: visualize
-#[element(Layout, LocalName, Figurable)]
-#[scope(
- scope.define("decode", image_decode_func());
- scope
-)]
+#[elem(scope, Layout, LocalName, Figurable)]
pub struct ImageElem {
/// Path to an image file.
#[required]
@@ -73,59 +66,58 @@ pub struct ImageElem {
pub fit: ImageFit,
}
-/// Decode a raster or vector graphic from bytes or a string.
-///
-/// ## Example { #example }
-/// ```example
-/// #let original = read("diagram.svg")
-/// #let changed = original.replace(
-/// "#2B80FF", // blue
-/// green.hex(),
-/// )
-///
-/// #image.decode(original)
-/// #image.decode(changed)
-/// ```
-///
-/// Display: Decode Image
-/// Category: visualize
-#[func]
-pub fn image_decode(
- /// The data to decode as an image. Can be a string for SVGs.
- data: Readable,
- /// The image's format. Detected automatically by default.
- #[named]
- format: Option<Smart<ImageFormat>>,
- /// The width of the image.
- #[named]
- width: Option<Smart<Rel<Length>>>,
- /// The height of the image.
- #[named]
- height: Option<Smart<Rel<Length>>>,
- /// A text describing the image.
- #[named]
- alt: Option<Option<EcoString>>,
- /// How the image should adjust itself to a given area.
- #[named]
- fit: Option<ImageFit>,
-) -> StrResult<Content> {
- let mut elem = ImageElem::new(EcoString::new(), data);
- if let Some(format) = format {
- elem.push_format(format);
- }
- if let Some(width) = width {
- elem.push_width(width);
- }
- if let Some(height) = height {
- elem.push_height(height);
- }
- if let Some(alt) = alt {
- elem.push_alt(alt);
- }
- if let Some(fit) = fit {
- elem.push_fit(fit);
+#[scope]
+impl ImageElem {
+ /// Decode a raster or vector graphic from bytes or a string.
+ ///
+ /// ```example
+ /// #let original = read("diagram.svg")
+ /// #let changed = original.replace(
+ /// "#2B80FF", // blue
+ /// green.to-hex(),
+ /// )
+ ///
+ /// #image.decode(original)
+ /// #image.decode(changed)
+ /// ```
+ #[func(title = "Decode Image")]
+ pub fn decode(
+ /// The data to decode as an image. Can be a string for SVGs.
+ data: Readable,
+ /// The image's format. Detected automatically by default.
+ #[named]
+ format: Option<Smart<ImageFormat>>,
+ /// The width of the image.
+ #[named]
+ width: Option<Smart<Rel<Length>>>,
+ /// The height of the image.
+ #[named]
+ height: Option<Smart<Rel<Length>>>,
+ /// A text describing the image.
+ #[named]
+ alt: Option<Option<EcoString>>,
+ /// How the image should adjust itself to a given area.
+ #[named]
+ fit: Option<ImageFit>,
+ ) -> StrResult<Content> {
+ let mut elem = ImageElem::new(EcoString::new(), data);
+ if let Some(format) = format {
+ elem.push_format(format);
+ }
+ if let Some(width) = width {
+ elem.push_width(width);
+ }
+ if let Some(height) = height {
+ elem.push_height(height);
+ }
+ if let Some(alt) = alt {
+ elem.push_alt(alt);
+ }
+ if let Some(fit) = fit {
+ elem.push_fit(fit);
+ }
+ Ok(elem.pack())
}
- Ok(elem.pack())
}
impl Layout for ImageElem {
@@ -175,8 +167,7 @@ impl Layout for ImageElem {
let sizing = Axes::new(self.width(styles), self.height(styles));
let region = sizing
- .zip(regions.base())
- .map(|(s, r)| s.map(|v| v.resolve(styles).relative_to(r)))
+ .zip_map(regions.base(), |s, r| s.map(|v| v.resolve(styles).relative_to(r)))
.unwrap_or(regions.base());
let expand = sizing.as_ref().map(Smart::is_custom) | regions.expand;
@@ -217,7 +208,7 @@ impl Layout for ImageElem {
// process.
let mut frame = Frame::new(fitted);
frame.push(Point::zero(), FrameItem::Image(image, fitted, self.span()));
- frame.resize(target, Align::CENTER_HORIZON);
+ frame.resize(target, Axes::splat(FixedAlign::Center));
// Create a clipping group if only part of the image should be visible.
if fit == ImageFit::Cover && !target.fits(fitted) {
diff --git a/crates/typst-library/src/visualize/line.rs b/crates/typst-library/src/visualize/line.rs
index a476ffa7..9960a2d3 100644
--- a/crates/typst-library/src/visualize/line.rs
+++ b/crates/typst-library/src/visualize/line.rs
@@ -2,7 +2,7 @@ use crate::prelude::*;
/// A line from one point to another.
///
-/// ## Example { #example }
+/// # Example
/// ```example
/// #set page(height: 100pt)
///
@@ -13,10 +13,7 @@ use crate::prelude::*;
/// stroke: 2pt + maroon,
/// )
/// ```
-///
-/// Display: Line
-/// Category: visualize
-#[element(Layout)]
+#[elem(Layout)]
pub struct LineElem {
/// The start point of the line.
///
@@ -37,42 +34,7 @@ pub struct LineElem {
/// respected if `end` is `none`.
pub angle: Angle,
- /// How to stroke the line. This can be:
- ///
- /// - A length specifying the stroke's thickness. The color is inherited,
- /// defaulting to black.
- /// - A color to use for the stroke. The thickness is inherited, defaulting
- /// to `{1pt}`.
- /// - A stroke combined from color and thickness using the `+` operator as
- /// in `{2pt + red}`.
- /// - A stroke described by a dictionary with any of the following keys:
- /// - `paint`: The [color]($type/color) to use for the stroke.
- /// - `thickness`: The stroke's thickness as a [length]($type/length).
- /// - `cap`: How the line terminates. One of `{"butt"}`, `{"round"}`, or
- /// `{"square"}`.
- /// - `join`: How sharp turns of a contour are rendered. One of
- /// `{"miter"}`, `{"round"}`, or `{"bevel"}`. Not applicable to lines
- /// but to [polygons]($func/polygon) or [paths]($func/path).
- /// - `miter-limit`: Number at which protruding sharp angles are rendered
- /// with a bevel instead. The higher the number, the sharper an angle
- /// can be before it is bevelled. Only applicable if `join` is
- /// `{"miter"}`. Defaults to `{4.0}`.
- /// - `dash`: The dash pattern to use. Can be any of the following:
- /// - One of the predefined patterns `{"solid"}`, `{"dotted"}`,
- /// `{"densely-dotted"}`, `{"loosely-dotted"}`, `{"dashed"}`,
- /// `{"densely-dashed"}`, `{"loosely-dashed"}`, `{"dash-dotted"}`,
- /// `{"densely-dash-dotted"}` or `{"loosely-dash-dotted"}`
- /// - An [array]($type/array) with alternating lengths for dashes and
- /// gaps. You can also use the string `{"dot"}` for a length equal to
- /// the line thickness.
- /// - A [dictionary]($type/dictionary) with the keys `array` (same as
- /// the array above), and `phase` (of type [length]($type/length)),
- /// which defines where in the pattern to start drawing.
- ///
- /// On a `stroke` object, you can access any of the fields mentioned in the
- /// dictionary format above. For example, `{(2pt + blue).thickness}` is
- /// `{2pt}`, `{(2pt + blue).miter-limit}` is `{4.0}` (the default), and so
- /// on.
+ /// How to [stroke]($stroke) the line.
///
/// ```example
/// #set line(length: 100%)
@@ -86,7 +48,7 @@ pub struct LineElem {
/// ```
#[resolve]
#[fold]
- pub stroke: PartialStroke,
+ pub stroke: Stroke,
}
impl Layout for LineElem {
@@ -97,10 +59,8 @@ impl Layout for LineElem {
styles: StyleChain,
regions: Regions,
) -> SourceResult<Fragment> {
- let resolve = |axes: Axes<Rel<Abs>>| {
- axes.zip(regions.base()).map(|(l, b)| l.relative_to(b))
- };
-
+ let resolve =
+ |axes: Axes<Rel<Abs>>| axes.zip_map(regions.base(), Rel::relative_to);
let start = resolve(self.start(styles));
let delta =
self.end(styles).map(|end| resolve(end) - start).unwrap_or_else(|| {
diff --git a/crates/typst-library/src/visualize/mod.rs b/crates/typst-library/src/visualize/mod.rs
index ea873f44..a013853f 100644
--- a/crates/typst-library/src/visualize/mod.rs
+++ b/crates/typst-library/src/visualize/mod.rs
@@ -16,30 +16,15 @@ use crate::prelude::*;
/// Hook up all visualize definitions.
pub(super) fn define(global: &mut Scope) {
- global.define("image", ImageElem::func());
- global.define("line", LineElem::func());
- global.define("rect", RectElem::func());
- global.define("square", SquareElem::func());
- global.define("ellipse", EllipseElem::func());
- global.define("circle", CircleElem::func());
- global.define("polygon", PolygonElem::func());
- global.define("path", PathElem::func());
- global.define("black", Color::BLACK);
- global.define("gray", Color::GRAY);
- global.define("silver", Color::SILVER);
- global.define("white", Color::WHITE);
- global.define("navy", Color::NAVY);
- global.define("blue", Color::BLUE);
- global.define("aqua", Color::AQUA);
- global.define("teal", Color::TEAL);
- global.define("eastern", Color::EASTERN);
- global.define("purple", Color::PURPLE);
- global.define("fuchsia", Color::FUCHSIA);
- global.define("maroon", Color::MAROON);
- global.define("red", Color::RED);
- global.define("orange", Color::ORANGE);
- global.define("yellow", Color::YELLOW);
- global.define("olive", Color::OLIVE);
- global.define("green", Color::GREEN);
- global.define("lime", Color::LIME);
+ global.category("visualize");
+ global.define_type::<Color>();
+ global.define_type::<Stroke>();
+ global.define_elem::<ImageElem>();
+ global.define_elem::<LineElem>();
+ global.define_elem::<RectElem>();
+ global.define_elem::<SquareElem>();
+ global.define_elem::<EllipseElem>();
+ global.define_elem::<CircleElem>();
+ global.define_elem::<PolygonElem>();
+ global.define_elem::<PathElem>();
}
diff --git a/crates/typst-library/src/visualize/path.rs b/crates/typst-library/src/visualize/path.rs
index d78abce1..c252e95f 100644
--- a/crates/typst-library/src/visualize/path.rs
+++ b/crates/typst-library/src/visualize/path.rs
@@ -7,7 +7,7 @@ use PathVertex::{AllControlPoints, MirroredControlPoint, Vertex};
/// A path through a list of points, connected by Bezier curves.
///
-/// ## Example { #example }
+/// # Example
/// ```example
/// #path(
/// fill: blue.lighten(80%),
@@ -18,26 +18,24 @@ use PathVertex::{AllControlPoints, MirroredControlPoint, Vertex};
/// ((50%, 0pt), (40pt, 0pt)),
/// )
/// ```
-///
-/// Display: Path
-/// Category: visualize
-#[element(Layout)]
+#[elem(Layout)]
pub struct PathElem {
- /// How to fill the path. See the
- /// [rectangle's documentation]($func/rect.fill) for more details.
+ /// How to fill the path.
+ ///
+ /// When setting a fill, the default stroke disappears. To create a
+ /// rectangle with both fill and stroke, you have to configure both.
///
- /// Currently all paths are filled according to the
- /// [non-zero winding rule](https://en.wikipedia.org/wiki/Nonzero-rule).
+ /// Currently all paths are filled according to the [non-zero winding
+ /// rule](https://en.wikipedia.org/wiki/Nonzero-rule).
pub fill: Option<Paint>,
- /// How to stroke the path. This can be:
+ /// How to [stroke]($stroke) the path. This can be:
///
- /// See the [line's documentation]($func/line.stroke) for more details. Can
- /// be set to `{none}` to disable the stroke or to `{auto}` for a stroke of
- /// `{1pt}` black if and if only if no fill is given.
+ /// Can be set to `{none}` to disable the stroke or to `{auto}` for a
+ /// stroke of `{1pt}` black if and if only if no fill is given.
#[resolve]
#[fold]
- pub stroke: Smart<Option<PartialStroke>>,
+ pub stroke: Smart<Option<Stroke>>,
/// Whether to close this path with one last bezier curve. This curve will
/// takes into account the adjacent control points. If you want to close
@@ -50,8 +48,8 @@ pub struct PathElem {
///
/// Each vertex can be defined in 3 ways:
///
- /// - A regular point, as given to the [`line`]($func/line) or
- /// [`polygon`]($func/polygon) function.
+ /// - A regular point, as given to the [`line`]($line) or
+ /// [`polygon`]($polygon) function.
/// - An array of two points, the first being the vertex and the second
/// being the control point. The control point is expressed relative to
/// the vertex and is mirrored to get the second control point. The given
@@ -60,7 +58,7 @@ pub struct PathElem {
/// the curve going out of this vertex.
/// - An array of three points, the first being the vertex and the next
/// being the control points (control point for curves coming in and out,
- /// respectively)
+ /// respectively).
#[variadic]
pub vertices: Vec<PathVertex>,
}
@@ -75,8 +73,7 @@ impl Layout for PathElem {
) -> SourceResult<Fragment> {
let resolve = |axes: Axes<Rel<Length>>| {
axes.resolve(styles)
- .zip(regions.base())
- .map(|(l, b)| l.relative_to(b))
+ .zip_map(regions.base(), Rel::relative_to)
.to_point()
};
@@ -136,9 +133,9 @@ impl Layout for PathElem {
// Prepare fill and stroke.
let fill = self.fill(styles);
let stroke = match self.stroke(styles) {
- Smart::Auto if fill.is_none() => Some(Stroke::default()),
+ Smart::Auto if fill.is_none() => Some(FixedStroke::default()),
Smart::Auto => None,
- Smart::Custom(stroke) => stroke.map(PartialStroke::unwrap_or_default),
+ Smart::Custom(stroke) => stroke.map(Stroke::unwrap_or_default),
};
let mut frame = Frame::new(size);
diff --git a/crates/typst-library/src/visualize/polygon.rs b/crates/typst-library/src/visualize/polygon.rs
index b244b2e9..9f573467 100644
--- a/crates/typst-library/src/visualize/polygon.rs
+++ b/crates/typst-library/src/visualize/polygon.rs
@@ -6,7 +6,7 @@ use crate::prelude::*;
///
/// The polygon is defined by its corner points and is closed automatically.
///
-/// ## Example { #example }
+/// # Example
/// ```example
/// #polygon(
/// fill: blue.lighten(80%),
@@ -17,37 +17,102 @@ use crate::prelude::*;
/// (0%, 2cm),
/// )
/// ```
-///
-/// Display: Polygon
-/// Category: visualize
-#[element(Layout)]
-#[scope(
- scope.define("regular", polygon_regular_func());
- scope
-)]
+#[elem(scope, Layout)]
pub struct PolygonElem {
- /// How to fill the polygon. See the
- /// [rectangle's documentation]($func/rect.fill) for more details.
+ /// How to fill the polygon.
+ ///
+ /// When setting a fill, the default stroke disappears. To create a
+ /// rectangle with both fill and stroke, you have to configure both.
///
/// Currently all polygons are filled according to the
/// [non-zero winding rule](https://en.wikipedia.org/wiki/Nonzero-rule).
pub fill: Option<Paint>,
- /// How to stroke the polygon. This can be:
+ /// How to [stroke]($stroke) the polygon. This can be:
///
- /// See the [line's documentation]($func/line.stroke) for more details. Can
- /// be set to `{none}` to disable the stroke or to `{auto}` for a stroke of
- /// `{1pt}` black if and if only if no fill is given.
+ /// Can be set to `{none}` to disable the stroke or to `{auto}` for a
+ /// stroke of `{1pt}` black if and if only if no fill is given.
#[resolve]
#[fold]
- pub stroke: Smart<Option<PartialStroke>>,
+ pub stroke: Smart<Option<Stroke>>,
/// The vertices of the polygon. Each point is specified as an array of two
- /// [relative lengths]($type/relative-length).
+ /// [relative lengths]($relative).
#[variadic]
pub vertices: Vec<Axes<Rel<Length>>>,
}
+#[scope]
+impl PolygonElem {
+ /// A regular polygon, defined by its size and number of vertices.
+ ///
+ /// ```example
+ /// #polygon.regular(
+ /// fill: blue.lighten(80%),
+ /// stroke: blue,
+ /// size: 30pt,
+ /// vertices: 3,
+ /// )
+ /// ```
+ #[func(title = "Regular Polygon")]
+ pub fn regular(
+ /// How to fill the polygon. See the general
+ /// [polygon's documentation]($polygon.fill) for more details.
+ #[named]
+ fill: Option<Option<Paint>>,
+
+ /// How to stroke the polygon. See the general
+ /// [polygon's documentation]($polygon.stroke) for more details.
+ #[named]
+ stroke: Option<Smart<Option<Stroke>>>,
+
+ /// The diameter of the [circumcircle](https://en.wikipedia.org/wiki/Circumcircle)
+ /// of the regular polygon.
+ #[named]
+ #[default(Em::one().into())]
+ size: Length,
+
+ /// The number of vertices in the polygon.
+ #[named]
+ #[default(3)]
+ vertices: u64,
+ ) -> Content {
+ let radius = size / 2.0;
+ let angle = |i: f64| {
+ 2.0 * PI * i / (vertices as f64) + PI * (1.0 / 2.0 - 1.0 / vertices as f64)
+ };
+ let (horizontal_offset, vertical_offset) = (0..=vertices)
+ .map(|v| {
+ (
+ (radius * angle(v as f64).cos()) + radius,
+ (radius * angle(v as f64).sin()) + radius,
+ )
+ })
+ .fold((radius, radius), |(min_x, min_y), (v_x, v_y)| {
+ (
+ if min_x < v_x { min_x } else { v_x },
+ if min_y < v_y { min_y } else { v_y },
+ )
+ });
+ let vertices = (0..=vertices)
+ .map(|v| {
+ let x = (radius * angle(v as f64).cos()) + radius - horizontal_offset;
+ let y = (radius * angle(v as f64).sin()) + radius - vertical_offset;
+ Axes::new(x, y).map(Rel::from)
+ })
+ .collect();
+
+ let mut elem = PolygonElem::new(vertices);
+ if let Some(fill) = fill {
+ elem.push_fill(fill);
+ }
+ if let Some(stroke) = stroke {
+ elem.push_stroke(stroke);
+ }
+ elem.pack()
+ }
+}
+
impl Layout for PolygonElem {
#[tracing::instrument(name = "PolygonElem::layout", skip_all)]
fn layout(
@@ -60,10 +125,7 @@ impl Layout for PolygonElem {
.vertices()
.iter()
.map(|c| {
- c.resolve(styles)
- .zip(regions.base())
- .map(|(l, b)| l.relative_to(b))
- .to_point()
+ c.resolve(styles).zip_map(regions.base(), Rel::relative_to).to_point()
})
.collect();
@@ -78,9 +140,9 @@ impl Layout for PolygonElem {
// Prepare fill and stroke.
let fill = self.fill(styles);
let stroke = match self.stroke(styles) {
- Smart::Auto if fill.is_none() => Some(Stroke::default()),
+ Smart::Auto if fill.is_none() => Some(FixedStroke::default()),
Smart::Auto => None,
- Smart::Custom(stroke) => stroke.map(PartialStroke::unwrap_or_default),
+ Smart::Custom(stroke) => stroke.map(Stroke::unwrap_or_default),
};
// Construct a closed path given all points.
@@ -97,71 +159,3 @@ impl Layout for PolygonElem {
Ok(Fragment::frame(frame))
}
}
-
-/// A regular polygon, defined by its size and number of vertices.
-///
-/// ## Example { #example }
-/// ```example
-/// #polygon.regular(
-/// fill: blue.lighten(80%),
-/// stroke: blue,
-/// size: 30pt,
-/// vertices: 3,
-/// )
-/// ```
-///
-/// Display: Regular Polygon
-/// Category: visualize
-#[func]
-pub fn polygon_regular(
- /// How to fill the polygon. See the general
- /// [polygon's documentation]($func/polygon.fill) for more details.
- #[named]
- fill: Option<Option<Paint>>,
-
- /// How to stroke the polygon. See the general
- /// [polygon's documentation]($func/polygon.stroke) for more details.
- #[named]
- stroke: Option<Smart<Option<PartialStroke>>>,
-
- /// The diameter of the circumcircle of the regular polygon (https://en.wikipedia.org/wiki/Circumcircle).
- #[named]
- #[default(Em::one().into())]
- size: Length,
-
- /// The number of vertices in the polygon.
- #[named]
- #[default(3)]
- vertices: u64,
-) -> Content {
- let radius = size / 2.0;
- let angle = |i: f64| {
- 2.0 * PI * i / (vertices as f64) + PI * (1.0 / 2.0 - 1.0 / vertices as f64)
- };
- let (horizontal_offset, vertical_offset) = (0..=vertices)
- .map(|v| {
- (
- (radius * angle(v as f64).cos()) + radius,
- (radius * angle(v as f64).sin()) + radius,
- )
- })
- .fold((radius, radius), |(min_x, min_y), (v_x, v_y)| {
- (if min_x < v_x { min_x } else { v_x }, if min_y < v_y { min_y } else { v_y })
- });
- let vertices = (0..=vertices)
- .map(|v| {
- let x = (radius * angle(v as f64).cos()) + radius - horizontal_offset;
- let y = (radius * angle(v as f64).sin()) + radius - vertical_offset;
- Axes::new(x, y).map(Rel::from)
- })
- .collect();
-
- let mut elem = PolygonElem::new(vertices);
- if let Some(fill) = fill {
- elem.push_fill(fill);
- }
- if let Some(stroke) = stroke {
- elem.push_stroke(stroke);
- }
- elem.pack()
-}
diff --git a/crates/typst-library/src/visualize/shape.rs b/crates/typst-library/src/visualize/shape.rs
index 6129b70b..64d1ece6 100644
--- a/crates/typst-library/src/visualize/shape.rs
+++ b/crates/typst-library/src/visualize/shape.rs
@@ -4,7 +4,7 @@ use crate::prelude::*;
/// A rectangle with optional content.
///
-/// ## Example { #example }
+/// # Example
/// ```example
/// // Without content.
/// #rect(width: 35%, height: 30pt)
@@ -15,10 +15,7 @@ use crate::prelude::*;
/// to fit the content.
/// ]
/// ```
-///
-/// Display: Rectangle
-/// Category: visualize
-#[element(Layout)]
+#[elem(title = "Rectangle", Layout)]
pub struct RectElem {
/// The rectangle's width, relative to its parent container.
pub width: Smart<Rel<Length>>,
@@ -41,8 +38,7 @@ pub struct RectElem {
/// - `{none}` to disable stroking
/// - `{auto}` for a stroke of `{1pt + black}` if and if only if no fill is
/// given.
- /// - Any kind of stroke that can also be used for
- /// [lines]($func/line.stroke).
+ /// - Any kind of [stroke]($stroke)
/// - A dictionary describing the stroke for each side inidvidually. The
/// dictionary can contain the following keys in order of precedence:
/// - `top`: The top stroke.
@@ -65,7 +61,7 @@ pub struct RectElem {
/// ```
#[resolve]
#[fold]
- pub stroke: Smart<Sides<Option<Option<PartialStroke>>>>,
+ pub stroke: Smart<Sides<Option<Option<Stroke>>>>,
/// How much to round the rectangle's corners, relative to the minimum of
/// the width and height divided by two. This can be:
@@ -106,20 +102,14 @@ pub struct RectElem {
pub radius: Corners<Option<Rel<Length>>>,
/// How much to pad the rectangle's content.
- ///
- /// _Note:_ When the rectangle contains text, its exact size depends on the
- /// current [text edges]($func/text.top-edge).
- ///
- /// ```example
- /// #rect(inset: 0pt)[Tight]
- /// ```
+ /// See the [box's documentation]($box.outset) for more details.
#[resolve]
#[fold]
#[default(Sides::splat(Abs::pt(5.0).into()))]
pub inset: Sides<Option<Rel<Length>>>,
/// How much to expand the rectangle's size without affecting the layout.
- /// See the [box's documentation]($func/box.outset) for more details.
+ /// See the [box's documentation]($box.outset) for more details.
#[resolve]
#[fold]
pub outset: Sides<Option<Rel<Length>>>,
@@ -159,7 +149,7 @@ impl Layout for RectElem {
/// A square with optional content.
///
-/// ## Example { #example }
+/// # Example
/// ```example
/// // Without content.
/// #square(size: 40pt)
@@ -170,10 +160,7 @@ impl Layout for RectElem {
/// sized to fit.
/// ]
/// ```
-///
-/// Display: Square
-/// Category: visualize
-#[element(Layout)]
+#[elem(Layout)]
pub struct SquareElem {
/// The square's side length. This is mutually exclusive with `width` and
/// `height`.
@@ -203,31 +190,31 @@ pub struct SquareElem {
})]
pub height: Smart<Rel<Length>>,
- /// How to fill the square. See the
- /// [rectangle's documentation]($func/rect.fill) for more details.
+ /// How to fill the square. See the [rectangle's documentation]($rect.fill)
+ /// for more details.
pub fill: Option<Paint>,
- /// How to stroke the square. See the [rectangle's
- /// documentation]($func/rect.stroke) for more details.
+ /// How to stroke the square. See the
+ /// [rectangle's documentation]($rect.stroke) for more details.
#[resolve]
#[fold]
- pub stroke: Smart<Sides<Option<Option<PartialStroke>>>>,
+ pub stroke: Smart<Sides<Option<Option<Stroke>>>>,
- /// How much to round the square's corners. See the [rectangle's
- /// documentation]($func/rect.radius) for more details.
+ /// How much to round the square's corners. See the
+ /// [rectangle's documentation]($rect.radius) for more details.
#[resolve]
#[fold]
pub radius: Corners<Option<Rel<Length>>>,
- /// How much to pad the square's content. See the [rectangle's
- /// documentation]($func/rect.inset) for more details.
+ /// How much to pad the square's content. See the
+ /// [box's documentation]($box.inset) for more details.
#[resolve]
#[fold]
#[default(Sides::splat(Abs::pt(5.0).into()))]
pub inset: Sides<Option<Rel<Length>>>,
/// How much to expand the square's size without affecting the layout. See
- /// the [rectangle's documentation]($func/rect.outset) for more details.
+ /// the [box's documentation]($box.outset) for more details.
#[resolve]
#[fold]
pub outset: Sides<Option<Rel<Length>>>,
@@ -268,7 +255,7 @@ impl Layout for SquareElem {
/// An ellipse with optional content.
///
-/// ## Example { #example }
+/// # Example
/// ```example
/// // Without content.
/// #ellipse(width: 35%, height: 30pt)
@@ -280,10 +267,7 @@ impl Layout for SquareElem {
/// to fit the content.
/// ]
/// ```
-///
-/// Display: Ellipse
-/// Category: visualize
-#[element(Layout)]
+#[elem(Layout)]
pub struct EllipseElem {
/// The ellipse's width, relative to its parent container.
pub width: Smart<Rel<Length>>,
@@ -291,25 +275,25 @@ pub struct EllipseElem {
/// The ellipse's height, relative to its parent container.
pub height: Smart<Rel<Length>>,
- /// How to fill the ellipse. See the
- /// [rectangle's documentation]($func/rect.fill) for more details.
+ /// How to fill the ellipse. See the [rectangle's documentation]($rect.fill)
+ /// for more details.
pub fill: Option<Paint>,
- /// How to stroke the ellipse. See the [rectangle's
- /// documentation]($func/rect.stroke) for more details.
+ /// How to stroke the ellipse. See the
+ /// [rectangle's documentation]($rect.stroke) for more details.
#[resolve]
#[fold]
- pub stroke: Smart<Option<PartialStroke>>,
+ pub stroke: Smart<Option<Stroke>>,
- /// How much to pad the ellipse's content. See the [rectangle's
- /// documentation]($func/rect.inset) for more details.
+ /// How much to pad the ellipse's content. See the
+ /// [box's documentation]($box.inset) for more details.
#[resolve]
#[fold]
#[default(Sides::splat(Abs::pt(5.0).into()))]
pub inset: Sides<Option<Rel<Length>>>,
/// How much to expand the ellipse's size without affecting the layout. See
- /// the [rectangle's documentation]($func/rect.outset) for more details.
+ /// the [box's documentation]($box.outset) for more details.
#[resolve]
#[fold]
pub outset: Sides<Option<Rel<Length>>>,
@@ -349,7 +333,7 @@ impl Layout for EllipseElem {
/// A circle with optional content.
///
-/// ## Example { #example }
+/// # Example
/// ```example
/// // Without content.
/// #circle(radius: 25pt)
@@ -361,10 +345,7 @@ impl Layout for EllipseElem {
/// sized to fit.
/// ]
/// ```
-///
-/// Display: Circle
-/// Category: visualize
-#[element(Layout)]
+#[elem(Layout)]
pub struct CircleElem {
/// The circle's radius. This is mutually exclusive with `width` and
/// `height`.
@@ -398,26 +379,26 @@ pub struct CircleElem {
})]
pub height: Smart<Rel<Length>>,
- /// How to fill the circle. See the
- /// [rectangle's documentation]($func/rect.fill) for more details.
+ /// How to fill the circle. See the [rectangle's documentation]($rect.fill)
+ /// for more details.
pub fill: Option<Paint>,
- /// How to stroke the circle. See the [rectangle's
- /// documentation]($func/rect.stroke) for more details.
+ /// How to stroke the circle. See the
+ /// [rectangle's documentation]($rect.stroke) for more details.
#[resolve]
#[fold]
#[default(Smart::Auto)]
- pub stroke: Smart<Option<PartialStroke>>,
+ pub stroke: Smart<Option<Stroke>>,
- /// How much to pad the circle's content. See the [rectangle's
- /// documentation]($func/rect.inset) for more details.
+ /// How much to pad the circle's content. See the
+ /// [box's documentation]($box.inset) for more details.
#[resolve]
#[fold]
#[default(Sides::splat(Abs::pt(5.0).into()))]
pub inset: Sides<Option<Rel<Length>>>,
/// How much to expand the circle's size without affecting the layout. See
- /// the [rectangle's documentation]($func/rect.outset) for more details.
+ /// the [box's documentation]($box.outset) for more details.
#[resolve]
#[fold]
pub outset: Sides<Option<Rel<Length>>>,
@@ -464,15 +445,14 @@ fn layout(
body: &Option<Content>,
sizing: Axes<Smart<Rel<Length>>>,
fill: Option<Paint>,
- stroke: Smart<Sides<Option<PartialStroke<Abs>>>>,
+ stroke: Smart<Sides<Option<Stroke<Abs>>>>,
mut inset: Sides<Rel<Abs>>,
outset: Sides<Rel<Abs>>,
radius: Corners<Rel<Abs>>,
span: Span,
) -> SourceResult<Fragment> {
let resolved = sizing
- .zip(regions.base())
- .map(|(s, r)| s.map(|v| v.resolve(styles).relative_to(r)));
+ .zip_map(regions.base(), |s, r| s.map(|v| v.resolve(styles).relative_to(r)));
let mut frame;
if let Some(child) = body {
@@ -517,11 +497,9 @@ fn layout(
// Prepare stroke.
let stroke = match stroke {
- Smart::Auto if fill.is_none() => Sides::splat(Some(Stroke::default())),
+ Smart::Auto if fill.is_none() => Sides::splat(Some(FixedStroke::default())),
Smart::Auto => Sides::splat(None),
- Smart::Custom(strokes) => {
- strokes.map(|s| s.map(PartialStroke::unwrap_or_default))
- }
+ Smart::Custom(strokes) => strokes.map(|s| s.map(Stroke::unwrap_or_default)),
};
// Add fill and/or stroke.