forked from HEL/circuiteria
Introduces wire.breakout: a 45° diagonal line of configurable length (default 1em) that starts at a given (x, y) and exposes a port anchor <id>-port-<id> at its end point so wires can attach to it as if it were a block port. A synthesized <id>.center anchor is placed opposite the configured port-side so wire.wire with style "u-shape" can auto-detect the port's cardinal side. - x/y accept number, (rel:, to:) and (from:) forms; (from:) sets the start point to the referenced anchor's coordinate. - direction: one of north-east/north-west/south-east/south-west. - Optional label positioned via name-pos (NE/NW/SE/SW); when name-pos is opposite to direction, the label falls back to the start point so it never covers the diagonal line. Label padding matches wire.wire (3pt). Also adds three examples (breakout, breakout-label, breakout-wire) in doc/examples.typ referenced from the docstring so tidy auto-renders them in the manual, and a gallery entry (gallery/test_breakout.typ) demonstrating orthogonal bus fan-out with breakouts in all four directions.
388 lines
12 KiB
Typst
388 lines
12 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 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(5) {
|
||
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"))
|
||
wire.wire("w5", ("p8", "p9"), style: "u-shape",
|
||
u-side: "east", u-depth: 0.5)
|
||
|
||
```, 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 breakout = example(```
|
||
// 16 breakouts: 4 directions × 4 label positions, laid out
|
||
// two per row so the labels have room to breathe.
|
||
let dirs = ("north-east", "north-west", "south-east", "south-west")
|
||
let poss = ("north-east", "north-west", "south-east", "south-west")
|
||
let short = (
|
||
"north-east": "NE", "north-west": "NW",
|
||
"south-east": "SE", "south-west": "SW",
|
||
)
|
||
let cols = 2
|
||
let dx = 5
|
||
let dy = 2
|
||
for (i, dir) in dirs.enumerate() {
|
||
for (j, pos) in poss.enumerate() {
|
||
let n = i * poss.len() + j
|
||
let r = calc.floor(n / cols)
|
||
let c = calc.rem(n, cols)
|
||
draw.circle((c * dx, -r * dy), radius: .05)
|
||
wire.breakout("b" + str(n),
|
||
x: c * dx, y: -r * dy,
|
||
direction: dir,
|
||
port-side: if dir.ends-with("east") { "west" } else { "east" },
|
||
name: short.at(dir) + "/" + short.at(pos),
|
||
name-pos: pos)
|
||
}
|
||
}
|
||
```)
|
||
|
||
#let breakout-label = example(```
|
||
// name-pos matches direction → label sits at the end point.
|
||
wire.breakout("b1", x: 0, y: 0, direction: "north-east",
|
||
port-side: "east",
|
||
name: "at end", name-pos: "north-east")
|
||
|
||
// name-pos opposite to direction → label would cover the line, so
|
||
// it falls back to the start point.
|
||
wire.breakout("b2", x: 4, y: 0, direction: "north-east",
|
||
port-side: "east",
|
||
name: "at start", name-pos: "south-west")
|
||
```, vertical: true)
|
||
|
||
#let breakout-wire = example(```
|
||
// Bus fan-out: a horizontal wire from the left block to the lower
|
||
// right block carries the full 10-bit bus. A breakout in the middle
|
||
// of that wire taps bits [9:5] and routes them to the upper right
|
||
// block via an L-shaped zigzag (up, then right).
|
||
element.block(x: 0, y: 0, w: 2, h: 2, id: "src",
|
||
fill: util.colors.blue,
|
||
ports: (east: ((id: "out"),)))
|
||
element.block(x: 6, y: 0, w: 2, h: 2, id: "dstL",
|
||
fill: util.colors.green,
|
||
ports: (west: ((id: "in"),)))
|
||
element.block(x: 6, y: 4, w: 2, h: 2, id: "dstU",
|
||
fill: util.colors.orange,
|
||
ports: (west: ((id: "in"),)))
|
||
|
||
// Full bus wire, labelled "[9:0]" on the left.
|
||
wire.wire("wbus", ("src-port-out", "dstL-port-in"),
|
||
name: "[9:0]", name-pos: "start")
|
||
|
||
// Breakout tapped from the midpoint of the bus.
|
||
wire.breakout("tap",
|
||
x: (rel: 2, to: "src-port-out"),
|
||
y: (from: "src-port-out"),
|
||
direction: "north-east",
|
||
port-side: "west",
|
||
name: "[9:5]", name-pos: "north-east")
|
||
|
||
// L-shaped wire from the breakout up and then right to the upper
|
||
// block's input.
|
||
wire.wire("wtap", ("tap-port-tap", "dstU-port-in"),
|
||
style: "zigzag", zigzag-dir: "horizontal",
|
||
zigzag-ratio: 100%)
|
||
```)
|
||
|
||
#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-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 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 u-shape-wire = example(```
|
||
// east <-> east (u-side auto-detected)
|
||
element.block(x: 0, y: 0, w: 1.5, h: 1.5, id: "eA",
|
||
fill: util.colors.orange,
|
||
ports: (east: ((id: "out"),)))
|
||
element.block(x: 0.5, y: -2.5, w: 1.5, h: 1.5, id: "eB",
|
||
fill: util.colors.orange,
|
||
ports: (east: ((id: "out"),)))
|
||
wire.wire("uEE", ("eA-port-out", "eB-port-out"),
|
||
style: "u-shape", u-depth: 0.5)
|
||
|
||
// west <-> west (auto-detected)
|
||
element.block(x: 6, y: 0, w: 1.5, h: 1.5, id: "wA",
|
||
fill: util.colors.yellow,
|
||
ports: (west: ((id: "in"),)))
|
||
element.block(x: 6.5, y: -2.5, w: 1.5, h: 1.5, id: "wB",
|
||
fill: util.colors.yellow,
|
||
ports: (west: ((id: "in"),)))
|
||
wire.wire("uWW", ("wA-port-in", "wB-port-in"),
|
||
style: "u-shape", u-depth: 0.5)
|
||
|
||
// north <-> north (auto-detected)
|
||
element.block(x: 10, y: -3, w: 1.5, h: 1.5, id: "nA",
|
||
fill: util.colors.green,
|
||
ports: (north: ((id: "out"),)))
|
||
element.block(x: 12.5, y: -2.5, w: 1.5, h: 1.5, id: "nB",
|
||
fill: util.colors.green,
|
||
ports: (north: ((id: "out"),)))
|
||
wire.wire("uNN", ("nA-port-out", "nB-port-out"),
|
||
style: "u-shape", u-depth: 0.5)
|
||
|
||
// south <-> south with explicit u-side override
|
||
element.block(x: 16, y: -3, w: 1.5, h: 1.5, id: "sA",
|
||
fill: util.colors.pink,
|
||
ports: (south: ((id: "out"),)))
|
||
element.block(x: 18.5, y: -3.5, w: 1.5, h: 1.5, id: "sB",
|
||
fill: util.colors.pink,
|
||
ports: (south: ((id: "out"),)))
|
||
wire.wire("uSS", ("sA-port-out", "sB-port-out"),
|
||
style: "u-shape", u-side: "south", u-depth: 0.5)
|
||
```, vertical: true)
|