forked from HEL/circuiteria
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72eab1620c | ||
|
|
fff5b2ecd5 | ||
|
|
f7cb457202 |
No files matched your search
@@ -23,6 +23,31 @@ wire.stub("block-port-in2", "west")
|
||||
wire.stub("block-port-out", "east")
|
||||
```)
|
||||
|
||||
#let align-with-ports = example(```
|
||||
// `align-with` reuses another port's coord
|
||||
// (E/W: y, N/S: x). Forms:
|
||||
// 1. "id" — local
|
||||
// 2. (element:"e", port:"p") — cross-elmt
|
||||
// 3. "raw-anchor" — CeTZ anchor
|
||||
element.block(x: 0, y: 0, w: 2, h: 2, id: "b1",
|
||||
ports: (
|
||||
west: ((id: "a", name: "A", y: 30%),),
|
||||
east: ((id: "oa", name: "A'",
|
||||
"align-with": "a"),),
|
||||
))
|
||||
element.block(x: 4, y: -.3, w: 2, h: 2.6, id: "b2",
|
||||
ports: (
|
||||
west: ((id: "ia", name: "A",
|
||||
"align-with":
|
||||
(element: "b1", port: "oa")),),
|
||||
east: ((id: "o", name: "C"),),
|
||||
))
|
||||
wire.stub("b1-port-a", "west")
|
||||
wire.wire("w", ("b1-port-oa", "b2-port-ia"),
|
||||
style: "direct")
|
||||
wire.stub("b2-port-o", "east")
|
||||
```, vertical: true)
|
||||
|
||||
#let extender = example(```
|
||||
element.extender(
|
||||
x: 0, y: 0, w: 3, h: 1,
|
||||
@@ -422,3 +447,24 @@ element.block(x: 18.5, y: -3.5, w: 1.5, h: 1.5, id: "sB",
|
||||
wire.wire("uSS", ("sA-port-out", "sB-port-out"),
|
||||
style: "u-shape", u-side: "south", u-depth: 0.5)
|
||||
```, vertical: true)
|
||||
|
||||
#let zigzag-at = example(```
|
||||
// `zigzag-at` places the middle bend of a zigzag wire
|
||||
// on the axis of a referenced anchor, overriding
|
||||
// `zigzag-ratio`.
|
||||
element.block(x: 0, y: 0, w: 1.5, h: 1.5, id: "src",
|
||||
ports: (east: ((id: "o", name: "O"),),))
|
||||
element.block(x: 3, y: -1.2, w: 1, h: 1, id: "ref",
|
||||
fill: util.colors.pink,
|
||||
ports: (west: ((id: "p", name: "R"),),))
|
||||
element.block(x: 5.5, y: -2, w: 1.5, h: 1.5, id: "dst",
|
||||
ports: (west: ((id: "i", name: "I"),),))
|
||||
// String form: anchor name of a port.
|
||||
wire.wire("w1", ("src-port-o", "dst-port-i"),
|
||||
style: "zigzag",
|
||||
zigzag-at: "ref-port-p")
|
||||
// Ergonomic offset: shift 8pt right of ref.east.
|
||||
wire.wire("w2", ("src-port-o", "dst-port-i"),
|
||||
style: "zigzag",
|
||||
zigzag-at: (rel: 8pt, to: "ref.east"))
|
||||
```, vertical: true)
|
||||
+8524
-8091
File diff suppressed because it is too large.
Load diff
@@ -36,6 +36,9 @@
|
||||
/// - `name` (`str`): Optional name displayed *in* the block
|
||||
/// - `clock` (`bool`): Whether it is a clock port (triangle symbol)
|
||||
/// - `vertical` (`bool`): Whether the name should be drawn vertically
|
||||
/// - `align-with` (`str`, `dictionary`): Optional. Reuse another port's placement coordinate (east/west: y, north/south: x). Accepted forms: `"<local-id>"` (same-element port; axis must match, transitively resolved, cycles panic), `(element: "<id>", port: "<id>")` (cross-element; axis not validated; target element must be drawn first), or `"<raw-anchor>"` (any unrecognised string is used as a CeTZ anchor name). See #examples.align-with-ports
|
||||
/// - `x` (length, ratio, function): Optional. North/south ports only. Explicit x-offset override measured from the left corner of the side. A `ratio` is used as a percentage along the side; a length/number is used as an absolute offset; a function is called with the element width and must return a length/number. Panics if used together with `align-with` or on an east/west port.
|
||||
/// - `y` (length, ratio, function): Optional. East/west ports only. Analogous to `x`, measured from the top corner of the side and called with the element height when a function is passed.
|
||||
/// - ports-margins (dictionary): Dictionary of ports margins (used with automatic port placement). They keys are cardinal directions ("north", "east", "south", "west"). The values are tuples of (`<start>`, `<end>`) margins (numbers)
|
||||
/// - port-name-padding (number, length): Padding between the port name label and the port anchor point (default `2pt`)
|
||||
/// - port-names-outside (bool, auto): If true, port name labels are drawn on the outside of the block instead of the inside. When `auto` (default), follows the `shell` setting (outside if `shell: true`, inside otherwise)
|
||||
@@ -154,6 +157,8 @@
|
||||
tl, tr, br, bl,
|
||||
ports,
|
||||
ports-margins,
|
||||
width: width,
|
||||
height: height,
|
||||
debug: debug.ports,
|
||||
name-padding: port-name-padding,
|
||||
outside-name: outside-name,
|
||||
|
||||
+219
-37
@@ -39,12 +39,14 @@
|
||||
if shape == "pentagon" {
|
||||
let sh = port-shape-vertices(side, kind, size)
|
||||
let v = sh.vertices.map(off => (rel: off, to: pos))
|
||||
draw.line(
|
||||
// Draw pentagon on a higher layer so it always renders on top of wires
|
||||
// and neighbouring elements.
|
||||
draw.on-layer(1, draw.line(
|
||||
v.at(0), v.at(1), v.at(2), v.at(3), v.at(4),
|
||||
close: true,
|
||||
stroke: shape-stroke,
|
||||
fill: shape-fill,
|
||||
)
|
||||
))
|
||||
outer-pos = (rel: sh.outer, to: pos)
|
||||
inner-pos = (rel: sh.inner, to: pos)
|
||||
} else if is-clock {
|
||||
@@ -113,11 +115,126 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize an `align-with` value to either:
|
||||
// - `(local: <id>)` → reference to another port of the *same* element
|
||||
// - `(external: <anchor-name>)` → CeTZ anchor name of a port in another element
|
||||
// Accepted input forms:
|
||||
// - `str` matching a local port id → local
|
||||
// - `str` not matching any local port id → external, used as-is as CeTZ anchor
|
||||
// - `(element: <str>, port: <str>)` → external, anchor = `<element>-port-<port>`
|
||||
#let classify-align-with(raw-val, placements) = {
|
||||
if type(raw-val) == dictionary {
|
||||
if "element" in raw-val and "port" in raw-val {
|
||||
return (external: raw-val.element + "-port-" + raw-val.port)
|
||||
}
|
||||
panic("`align-with` dictionary must contain `element` and `port` keys, got: " + repr(raw-val))
|
||||
}
|
||||
if type(raw-val) != str {
|
||||
panic("`align-with` must be a string or `(element:, port:)` dictionary, got: " + repr(raw-val))
|
||||
}
|
||||
if raw-val in placements {
|
||||
return (local: raw-val)
|
||||
}
|
||||
return (external: raw-val)
|
||||
}
|
||||
|
||||
// Resolve a single port's placement, following `align-with` references
|
||||
// transitively and detecting cycles.
|
||||
//
|
||||
// Returns `(placements, result)` where `result` is either:
|
||||
// - `(pct: <ratio>)` → percentage along the side (local placement)
|
||||
// - `(external: <anchor-name>)` → align with the given remote CeTZ anchor
|
||||
#let resolve-placement(placements, id, visiting, elmt-id: "") = {
|
||||
let where = if elmt-id != "" { " (element '" + elmt-id + "')" } else { "" }
|
||||
let p = placements.at(id)
|
||||
if p.at("resolved", default: false) {
|
||||
if "external-ref" in p {
|
||||
return (placements, (external: p.external-ref))
|
||||
}
|
||||
return (placements, (pct: p.pct))
|
||||
}
|
||||
if id in visiting {
|
||||
panic("align-with cycle involving port '" + id + "'" + where + " (path: "
|
||||
+ visiting.join(" -> ") + " -> " + id + ")")
|
||||
}
|
||||
|
||||
let raw = p.raw
|
||||
let side = p.side
|
||||
let is-ew = side in ("east", "west")
|
||||
|
||||
let has-align = "align-with" in raw
|
||||
let has-x = "x" in raw
|
||||
let has-y = "y" in raw
|
||||
let has-num = has-x or has-y
|
||||
|
||||
if has-align and has-num {
|
||||
panic("Port '" + id + "'" + where + ": cannot combine `align-with` with `x`/`y` override")
|
||||
}
|
||||
if is-ew and has-x {
|
||||
panic("Port '" + id + "'" + where + " on side '" + side
|
||||
+ "': use `y` for placement override on east/west sides, not `x`")
|
||||
}
|
||||
if (not is-ew) and has-y {
|
||||
panic("Port '" + id + "'" + where + " on side '" + side
|
||||
+ "': use `x` for placement override on north/south sides, not `y`")
|
||||
}
|
||||
|
||||
let result = none
|
||||
if has-num {
|
||||
let val = if is-ew { raw.y } else { raw.x }
|
||||
let dim = if is-ew { p.height } else { p.width }
|
||||
if type(val) == function {
|
||||
val = val(dim)
|
||||
}
|
||||
let pct
|
||||
if type(val) == ratio {
|
||||
pct = val
|
||||
} else {
|
||||
// Numeric length: measured from `pt0` along the side.
|
||||
pct = (val / dim) * 100%
|
||||
}
|
||||
result = (pct: pct)
|
||||
} else if has-align {
|
||||
let classified = classify-align-with(raw.at("align-with"), placements)
|
||||
if "external" in classified {
|
||||
// Cross-element / raw anchor reference: axis cannot be validated here
|
||||
// (remote side unknown at eval time). Propagate as external.
|
||||
result = (external: classified.external)
|
||||
} else {
|
||||
let ref-id = classified.local
|
||||
let ref-side = placements.at(ref-id).side
|
||||
let ref-ew = ref-side in ("east", "west")
|
||||
if is-ew != ref-ew {
|
||||
panic("Port '" + id + "'" + where + " on '" + side + "' cannot `align-with` port '"
|
||||
+ ref-id + "' on '" + ref-side + "': incompatible axis")
|
||||
}
|
||||
let new-visiting = visiting + (id,)
|
||||
let res = resolve-placement(placements, ref-id, new-visiting, elmt-id: elmt-id)
|
||||
placements = res.at(0)
|
||||
result = res.at(1)
|
||||
}
|
||||
} else {
|
||||
result = (pct: p.pct-auto)
|
||||
}
|
||||
|
||||
if "external" in result {
|
||||
p.insert("external-ref", result.external)
|
||||
p.insert("pct", none)
|
||||
} else {
|
||||
p.insert("pct", result.pct)
|
||||
}
|
||||
p.insert("resolved", true)
|
||||
placements.insert(id, p)
|
||||
return (placements, result)
|
||||
}
|
||||
|
||||
#let add-ports(
|
||||
elmt-id,
|
||||
tl, tr, br, bl,
|
||||
ports,
|
||||
ports-margins,
|
||||
width: 0,
|
||||
height: 0,
|
||||
debug: false,
|
||||
outside-name: false,
|
||||
name-padding: 2pt,
|
||||
@@ -139,52 +256,117 @@
|
||||
return
|
||||
}
|
||||
|
||||
// ---- Phase 1: build placement table with per-port baseline pct. ----
|
||||
let placements = (:)
|
||||
// Preserve iteration order for phase 3.
|
||||
let order = ()
|
||||
// Per-side (pt0, pt1) after margin adjustment, and per-side spacing.
|
||||
let side-info = (:)
|
||||
|
||||
for (side, props) in sides {
|
||||
let side-ports = ports.at(side, default: ())
|
||||
let pt0 = props.at(0)
|
||||
let pt1 = props.at(1)
|
||||
|
||||
if side in ports-margins {
|
||||
let margins = ports-margins.at(side)
|
||||
let a = (pt0, margins.at(0), pt1)
|
||||
let b = (pt0, 100% - margins.at(1), pt1)
|
||||
pt0 = a
|
||||
pt1 = b
|
||||
}
|
||||
|
||||
let space = 100% / (side-ports.len() + 1)
|
||||
side-info.insert(side, (pt0: pt0, pt1: pt1, space: space))
|
||||
|
||||
for (i, port) in side-ports.enumerate() {
|
||||
let pct = (i + 1) * space
|
||||
let pt0 = props.at(0)
|
||||
let pt1 = props.at(1)
|
||||
|
||||
if side in ports-margins {
|
||||
let (a, b) = (pt0, pt1)
|
||||
let margins = ports-margins.at(side)
|
||||
a = (pt0, margins.at(0), pt1)
|
||||
b = (pt0, 100% - margins.at(1), pt1)
|
||||
pt0 = a
|
||||
pt1 = b
|
||||
let pid = port.at("id")
|
||||
if pid in placements {
|
||||
panic("Duplicate port id '" + pid + "' in element '" + elmt-id
|
||||
+ "' on side '" + side + "' (existing side: '"
|
||||
+ placements.at(pid).side + "', new port: " + repr(port) + ")")
|
||||
}
|
||||
|
||||
let pos = (pt0, pct, pt1)
|
||||
let pct-prev = (i + 0.5) * space
|
||||
let pct-next = (i + 1.5) * space
|
||||
let pos-prev = (pt0, pct-prev, pt1)
|
||||
let pos-next = (pt0, pct-next, pt1)
|
||||
let pct-auto = (i + 1) * space
|
||||
placements.insert(pid, (
|
||||
side: side,
|
||||
raw: port,
|
||||
index: i,
|
||||
pct-auto: pct-auto,
|
||||
width: width,
|
||||
height: height,
|
||||
resolved: false,
|
||||
))
|
||||
order.push(pid)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Phase 2: resolve overrides & align-with references. ----
|
||||
for pid in order {
|
||||
let res = resolve-placement(placements, pid, (), elmt-id: elmt-id)
|
||||
placements = res.at(0)
|
||||
}
|
||||
|
||||
// ---- Phase 3: draw. ----
|
||||
for pid in order {
|
||||
let p = placements.at(pid)
|
||||
let side = p.side
|
||||
let info = side-info.at(side)
|
||||
let pt0 = info.pt0
|
||||
let pt1 = info.pt1
|
||||
let space = info.space
|
||||
let port = p.raw
|
||||
let is-ew = side in ("east", "west")
|
||||
|
||||
let pos
|
||||
let pos-prev
|
||||
let pos-next
|
||||
|
||||
if "external-ref" in p {
|
||||
// Cross-element alignment: build pos via CeTZ perpendicular so this
|
||||
// port shares one coordinate with the remote anchor and stays on this
|
||||
// element's side line for the other coordinate.
|
||||
// east/west: same y as remote, x from this side (pt0.x == pt1.x)
|
||||
// north/south: same x as remote, y from this side (pt0.y == pt1.y)
|
||||
let ref = p.external-ref
|
||||
if is-ew {
|
||||
pos = (pt0, "|-", ref)
|
||||
// Fallback prev/next: just the pos itself; clock ports do not
|
||||
// combine meaningfully with cross-element align.
|
||||
pos-prev = pos
|
||||
pos-next = pos
|
||||
} else {
|
||||
pos = (ref, "|-", pt0)
|
||||
pos-prev = pos
|
||||
pos-next = pos
|
||||
}
|
||||
} else {
|
||||
let pct = p.pct
|
||||
pos = (pt0, pct, pt1)
|
||||
pos-prev = (pt0, pct - 0.5 * space, pt1)
|
||||
pos-next = (pt0, pct + 0.5 * space, pt1)
|
||||
|
||||
if port.at("small", default: false) {
|
||||
pos-prev = (pos, 4pt, pt0)
|
||||
pos-next = (pos, 4pt, pt1)
|
||||
}
|
||||
|
||||
add-port(
|
||||
elmt-id,
|
||||
side,
|
||||
port,
|
||||
pos,
|
||||
prev: pos-prev,
|
||||
next: pos-next,
|
||||
debug: debug,
|
||||
outside-name: outside-name,
|
||||
name-padding: name-padding,
|
||||
port-shape: port-shape,
|
||||
port-kind: port-kind,
|
||||
port-size: port-size,
|
||||
elmt-stroke: elmt-stroke,
|
||||
elmt-fill: elmt-fill,
|
||||
wire-inside: wire-inside,
|
||||
)
|
||||
}
|
||||
|
||||
add-port(
|
||||
elmt-id,
|
||||
side,
|
||||
port,
|
||||
pos,
|
||||
prev: pos-prev,
|
||||
next: pos-next,
|
||||
debug: debug,
|
||||
outside-name: outside-name,
|
||||
name-padding: name-padding,
|
||||
port-shape: port-shape,
|
||||
port-kind: port-kind,
|
||||
port-size: port-size,
|
||||
elmt-stroke: elmt-stroke,
|
||||
elmt-fill: elmt-fill,
|
||||
wire-inside: wire-inside,
|
||||
)
|
||||
}
|
||||
}
|
||||
+93
-22
@@ -27,29 +27,80 @@
|
||||
return (pts, anchors)
|
||||
}
|
||||
|
||||
#let get-zigzag-wire(pts, ratio, dir) = {
|
||||
// Normalize a `zigzag-at` value.
|
||||
//
|
||||
// Accepted forms:
|
||||
// - `none` → unset; ratio-based placement is used.
|
||||
// - `str` / tuple / native CeTZ dict → passed through as a CeTZ coordinate.
|
||||
// - `(rel: <length>, to: <coord>)` with a *scalar* `rel`
|
||||
// → ergonomic axial offset, rewritten
|
||||
// to `(rel: (rel, 0), to: to)` for
|
||||
// `dir == "vertical"` or
|
||||
// `(rel: (0, rel), to: to)` for
|
||||
// `dir == "horizontal"`. If `rel` is
|
||||
// already an array/tuple, the value
|
||||
// is passed through unchanged.
|
||||
#let normalize-zigzag-at(at, dir) = {
|
||||
if at == none { return none }
|
||||
if type(at) == dictionary and "rel" in at and "to" in at and type(at.rel) != array {
|
||||
let r = at.rel
|
||||
if dir == "vertical" {
|
||||
return (rel: (r, 0), to: at.to)
|
||||
} else {
|
||||
return (rel: (0, r), to: at.to)
|
||||
}
|
||||
}
|
||||
return at
|
||||
}
|
||||
|
||||
#let get-zigzag-wire(pts, ratio, dir, at: none) = {
|
||||
let start = pts.first()
|
||||
let end = pts.last()
|
||||
let mid = if dir == "vertical" {
|
||||
(start, ratio, (horizontal: end, vertical: ()))
|
||||
} else {
|
||||
(start, ratio, (horizontal: (), vertical: end))
|
||||
}
|
||||
|
||||
let points = if dir == "vertical" {
|
||||
(
|
||||
start,
|
||||
(horizontal: mid, vertical: ()),
|
||||
(horizontal: (), vertical: end),
|
||||
end
|
||||
)
|
||||
let points
|
||||
if at == none {
|
||||
// Ratio-based placement (default).
|
||||
let mid = if dir == "vertical" {
|
||||
(start, ratio, (horizontal: end, vertical: ()))
|
||||
} else {
|
||||
(start, ratio, (horizontal: (), vertical: end))
|
||||
}
|
||||
|
||||
points = if dir == "vertical" {
|
||||
(
|
||||
start,
|
||||
(horizontal: mid, vertical: ()),
|
||||
(horizontal: (), vertical: end),
|
||||
end
|
||||
)
|
||||
} else {
|
||||
(
|
||||
start,
|
||||
(horizontal: (), vertical: mid),
|
||||
(horizontal: end, vertical: ()),
|
||||
end
|
||||
)
|
||||
}
|
||||
} else {
|
||||
(
|
||||
start,
|
||||
(horizontal: (), vertical: mid),
|
||||
(horizontal: end, vertical: ()),
|
||||
end
|
||||
)
|
||||
// Anchor-based placement: the middle bend segment sits on the axis
|
||||
// extracted from `at` via CeTZ's perpendicular operator `|-`:
|
||||
// - vertical: middle segment at x = at.x
|
||||
// - horizontal: middle segment at y = at.y
|
||||
points = if dir == "vertical" {
|
||||
(
|
||||
start,
|
||||
(at, "|-", start),
|
||||
(at, "|-", end),
|
||||
end
|
||||
)
|
||||
} else {
|
||||
(
|
||||
start,
|
||||
(start, "|-", at),
|
||||
(end, "|-", at),
|
||||
end
|
||||
)
|
||||
}
|
||||
}
|
||||
let anchors = (
|
||||
"start": start,
|
||||
@@ -301,8 +352,9 @@
|
||||
/// - reverse (bool): If true, the start and end points will be swapped (useful in cases where the start point depends on the end point, for example with perpendiculars)
|
||||
/// - 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 (ratio): Position of the zigzag vertical relative to the horizontal span (only with style "zigzag")
|
||||
/// - 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-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")
|
||||
/// - dodge-margins (array): The start and end margins (i.e. space before dodging) of the wire (only with style "dodge")
|
||||
@@ -326,8 +378,9 @@
|
||||
guided-sides: ("east", "west"),
|
||||
directed: false,
|
||||
rotate-name: true,
|
||||
zigzag-ratio: 50%,
|
||||
zigzag-ratio: auto,
|
||||
zigzag-dir: "vertical",
|
||||
zigzag-at: none,
|
||||
dodge-y: 0,
|
||||
dodge-sides: ("east", "west"),
|
||||
dodge-margins: (5%, 5%),
|
||||
@@ -342,6 +395,19 @@
|
||||
panic("Wrong number of points (got " + str(pts.len()) + " instead of 2)")
|
||||
}
|
||||
|
||||
// `zigzag-at` validation.
|
||||
if zigzag-at != none {
|
||||
if style != "zigzag" {
|
||||
panic("wire '" + id + "': `zigzag-at` requires style: \"zigzag\" (got \""
|
||||
+ style + "\")")
|
||||
}
|
||||
if zigzag-ratio != auto {
|
||||
panic("wire '" + id
|
||||
+ "': cannot combine `zigzag-at` with an explicit `zigzag-ratio`")
|
||||
}
|
||||
}
|
||||
let zigzag-ratio-effective = if zigzag-ratio == auto { 50% } else { zigzag-ratio }
|
||||
|
||||
// Normalize user-provided coordinates: (x, y) -> (x, y, 0)
|
||||
let pts = pts.map(normalize-coord)
|
||||
|
||||
@@ -360,7 +426,12 @@
|
||||
(points, anchors) = get-direct-wire(pts)
|
||||
|
||||
} else if style == "zigzag" {
|
||||
(points, anchors) = get-zigzag-wire(pts, zigzag-ratio, zigzag-dir)
|
||||
(points, anchors) = get-zigzag-wire(
|
||||
pts,
|
||||
zigzag-ratio-effective,
|
||||
zigzag-dir,
|
||||
at: normalize-zigzag-at(zigzag-at, zigzag-dir),
|
||||
)
|
||||
|
||||
} else if style == "dodge" {
|
||||
(points, anchors) = get-dodge-wire(
|
||||
|
||||
Reference in new issue
Block a user