summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/visualize/polygon.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/typst-library/src/visualize/polygon.rs')
-rw-r--r--crates/typst-library/src/visualize/polygon.rs176
1 files changed, 85 insertions, 91 deletions
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()
-}