Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3076e44207 | ||
|
|
473dca962f | ||
|
|
9edc60516e |
No files matched your search
@@ -3,7 +3,7 @@
|
|||||||
Circuiteria is a [Typst](https://typst.app) package for drawing block circuit diagrams using the [CeTZ](https://typst.app/universe/package/cetz) package.
|
Circuiteria is a [Typst](https://typst.app) package for drawing block circuit diagrams using the [CeTZ](https://typst.app/universe/package/cetz) package.
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="./gallery/platypus.png" alt="A platypus / Perry the Platypus meme made with colored rounded rectangles">
|
<img src="./gallery/platypus.png" alt="Perry the platypus">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
@@ -11,17 +11,17 @@ Circuiteria is a [Typst](https://typst.app) package for drawing block circuit di
|
|||||||
<tr>
|
<tr>
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<a href="./gallery/test.typ">
|
<a href="./gallery/test.typ">
|
||||||
<img src="./gallery/test.png" width="500px" alt="Block diagram of a multi-cycle RISC-V FPGA processor">
|
<img src="./gallery/test.png" width="500px">
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2">A bit of everything</td>
|
<td colspan="2">A bit of eveything</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<a href="./gallery/test5.typ">
|
<a href="./gallery/test5.typ">
|
||||||
<img src="./gallery/test5.png" width="500px" alt="Block diagram of a single-cycle RISC-V processor">
|
<img src="./gallery/test5.png" width="500px">
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -31,12 +31,12 @@ Circuiteria is a [Typst](https://typst.app) package for drawing block circuit di
|
|||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="./gallery/test4.typ">
|
<a href="./gallery/test4.typ">
|
||||||
<img src="./gallery/test4.png" width="250px" alt="Top-level block diagram of a single-cycle RISC-V processor">
|
<img src="./gallery/test4.png" width="250px">
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="./gallery/test6.typ">
|
<a href="./gallery/test6.typ">
|
||||||
<img src="./gallery/test6.png" width="250px" alt="Block diagram of a simple single-cycle RISC-V ALU">
|
<img src="./gallery/test6.png" width="250px">
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -56,7 +56,7 @@ For more information, see the [manual](manual.pdf)
|
|||||||
|
|
||||||
To use this package, simply import [circuiteria](https://typst.app/universe/package/circuiteria) and call the `circuit` function:
|
To use this package, simply import [circuiteria](https://typst.app/universe/package/circuiteria) and call the `circuit` function:
|
||||||
```typ
|
```typ
|
||||||
#import "@preview/circuiteria:0.2.1"
|
#import "@preview/circuiteria:0.2.0"
|
||||||
#circuiteria.circuit({
|
#circuiteria.circuit({
|
||||||
import circuiteria: *
|
import circuiteria: *
|
||||||
...
|
...
|
||||||
|
|||||||
@@ -71,30 +71,6 @@ wire.wire("w4", ("p6","p7"), style: "guided",
|
|||||||
|
|
||||||
```, vertical: true)
|
```, vertical: true)
|
||||||
|
|
||||||
#let zigzag-at = example(
|
|
||||||
```
|
|
||||||
// `zigzag-at` places the middle bend of a zigzag wire
|
|
||||||
// on the axis of a referenced anchor, overriding
|
|
||||||
// `zigzag-ratio`.
|
|
||||||
element.block(x: 0, y: 0, w: 1.5, h: 1.5, id: "src",
|
|
||||||
ports: (east: ((id: "o", name: "O"),),))
|
|
||||||
element.block(x: 3, y: -1.2, w: 1, h: 1, id: "ref",
|
|
||||||
fill: util.colors.pink,
|
|
||||||
ports: (west: ((id: "p", name: "R"),),))
|
|
||||||
element.block(x: 5.5, y: -2, w: 1.5, h: 1.5, id: "dst",
|
|
||||||
ports: (west: ((id: "i", name: "I"),),))
|
|
||||||
// String form: anchor name of a port.
|
|
||||||
wire.wire("w1", ("src-port-o", "dst-port-i"),
|
|
||||||
style: "zigzag",
|
|
||||||
zigzag-at: "ref-port-p")
|
|
||||||
// Ergonomic offset: shift 8pt right of ref.east.
|
|
||||||
wire.wire("w2", ("src-port-o", "dst-port-i"),
|
|
||||||
style: "zigzag",
|
|
||||||
zigzag-at: (rel: 8pt, to: "ref.east"))
|
|
||||||
```,
|
|
||||||
vertical: true,
|
|
||||||
)
|
|
||||||
|
|
||||||
#let stub = example(```
|
#let stub = example(```
|
||||||
draw.circle((0, 0), radius: .1, name: "p")
|
draw.circle((0, 0), radius: .1, name: "p")
|
||||||
wire.stub("p", "north", name: "north", length: 1)
|
wire.stub("p", "north", name: "north", length: 1)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 45 KiB |
@@ -1,4 +1,4 @@
|
|||||||
#import "@preview/circuiteria:0.2.1": *
|
#import "../src/lib.typ": *
|
||||||
|
|
||||||
#set page(width: auto, height: auto, margin: .5cm)
|
#set page(width: auto, height: auto, margin: .5cm)
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 160 KiB |
@@ -1,4 +1,4 @@
|
|||||||
#import "@preview/circuiteria:0.2.1": circuit, element, util, wire
|
#import "../src/lib.typ": circuit, element, util, wire
|
||||||
|
|
||||||
#set page(width: auto, height: auto, margin: .5cm)
|
#set page(width: auto, height: auto, margin: .5cm)
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
wire.wire(
|
wire.wire(
|
||||||
"wExtender-ImmSrc", (
|
"wExtender-ImmSrc", (
|
||||||
"Extender.north",
|
"Extender.north",
|
||||||
(18, -2)
|
(18, -2, 0)
|
||||||
),
|
),
|
||||||
style: "zigzag",
|
style: "zigzag",
|
||||||
zigzag-ratio: 0%,
|
zigzag-ratio: 0%,
|
||||||
@@ -138,8 +138,8 @@
|
|||||||
)
|
)
|
||||||
wire.wire(
|
wire.wire(
|
||||||
"wBus", (
|
"wBus", (
|
||||||
(v => (v.at(0), -3.5), mid),
|
(v => (v.at(0), -3.5, 0), mid),
|
||||||
(horizontal: (), vertical: (0, 3.5)),
|
(horizontal: (), vertical: (0, 3.5, 0)),
|
||||||
),
|
),
|
||||||
bus: true
|
bus: true
|
||||||
)
|
)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 160 KiB |
@@ -1,4 +1,4 @@
|
|||||||
#import "@preview/circuiteria:0.2.1": circuit, element, util, wire
|
#import "../src/lib.typ": circuit, element, util, wire
|
||||||
|
|
||||||
#set page(width: auto, height: auto, margin: .5cm)
|
#set page(width: auto, height: auto, margin: .5cm)
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
wire.wire(
|
wire.wire(
|
||||||
"wExtender-ImmSrc", (
|
"wExtender-ImmSrc", (
|
||||||
"Extender.north",
|
"Extender.north",
|
||||||
(18, -2)
|
(18, -2, 0)
|
||||||
),
|
),
|
||||||
style: "zigzag",
|
style: "zigzag",
|
||||||
zigzag-ratio: 0%,
|
zigzag-ratio: 0%,
|
||||||
@@ -145,8 +145,8 @@
|
|||||||
)
|
)
|
||||||
wire.wire(
|
wire.wire(
|
||||||
"wBus", (
|
"wBus", (
|
||||||
(v => (v.at(0), -3.5), mid),
|
(v => (v.at(0), -3.5, 0), mid),
|
||||||
(horizontal: (), vertical: (0, 3.5)),
|
(horizontal: (), vertical: (0, 3.5, 0)),
|
||||||
),
|
),
|
||||||
bus: true
|
bus: true
|
||||||
)
|
)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 66 KiB |
@@ -1,4 +1,4 @@
|
|||||||
#import "@preview/circuiteria:0.2.1": circuit, element, util, wire
|
#import "../src/lib.typ": circuit, element, util, wire
|
||||||
|
|
||||||
#set page(width: auto, height: auto, margin: .5cm)
|
#set page(width: auto, height: auto, margin: .5cm)
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 195 KiB |
@@ -1,5 +1,5 @@
|
|||||||
#import "/src/cetz.typ": draw
|
#import "/src/cetz.typ": draw
|
||||||
#import "@preview/circuiteria:0.2.1": *
|
#import "../src/lib.typ": *
|
||||||
|
|
||||||
#set page(width: auto, height: auto, margin: .5cm)
|
#set page(width: auto, height: auto, margin: .5cm)
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 274 KiB |
@@ -1,5 +1,5 @@
|
|||||||
#import "/src/cetz.typ": draw
|
#import "/src/cetz.typ": draw
|
||||||
#import "@preview/circuiteria:0.2.1": *
|
#import "../src/lib.typ": *
|
||||||
|
|
||||||
#set page(width: auto, height: auto, margin: .5cm)
|
#set page(width: auto, height: auto, margin: .5cm)
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 76 KiB |
@@ -1,5 +1,5 @@
|
|||||||
#import "/src/cetz.typ": draw
|
#import "/src/cetz.typ": draw
|
||||||
#import "@preview/circuiteria:0.2.1": *
|
#import "../src/lib.typ": *
|
||||||
|
|
||||||
#set page(width: auto, height: auto, margin: .5cm)
|
#set page(width: auto, height: auto, margin: .5cm)
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 40 KiB |
@@ -1,4 +1,4 @@
|
|||||||
#import "@preview/circuiteria:0.2.1": circuit, element, util, wire
|
#import "../src/lib.typ": circuit, element, util, wire
|
||||||
|
|
||||||
#set page(width: auto, height: auto, margin: .5cm)
|
#set page(width: auto, height: auto, margin: .5cm)
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ This package provides a way to make beautiful block circuit diagrams using the C
|
|||||||
|
|
||||||
Simply import Circuiteria and call the `circuit` function:
|
Simply import Circuiteria and call the `circuit` function:
|
||||||
#pad(left: 1em)[```typ
|
#pad(left: 1em)[```typ
|
||||||
#import "@preview/circuiteria:0.2.1"
|
#import "@preview/circuiteria:0.2.0"
|
||||||
#circuiteria.circuit({
|
#circuiteria.circuit({
|
||||||
import circuiteria: *
|
import circuiteria: *
|
||||||
...
|
...
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
if (type(x) == dictionary) {
|
if (type(x) == dictionary) {
|
||||||
let offset = x.rel
|
let offset = x.rel
|
||||||
let to = x.to
|
let to = x.to
|
||||||
let (ctx, to-pos) = coordinate.resolve(ctx, (rel: (offset, 0), to: to))
|
let (ctx, to-pos) = coordinate.resolve(ctx, (rel: (offset, 0, 0), to: to))
|
||||||
x = to-pos.at(0)
|
x = to-pos.at(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
debug: debug
|
debug: debug
|
||||||
)
|
)
|
||||||
|
|
||||||
let in-pos = (rel: (0, h * (h-ratio / 200%)), to: id+".south-west")
|
let in-pos = (rel: (0, h * (h-ratio / 200%), 0), to: id+".south-west")
|
||||||
let out-pos = (id+".south-east", out-pct, id+".north-east")
|
let out-pos = (id+".south-east", out-pct, id+".north-east")
|
||||||
add-port(id, "west", ports.west.first(), in-pos)
|
add-port(id, "west", ports.west.first(), in-pos)
|
||||||
add-port(id, "east", ports.east.first(), out-pos)
|
add-port(id, "east", ports.east.first(), out-pos)
|
||||||
|
|||||||
@@ -61,8 +61,8 @@
|
|||||||
|
|
||||||
draw.hide(draw.group(name: id+"-inner", body))
|
draw.hide(draw.group(name: id+"-inner", body))
|
||||||
draw.rect(
|
draw.rect(
|
||||||
(rel: (-pad-left, -pad-bottom), to: id+"-inner.south-west"),
|
(rel: (-pad-left, -pad-bottom, 0), to: id+"-inner.south-west"),
|
||||||
(rel: (pad-right, pad-top), to: id+"-inner.north-east"),
|
(rel: (pad-right, pad-top, 0), to: id+"-inner.north-east"),
|
||||||
name: id,
|
name: id,
|
||||||
radius: radius,
|
radius: radius,
|
||||||
stroke: stroke,
|
stroke: stroke,
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
if (type(x) == dictionary) {
|
if (type(x) == dictionary) {
|
||||||
let offset = x.rel
|
let offset = x.rel
|
||||||
let to = x.to
|
let to = x.to
|
||||||
let (ctx, to-pos) = coordinate.resolve(ctx, (rel: (offset, 0), to: to))
|
let (ctx, to-pos) = coordinate.resolve(ctx, (rel: (offset, 0, 0), to: to))
|
||||||
x = to-pos.at(0)
|
x = to-pos.at(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
let port-pos = int-name + ".0"
|
let port-pos = int-name + ".0"
|
||||||
if inverted == "all" or port-name in inverted {
|
if inverted == "all" or port-name in inverted {
|
||||||
draw.circle(port-pos, radius: inverted-radius, anchor: "east", stroke: stroke)
|
draw.circle(port-pos, radius: inverted-radius, anchor: "east", stroke: stroke)
|
||||||
port-pos = (rel: (-2 * inverted-radius, 0), to: port-pos)
|
port-pos = (rel: (-2 * inverted-radius, 0, 0), to: port-pos)
|
||||||
}
|
}
|
||||||
add-port(
|
add-port(
|
||||||
id, "west",
|
id, "west",
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
let out-pos = id + ".east"
|
let out-pos = id + ".east"
|
||||||
if inverted == "all" or "out" in inverted {
|
if inverted == "all" or "out" in inverted {
|
||||||
draw.circle(out-pos, radius: inverted-radius, anchor: "west", stroke: stroke)
|
draw.circle(out-pos, radius: inverted-radius, anchor: "west", stroke: stroke)
|
||||||
out-pos = (rel: (2 * inverted-radius, 0), to: out-pos)
|
out-pos = (rel: (2 * inverted-radius, 0, 0), to: out-pos)
|
||||||
}
|
}
|
||||||
add-port(
|
add-port(
|
||||||
id, "east",
|
id, "east",
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
if (type(x) == dictionary) {
|
if (type(x) == dictionary) {
|
||||||
let offset = x.rel
|
let offset = x.rel
|
||||||
let to = x.to
|
let to = x.to
|
||||||
let (ctx, to-pos) = coordinate.resolve(ctx, (rel: (offset, 0), to: to))
|
let (ctx, to-pos) = coordinate.resolve(ctx, (rel: (offset, 0, 0), to: to))
|
||||||
x = to-pos.at(0)
|
x = to-pos.at(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
anchor: "east",
|
anchor: "east",
|
||||||
stroke: stroke
|
stroke: stroke
|
||||||
)
|
)
|
||||||
port-pos = (rel: (-2 * inverted-radius, 0), to: port-pos)
|
port-pos = (rel: (-2 * inverted-radius, 0, 0), to: port-pos)
|
||||||
}
|
}
|
||||||
add-port(
|
add-port(
|
||||||
id, "west",
|
id, "west",
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
let out-pos = id + ".east"
|
let out-pos = id + ".east"
|
||||||
if inverted == "all" or "out" in inverted {
|
if inverted == "all" or "out" in inverted {
|
||||||
draw.circle(out-pos, radius: inverted-radius, anchor: "west", stroke: stroke)
|
draw.circle(out-pos, radius: inverted-radius, anchor: "west", stroke: stroke)
|
||||||
out-pos = (rel: (2 * inverted-radius, 0), to: out-pos)
|
out-pos = (rel: (2 * inverted-radius, 0, 0), to: out-pos)
|
||||||
}
|
}
|
||||||
add-port(
|
add-port(
|
||||||
id, "east",
|
id, "east",
|
||||||
|
|||||||
@@ -17,10 +17,10 @@
|
|||||||
|
|
||||||
let size = if port.at("small", default: false) {8pt} else {1em}
|
let size = if port.at("small", default: false) {8pt} else {1em}
|
||||||
let offset
|
let offset
|
||||||
if (side == "north") { offset = ( 0, -size) }
|
if (side == "north") { offset = ( 0, -size, 0) }
|
||||||
else if (side == "east") { offset = (-size, 0) }
|
else if (side == "east") { offset = (-size, 0, 0) }
|
||||||
else if (side == "south") { offset = ( 0, size) }
|
else if (side == "south") { offset = ( 0, size, 0) }
|
||||||
else if (side == "west") { offset = ( size, 0) }
|
else if (side == "west") { offset = ( size, 0, 0) }
|
||||||
|
|
||||||
let pos1 = (rel: offset, to: pos)
|
let pos1 = (rel: offset, to: pos)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#let version = version(0, 2, 1)
|
#let version = version(0, 2, 0)
|
||||||
#let name = "circuiteria"
|
#let name = "circuiteria"
|
||||||
|
|
||||||
#import "circuit.typ": circuit
|
#import "circuit.typ": circuit
|
||||||
|
|||||||
@@ -74,44 +74,3 @@
|
|||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
/// Normalizes a coordinate so that 2D points (x, y) are padded
|
|
||||||
/// to (x, y, 0) as required by CeTZ's coordinate resolver.
|
|
||||||
/// Also handles function-based and dictionary-based coordinates
|
|
||||||
/// recursively.
|
|
||||||
///
|
|
||||||
/// - coord (any): A CeTZ-compatible coordinate
|
|
||||||
/// -> any
|
|
||||||
#let normalize-coord(coord) = {
|
|
||||||
if type(coord) == array {
|
|
||||||
if coord.len() == 2 {
|
|
||||||
let (a, b) = coord
|
|
||||||
// Raw 2D point: (x, y) -> (x, y, 0)
|
|
||||||
if type(a) in (int, float) and type(b) in (int, float) {
|
|
||||||
return (a, b, 0)
|
|
||||||
}
|
|
||||||
// Function-based coordinate: (fn, point)
|
|
||||||
if type(a) == function {
|
|
||||||
let orig = a
|
|
||||||
return (v => {
|
|
||||||
let result = orig(v)
|
|
||||||
if type(result) == array and result.len() == 2 {
|
|
||||||
let (rx, ry) = result
|
|
||||||
if type(rx) in (int, float) and type(ry) in (int, float) {
|
|
||||||
return (rx, ry, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if type(coord) == dictionary {
|
|
||||||
let result = (:)
|
|
||||||
for (key, value) in coord {
|
|
||||||
result.insert(key, normalize-coord(value))
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
return coord
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#import "/src/cetz.typ": draw, coordinate
|
#import "/src/cetz.typ": draw, coordinate
|
||||||
#import "util.typ": opposite-anchor, normalize-coord
|
#import "util.typ": opposite-anchor
|
||||||
|
|
||||||
/// List of valid wire styles
|
/// List of valid wire styles
|
||||||
/// #examples.wires
|
/// #examples.wires
|
||||||
@@ -24,123 +24,29 @@
|
|||||||
return (pts, anchors)
|
return (pts, anchors)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize an anchor-based placement value used by `zigzag-at`, `u-at`, and
|
#let get-zigzag-wire(pts, ratio, dir) = {
|
||||||
// the `s-at-*` family.
|
|
||||||
//
|
|
||||||
// `axis` selects which cartesian component the offset applies to:
|
|
||||||
// - `"x"` → offset in x (used when the placement fixes an x coordinate, e.g.
|
|
||||||
// a vertical bend line for horizontal wire flow).
|
|
||||||
// - `"y"` → offset in y (used when the placement fixes a y coordinate).
|
|
||||||
//
|
|
||||||
// Accepted forms:
|
|
||||||
// - `none` → unset; caller falls back to
|
|
||||||
// depth/ratio-based placement.
|
|
||||||
// - `str` / tuple / native CeTZ dict → passed through as a CeTZ coordinate.
|
|
||||||
// - `(rel: <length>, to: <coord>)` with a *scalar* `rel`
|
|
||||||
// → ergonomic axial offset, rewritten
|
|
||||||
// to `(rel: (rel, 0), to: to)` for
|
|
||||||
// `axis == "x"` or
|
|
||||||
// `(rel: (0, rel), to: to)` for
|
|
||||||
// `axis == "y"`. If `rel` is already
|
|
||||||
// an array/tuple, the value is
|
|
||||||
// passed through unchanged.
|
|
||||||
#let normalize-scalar-offset(at, axis) = {
|
|
||||||
if at == none { return none }
|
|
||||||
if type(at) == dictionary and "rel" in at and "to" in at and type(at.rel) != array {
|
|
||||||
let r = at.rel
|
|
||||||
if axis == "x" {
|
|
||||||
return (rel: (r, 0), to: at.to)
|
|
||||||
} else {
|
|
||||||
return (rel: (0, r), to: at.to)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return at
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backwards-compatible wrapper: translates the old `dir` vocabulary
|
|
||||||
// (`"vertical"` / `"horizontal"`) to the `axis` vocabulary used by
|
|
||||||
// `normalize-scalar-offset`.
|
|
||||||
#let normalize-zigzag-at(at, dir) = {
|
|
||||||
let axis = if dir == "vertical" { "x" } else { "y" }
|
|
||||||
return normalize-scalar-offset(at, axis)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to deduce the cardinal side a port anchor faces by comparing its
|
|
||||||
// position to the center of the element it belongs to. Returns none if the
|
|
||||||
// anchor is not a port-style anchor (i.e. does not contain "-port-") or if
|
|
||||||
// resolution fails.
|
|
||||||
#let detect-port-side(anchor, ctx) = {
|
|
||||||
if type(anchor) != str { return none }
|
|
||||||
let sep = "-port-"
|
|
||||||
if not anchor.contains(sep) { return none }
|
|
||||||
let elmt-id = anchor.split(sep).first()
|
|
||||||
let port-pos = none
|
|
||||||
let center-pos = none
|
|
||||||
// coordinate.resolve panics on unknown anchors; there is no try/catch in
|
|
||||||
// Typst, so we assume port anchors created by add-port always come with a
|
|
||||||
// matching group of the same name. If not, the user must set the relevant side parameter
|
|
||||||
// explicitly.
|
|
||||||
let (_, p1) = coordinate.resolve(ctx, anchor)
|
|
||||||
let (_, p2) = coordinate.resolve(ctx, elmt-id + ".center")
|
|
||||||
port-pos = p1
|
|
||||||
center-pos = p2
|
|
||||||
let dx = port-pos.at(0) - center-pos.at(0)
|
|
||||||
let dy = port-pos.at(1) - center-pos.at(1)
|
|
||||||
if calc.abs(dx) >= calc.abs(dy) {
|
|
||||||
return if dx >= 0 { "east" } else { "west" }
|
|
||||||
} else {
|
|
||||||
return if dy >= 0 { "north" } else { "south" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#let get-zigzag-wire(pts, ratio, dir, at: none) = {
|
|
||||||
let start = pts.first()
|
let start = pts.first()
|
||||||
let end = pts.last()
|
let end = pts.last()
|
||||||
|
let mid = if dir == "vertical" {
|
||||||
let points
|
(start, ratio, (horizontal: end, vertical: ()))
|
||||||
if at == none {
|
|
||||||
// Ratio-based placement (default).
|
|
||||||
let mid = if dir == "vertical" {
|
|
||||||
(start, ratio, (horizontal: end, vertical: ()))
|
|
||||||
} else {
|
|
||||||
(start, ratio, (horizontal: (), vertical: end))
|
|
||||||
}
|
|
||||||
|
|
||||||
points = if dir == "vertical" {
|
|
||||||
(
|
|
||||||
start,
|
|
||||||
(horizontal: mid, vertical: ()),
|
|
||||||
(horizontal: (), vertical: end),
|
|
||||||
end
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
start,
|
|
||||||
(horizontal: (), vertical: mid),
|
|
||||||
(horizontal: end, vertical: ()),
|
|
||||||
end
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Anchor-based placement: the middle bend segment sits on the axis
|
(start, ratio, (horizontal: (), vertical: end))
|
||||||
// extracted from `at` via CeTZ's perpendicular operator `|-`:
|
}
|
||||||
// - vertical: middle segment at x = at.x
|
|
||||||
// - horizontal: middle segment at y = at.y
|
let points = if dir == "vertical" {
|
||||||
points = if dir == "vertical" {
|
(
|
||||||
(
|
start,
|
||||||
start,
|
(horizontal: mid, vertical: ()),
|
||||||
(at, "|-", start),
|
(horizontal: (), vertical: end),
|
||||||
(at, "|-", end),
|
end
|
||||||
end
|
)
|
||||||
)
|
} else {
|
||||||
} else {
|
(
|
||||||
(
|
start,
|
||||||
start,
|
(horizontal: (), vertical: mid),
|
||||||
(start, "|-", at),
|
(horizontal: end, vertical: ()),
|
||||||
(end, "|-", at),
|
end
|
||||||
end
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let anchors = (
|
let anchors = (
|
||||||
"start": start,
|
"start": start,
|
||||||
@@ -251,8 +157,8 @@
|
|||||||
let p3 = (center_x, center_y, 0)
|
let p3 = (center_x, center_y, 0)
|
||||||
|
|
||||||
// setting up the points for that touch the guides
|
// setting up the points for that touch the guides
|
||||||
let p2 = (0, 0, 0)
|
let p2 = (0,0,0)
|
||||||
let p4 = (0, 0, 0)
|
let p4 = (0,0,0)
|
||||||
if side-start in ("north", "south") {
|
if side-start in ("north", "south") {
|
||||||
p2 = (horizontal: p3, vertical: p1)
|
p2 = (horizontal: p3, vertical: p1)
|
||||||
} else {
|
} else {
|
||||||
@@ -298,9 +204,8 @@
|
|||||||
/// - reverse (bool): If true, the start and end points will be swapped (useful in cases where the start point depends on the end point, for example with perpendiculars)
|
/// - reverse (bool): If true, the start and end points will be swapped (useful in cases where the start point depends on the end point, for example with perpendiculars)
|
||||||
/// - directed (bool): If true, the wire will be directed, meaning an arrow will be drawn at the endpoint
|
/// - directed (bool): If true, the wire will be directed, meaning an arrow will be drawn at the endpoint
|
||||||
/// - rotate-name (bool): If true, the name will be rotated according to the wire's slope
|
/// - rotate-name (bool): If true, the name will be rotated according to the wire's slope
|
||||||
/// - zigzag-ratio (auto, ratio): Position of the zigzag middle bend segment relative to the horizontal (or vertical) span, as a fraction between the two endpoints (only with style "zigzag"). Defaults to `auto`, which resolves to `50%`. Panics if combined with an explicitly set `zigzag-at`.
|
/// - zigzag-ratio (ratio): Position of the zigzag vertical relative to the horizontal span (only with style "zigzag")
|
||||||
/// - zigzag-dir (str): The zigzag's direction. As either "vertical" or "horizontal" (only with dstyle "zigzag")
|
/// - zigzag-dir (str): The zigzag's direction. As either "vertical" or "horizontal" (only with dstyle "zigzag")
|
||||||
/// - zigzag-at (none, str, array, dictionary): Optional. Anchor-based placement of the zigzag middle bend segment (only with style "zigzag"). When set, overrides `zigzag-ratio` (which must be left at its default `auto`). The middle segment sits on the axis extracted from the given coordinate: `x` for `zigzag-dir: "vertical"`, `y` for `"horizontal"`. Accepted forms: a raw CeTZ anchor name string (e.g. `"elmt.center"` or `"elmt-port-a"`); any native CeTZ coordinate (tuple, dict such as `(rel: (dx, dy), to: anchor)`); or the ergonomic scalar-offset form `(rel: <length>, to: <coord>)`, which is rewritten to a `(dx, 0)` offset for `"vertical"` and `(0, dy)` for `"horizontal"`. See #examples.zigzag-at
|
|
||||||
/// - dodge-y (number): Y position to dodge the wire to (only with style "dodge")
|
/// - dodge-y (number): Y position to dodge the wire to (only with style "dodge")
|
||||||
/// - dodge-sides (array): The start and end sides (going out of the connected element) of the wire (only with style "dodge")
|
/// - dodge-sides (array): The start and end sides (going out of the connected element) of the wire (only with style "dodge")
|
||||||
/// - dodge-margins (array): The start and end margins (i.e. space before dodging) of the wire (only with style "dodge")
|
/// - dodge-margins (array): The start and end margins (i.e. space before dodging) of the wire (only with style "dodge")
|
||||||
@@ -322,9 +227,8 @@
|
|||||||
guided-sides: ("east", "west"),
|
guided-sides: ("east", "west"),
|
||||||
directed: false,
|
directed: false,
|
||||||
rotate-name: true,
|
rotate-name: true,
|
||||||
zigzag-ratio: auto,
|
zigzag-ratio: 50%,
|
||||||
zigzag-dir: "vertical",
|
zigzag-dir: "vertical",
|
||||||
zigzag-at: none,
|
|
||||||
dodge-y: 0,
|
dodge-y: 0,
|
||||||
dodge-sides: ("east", "west"),
|
dodge-sides: ("east", "west"),
|
||||||
dodge-margins: (5%, 5%)
|
dodge-margins: (5%, 5%)
|
||||||
@@ -337,22 +241,6 @@
|
|||||||
panic("Wrong number of points (got " + str(pts.len()) + " instead of 2)")
|
panic("Wrong number of points (got " + str(pts.len()) + " instead of 2)")
|
||||||
}
|
}
|
||||||
|
|
||||||
// `zigzag-at` validation.
|
|
||||||
if zigzag-at != none {
|
|
||||||
if style != "zigzag" {
|
|
||||||
panic("wire '" + id + "': `zigzag-at` requires style: \"zigzag\" (got \""
|
|
||||||
+ style + "\")")
|
|
||||||
}
|
|
||||||
if zigzag-ratio != auto {
|
|
||||||
panic("wire '" + id
|
|
||||||
+ "': cannot combine `zigzag-at` with an explicit `zigzag-ratio`")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let zigzag-ratio-effective = if zigzag-ratio == auto { 50% } else { zigzag-ratio }
|
|
||||||
|
|
||||||
// Normalize user-provided coordinates: (x, y) -> (x, y, 0)
|
|
||||||
let pts = pts.map(normalize-coord)
|
|
||||||
|
|
||||||
let stroke = (
|
let stroke = (
|
||||||
paint: color,
|
paint: color,
|
||||||
thickness: if bus {bus-width} else {signal-width}
|
thickness: if bus {bus-width} else {signal-width}
|
||||||
@@ -368,12 +256,7 @@
|
|||||||
(points, anchors) = get-direct-wire(pts)
|
(points, anchors) = get-direct-wire(pts)
|
||||||
|
|
||||||
} else if style == "zigzag" {
|
} else if style == "zigzag" {
|
||||||
(points, anchors) = get-zigzag-wire(
|
(points, anchors) = get-zigzag-wire(pts, zigzag-ratio, zigzag-dir)
|
||||||
pts,
|
|
||||||
zigzag-ratio-effective,
|
|
||||||
zigzag-dir,
|
|
||||||
at: normalize-zigzag-at(zigzag-at, zigzag-dir),
|
|
||||||
)
|
|
||||||
|
|
||||||
} else if style == "dodge" {
|
} else if style == "dodge" {
|
||||||
(points, anchors) = get-dodge-wire(
|
(points, anchors) = get-dodge-wire(
|
||||||
@@ -482,17 +365,17 @@
|
|||||||
/// - name-offset (number): The name offset, perpendicular to the stub
|
/// - name-offset (number): The name offset, perpendicular to the stub
|
||||||
#let stub(port-id, side, name: none, vertical: false, length: 1em, name-offset: 0) = {
|
#let stub(port-id, side, name: none, vertical: false, length: 1em, name-offset: 0) = {
|
||||||
let end-offset = (
|
let end-offset = (
|
||||||
north: (0, length),
|
north: (0, length, 0),
|
||||||
east: (length, 0),
|
east: (length, 0, 0),
|
||||||
south: (0, -length),
|
south: (0, -length, 0),
|
||||||
west: (-length, 0)
|
west: (-length, 0, 0)
|
||||||
).at(side)
|
).at(side)
|
||||||
|
|
||||||
let name-offset = (
|
let name-offset = (
|
||||||
north: (name-offset, length),
|
north: (name-offset, length, 0),
|
||||||
east: (length, name-offset),
|
east: (length, name-offset, 0),
|
||||||
south: (name-offset, -length),
|
south: (name-offset, -length, 0),
|
||||||
west: (-length, name-offset)
|
west: (-length, name-offset, 0)
|
||||||
).at(side)
|
).at(side)
|
||||||
|
|
||||||
draw.line(
|
draw.line(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "circuiteria"
|
name = "circuiteria"
|
||||||
version = "0.2.1"
|
version = "0.2.0"
|
||||||
compiler = "0.15.0"
|
compiler = "0.15.0"
|
||||||
repository = "https://git.kb28.ch/HEL/circuiteria"
|
repository = "https://git.kb28.ch/HEL/circuiteria"
|
||||||
entrypoint = "src/lib.typ"
|
entrypoint = "src/lib.typ"
|
||||||
@@ -9,6 +9,6 @@ authors = [
|
|||||||
]
|
]
|
||||||
categories = [ "visualization" ]
|
categories = [ "visualization" ]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
description = "Drawing block circuits made easy, using CeTZ"
|
description = "Drawing block circuits with Typst made easy, using CeTZ"
|
||||||
keywords = [ "circuit", "block", "draw" ]
|
keywords = [ "circuit", "block", "draw" ]
|
||||||
exclude = [ "gallery", "justfile", "doc" ]
|
exclude = [ "gallery", "justfile", "doc" ]
|
||||||