forked from HEL/circuiteria
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f69c9cf49 | ||
|
|
add0e2929b |
No files matched your search
+75
-42
@@ -79,6 +79,81 @@ 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")
|
||||
@@ -186,48 +261,6 @@ 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 intersection = example(```
|
||||
wire.wire("w1", ((0, 0), (1, 1)), style: "zigzag")
|
||||
wire.wire("w2", ((0, 0), (1, -.5)),
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,117 @@
|
||||
// Demonstrates the wire.breakout element used to split off slices of a
|
||||
// bus that connects blocks. The main bus wires are always laid out
|
||||
// orthogonally; breakouts diagonally tap intermediate points on those
|
||||
// wires and route the sub-bus to another block via an L-shaped zigzag.
|
||||
#import "/src/lib.typ": circuit, element, util, wire
|
||||
|
||||
#set page(width: auto, height: auto, margin: .5cm)
|
||||
|
||||
#circuit({
|
||||
// ---- Source block on the left, main sink on the right ----
|
||||
element.block(x: 0, y: 0, w: 2, h: 2, id: "src", fill: util.colors.blue, ports: (
|
||||
east: ((id: "out"),),
|
||||
))
|
||||
|
||||
element.block(x: 15, y: 0, w: 2, h: 2, id: "dstMain", fill: util.colors.green, ports: (
|
||||
west: ((id: "in"),),
|
||||
))
|
||||
|
||||
// Full bus from src to dstMain, labelled [15:0] at the start.
|
||||
wire.wire("wbus", ("src-port-out", "dstMain-port-in"), name: "[15:0]", name-pos: "start")
|
||||
|
||||
// ---- Upper-left sink: taps bits [11:8] via NW breakout ----
|
||||
element.block(x: 0, y: 4, w: 2, h: 2, id: "dstNW", fill: util.colors.yellow, ports: (
|
||||
east: ((id: "in"),),
|
||||
))
|
||||
|
||||
wire.breakout(
|
||||
"tapNE",
|
||||
x: (rel: 3, to: "src-port-out"),
|
||||
y: (from: "src-port-out"),
|
||||
direction: "north-east",
|
||||
port-side: "west",
|
||||
name: "[15:12]",
|
||||
name-pos: "north-east",
|
||||
)
|
||||
|
||||
// L-shape: up first, then left into dstNW.
|
||||
wire.wire(
|
||||
"wNW",
|
||||
("tapNE-port-tapNE", "dstNW-port-in"),
|
||||
style: "zigzag",
|
||||
zigzag-dir: "horizontal",
|
||||
zigzag-ratio: 100%,
|
||||
)
|
||||
|
||||
// ---- Upper-right sink: taps bits [15:12] via NE breakout ----
|
||||
element.block(x: 15, y: 4, w: 2, h: 2, id: "dstNE", fill: util.colors.orange, ports: (
|
||||
west: ((id: "in"),),
|
||||
))
|
||||
|
||||
wire.breakout(
|
||||
"tapNW",
|
||||
x: (rel: 7, to: "src-port-out"),
|
||||
y: (from: "src-port-out"),
|
||||
direction: "north-west",
|
||||
port-side: "east",
|
||||
name: "[11:8]",
|
||||
name-pos: "north-east",
|
||||
)
|
||||
|
||||
// L-shape: up first, then right into dstNE.
|
||||
wire.wire(
|
||||
"wNE",
|
||||
("tapNW-port-tapNW", "dstNE-port-in"),
|
||||
style: "zigzag",
|
||||
zigzag-dir: "horizontal",
|
||||
zigzag-ratio: 100%,
|
||||
)
|
||||
|
||||
// ---- Lower-left sink: taps bits [3:0] via SW breakout ----
|
||||
element.block(x: 0, y: -4, w: 2, h: 2, id: "dstSW", fill: util.colors.purple, ports: (
|
||||
east: ((id: "in"),),
|
||||
))
|
||||
|
||||
wire.breakout(
|
||||
"tapSE",
|
||||
x: (rel: 5, to: "src-port-out"),
|
||||
y: (from: "src-port-out"),
|
||||
direction: "south-east",
|
||||
port-side: "west",
|
||||
name: "[7:4]",
|
||||
name-pos: "south-east",
|
||||
)
|
||||
|
||||
// L-shape: down first, then left into dstSW.
|
||||
wire.wire(
|
||||
"wSW",
|
||||
("tapSE-port-tapSE", "dstSW-port-in"),
|
||||
style: "zigzag",
|
||||
zigzag-dir: "horizontal",
|
||||
zigzag-ratio: 100%,
|
||||
)
|
||||
|
||||
// ---- Lower-right sink: taps bits [7:4] via SE breakout ----
|
||||
element.block(x: 15, y: -4, w: 2, h: 2, id: "dstSE", fill: util.colors.pink, ports: (
|
||||
west: ((id: "in"),),
|
||||
))
|
||||
|
||||
wire.breakout(
|
||||
"tapSW",
|
||||
x: (rel: 11, to: "src-port-out"),
|
||||
y: (from: "src-port-out"),
|
||||
direction: "south-west",
|
||||
port-side: "east",
|
||||
name: "[3:0]",
|
||||
name-pos: "south-west",
|
||||
)
|
||||
|
||||
// L-shape: down first, then right into dstSE.
|
||||
wire.wire(
|
||||
"wSE",
|
||||
("tapSW-port-tapSW", "dstSE-port-in"),
|
||||
style: "zigzag",
|
||||
zigzag-dir: "horizontal",
|
||||
zigzag-ratio: 100%,
|
||||
)
|
||||
})
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB |
@@ -1,86 +0,0 @@
|
||||
// Demonstrates ports on a group element.
|
||||
// outside-port-names: true (default) — labels outside the group border
|
||||
// outside-port-names: false — labels inside (block-style)
|
||||
#import "/src/lib.typ": circuit, element, util, wire
|
||||
|
||||
#set page(width: auto, height: auto, margin: .5cm)
|
||||
|
||||
#circuit({
|
||||
// --- Group with port labels INSIDE (outside-port-names: false) ---
|
||||
element.group(
|
||||
id: "cpu",
|
||||
name: "CPU (labels outside)",
|
||||
name-anchor: "north",
|
||||
stroke: (dash: "dashed"),
|
||||
fill: util.colors.yellow.lighten(60%),
|
||||
padding: 1em,
|
||||
port-name-padding: .5em,
|
||||
outside-port-names: true,
|
||||
ports: (
|
||||
west: (
|
||||
(id: "in1", name: "D1"),
|
||||
(id: "in2", name: "D2"),
|
||||
),
|
||||
east: ((id: "out", name: "Q"),),
|
||||
south: (
|
||||
(id: "rst", name: "RST", vertical: true),
|
||||
(id: "done", name: "DONE", vertical: true),
|
||||
),
|
||||
),
|
||||
{
|
||||
element.block(
|
||||
id: "reg2",
|
||||
w: 2.5,
|
||||
h: 3,
|
||||
x: 0,
|
||||
y: 0,
|
||||
fill: util.colors.green,
|
||||
ports: (
|
||||
west: ((id: "d1"), (id: "d2")),
|
||||
east: ((id: "q"),),
|
||||
north: ((id: "clk", clock: true),),
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
// --- Group with port labels INSIDE (outside-port-names: false) ---
|
||||
element.group(
|
||||
id: "cpu2",
|
||||
name: "CPU (labels inside)",
|
||||
name-anchor: "north",
|
||||
stroke: (dash: "dashed"),
|
||||
fill: util.colors.yellow.lighten(60%),
|
||||
padding: 4em,
|
||||
outside-port-names: false,
|
||||
port-name-padding: 0.5em,
|
||||
ports: (
|
||||
west: (
|
||||
(id: "in1", name: "D1"),
|
||||
(id: "in2", name: "D2"),
|
||||
),
|
||||
east: ((id: "out", name: "Q"),),
|
||||
south: (
|
||||
(id: "rst", name: "RST", vertical: true),
|
||||
(id: "done", name: "DONE", vertical: true),
|
||||
),
|
||||
),
|
||||
{
|
||||
element.block(
|
||||
id: "reg2",
|
||||
w: 2.5,
|
||||
h: 3,
|
||||
x: 10,
|
||||
y: 0,
|
||||
fill: util.colors.green,
|
||||
ports: (
|
||||
west: ((id: "d1"), (id: "d2")),
|
||||
east: ((id: "q"),),
|
||||
north: ((id: "clk", clock: true),),
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
wire.stub("cpu2-port-in1", "west", name: "A")
|
||||
wire.stub("cpu2-port-in2", "west", name: "B")
|
||||
wire.stub("cpu2-port-out", "east", name: "Q")
|
||||
})
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
@@ -0,0 +1,46 @@
|
||||
// Demonstrates the `(rel: <number>, to: <anchor>)` shorthand for the `y`
|
||||
// parameter (analogous to the existing shorthand for `x`).
|
||||
#import "/src/lib.typ": circuit, element, util, wire
|
||||
|
||||
#set page(width: auto, height: auto, margin: .5cm)
|
||||
|
||||
#circuit({
|
||||
// Anchor block: absolute placement.
|
||||
element.block(
|
||||
x: 0, y: 0, w: 2, h: 1.5,
|
||||
id: "A",
|
||||
fill: util.colors.orange,
|
||||
ports: (east: ((id: "out"),))
|
||||
)
|
||||
|
||||
// X relative, Y absolute (existing feature).
|
||||
element.block(
|
||||
x: (rel: 1, to: "A.east"), y: 0, w: 2, h: 1.5,
|
||||
id: "B",
|
||||
fill: util.colors.yellow,
|
||||
ports: (west: ((id: "in"),), east: ((id: "out"),))
|
||||
)
|
||||
|
||||
// Both X and Y relative to another element's anchor (new feature).
|
||||
element.block(
|
||||
x: (rel: 1, to: "B.east"),
|
||||
y: (rel: 2, to: "B.south-west"),
|
||||
w: 2, h: 1.5,
|
||||
id: "C",
|
||||
fill: util.colors.green,
|
||||
ports: (west: ((id: "in"),))
|
||||
)
|
||||
|
||||
// Y relative only, X absolute.
|
||||
element.block(
|
||||
x: 0,
|
||||
y: (rel: -2.5, to: "A.south-west"),
|
||||
w: 2, h: 1.5,
|
||||
id: "D",
|
||||
fill: util.colors.blue,
|
||||
ports: (east: ((id: "out"),))
|
||||
)
|
||||
|
||||
wire.wire("wAB", ("A-port-out", "B-port-in"))
|
||||
wire.wire("wBC", ("B-port-out", "C-port-in"), style: "zigzag")
|
||||
})
|
||||
+7547
-6701
File diff suppressed because it is too large.
Load diff
@@ -27,7 +27,6 @@
|
||||
name-anchor: "center",
|
||||
ports: (),
|
||||
ports-margins: (),
|
||||
port-name-padding: 2pt,
|
||||
fill: none,
|
||||
radius: 0.5em,
|
||||
stroke: black + 1pt,
|
||||
@@ -45,7 +44,6 @@
|
||||
name-anchor: name-anchor,
|
||||
ports: ports,
|
||||
ports-margins: ports-margins,
|
||||
port-name-padding: port-name-padding,
|
||||
fill: fill,
|
||||
stroke: stroke,
|
||||
id: id,
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
/// If it is a dictionary, it should be in the format `(rel: number, to: str)`, where `rel` is the offset and `to` the base anchor
|
||||
/// - y (number, dictionary): The y position (bottom-left corner).
|
||||
///
|
||||
/// If it is a dictionary, it should be in the format `(from: str, to: str)`, where `from` is the base anchor and `to` is the id of the port to align with the anchor
|
||||
/// If it is a dictionary, it can take one of two forms:
|
||||
/// - `(rel: number, to: str)`: `rel` is the y offset and `to` the base anchor (analogous to `x`)
|
||||
/// - `(from: str, to: str)`: `from` is the base anchor and `to` is the id of the port to align with the anchor
|
||||
/// - w (number): Width of the element
|
||||
/// - h (number): Height of the element
|
||||
/// - name (none, str): Optional name of the block
|
||||
@@ -35,7 +37,6 @@
|
||||
/// - `clock` (`bool`): Whether it is a clock port (triangle symbol)
|
||||
/// - `vertical` (`bool`): Whether the name should be drawn vertically
|
||||
/// - 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`)
|
||||
/// - fill (none, color): Fill color
|
||||
/// - stroke (stroke): Border stroke
|
||||
/// - id (str): The block id (for future reference)
|
||||
@@ -55,7 +56,6 @@
|
||||
name-anchor: "center",
|
||||
ports: (:),
|
||||
ports-margins: (:),
|
||||
port-name-padding: 2pt,
|
||||
fill: none,
|
||||
stroke: black + 1pt,
|
||||
id: "",
|
||||
@@ -76,13 +76,12 @@
|
||||
if h == none { panic("Parameter h must be set") }
|
||||
|
||||
if (type(x) == dictionary) {
|
||||
let offset = x.rel
|
||||
let to = x.to
|
||||
let (ctx, to-pos) = coordinate.resolve(ctx, (rel: (offset, 0), to: to))
|
||||
x = to-pos.at(0)
|
||||
x = util.resolve-rel-coord(ctx, x, 0)
|
||||
}
|
||||
|
||||
if (type(y) == dictionary) {
|
||||
if (type(y) == dictionary and "rel" in y) {
|
||||
y = util.resolve-rel-coord(ctx, y, 1)
|
||||
} else if (type(y) == dictionary) {
|
||||
let from = y.from
|
||||
let to = y.to
|
||||
let (to-side, i) = find-port(ports, to)
|
||||
@@ -141,8 +140,7 @@
|
||||
tl, tr, br, bl,
|
||||
ports,
|
||||
ports-margins,
|
||||
debug: debug.ports,
|
||||
name-padding: port-name-padding,
|
||||
debug: debug.ports
|
||||
)
|
||||
}
|
||||
})
|
||||
+1
-33
@@ -1,15 +1,9 @@
|
||||
#import "/src/cetz.typ": draw, coordinate
|
||||
#import "../util.typ"
|
||||
#import "ports.typ": add-ports
|
||||
|
||||
/// Draws a group of elements
|
||||
///
|
||||
/// #examples.group
|
||||
///
|
||||
/// Ports can also be defined on a group, just like on a block. By default
|
||||
/// port labels are drawn *outside* the group border so they do not overlap
|
||||
/// child elements. Set `outside-port-names: false` to draw them inside.
|
||||
/// #examples.group-ports
|
||||
/// - body (elements, function): Elements to group
|
||||
/// - id (str): see #doc-ref("element.elmt")
|
||||
/// - name (str): The group's name
|
||||
@@ -22,11 +16,6 @@
|
||||
/// - array: either (`<all>`,), (`<vertical>`, `<horizontal>`) or (`<top>`, `<right>`, `<bottom>`, `<left>`)
|
||||
/// - dictionary: valid keys are "top", "right", "bottom" and "left"
|
||||
/// - radius (number): The corner radius
|
||||
/// - ports (dictionary): see #doc-ref("element.elmt")
|
||||
/// - ports-margins (dictionary): see #doc-ref("element.elmt")
|
||||
/// - outside-port-names (bool): If `true` (default), port name labels are drawn outside the group border so they do not overlap child elements. Set to `false` to draw labels inside, matching the block behaviour
|
||||
/// - port-name-padding (number, length): Padding between the port name label and the group border (default `2pt`). Accepts anything CeTZ accepts as `padding` (number or length)
|
||||
/// - debug (dictionary): see #doc-ref("element.elmt")
|
||||
#let group(
|
||||
body,
|
||||
id: "",
|
||||
@@ -35,14 +24,7 @@
|
||||
fill: none,
|
||||
stroke: black + 1pt,
|
||||
padding: 0.5em,
|
||||
radius: 0.5em,
|
||||
ports: (:),
|
||||
ports-margins: (:),
|
||||
outside-port-names: true,
|
||||
port-name-padding: 2pt,
|
||||
debug: (
|
||||
ports: false
|
||||
)
|
||||
radius: 0.5em
|
||||
) = {
|
||||
let min-x = none
|
||||
let max-x = none
|
||||
@@ -86,20 +68,6 @@
|
||||
stroke: stroke,
|
||||
fill: fill
|
||||
)
|
||||
|
||||
add-ports(
|
||||
id,
|
||||
id + ".north-west",
|
||||
id + ".north-east",
|
||||
id + ".south-east",
|
||||
id + ".south-west",
|
||||
ports,
|
||||
ports-margins,
|
||||
debug: debug.ports,
|
||||
outside-name: outside-port-names,
|
||||
name-padding: port-name-padding,
|
||||
)
|
||||
|
||||
if name != none {
|
||||
draw.content(
|
||||
id + "." + name-anchor,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#import "/src/cetz.typ": draw, coordinate
|
||||
#import "/src/util.typ"
|
||||
#import "../ports.typ": add-ports, add-port
|
||||
#import "../element.typ"
|
||||
|
||||
@@ -49,13 +50,12 @@
|
||||
if h == none { panic("Parameter h must be set") }
|
||||
|
||||
if (type(x) == dictionary) {
|
||||
let offset = x.rel
|
||||
let to = x.to
|
||||
let (ctx, to-pos) = coordinate.resolve(ctx, (rel: (offset, 0), to: to))
|
||||
x = to-pos.at(0)
|
||||
x = util.resolve-rel-coord(ctx, x, 0)
|
||||
}
|
||||
|
||||
if (type(y) == dictionary) {
|
||||
if (type(y) == dictionary and "rel" in y) {
|
||||
y = util.resolve-rel-coord(ctx, y, 1)
|
||||
} else if (type(y) == dictionary) {
|
||||
let from = y.from
|
||||
let to = y.to
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#import "/src/cetz.typ": draw, coordinate
|
||||
#import "/src/util.typ"
|
||||
#import "../ports.typ": add-ports, add-port
|
||||
#import "../element.typ"
|
||||
|
||||
@@ -60,13 +61,12 @@
|
||||
if h == none { panic("Parameter h must be set") }
|
||||
|
||||
if (type(x) == dictionary) {
|
||||
let offset = x.rel
|
||||
let to = x.to
|
||||
let (ctx, to-pos) = coordinate.resolve(ctx, (rel: (offset, 0), to: to))
|
||||
x = to-pos.at(0)
|
||||
x = util.resolve-rel-coord(ctx, x, 0)
|
||||
}
|
||||
|
||||
if (type(y) == dictionary) {
|
||||
if (type(y) == dictionary and "rel" in y) {
|
||||
y = util.resolve-rel-coord(ctx, y, 1)
|
||||
} else if (type(y) == dictionary) {
|
||||
let from = y.from
|
||||
let to = y.to
|
||||
|
||||
|
||||
+6
-24
@@ -1,13 +1,11 @@
|
||||
#import "/src/cetz.typ": draw
|
||||
#import "../util.typ": rotate-anchor, opposite-anchor
|
||||
#import "../util.typ": rotate-anchor
|
||||
|
||||
#let add-port(
|
||||
elmt-id, side, port, pos,
|
||||
prev: none,
|
||||
next: none,
|
||||
debug: false,
|
||||
outside-name: false,
|
||||
name-padding: 2pt,
|
||||
debug: false
|
||||
) = {
|
||||
let name = port.at("name", default: "")
|
||||
let name-rotate = port.at("vertical", default: false)
|
||||
@@ -29,22 +27,10 @@
|
||||
// TODO: use context or vectors to have the height relative to the width
|
||||
draw.line(prev, pos1, next)
|
||||
}
|
||||
|
||||
// When outside-name is true (e.g. for groups) the label is placed on the
|
||||
// exterior of the element border so it does not overlap child elements.
|
||||
// Horizontal labels: anchor = opposite of the side (e.g. east port → west-anchored text, outside).
|
||||
// Vertical labels: same opposite side but rotated 90°, keeping text readable.
|
||||
let name-anchor = if outside-name {
|
||||
if name-rotate { rotate-anchor(opposite-anchor(side)) }
|
||||
else { opposite-anchor(side) }
|
||||
} else {
|
||||
if name-rotate { rotate-anchor(side) } else { side }
|
||||
}
|
||||
|
||||
draw.content(
|
||||
pos,
|
||||
anchor: name-anchor,
|
||||
padding: name-padding,
|
||||
anchor: if name-rotate {rotate-anchor(side)} else {side},
|
||||
padding: 2pt,
|
||||
angle: if name-rotate {90deg} else {0deg},
|
||||
name
|
||||
)
|
||||
@@ -74,9 +60,7 @@
|
||||
tl, tr, br, bl,
|
||||
ports,
|
||||
ports-margins,
|
||||
debug: false,
|
||||
outside-name: false,
|
||||
name-padding: 2pt,
|
||||
debug: false
|
||||
) = {
|
||||
let sides = (
|
||||
"north": (tl, tr),
|
||||
@@ -125,9 +109,7 @@
|
||||
pos,
|
||||
prev: pos-prev,
|
||||
next: pos-next,
|
||||
debug: debug,
|
||||
outside-name: outside-name,
|
||||
name-padding: name-padding,
|
||||
debug: debug
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
#import "cetz.typ": coordinate
|
||||
|
||||
/// Resolves a relative coordinate shorthand of the form `(rel: <number>, to: <anchor>)`
|
||||
/// to a single scalar along the requested axis.
|
||||
///
|
||||
/// Used to implement the shared `(rel:, to:)` shorthand accepted by the `x` and
|
||||
/// `y` parameters of `elmt`, `gate` and `iec-gate`.
|
||||
///
|
||||
/// - ctx (dictionary): The current CeTZ context
|
||||
/// - val (dictionary): A dictionary with fields `rel` (number) and `to` (anchor)
|
||||
/// - axis (int): `0` for x, `1` for y
|
||||
/// -> number
|
||||
#let resolve-rel-coord(ctx, val, axis) = {
|
||||
let offset = val.rel
|
||||
let to = val.to
|
||||
let delta = if axis == 0 { (offset, 0) } else { (0, offset) }
|
||||
let (_, to-pos) = coordinate.resolve(ctx, (rel: delta, to: to))
|
||||
return to-pos.at(axis)
|
||||
}
|
||||
|
||||
/// Predefined color palette
|
||||
/// #let w = 4
|
||||
/// #box(width: 100%, align(center)[
|
||||
|
||||
+139
-1
@@ -1,5 +1,5 @@
|
||||
#import "/src/cetz.typ": draw, coordinate
|
||||
#import "util.typ": opposite-anchor, normalize-coord
|
||||
#import "util.typ": opposite-anchor, normalize-coord, resolve-rel-coord
|
||||
|
||||
/// List of valid wire styles
|
||||
/// #examples.wires
|
||||
@@ -403,3 +403,141 @@
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws a wire breakout: a 45° diagonal line starting at (x, y) and
|
||||
/// ending 1 unit away in one of the four diagonal directions. The end
|
||||
/// 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 the cardinal side the port faces
|
||||
/// can be deduced by comparing the port with the `.center` anchor.
|
||||
///
|
||||
/// #examples.breakout
|
||||
///
|
||||
/// Label positions are anchored so that the label sits neatly next to the
|
||||
/// end point of the breakout. When the label direction matches the
|
||||
/// breakout direction (which would overlap the line) the label is
|
||||
/// automatically placed at the start point instead so the text does not
|
||||
/// cover the line.
|
||||
///
|
||||
/// #examples.breakout-label
|
||||
///
|
||||
/// The port anchor makes it easy to fan out a bus:
|
||||
///
|
||||
/// #examples.breakout-wire
|
||||
///
|
||||
/// - id (str): The breakout id (also used as the port id)
|
||||
/// - x (number, dictionary): The starting x position (same forms as `element.block`)
|
||||
/// - y (number, dictionary): The starting y position (same forms as `element.block`)
|
||||
/// - direction (str): One of `"north-east"`, `"north-west"`, `"south-east"`, `"south-west"`
|
||||
/// - length (length, number): The diagonal length (default `1em`, matches port size)
|
||||
/// - port-side (str): Cardinal side used for wire-attach behavior (`"north"`, `"east"`, `"south"`, `"west"`)
|
||||
/// - stroke (stroke): Line stroke (paint + thickness)
|
||||
/// - name (none, str): Optional label displayed near the breakout
|
||||
/// - name-pos (none, str): Label position, one of `"north-west"`, `"north-east"`, `"south-west"`, `"south-east"`
|
||||
/// - debug (bool): If true, shows a red dot at the port anchor
|
||||
#let breakout(
|
||||
id,
|
||||
x: none, y: none,
|
||||
direction: "north-east",
|
||||
length: 1em,
|
||||
port-side: "east",
|
||||
stroke: black + 1pt,
|
||||
name: none,
|
||||
name-pos: none,
|
||||
debug: false,
|
||||
) = draw.get-ctx(ctx => {
|
||||
if x == none { panic("breakout: parameter x must be set") }
|
||||
if y == none { panic("breakout: parameter y must be set") }
|
||||
|
||||
let valid-directions = ("north-east", "north-west", "south-east", "south-west")
|
||||
if not direction in valid-directions {
|
||||
panic("breakout: invalid direction '" + repr(direction)
|
||||
+ "'; must be one of " + repr(valid-directions))
|
||||
}
|
||||
let valid-sides = ("north", "east", "south", "west")
|
||||
if not port-side in valid-sides {
|
||||
panic("breakout: invalid port-side '" + repr(port-side)
|
||||
+ "'; must be one of " + repr(valid-sides))
|
||||
}
|
||||
|
||||
// Diagonal component: length / sqrt(2). Resolve to a canvas-space
|
||||
// number so we can do arithmetic with anchor coordinates.
|
||||
let d-raw = length / calc.sqrt(2)
|
||||
let (_, d-vec) = coordinate.resolve(ctx, (d-raw, 0, 0))
|
||||
let d = d-vec.at(0)
|
||||
|
||||
// Direction sign vector for (dx, dy).
|
||||
let sx = if direction.ends-with("east") { 1 } else { -1 }
|
||||
let sy = if direction.starts-with("north") { 1 } else { -1 }
|
||||
|
||||
// Resolve x.
|
||||
let sx0 = x
|
||||
if type(x) == dictionary {
|
||||
if "rel" in x {
|
||||
sx0 = resolve-rel-coord(ctx, x, 0)
|
||||
} else if "from" in x {
|
||||
let (_, from-pos) = coordinate.resolve(ctx, x.from)
|
||||
sx0 = from-pos.at(0)
|
||||
} else {
|
||||
panic("breakout: unsupported x dictionary: " + repr(x))
|
||||
}
|
||||
}
|
||||
// Resolve y.
|
||||
let sy0 = y
|
||||
if type(y) == dictionary {
|
||||
if "rel" in y {
|
||||
sy0 = resolve-rel-coord(ctx, y, 1)
|
||||
} else if "from" in y {
|
||||
let (_, from-pos) = coordinate.resolve(ctx, y.from)
|
||||
sy0 = from-pos.at(1)
|
||||
} else {
|
||||
panic("breakout: unsupported y dictionary: " + repr(y))
|
||||
}
|
||||
}
|
||||
|
||||
let start = (sx0, sy0)
|
||||
let end = (rel: (sx * d, sy * d), to: start)
|
||||
|
||||
// Offset used for the synthesized center anchor: opposite of port-side.
|
||||
let center-offset = (
|
||||
"north": (0, -1em),
|
||||
"east": (-1em, 0),
|
||||
"south": (0, 1em),
|
||||
"west": (1em, 0),
|
||||
).at(port-side)
|
||||
|
||||
let port-anchor-name = id + "-port-" + id
|
||||
|
||||
draw.group(name: id, {
|
||||
draw.line(start, end, stroke: stroke)
|
||||
draw.anchor("start", start)
|
||||
draw.anchor("end", end)
|
||||
draw.anchor("center", (rel: center-offset, to: end))
|
||||
|
||||
if name != none and name-pos != none {
|
||||
let label-anchor = (
|
||||
"north-west": "south-east",
|
||||
"north-east": "south-west",
|
||||
"south-west": "north-east",
|
||||
"south-east": "north-west",
|
||||
).at(name-pos)
|
||||
|
||||
// Overlap case: label direction is opposite to breakout direction,
|
||||
// so the label at the end point would extend back over the line.
|
||||
// Anchor it at the start point instead.
|
||||
let label-pos = if name-pos == opposite-anchor(direction) { start } else { end }
|
||||
|
||||
draw.content(label-pos, anchor: label-anchor, padding: 3pt, name)
|
||||
}
|
||||
})
|
||||
|
||||
// Port anchor emitted at top level so it can be referenced by its
|
||||
// bare name (mirrors add-port's approach in ports.typ).
|
||||
if debug {
|
||||
draw.circle(end, name: port-anchor-name,
|
||||
radius: .1, stroke: none, fill: red)
|
||||
} else {
|
||||
draw.hide(draw.circle(end, name: port-anchor-name,
|
||||
radius: 0, stroke: none))
|
||||
}
|
||||
})
|
||||
Reference in new issue
Block a user