forked from HEL/circuiteria
feat: Expose cardinal sub-anchors on pentagon-shaped ports
Wire up each port as a CeTZ group carrying, in addition to the
default attach anchor, four world-cardinal sub-anchors
`.east`, `.west`, `.north`, `.south` sitting on the pentagon's
bounding-box midpoints. This lets wires attach to any face of a
pentagon port instead of only the default outer/inner point:
wire.wire("w", ("src", "blk-port-a.east"), ...)
Sub-anchor semantics are world-oriented (not rotated with the shape),
so the tip's cardinal name follows its actual world direction:
- west side, kind "in" → tip = .east (kind "out" → .west)
- east side, kind "in" → tip = .west (kind "out" → .east)
- north side, kind "in" → tip = .south (kind "out" → .north)
- south side, kind "in" → tip = .north (kind "out" → .south)
Bounding-box extents depend only on side orientation (horizontal for
east/west, vertical for north/south); `kind` mirrors the shape but
preserves the bbox, so cardinal names stay stable.
Non-pentagon ports degenerate all four sub-anchors to the default
attach point, so user code can address `.east`/`.west`/etc.
uniformly regardless of port shape. The bare port anchor name
(`<elmt-id>-port-<port-id>`) resolves to the same coordinate as
before via a custom "default" anchor, preserving backward
compatibility. Debug mode still marks only the default attach point.
Add `examples.port-shape-attach` demonstrating attaching wires to
the `.west`, `.east` and `.north` faces of a single pentagon port.
This commit is contained in:
1 parent
72eab1620c
commit
663614056e
5 files changed
+7543
-7335
No files matched your search
@@ -468,3 +468,23 @@ wire.wire("w2", ("src-port-o", "dst-port-i"),
|
||||
style: "zigzag",
|
||||
zigzag-at: (rel: 8pt, to: "ref.east"))
|
||||
```, vertical: true)
|
||||
|
||||
#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)
|
||||
+7459
-7324
File diff suppressed because it is too large.
Load diff
@@ -42,7 +42,7 @@
|
||||
/// - 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)
|
||||
/// - 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-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. Pentagon-shaped ports expose four cardinal sub-anchors `.east`, `.west`, `.north`, `.south` on the pentagon's bounding box (all lying on the pentagon boundary) so wires can attach to different faces (for a north/south-side port the pentagon is rotated, so `.north`/`.south` are the tip/outer base, and `.east`/`.west` are the transverse edge midpoints). For non-pentagon ports these sub-anchors degenerate to the default attach point. See #examples.port-shape and #examples.port-shape-attach
|
||||
/// - 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
|
||||
|
||||
+28
-9
@@ -96,22 +96,41 @@
|
||||
)
|
||||
let id = elmt-id + "-port-" + port.at("id")
|
||||
|
||||
// Cardinal sub-anchor positions.
|
||||
// - Pentagon ports: the four world-cardinal midpoints of the pentagon's
|
||||
// bounding box (all lie on the pentagon boundary).
|
||||
// - Non-pentagon ports: all four collapse to the default attach point.
|
||||
let sub-east = anchor-pos
|
||||
let sub-west = anchor-pos
|
||||
let sub-north = anchor-pos
|
||||
let sub-south = anchor-pos
|
||||
if shape == "pentagon" {
|
||||
let sh = port-shape-vertices(side, kind, size)
|
||||
sub-east = (rel: sh.east, to: pos)
|
||||
sub-west = (rel: sh.west, to: pos)
|
||||
sub-north = (rel: sh.north, to: pos)
|
||||
sub-south = (rel: sh.south, to: pos)
|
||||
}
|
||||
|
||||
// Expose the port as a CeTZ group carrying the default attach point plus
|
||||
// the four cardinal sub-anchors (`.east`, `.west`, `.north`, `.south`).
|
||||
// The custom anchors override CeTZ's auto-generated bbox anchors of the
|
||||
// same name.
|
||||
draw.group(name: id, {
|
||||
draw.anchor("default", anchor-pos)
|
||||
draw.anchor("east", sub-east)
|
||||
draw.anchor("west", sub-west)
|
||||
draw.anchor("north", sub-north)
|
||||
draw.anchor("south", sub-south)
|
||||
})
|
||||
|
||||
if debug {
|
||||
draw.circle(
|
||||
anchor-pos,
|
||||
name: id,
|
||||
radius: .1,
|
||||
stroke: none,
|
||||
fill: red
|
||||
)
|
||||
|
||||
} else {
|
||||
draw.hide(draw.circle(
|
||||
anchor-pos,
|
||||
radius: 0,
|
||||
stroke: none,
|
||||
name: id
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+35
-1
@@ -97,10 +97,15 @@
|
||||
/// height ±0.5 units. For `kind: "in"` the tip points into the block; for
|
||||
/// `kind: "out"` it points away.
|
||||
///
|
||||
/// The returned dictionary also carries the four world-cardinal midpoints
|
||||
/// of the pentagon's bounding box (`east`, `west`, `north`, `south`) — each
|
||||
/// lies on the pentagon boundary and is used as an alternate wire-attach
|
||||
/// anchor.
|
||||
///
|
||||
/// - 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))`
|
||||
/// -> dictionary: `(vertices: (..5 pairs..), outer: (dx, dy), inner: (dx, dy), east: (dx, dy), west: (dx, dy), north: (dx, dy), south: (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.
|
||||
@@ -132,10 +137,39 @@
|
||||
else if side == "south" { p => (-p.at(1), p.at(0)) }
|
||||
else { panic("Unknown side: " + side) }
|
||||
|
||||
// World-cardinal midpoints of the pentagon's bounding box. These are
|
||||
// named in *world* coordinates (not rotated with the shape), so `.east`
|
||||
// always sits at the world +x extent of the pentagon, `.north` at +y, etc.
|
||||
// Consequently the tip's cardinal name follows its world direction:
|
||||
// - west side, kind "in" → tip world +x → tip = .east
|
||||
// - west side, kind "out" → tip world -x → tip = .west
|
||||
// - east side, kind "in" → tip world -x → tip = .west
|
||||
// - east side, kind "out" → tip world +x → tip = .east
|
||||
// - north side, kind "in" → tip world -y → tip = .south
|
||||
// - north side, kind "out" → tip world +y → tip = .north
|
||||
// - south side, kind "in" → tip world +y → tip = .north
|
||||
// - south side, kind "out" → tip world -y → tip = .south
|
||||
//
|
||||
// The pentagon bounding box extents depend only on the side's orientation
|
||||
// (horizontal for west/east, vertical for north/south), not on `kind`.
|
||||
let (hx, hy) = if side == "west" or side == "east" {
|
||||
(u, u/2)
|
||||
} else {
|
||||
(u/2, u)
|
||||
}
|
||||
let world-east = ( hx, 0)
|
||||
let world-west = (-hx, 0)
|
||||
let world-north = (0, hy)
|
||||
let world-south = (0, -hy)
|
||||
|
||||
return (
|
||||
vertices: base-vertices.map(rot),
|
||||
outer: rot(base-outer),
|
||||
inner: rot(base-inner),
|
||||
east: world-east,
|
||||
west: world-west,
|
||||
north: world-north,
|
||||
south: world-south,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user