diff options
Diffstat (limited to 'crates/typst-library/src/visualize/polygon.rs')
| -rw-r--r-- | crates/typst-library/src/visualize/polygon.rs | 176 |
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() -} |
