From 1661c1791aef1688ca1a1f79e4fc915da0546fe9 Mon Sep 17 00:00:00 2001
From: Johannes Wolf <519002+johannes-wolf@users.noreply.github.com>
Date: Fri, 18 Oct 2024 13:51:21 +0200
Subject: [PATCH] Add fill-rule style key (#722)
* line: Add fill-rule style key
* changelog
* tests: Update ref images
* docs: Mention new fill-rule style
---
CHANGES.md | 5 ++++-
docs/basics/styling.mdx | 4 ++++
src/canvas.typ | 1 +
src/draw/shapes.typ | 6 +++++-
src/drawable.typ | 4 +++-
src/styles.typ | 9 +++++++--
tests/line/fill-rule/ref/1.png | Bin 0 -> 3323 bytes
tests/line/fill-rule/test.typ | 24 ++++++++++++++++++++++++
8 files changed, 48 insertions(+), 5 deletions(-)
create mode 100644 tests/line/fill-rule/ref/1.png
create mode 100644 tests/line/fill-rule/test.typ
diff --git a/CHANGES.md b/CHANGES.md
index 0413e4ca2..56f2a526f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,10 +1,13 @@
# 0.3.1
+CeTZ 0.3.1 requires Typst 0.12.0.
+
- Added a new `padding` parameter to the canvas element.
+- Some elements now support Typst 0.12.0 `fill-rule` style.
# 0.3.0
-CeTZ 0.3.0 requires Typst 0.11.0
+CeTZ 0.3.0 requires Typst 0.11.0.
The licence changed from Apache-2.0 to LGPLv3.
CeTZ' plotting and charting functionality has been moved to a separate
diff --git a/docs/basics/styling.mdx b/docs/basics/styling.mdx
index c017d3778..c64d7ac70 100644
--- a/docs/basics/styling.mdx
+++ b/docs/basics/styling.mdx
@@ -20,6 +20,10 @@ You can style draw elements by passing the relevant named arguments to their dra
documentation for more
details.](https://typst.app/docs/reference/visualize/line/#parameters-stroke)
+
+ How to fill self-intersecting paths. Can be "non-zero" or "even-odd".
+ [See Typst's path documentation for more details.](https://typst.app/docs/reference/visualize/path/#parameters-fill-rule)
+
diff --git a/src/canvas.typ b/src/canvas.typ
index 57c7df766..cebefb64c 100644
--- a/src/canvas.typ
+++ b/src/canvas.typ
@@ -126,6 +126,7 @@
path(
stroke: drawable.stroke,
fill: drawable.fill,
+ fill-rule: drawable.at("fill-rule", default: "non-zero"),
closed: drawable.at("close", default: false),
..vertices,
)
diff --git a/src/draw/shapes.typ b/src/draw/shapes.typ
index f52779cb5..dcea8f0d9 100644
--- a/src/draw/shapes.typ
+++ b/src/draw/shapes.typ
@@ -553,6 +553,7 @@
let drawables = drawable.path(
(path-util.line-segment(pts),),
fill: style.fill,
+ fill-rule: style.fill-rule,
stroke: style.stroke,
close: close
)
@@ -1223,6 +1224,7 @@
let drawables = drawable.path(
(path-util.cubic-segment(start, end, ..ctrl),),
fill: style.fill,
+ fill-rule: style.fill-rule,
stroke: style.stroke,
)
@@ -1325,6 +1327,7 @@
let drawables = drawable.path(
segments,
fill: style.fill,
+ fill-rule: style.fill-rule,
stroke: style.stroke,
close: close)
@@ -1402,6 +1405,7 @@
let drawables = drawable.path(
segments,
fill: style.fill,
+ fill-rule: style.fill-rule,
stroke: style.stroke,
close: close)
@@ -1492,7 +1496,7 @@
}
let style = styles.resolve(ctx.style, merge: style)
- let drawables = drawable.path(fill: style.fill, stroke: style.stroke, close: close, segments)
+ let drawables = drawable.path(fill: style.fill, fill-rule: style.fill-rule, stroke: style.stroke, close: close, segments)
let (transform, anchors) = anchor_.setup(
name => {
diff --git a/src/drawable.typ b/src/drawable.typ
index f7e6d0194..615bde394 100644
--- a/src/drawable.typ
+++ b/src/drawable.typ
@@ -36,9 +36,10 @@
/// - segments (array): The segments to create the path from.
/// - close (bool): If `true` the path will be closed.
/// - fill (color,none): The color to fill the path with.
+/// - fill-rule (string): One of "even-odd" or "non-zero".
/// - stroke (stroke): The stroke of the path.
/// -> drawable
-#let path(close: false, fill: none, stroke: none, segments) = {
+#let path(close: false, fill: none, stroke: none, fill-rule: "non-zero", segments) = {
let segments = segments
// Handle case where only one segment has been passed
if type(segments.first()) == str {
@@ -58,6 +59,7 @@
close: close,
segments: segments,
fill: fill,
+ fill-rule: fill-rule,
stroke: stroke,
hidden: false,
bounds: true,
diff --git a/src/styles.typ b/src/styles.typ
index 98c7bb096..f746db3b4 100644
--- a/src/styles.typ
+++ b/src/styles.typ
@@ -2,6 +2,7 @@
#let default = (
fill: none,
+ fill-rule: "non-zero",
stroke: black + 1pt,
radius: 1,
/// Bezier shortening mode:
@@ -69,11 +70,13 @@
line: (
mark: auto,
fill: auto,
+ fill-rule: auto,
stroke: auto,
),
bezier: (
stroke: auto,
fill: auto,
+ fill-rule: auto,
mark: auto,
shorten: auto,
),
@@ -82,7 +85,8 @@
mark: auto,
shorten: auto,
stroke: auto,
- fill: auto
+ fill: auto,
+ fill-rule: auto,
),
hobby: (
/// Curve start and end omega (curlyness)
@@ -90,7 +94,8 @@
mark: auto,
shorten: auto,
stroke: auto,
- fill: auto
+ fill: auto,
+ fill-rule: auto,
),
rect: (
/// Rect corner radius that supports the following types:
diff --git a/tests/line/fill-rule/ref/1.png b/tests/line/fill-rule/ref/1.png
new file mode 100644
index 0000000000000000000000000000000000000000..03687335ad8cb527d6e22f54c040b527110a1695
GIT binary patch
literal 3323
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
literal 0
HcmV?d00001
diff --git a/tests/line/fill-rule/test.typ b/tests/line/fill-rule/test.typ
new file mode 100644
index 000000000..a0de82d0c
--- /dev/null
+++ b/tests/line/fill-rule/test.typ
@@ -0,0 +1,24 @@
+#set page(width: auto, height: auto)
+
+#import "/src/lib.typ": *
+#import "/tests/helper.typ": *
+
+#test-case({
+ import draw: *
+
+ line((25pt, 0pt),
+ (10pt, 50pt),
+ (50pt, 20pt),
+ (0pt, 20pt),
+ (40pt, 50pt), close: true, fill: blue, fill-rule: "non-zero")
+})
+
+#test-case({
+ import draw: *
+
+ line((25pt, 0pt),
+ (10pt, 50pt),
+ (50pt, 20pt),
+ (0pt, 20pt),
+ (40pt, 50pt), close: true, fill: blue, fill-rule: "even-odd")
+})