summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlle Lögdahl <olle.logdahl@gmail.com>2023-03-28 16:43:16 +0200
committerGitHub <noreply@github.com>2023-03-28 16:43:16 +0200
commit44165d09a9e6c09d4c5a9dbeab1af7f23e3d887a (patch)
treefe0efc6ab2aa198e6c53a9761a10b21a6952abc8
parent628e589bcda538f5f5d7666a4c9ba82ce3ee66df (diff)
Add polygon drawing primitive (#314)
-rw-r--r--library/src/lib.rs1
-rw-r--r--library/src/visualize/mod.rs2
-rw-r--r--library/src/visualize/polygon.rs71
-rw-r--r--tests/ref/visualize/polygon.pngbin0 -> 7577 bytes
-rw-r--r--tests/typ/visualize/polygon.typ31
5 files changed, 105 insertions, 0 deletions
diff --git a/library/src/lib.rs b/library/src/lib.rs
index 1a998700..178db8a2 100644
--- a/library/src/lib.rs
+++ b/library/src/lib.rs
@@ -83,6 +83,7 @@ fn global(math: Module, calc: Module) -> Module {
global.define("square", visualize::SquareElem::func());
global.define("ellipse", visualize::EllipseElem::func());
global.define("circle", visualize::CircleElem::func());
+ global.define("polygon", visualize::PolygonElem::func());
// Meta.
global.define("document", meta::DocumentElem::func());
diff --git a/library/src/visualize/mod.rs b/library/src/visualize/mod.rs
index 1c87eeb3..198c707d 100644
--- a/library/src/visualize/mod.rs
+++ b/library/src/visualize/mod.rs
@@ -3,7 +3,9 @@
mod image;
mod line;
mod shape;
+mod polygon;
pub use self::image::*;
pub use self::line::*;
pub use self::shape::*;
+pub use self::polygon::*; \ No newline at end of file
diff --git a/library/src/visualize/polygon.rs b/library/src/visualize/polygon.rs
new file mode 100644
index 00000000..40058834
--- /dev/null
+++ b/library/src/visualize/polygon.rs
@@ -0,0 +1,71 @@
+use crate::prelude::*;
+
+/// A closed-path polygon.
+///
+/// ## Example
+/// ```example
+/// #polygon(fill: blue, (0pt, 0pt), (10pt, 0pt), (10pt, 10pt))
+/// ```
+///
+/// Display: Polygon
+/// Category: visualize
+#[element(Layout)]
+pub struct PolygonElem {
+ /// How to fill the polygon. See the
+ /// [rectangle's documentation]($func/rect.fill) for more details.
+ pub fill: Option<Paint>,
+
+ /// How to stroke the polygon. See the [lines's
+ /// documentation]($func/line.stroke) for more details.
+ #[resolve]
+ #[fold]
+ pub stroke: Option<PartialStroke>,
+
+ /// The vertices of the polygon. The polygon automatically closes itself.
+ #[variadic]
+ pub vertices: Vec<Axes<Rel<Length>>>,
+}
+
+impl Layout for PolygonElem {
+ fn layout(
+ &self,
+ _: &mut Vt,
+ styles: StyleChain,
+ regions: Regions,
+ ) -> SourceResult<Fragment> {
+ let points: Vec<Point> = self
+ .vertices()
+ .iter()
+ .map(|c| {
+ c.resolve(styles)
+ .zip(regions.base())
+ .map(|(l, b)| l.relative_to(b))
+ .to_point()
+ })
+ .collect();
+
+ let size = points.iter().fold(Point::zero(), |max, c| c.max(max)).to_size();
+
+ let target = regions.expand.select(regions.size, size);
+ let mut frame = Frame::new(target);
+
+ // only create a path if there is more than zero points.
+ if points.len() > 0 {
+ let stroke = self.stroke(styles).map(|e| e.unwrap_or_default());
+ let fill = self.fill(styles);
+
+ // construct a closed path given all points.
+ let mut path = Path::new();
+ path.move_to(points[0]);
+ for point in &points[1..] {
+ path.line_to(*point);
+ }
+ path.close_path();
+
+ let shape = Shape { geometry: Geometry::Path(path), stroke, fill };
+ frame.push(Point::zero(), FrameItem::Shape(shape, self.span()));
+ }
+
+ Ok(Fragment::frame(frame))
+ }
+}
diff --git a/tests/ref/visualize/polygon.png b/tests/ref/visualize/polygon.png
new file mode 100644
index 00000000..d2a86a53
--- /dev/null
+++ b/tests/ref/visualize/polygon.png
Binary files differ
diff --git a/tests/typ/visualize/polygon.typ b/tests/typ/visualize/polygon.typ
new file mode 100644
index 00000000..defd89be
--- /dev/null
+++ b/tests/typ/visualize/polygon.typ
@@ -0,0 +1,31 @@
+// Test polygons.
+
+---
+#set page(height: 220pt, width: 50pt)
+#box({
+ set polygon(stroke: 0.75pt, fill: blue)
+ polygon((0em, 0pt))
+ // this should not give an error
+ polygon()
+ polygon((0pt, 0pt), (10pt, 0pt))
+ polygon((5pt, 0pt), (0pt, 10pt), (10pt, 10pt))
+ polygon(
+ (0pt, 0pt), (5pt, 5pt), (10pt, 0pt),
+ (15pt, 5pt),
+ (5pt, 10pt)
+ )
+ polygon(stroke: none, (5pt, 0pt), (0pt, 10pt), (10pt, 10pt))
+ polygon(stroke: 3pt, fill: none, (5pt, 0pt), (0pt, 10pt), (10pt, 10pt))
+ // relative size
+ polygon((0pt, 0pt), (100%, 5pt), (50%, 10pt))
+ // antiparallelogram
+ polygon((0pt, 5pt), (5pt, 0pt), (0pt, 10pt), (5pt, 15pt))
+ // self-intersections
+ polygon((0pt, 10pt), (30pt, 20pt), (0pt, 30pt), (20pt, 0pt), (20pt, 35pt))
+})
+
+---
+// Test errors.
+
+// Error: 10-17 point array must contain exactly two entries
+#polygon((50pt,)) \ No newline at end of file