feat: Auto-detect zigzag-dir from port anchors

Analogous to u-shape's u-side auto-detection: when zigzag-dir is auto
(new default), deduce it from the endpoint port anchors by comparing
each port's position with its element's .center anchor. East/west ports
yield "vertical", north/south yield "horizontal". Panics on
conflicting endpoints or when neither endpoint is a port anchor.
This commit is contained in:
David Stadelmann committed 2026-09-30 14:37:07 +02:00
1 parent 26229d8fc4
commit 018192db86
3 files changed
+8674 -8503

No files matched your search

+3 -3
View File
@@ -91,7 +91,7 @@ for i in range(3) {
draw.content((i * 3 + 1, -1), raw(wire.wire-styles.at(i)))
}
wire.wire("w1", ("p0", "p1"), style: "direct")
wire.wire("w2", ("p2", "p3"), style: "zigzag")
wire.wire("w2", ("p2", "p3"), style: "zigzag", zigzag-dir: "vertical")
wire.wire("w3", ("p4", "p5"), style: "dodge",
dodge-y: -0.5, dodge-margins: (0.5, 0.5))
wire.wire("w4", ("p6","p7"), style: "guided",
@@ -460,9 +460,9 @@ wire.wire("w-out", ("inner-port-b", "sh-port-out"))
```)
#let intersection = example(```
wire.wire("w1", ((0, 0), (1, 1)), style: "zigzag")
wire.wire("w1", ((0, 0), (1, 1)), style: "zigzag", zigzag-dir: "vertical")
wire.wire("w2", ((0, 0), (1, -.5)),
style: "zigzag", zigzag-ratio: 80%)
style: "zigzag", zigzag-dir: "vertical", zigzag-ratio: 80%)
wire.intersection("w1.zig")
```)
+8650 -8495
View File
File diff suppressed because it is too large. Load diff
+21 -5
View File
@@ -353,7 +353,7 @@
/// - directed (bool): If true, the wire will be directed, meaning an arrow will be drawn at the endpoint
/// - rotate-name (bool): If true, the name will be rotated according to the wire's slope
/// - zigzag-ratio (auto, ratio): Position of the zigzag middle bend segment relative to the horizontal (or vertical) span, as a fraction between the two endpoints (only with style "zigzag"). Defaults to `auto`, which resolves to `50%`. Panics if combined with an explicitly set `zigzag-at`.
/// - zigzag-dir (str): The zigzag's direction. As either "vertical" or "horizontal" (only with dstyle "zigzag")
/// - zigzag-dir (auto, str): The zigzag's direction, as either "vertical" or "horizontal" (only with style "zigzag"). If `auto`, it is deduced from the endpoints when they are port anchors of the form `<elmt-id>-port-<port-id>` by comparing each port's position with its element's `.center` anchor: east/west-facing ports yield `"vertical"` (wire exits horizontally) and north/south-facing ports yield `"horizontal"`. Auto-detection panics if the two endpoints resolve to conflicting directions, or if neither endpoint is a port anchor.
/// - zigzag-at (none, str, array, dictionary): Optional. Anchor-based placement of the zigzag middle bend segment (only with style "zigzag"). When set, overrides `zigzag-ratio` (which must be left at its default `auto`). The middle segment sits on the axis extracted from the given coordinate: `x` for `zigzag-dir: "vertical"`, `y` for `"horizontal"`. Accepted forms: a raw CeTZ anchor name string (e.g. `"elmt.center"` or `"elmt-port-a"`); any native CeTZ coordinate (tuple, dict such as `(rel: (dx, dy), to: anchor)`); or the ergonomic scalar-offset form `(rel: <length>, to: <coord>)`, which is rewritten to a `(dx, 0)` offset for `"vertical"` and `(0, dy)` for `"horizontal"`. See #examples.zigzag-at
/// - dodge-y (number): Y position to dodge the wire to (only with style "dodge")
/// - dodge-sides (array): The start and end sides (going out of the connected element) of the wire (only with style "dodge")
@@ -379,7 +379,7 @@
directed: false,
rotate-name: true,
zigzag-ratio: auto,
zigzag-dir: "vertical",
zigzag-dir: auto,
zigzag-at: none,
dodge-y: 0,
dodge-sides: ("east", "west"),
@@ -426,11 +426,27 @@
(points, anchors) = get-direct-wire(pts)
} else if style == "zigzag" {
let zz-dir = zigzag-dir
if zz-dir == auto {
let side-start = _u-detect-side(pts.first(), ctx)
let side-end = _u-detect-side(pts.last(), ctx)
let dir-from-side(s) = if s == none { none } else if s == "east" or s == "west" { "vertical" } else { "horizontal" }
let d-start = dir-from-side(side-start)
let d-end = dir-from-side(side-end)
if d-start != none and d-end != none and d-start != d-end {
panic("zigzag: start port faces '" + side-start
+ "' but end port faces '" + side-end
+ "'; cannot auto-detect `zigzag-dir`. Set it explicitly.")
}
if d-start != none { zz-dir = d-start }
else if d-end != none { zz-dir = d-end }
else { panic("zigzag: could not auto-detect `zigzag-dir` (no port anchor on either endpoint). Set it explicitly.") }
}
(points, anchors) = get-zigzag-wire(
pts,
zigzag-ratio-effective,
zigzag-dir,
at: normalize-zigzag-at(zigzag-at, zigzag-dir),
zz-dir,
at: normalize-zigzag-at(zigzag-at, zz-dir),
)
} else if style == "dodge" {
@@ -588,7 +604,7 @@
/// point exposes a port anchor named `<id>-port-<id>` so wires can attach
/// to it as if it were a block port. A synthesized `<id>.center` anchor
/// is placed opposite `port-side` so #doc-ref("wire.wire") with
/// `style: "u-shape"` can auto-detect the port side.
/// `style: "u-shape"` (or `"zigzag"`) can auto-detect the port side.
///
/// #examples.breakout
///