forked from HEL/circuiteria
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a51e8bb93b | ||
|
|
563bb7b1d2 | ||
|
|
0d1478c765 |
No files matched your search
@@ -188,6 +188,80 @@ wire.wire("w2", ("b2-port-out", "b3-port-in2"),
|
|||||||
style: "zigzag")
|
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(```
|
#let intersection = example(```
|
||||||
wire.wire("w1", ((0, 0), (1, 1)), style: "zigzag")
|
wire.wire("w1", ((0, 0), (1, 1)), style: "zigzag")
|
||||||
wire.wire("w2", ((0, 0), (1, -.5)),
|
wire.wire("w2", ((0, 0), (1, -.5)),
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,86 @@
|
|||||||
|
// 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: 39 KiB |
@@ -0,0 +1,117 @@
|
|||||||
|
// Demonstrates pentagon-shaped ports.
|
||||||
|
//
|
||||||
|
// - Element-level defaults: `port-shape`, `port-kind`, `port-size`.
|
||||||
|
// - Per-port overrides: `shape`, `kind`, `size`, `stroke`, `fill`.
|
||||||
|
// - Wire endpoints connect at the outer end of each pentagon.
|
||||||
|
// - Clock ports with pentagon shape drop the clock triangle and default
|
||||||
|
// to a gray fill.
|
||||||
|
#import "/src/lib.typ": circuit, element, util, wire
|
||||||
|
|
||||||
|
#set page(width: auto, height: auto, margin: .5cm)
|
||||||
|
|
||||||
|
#circuit({
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
// 1) All four sides × input/output
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
element.block(
|
||||||
|
id: "grid", w: 4, h: 4, x: 0, y: 0,
|
||||||
|
name: "sides × in/out", name-anchor: "center",
|
||||||
|
fill: util.colors.yellow.lighten(60%),
|
||||||
|
port-shape: "pentagon",
|
||||||
|
ports: (
|
||||||
|
west: (
|
||||||
|
(id: "wi", name: "W-in"),
|
||||||
|
(id: "wo", name: "W-out", kind: "out"),
|
||||||
|
),
|
||||||
|
east: (
|
||||||
|
(id: "ei", name: "E-in"),
|
||||||
|
(id: "eo", name: "E-out", kind: "out"),
|
||||||
|
),
|
||||||
|
north: (
|
||||||
|
(id: "ni", name: "N-in", vertical: true),
|
||||||
|
(id: "no", name: "N-out", kind: "out", vertical: true),
|
||||||
|
),
|
||||||
|
south: (
|
||||||
|
(id: "si", name: "S-in", vertical: true),
|
||||||
|
(id: "so", name: "S-out", kind: "out", vertical: true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
// 2) Per-port styling: fill and stroke overrides + size override
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
element.block(
|
||||||
|
id: "styled", w: 3, h: 3, x: 10, y: 0,
|
||||||
|
name: "per-port styling", name-anchor: "center",
|
||||||
|
fill: util.colors.green.lighten(40%),
|
||||||
|
port-shape: "pentagon",
|
||||||
|
ports: (
|
||||||
|
west: ((id: "a", name: "A", fill: util.colors.blue),),
|
||||||
|
east: ((id: "b", name: "B", kind: "out",
|
||||||
|
stroke: red + 1.5pt, fill: util.colors.pink),),
|
||||||
|
north: ((id: "c", name: "C", size: 1.5em),),
|
||||||
|
south: ((id: "d", name: "D", kind: "out",
|
||||||
|
fill: util.colors.orange),),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
// 3) Clock port with pentagon → gray fill, no triangle
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
element.block(
|
||||||
|
id: "clk", w: 3, h: 2.5, x: 16, y: 0,
|
||||||
|
name: "clock + pentagon", name-anchor: "center",
|
||||||
|
fill: util.colors.orange.lighten(60%),
|
||||||
|
port-shape: "pentagon",
|
||||||
|
ports: (
|
||||||
|
west: ((id: "d", name: "D"),),
|
||||||
|
east: ((id: "q", name: "Q", kind: "out"),),
|
||||||
|
north: ((id: "clk", name: "CLK", clock: true),),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
// 4) Two pentagon-ported blocks connected by a wire
|
||||||
|
// (wire terminates at outer edge of each pentagon)
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
element.block(
|
||||||
|
id: "src", w: 2, h: 1.5, x: 0, y: -5,
|
||||||
|
name: "src", name-anchor: "center",
|
||||||
|
fill: util.colors.pink,
|
||||||
|
port-shape: "pentagon",
|
||||||
|
ports: (east: ((id: "q", name: "Q", kind: "out"),)),
|
||||||
|
)
|
||||||
|
element.block(
|
||||||
|
id: "dst", w: 2, h: 1.5, x: 6, y: -5,
|
||||||
|
name: "dst", name-anchor: "center",
|
||||||
|
fill: util.colors.blue,
|
||||||
|
port-shape: "pentagon",
|
||||||
|
ports: (west: ((id: "d", name: "D"),)),
|
||||||
|
)
|
||||||
|
wire.wire("w-sd", ("src-port-q", "dst-port-d"))
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
// 5) Group with pentagon ports enclosing a child block
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
element.group(
|
||||||
|
id: "g", name: "group w/ pentagon ports", name-anchor: "north",
|
||||||
|
stroke: (dash: "dashed"),
|
||||||
|
fill: util.colors.purple.lighten(70%),
|
||||||
|
padding: 2em,
|
||||||
|
port-shape: "pentagon",
|
||||||
|
ports: (
|
||||||
|
west: ((id: "in", name: "IN"),),
|
||||||
|
east: ((id: "out", name: "OUT", kind: "out"),),
|
||||||
|
),
|
||||||
|
{
|
||||||
|
element.block(
|
||||||
|
id: "inner", w: 2, h: 1.5, x: 12, y: -5,
|
||||||
|
fill: util.colors.green,
|
||||||
|
ports: (west: ((id: "d"),), east: ((id: "q"),)),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
wire.wire("wgi", ("g-port-in", "inner-port-d"))
|
||||||
|
wire.wire("wgo", ("inner-port-q", "g-port-out"))
|
||||||
|
})
|
||||||
+8335
-6811
File diff suppressed because it is too large.
Load diff
@@ -16,6 +16,10 @@
|
|||||||
/// Draws a block element
|
/// Draws a block element
|
||||||
///
|
///
|
||||||
/// #examples.block
|
/// #examples.block
|
||||||
|
///
|
||||||
|
/// Ports can be given a pentagon shape (with configurable direction, size,
|
||||||
|
/// stroke and fill) via `port-shape` and per-port overrides:
|
||||||
|
/// #examples.port-shape
|
||||||
/// For other parameters description, see #doc-ref("element.elmt")
|
/// For other parameters description, see #doc-ref("element.elmt")
|
||||||
/// - radius (number, length, ratio, dictionary): The corner radius of the block. See CeTZ documentation for more information
|
/// - radius (number, length, ratio, dictionary): The corner radius of the block. See CeTZ documentation for more information
|
||||||
#let block(
|
#let block(
|
||||||
@@ -27,6 +31,10 @@
|
|||||||
name-anchor: "center",
|
name-anchor: "center",
|
||||||
ports: (),
|
ports: (),
|
||||||
ports-margins: (),
|
ports-margins: (),
|
||||||
|
port-name-padding: 2pt,
|
||||||
|
port-shape: none,
|
||||||
|
port-kind: "in",
|
||||||
|
port-size: 1em,
|
||||||
fill: none,
|
fill: none,
|
||||||
radius: 0.5em,
|
radius: 0.5em,
|
||||||
stroke: black + 1pt,
|
stroke: black + 1pt,
|
||||||
@@ -44,6 +52,10 @@
|
|||||||
name-anchor: name-anchor,
|
name-anchor: name-anchor,
|
||||||
ports: ports,
|
ports: ports,
|
||||||
ports-margins: ports-margins,
|
ports-margins: ports-margins,
|
||||||
|
port-name-padding: port-name-padding,
|
||||||
|
port-shape: port-shape,
|
||||||
|
port-kind: port-kind,
|
||||||
|
port-size: port-size,
|
||||||
fill: fill,
|
fill: fill,
|
||||||
stroke: stroke,
|
stroke: stroke,
|
||||||
id: id,
|
id: id,
|
||||||
|
|||||||
@@ -37,6 +37,10 @@
|
|||||||
/// - `clock` (`bool`): Whether it is a clock port (triangle symbol)
|
/// - `clock` (`bool`): Whether it is a clock port (triangle symbol)
|
||||||
/// - `vertical` (`bool`): Whether the name should be drawn vertically
|
/// - `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)
|
/// - 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-shape (none, str): Default port shape for all ports of this element. Set to `"pentagon"` to draw pentagon-shaped ports. Overridable per port via the port's `shape` field. See #examples.port-shape
|
||||||
|
/// - port-kind (str): Default direction of ports (`"in"` or `"out"`). Overridable per port via the port's `kind` field
|
||||||
|
/// - port-size (length): Default unit size of pentagon-shaped ports (default `1em`). Overridable per port via the port's `size` field
|
||||||
/// - fill (none, color): Fill color
|
/// - fill (none, color): Fill color
|
||||||
/// - stroke (stroke): Border stroke
|
/// - stroke (stroke): Border stroke
|
||||||
/// - id (str): The block id (for future reference)
|
/// - id (str): The block id (for future reference)
|
||||||
@@ -56,6 +60,10 @@
|
|||||||
name-anchor: "center",
|
name-anchor: "center",
|
||||||
ports: (:),
|
ports: (:),
|
||||||
ports-margins: (:),
|
ports-margins: (:),
|
||||||
|
port-name-padding: 2pt,
|
||||||
|
port-shape: none,
|
||||||
|
port-kind: "in",
|
||||||
|
port-size: 1em,
|
||||||
fill: none,
|
fill: none,
|
||||||
stroke: black + 1pt,
|
stroke: black + 1pt,
|
||||||
id: "",
|
id: "",
|
||||||
@@ -140,7 +148,13 @@
|
|||||||
tl, tr, br, bl,
|
tl, tr, br, bl,
|
||||||
ports,
|
ports,
|
||||||
ports-margins,
|
ports-margins,
|
||||||
debug: debug.ports
|
debug: debug.ports,
|
||||||
|
name-padding: port-name-padding,
|
||||||
|
port-shape: port-shape,
|
||||||
|
port-kind: port-kind,
|
||||||
|
port-size: port-size,
|
||||||
|
elmt-stroke: stroke,
|
||||||
|
elmt-fill: fill,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
+44
-1
@@ -1,9 +1,15 @@
|
|||||||
#import "/src/cetz.typ": draw, coordinate
|
#import "/src/cetz.typ": draw, coordinate
|
||||||
#import "../util.typ"
|
#import "../util.typ"
|
||||||
|
#import "ports.typ": add-ports
|
||||||
|
|
||||||
/// Draws a group of elements
|
/// Draws a group of elements
|
||||||
///
|
///
|
||||||
/// #examples.group
|
/// #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
|
/// - body (elements, function): Elements to group
|
||||||
/// - id (str): see #doc-ref("element.elmt")
|
/// - id (str): see #doc-ref("element.elmt")
|
||||||
/// - name (str): The group's name
|
/// - name (str): The group's name
|
||||||
@@ -16,6 +22,14 @@
|
|||||||
/// - array: either (`<all>`,), (`<vertical>`, `<horizontal>`) or (`<top>`, `<right>`, `<bottom>`, `<left>`)
|
/// - array: either (`<all>`,), (`<vertical>`, `<horizontal>`) or (`<top>`, `<right>`, `<bottom>`, `<left>`)
|
||||||
/// - dictionary: valid keys are "top", "right", "bottom" and "left"
|
/// - dictionary: valid keys are "top", "right", "bottom" and "left"
|
||||||
/// - radius (number): The corner radius
|
/// - 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)
|
||||||
|
/// - port-shape (none, str): see #doc-ref("element.elmt")
|
||||||
|
/// - port-kind (str): see #doc-ref("element.elmt")
|
||||||
|
/// - port-size (length): see #doc-ref("element.elmt")
|
||||||
|
/// - debug (dictionary): see #doc-ref("element.elmt")
|
||||||
#let group(
|
#let group(
|
||||||
body,
|
body,
|
||||||
id: "",
|
id: "",
|
||||||
@@ -24,7 +38,17 @@
|
|||||||
fill: none,
|
fill: none,
|
||||||
stroke: black + 1pt,
|
stroke: black + 1pt,
|
||||||
padding: 0.5em,
|
padding: 0.5em,
|
||||||
radius: 0.5em
|
radius: 0.5em,
|
||||||
|
ports: (:),
|
||||||
|
ports-margins: (:),
|
||||||
|
outside-port-names: true,
|
||||||
|
port-name-padding: 2pt,
|
||||||
|
port-shape: none,
|
||||||
|
port-kind: "in",
|
||||||
|
port-size: 1em,
|
||||||
|
debug: (
|
||||||
|
ports: false
|
||||||
|
)
|
||||||
) = {
|
) = {
|
||||||
let min-x = none
|
let min-x = none
|
||||||
let max-x = none
|
let max-x = none
|
||||||
@@ -68,6 +92,25 @@
|
|||||||
stroke: stroke,
|
stroke: stroke,
|
||||||
fill: fill
|
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,
|
||||||
|
port-shape: port-shape,
|
||||||
|
port-kind: port-kind,
|
||||||
|
port-size: port-size,
|
||||||
|
elmt-stroke: stroke,
|
||||||
|
elmt-fill: fill,
|
||||||
|
)
|
||||||
|
|
||||||
if name != none {
|
if name != none {
|
||||||
draw.content(
|
draw.content(
|
||||||
id + "." + name-anchor,
|
id + "." + name-anchor,
|
||||||
|
|||||||
+82
-15
@@ -1,36 +1,89 @@
|
|||||||
#import "/src/cetz.typ": draw
|
#import "/src/cetz.typ": draw
|
||||||
#import "../util.typ": rotate-anchor
|
#import "../util.typ": rotate-anchor, opposite-anchor, port-shape-vertices
|
||||||
|
|
||||||
#let add-port(
|
#let add-port(
|
||||||
elmt-id, side, port, pos,
|
elmt-id, side, port, pos,
|
||||||
prev: none,
|
prev: none,
|
||||||
next: none,
|
next: none,
|
||||||
debug: false
|
debug: false,
|
||||||
|
outside-name: false,
|
||||||
|
name-padding: 2pt,
|
||||||
|
port-shape: none,
|
||||||
|
port-kind: "in",
|
||||||
|
port-size: 1em,
|
||||||
|
elmt-stroke: black + 1pt,
|
||||||
|
elmt-fill: none,
|
||||||
) = {
|
) = {
|
||||||
let name = port.at("name", default: "")
|
let name = port.at("name", default: "")
|
||||||
let name-rotate = port.at("vertical", default: false)
|
let name-rotate = port.at("vertical", default: false)
|
||||||
|
let is-clock = port.at("clock", default: false)
|
||||||
|
|
||||||
if (port.at("clock", default: false)) {
|
// Per-port overrides fall back to element-level defaults.
|
||||||
|
let shape = port.at("shape", default: port-shape)
|
||||||
|
let kind = port.at("kind", default: port-kind)
|
||||||
|
let size = port.at("size", default: port-size)
|
||||||
|
let shape-stroke = port.at("stroke", default: elmt-stroke)
|
||||||
|
// Pentagon fill defaults to the element's fill (so the pentagon covers the
|
||||||
|
// block-border segment it crosses). Clock ports default to gray. Per-port
|
||||||
|
// `fill` overrides both.
|
||||||
|
let shape-fill = port.at("fill",
|
||||||
|
default: if is-clock and shape == "pentagon" { gray } else { elmt-fill })
|
||||||
|
|
||||||
|
// When shape is enabled, wire-connection anchor moves outside the block, and
|
||||||
|
// inside-name labels reference the inner tip/base; otherwise both collapse to
|
||||||
|
// the boundary point (unchanged behaviour).
|
||||||
|
let outer-pos = pos
|
||||||
|
let inner-pos = pos
|
||||||
|
|
||||||
|
if shape == "pentagon" {
|
||||||
|
let sh = port-shape-vertices(side, kind, size)
|
||||||
|
let v = sh.vertices.map(off => (rel: off, to: pos))
|
||||||
|
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 {
|
||||||
if prev == none or next == none {
|
if prev == none or next == none {
|
||||||
panic("Clock port must have previous and next positions")
|
panic("Clock port must have previous and next positions")
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = if port.at("small", default: false) {8pt} else {1em}
|
let csize = if port.at("small", default: false) {8pt} else {1em}
|
||||||
let offset
|
let offset
|
||||||
if (side == "north") { offset = ( 0, -size) }
|
if (side == "north") { offset = ( 0, -csize) }
|
||||||
else if (side == "east") { offset = (-size, 0) }
|
else if (side == "east") { offset = (-csize, 0) }
|
||||||
else if (side == "south") { offset = ( 0, size) }
|
else if (side == "south") { offset = ( 0, csize) }
|
||||||
else if (side == "west") { offset = ( size, 0) }
|
else if (side == "west") { offset = ( csize, 0) }
|
||||||
|
|
||||||
let pos1 = (rel: offset, to: pos)
|
let pos1 = (rel: offset, to: pos)
|
||||||
|
|
||||||
// TODO: use context or vectors to have the height relative to the width
|
// TODO: use context or vectors to have the height relative to the width
|
||||||
draw.line(prev, pos1, next)
|
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 }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Label sits at the outer edge of the shape for outside-name, inner edge for
|
||||||
|
// inside-name. `name-padding` is thus naturally measured from the shape edge
|
||||||
|
// (or from the boundary when shape is disabled).
|
||||||
|
let label-pos = if outside-name { outer-pos } else { inner-pos }
|
||||||
|
|
||||||
draw.content(
|
draw.content(
|
||||||
pos,
|
label-pos,
|
||||||
anchor: if name-rotate {rotate-anchor(side)} else {side},
|
anchor: name-anchor,
|
||||||
padding: 2pt,
|
padding: name-padding,
|
||||||
angle: if name-rotate {90deg} else {0deg},
|
angle: if name-rotate {90deg} else {0deg},
|
||||||
name
|
name
|
||||||
)
|
)
|
||||||
@@ -38,7 +91,7 @@
|
|||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
draw.circle(
|
draw.circle(
|
||||||
pos,
|
outer-pos,
|
||||||
name: id,
|
name: id,
|
||||||
radius: .1,
|
radius: .1,
|
||||||
stroke: none,
|
stroke: none,
|
||||||
@@ -47,7 +100,7 @@
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
draw.hide(draw.circle(
|
draw.hide(draw.circle(
|
||||||
pos,
|
outer-pos,
|
||||||
radius: 0,
|
radius: 0,
|
||||||
stroke: none,
|
stroke: none,
|
||||||
name: id
|
name: id
|
||||||
@@ -60,7 +113,14 @@
|
|||||||
tl, tr, br, bl,
|
tl, tr, br, bl,
|
||||||
ports,
|
ports,
|
||||||
ports-margins,
|
ports-margins,
|
||||||
debug: false
|
debug: false,
|
||||||
|
outside-name: false,
|
||||||
|
name-padding: 2pt,
|
||||||
|
port-shape: none,
|
||||||
|
port-kind: "in",
|
||||||
|
port-size: 1em,
|
||||||
|
elmt-stroke: black + 1pt,
|
||||||
|
elmt-fill: none,
|
||||||
) = {
|
) = {
|
||||||
let sides = (
|
let sides = (
|
||||||
"north": (tl, tr),
|
"north": (tl, tr),
|
||||||
@@ -109,7 +169,14 @@
|
|||||||
pos,
|
pos,
|
||||||
prev: pos-prev,
|
prev: pos-prev,
|
||||||
next: pos-next,
|
next: pos-next,
|
||||||
debug: debug
|
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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,6 +90,55 @@
|
|||||||
).at(anchor)
|
).at(anchor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the pentagon port shape vertices and the outer/inner anchor
|
||||||
|
/// offsets for the given side and direction.
|
||||||
|
///
|
||||||
|
/// The pentagon straddles the block boundary: 1 unit outside, 1 unit inside;
|
||||||
|
/// height ±0.5 units. For `kind: "in"` the tip points into the block; for
|
||||||
|
/// `kind: "out"` it points away.
|
||||||
|
///
|
||||||
|
/// - side (str): One of `"north"`, `"east"`, `"south"`, `"west"`
|
||||||
|
/// - kind (str): `"in"` or `"out"`
|
||||||
|
/// - u (length,number): The pentagon unit size
|
||||||
|
/// -> dictionary: `(vertices: (..5 pairs..), outer: (dx, dy), inner: (dx, dy))`
|
||||||
|
#let port-shape-vertices(side, kind, u) = {
|
||||||
|
// Base case: west port, input (tip east / into block).
|
||||||
|
// Vertices as (dx, dy) offsets relative to the boundary anchor.
|
||||||
|
let base-vertices = (
|
||||||
|
(-u, -u/2),
|
||||||
|
(-u, +u/2),
|
||||||
|
(u/2, +u/2),
|
||||||
|
(u, 0), // tip east (inner side)
|
||||||
|
(u/2, -u/2),
|
||||||
|
)
|
||||||
|
// For output, mirror horizontally so the tip points west (outside).
|
||||||
|
if kind == "out" {
|
||||||
|
base-vertices = base-vertices.map(p => (-p.at(0), p.at(1)))
|
||||||
|
}
|
||||||
|
// Outer (wire-connection) and inner (inside-label) anchors for a west port.
|
||||||
|
// Outer is always 1u outside the boundary along the port normal; inner is 1u
|
||||||
|
// inside. Independent of `kind` (only the pentagon fill/tip direction change).
|
||||||
|
let base-outer = (-u, 0)
|
||||||
|
let base-inner = (u, 0)
|
||||||
|
|
||||||
|
// Rotation from west's frame to target side:
|
||||||
|
// west: identity (x, y)
|
||||||
|
// north: (x, y) → (y, -x)
|
||||||
|
// east: (x, y) → (-x, -y)
|
||||||
|
// south: (x, y) → (-y, x)
|
||||||
|
let rot = if side == "west" { p => p }
|
||||||
|
else if side == "north" { p => (p.at(1), -p.at(0)) }
|
||||||
|
else if side == "east" { p => (-p.at(0), -p.at(1)) }
|
||||||
|
else if side == "south" { p => (-p.at(1), p.at(0)) }
|
||||||
|
else { panic("Unknown side: " + side) }
|
||||||
|
|
||||||
|
return (
|
||||||
|
vertices: base-vertices.map(rot),
|
||||||
|
outer: rot(base-outer),
|
||||||
|
inner: rot(base-inner),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#let valid-anchors = (
|
#let valid-anchors = (
|
||||||
"center", "north", "east", "west", "south",
|
"center", "north", "east", "west", "south",
|
||||||
"north-east", "north-west", "south-east", "south-west"
|
"north-east", "north-west", "south-east", "south-west"
|
||||||
|
|||||||
Reference in new issue
Block a user