Files
circuiteria/doc/examples.typ
T
David Stadelmann 6595387c5d feat: support cross-element port alignment via align-with
Extend the port `align-with` field to accept references to ports of
other elements, in addition to the existing local-port form:

  - "<local-id>"                    → same-element port (unchanged)
  - (element: "<id>", port: "<id>") → cross-element reference
  - "<raw-anchor>"                  → arbitrary CeTZ anchor name

Cross-element alignment is implemented via CeTZ perpendicular
projection (`|-`) so the port sits on this element's side line while
sharing the remote port's y (east/west) or x (north/south) coordinate.
Local resolution still validates matching axis and detects cycles;
external refs skip axis validation (remote side unknown at eval time)
and require the target element to be drawn first.

Also enrich port-related panic messages with the element id and port
context to make offending figures easier to locate, and add a manual
example (`examples.align-with-ports`) demonstrating both local and
cross-element alignment.
2026-10-22 00:30:22 +02:00

354 lines
11 KiB
Typst

#import "example.typ": example
#let alu = example(```
element.alu(x: 0, y: 0, w: 1, h: 2, id: "alu")
wire.stub("alu-port-in1", "west")
wire.stub("alu-port-in2", "west")
wire.stub("alu-port-out", "east")
```)
#let block = example(```
element.block(
x: 0, y: 0, w: 2, h: 2, id: "block",
ports: (
north: ((id: "clk", clock: true),),
west: ((id: "in1", name: "A"),
(id: "in2", name: "B")),
east: ((id: "out", name: "C"),)
)
)
wire.stub("block-port-clk", "north")
wire.stub("block-port-in1", "west")
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,
id: "extender"
)
wire.stub("extender-port-in", "west")
wire.stub("extender-port-out", "east")
```)
#let multiplexer = example(```
element.multiplexer(
x: 0, y: 0, w: 1, h: 3,
id: "multiplexer",
entries: 3
)
wire.stub("multiplexer.north", "north")
wire.stub("multiplexer-port-out", "east")
element.multiplexer(
x: 0, y: -4, w: 1, h: 3,
id: "multiplexer2",
entries: ("A", "B", "C")
)
wire.stub("multiplexer2.south", "south")
wire.stub("multiplexer2-port-out", "east")
for i in range(3) {
wire.stub("multiplexer-port-in" + str(i), "west")
wire.stub("multiplexer2-port-in" + str(i), "west")
}
```)
#let wires = example(```
for i in range(4) {
draw.circle((i * 3, 0), radius: .1, name: "p" + str(i * 2))
draw.circle((i * 3 + 2, 1), radius: .1, name: "p" + str(i * 2 + 1))
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("w3", ("p4", "p5"), style: "dodge",
dodge-y: -0.5, dodge-margins: (0.5, 0.5))
wire.wire("w4", ("p6","p7"), style: "guided",
guided-center:(20%, 40%), guided-margins: (90%,87%),
guided-sides: ("north","south"))
```, vertical: true)
#let stub = example(```
draw.circle((0, 0), radius: .1, name: "p")
wire.stub("p", "north", name: "north", length: 1)
wire.stub("p", "east", name: "east", vertical: true)
wire.stub("p", "south", name: "south", length: 15pt)
wire.stub("p", "west", name: "west", length: 3em)
```)
#let gate-and = example(```
gates.gate-and(x: 0, y: 0, w: 1.5, h: 1.5)
gates.gate-and(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let gate-nand = example(```
gates.gate-nand(x: 0, y: 0, w: 1.5, h: 1.5)
gates.gate-nand(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let gate-buf = example(```
gates.gate-buf(x: 0, y: 0, w: 1.5, h: 1.5)
gates.gate-buf(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let gate-not = example(```
gates.gate-not(x: 0, y: 0, w: 1.5, h: 1.5)
gates.gate-not(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let gate-or = example(```
gates.gate-or(x: 0, y: 0, w: 1.5, h: 1.5)
gates.gate-or(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let gate-nor = example(```
gates.gate-nor(x: 0, y: 0, w: 1.5, h: 1.5)
gates.gate-nor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let gate-xor = example(```
gates.gate-xor(x: 0, y: 0, w: 1.5, h: 1.5)
gates.gate-xor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let gate-xnor = example(```
gates.gate-xnor(x: 0, y: 0, w: 1.5, h: 1.5)
gates.gate-xnor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-and = example(```
gates.iec-gate-and(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-and(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-nand = example(```
gates.iec-gate-nand(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-nand(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-or = example(```
gates.iec-gate-or(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-or(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-nor = example(```
gates.iec-gate-nor(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-nor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-xor = example(```
gates.iec-gate-xor(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-xor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-xnor = example(```
gates.iec-gate-xnor(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-xnor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-buf = example(```
gates.iec-gate-buf(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-buf(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-not = example(```
gates.iec-gate-not(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-not(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let group = example(```
element.group(
id: "g1", name: "Group 1", stroke: (dash: "dashed"),
{
element.block(id: "b1", w: 2, h: 2,
x: 0, y: 1.5,
ports: (east: ((id: "out"),)),
fill: util.colors.green
)
element.block(id: "b2", w: 2, h: 1,
x: 0, y: 0,
ports: (east: ((id: "out"),)),
fill: util.colors.orange
)
}
)
element.block(id: "b3", w: 2, h: 3,
x: (rel: 1, to: "g1.east"),
y: (from: "b1-port-out", to: "in1"),
ports: (west: ((id: "in1"), (id: "in2"))),
fill: util.colors.blue
)
wire.wire("w1", ("b1-port-out", "b3-port-in1"))
wire.wire("w2", ("b2-port-out", "b3-port-in2"),
style: "zigzag")
```)
#let group-ports = example(```
// outside-port-names: true (default) — labels outside the border
element.group(
id: "g1", name: "outside (default)",
stroke: (dash: "dashed"),
fill: util.colors.blue.lighten(60%),
padding: 2em,
outside-port-names: true,
port-name-padding: 4pt,
ports: (
west: ((id: "in", name: "D"),),
east: ((id: "out", name: "Q"),),
),
{ element.block(id: "b1", w: 2, h: 1.5, x: 0, y: 0,
fill: util.colors.green,
ports: (west: ((id: "d", name: "d"),),
east: ((id: "q", name: "q"),))) }
)
wire.wire("wG1in", ("g1-port-in", "b1-port-d"), directed: true)
wire.wire("wG1out", ("b1-port-q", "g1-port-out"), directed: true)
// outside-port-names: false — labels inside the border (block style)
// with extra padding via port-name-padding
element.group(
id: "g2", name: "inside",
stroke: (dash: "dashed"),
fill: util.colors.orange.lighten(60%),
padding: 2em,
outside-port-names: false,
port-name-padding: 0.4em,
ports: (
west: ((id: "in", name: "D"),),
east: ((id: "out", name: "Q"),),
),
{ element.block(id: "b2", w: 2, h: 1.5, x: 6, y: 0,
fill: util.colors.green,
ports: (west: ((id: "d"),), east: ((id: "q"),))) }
)
wire.stub("g2-port-in", "west")
wire.stub("g2-port-out", "east")
```, vertical: true)
#let port-names-outside = example(```
element.block(
x: 0, y: 0, w: 2, h: 2, id: "block",
port-names-outside: true,
port-name-padding: 4pt,
ports: (
north: ((id: "clk", name: "CLK", clock: true),),
west: ((id: "in1", name: "A"),
(id: "in2", name: "B")),
east: ((id: "out", name: "Y"),)
)
)
```)
#let port-shape = example(```
// Block with pentagon-shaped ports.
// - port-shape / port-kind / port-size are element-level defaults.
// - Any port field (shape, kind, size, stroke, fill) overrides per port.
// - kind: "in" → tip points into the block (default)
// kind: "out" → tip points away from the block
// - clock ports with shape: "pentagon" drop their triangle and default
// to a gray fill.
element.block(
id: "b", w: 3, h: 3, x: 0, y: 0,
fill: util.colors.yellow.lighten(60%),
port-shape: "pentagon",
port-size: 1em,
ports: (
// west: default input
west: ((id: "in", name: "D"),),
// east: output — tip points away
east: ((id: "out", name: "Q", kind: "out"),),
// north: clock port — pentagon replaces the triangle, gray fill
north: ((id: "clk", name: "CLK", clock: true),),
// south: styled output with per-port fill and stroke
south: ((id: "rdy", name: "RDY", kind: "out",
fill: util.colors.blue, stroke: red + 1.5pt),),
),
)
// Wires connect at the outer end of each pentagon.
wire.stub("b-port-in", "west")
wire.stub("b-port-out", "east")
wire.stub("b-port-clk", "north")
wire.stub("b-port-rdy", "south")
```)
#let shell = example(```
// A shell block acts as a container: port name labels are drawn
// outside by default, and wires attach at the *inside* tip of
// pentagon-shaped ports so child elements can sit within.
element.block(
id: "sh", w: 5, h: 3, x: 0, y: 0,
shell: true,
port-shape: "pentagon",
fill: util.colors.blue.lighten(80%),
ports: (
west: ((id: "in", name: "IN"),),
east: ((id: "out", name: "OUT", kind: "out"),),
),
{
element.block(id: "inner", w: 2, h: 1.5, x: 1.5, y: 0.75,
fill: util.colors.yellow,
ports: (west: ((id: "a"),), east: ((id: "b"),)))
}
)
wire.wire("w-in", ("sh-port-in", "inner-port-a"))
wire.wire("w-out", ("inner-port-b", "sh-port-out"))
```)
#let intersection = example(```
wire.wire("w1", ((0, 0), (1, 1)), style: "zigzag")
wire.wire("w2", ((0, 0), (1, -.5)),
style: "zigzag", zigzag-ratio: 80%)
wire.intersection("w1.zig")
```)
#let port-shape-attach = example(
```
// Pentagon ports expose `.east`, `.west`, `.north`,
// `.south` sub-anchors on the pentagon boundary,
// letting wires attach to any face.
element.block(x: 0, y: 0, w: 2, h: 2, id: "a",
port-shape: "pentagon",
ports: (west: ((id: "p", name: "P"),),))
draw.circle((-2, 0.5), radius: .05, name: "L")
draw.circle(( 3, 1.5), radius: .05, name: "R")
draw.circle((-1, 2.5), radius: .05, name: "T")
// .west = outer flat base
wire.wire("w1", ("L", "a-port-p.west"), style: "direct")
// .east = tip inside the block
wire.wire("w2", ("R", "a-port-p.east"),
style: "direct", color: red)
// .north = top edge midpoint
wire.wire("w3", ("T", "a-port-p.north"),
style: "direct", color: blue)
```,
vertical: true,
)