39 Commits

Author SHA1 Message Date
David Stadelmann
f4ebf30a08 chore: update minimum compiler version to 0.15.0 2026-07-01 11:10:01 +02:00
David Stadelmann
5a0893c7c3 deps: update tidy to 0.4.3 2026-07-01 11:09:49 +02:00
David Stadelmann
d0b906ae07 deps: update CeTZ 0.3.4 to 0.5.2 with coordinate fixes
CeTZ 0.5.2 requires 3D coordinate vectors (x, y, z) for raw
coordinate tuples passed to coordinate.resolve(). Update internal
wire computations (dodge, guided) to use 3-element tuples, fix
.last() to .at(1) for y-component access on resolved coordinates,
and add a normalize-coord utility that automatically pads user-
provided 2D points (x, y) to (x, y, 0) so users do not need to
specify the z dimension.
2026-07-01 11:09:39 +02:00
b71e822bb8 chore: use preview import paths in examples 2026-06-30 21:40:09 +02:00
4c1025b1eb chore: remove "Typst" from description 2026-06-30 21:31:55 +02:00
b35c04a7d9 chore: add alt text on README images 2026-06-30 21:29:01 +02:00
0612e9b6ec chore: bump to 0.2.1 2026-06-30 21:13:41 +02:00
42d6915b15 Merge pull request 'fix: handle y port alignment with gates' (#22) from fix/15-gate-y-port-alignment into dev
Reviewed-on: #22
2026-06-30 19:08:48 +00:00
b646d24930 fix: handle y port alignment with gates 2026-06-30 21:07:52 +02:00
1533b5ccd4 Merge pull request 'Update manual and gallery' (#21) from feat/update-manual-and-gallery into dev
Reviewed-on: #21
2026-06-30 17:18:26 +00:00
2633b35f39 chore: remove gallery PDFs 2026-06-30 19:16:17 +02:00
415029e0c3 chore: update gallery examples 2026-06-30 19:15:26 +02:00
091ec8c08a chore: update manual 2026-06-30 19:13:19 +02:00
ffd3356ab2 docs: add radius parameter for block 2026-06-30 19:02:08 +02:00
02e59c5d7a Merge pull request 'Update CeTZ' (#20) from feat/18-update-cetz into dev
Reviewed-on: #20
2026-06-30 16:54:30 +00:00
96dd9b0543 chore: bump compiler version to 0.13.1 2026-06-30 18:52:01 +02:00
da9c2b568b chore: update cetz to 0.3.4 2026-06-30 18:50:22 +02:00
459892bb13 refactor: centralize cetz import 2026-06-30 18:48:34 +02:00
255eec7f35 Merge pull request 'chore: expose package name' (#19) from feat/17-expose-name into dev
Reviewed-on: #19
2026-06-30 16:34:28 +00:00
536f5a075d chore: expose package name 2026-06-30 18:31:51 +02:00
d73215c889 Merge pull request 'feature: added guided wire style' (#14) from Shmerlard/circuiteria:dev into dev
Reviewed-on: #14
Reviewed-by: Louis Heredero <louis@herdac.ch>
2025-08-24 11:01:02 +00:00
87643983ec added guided wire 2025-08-17 22:05:36 +03:00
94fef0a245 Merge pull request 'Add radius parameter to block' (#12) from Shmerlard/circuiteria:main into dev
Reviewed-on: #12
Reviewed-by: Louis Heredero <louis@herdac.ch>
2025-08-17 12:36:04 +00:00
1a0a659ace no font is set in circuit 2025-08-17 15:31:09 +03:00
f2ce91ec39 small fix for radius 2025-08-17 14:33:31 +03:00
043bb339fe font arguement, radius arguement
now in `block.typ` the radius is optional
and the font can be set in the canvas
2025-08-17 14:00:21 +03:00
693676d61a Merge pull request 'feature: add IEC gates' (#11) from bono/circuiteria:add_iec_gates into dev
Reviewed-on: #11
Reviewed-by: Louis Heredero <louis@herdac.ch>
2025-06-29 13:19:56 +00:00
94d5d6b854 add examples and add iec gates to manual 2025-06-28 13:56:22 -03:00
71f128f6c9 fix typo in xnor gate 2025-06-28 13:55:14 -03:00
ea8277ee5b rename iec gates to have consistent naming 2025-06-28 12:28:28 -03:00
c5e4f8039a improve iec-gate 2025-06-28 12:10:00 -03:00
8c91ccdd54 improve default-draw-shape by drawing a rectangle 2025-06-28 11:40:36 -03:00
9966656e8b add examples 2025-06-22 14:49:11 -03:00
3ccb79c6c2 add iec gates 2025-06-22 14:46:52 -03:00
2bb7e3b5a9 Merge pull request 'v0.2.0: Updates' (#5) from dev into main
Reviewed-on: #5
2025-03-01 21:28:24 +00:00
371caf094c updated README and manual with new version 2025-03-01 22:23:52 +01:00
841f53e76c Merge pull request 'updated compiler version + dependencies' (#4) from feat/updates-1 into dev
Reviewed-on: #4
2025-03-01 21:17:27 +00:00
ff0b91e683 updated compiler version + dependencies 2025-03-01 21:22:08 +01:00
e1e561bb6c fixed y-alignment for north/south ports 2024-11-09 23:03:03 +01:00
55 changed files with 817 additions and 119 deletions

View File

@@ -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.
<p align="center">
<img src="./gallery/platypus.png" alt="Perry the platypus">
<img src="./gallery/platypus.png" alt="Perry the platypus" alt="A platypus / Perry the Platypus meme made with colored rounded rectangles">
</p>
## Examples
@@ -11,17 +11,17 @@ Circuiteria is a [Typst](https://typst.app) package for drawing block circuit di
<tr>
<td colspan="2">
<a href="./gallery/test.typ">
<img src="./gallery/test.png" width="500px">
<img src="./gallery/test.png" width="500px" alt="Block diagram of a multi-cycle RISC-V FPGA processor">
</a>
</td>
</tr>
<tr>
<td colspan="2">A bit of eveything</td>
<td colspan="2">A bit of everything</td>
</tr>
<tr>
<td colspan="2">
<a href="./gallery/test5.typ">
<img src="./gallery/test5.png" width="500px">
<img src="./gallery/test5.png" width="500px" alt="Block diagram of a single-cycle RISC-V processor">
</a>
</td>
</tr>
@@ -31,12 +31,12 @@ Circuiteria is a [Typst](https://typst.app) package for drawing block circuit di
<tr>
<td>
<a href="./gallery/test4.typ">
<img src="./gallery/test4.png" width="250px">
<img src="./gallery/test4.png" width="250px" alt="Top-level block diagram of a single-cycle RISC-V processor">
</a>
</td>
<td>
<a href="./gallery/test6.typ">
<img src="./gallery/test6.png" width="250px">
<img src="./gallery/test6.png" width="250px" alt="Block diagram of a simple single-cycle RISC-V ALU">
</a>
</td>
</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:
```typ
#import "@preview/circuiteria:0.1.0"
#import "@preview/circuiteria:0.2.1"
#circuiteria.circuit({
import circuiteria: *
...

View File

@@ -1,4 +1,4 @@
#import "@preview/cetz:0.2.2": draw
#import "/src/cetz.typ": draw
#import "../src/circuit.typ": circuit
#import "../src/util.typ"

View File

@@ -56,7 +56,7 @@ for i in range(3) {
```)
#let wires = example(```
for i in range(3) {
for i in range(4) {
draw.circle((i * 3, 0), radius: .1, name: "p" + str(i * 2))
draw.circle((i * 3 + 2, 1), radius: .1, name: "p" + str(i * 2 + 1))
draw.content((i * 3 + 1, -1), raw(wire.wire-styles.at(i)))
@@ -65,6 +65,10 @@ wire.wire("w1", ("p0", "p1"), style: "direct")
wire.wire("w2", ("p2", "p3"), style: "zigzag")
wire.wire("w3", ("p4", "p5"), style: "dodge",
dodge-y: -0.5, dodge-margins: (0.5, 0.5))
wire.wire("w4", ("p6","p7"), style: "guided",
guided-center:(20%, 40%), guided-margins: (90%,87%),
guided-sides: ("north","south"))
```, vertical: true)
#let stub = example(```
@@ -115,6 +119,46 @@ gates.gate-xnor(x: 0, y: 0, w: 1.5, h: 1.5)
gates.gate-xnor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-and = example(```
gates.iec-gate-and(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-and(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-nand = example(```
gates.iec-gate-nand(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-nand(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-or = example(```
gates.iec-gate-or(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-or(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-nor = example(```
gates.iec-gate-nor(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-nor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-xor = example(```
gates.iec-gate-xor(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-xor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-xnor = example(```
gates.iec-gate-xnor(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-xnor(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-buf = example(```
gates.iec-gate-buf(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-buf(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let iec-gate-not = example(```
gates.iec-gate-not(x: 0, y: 0, w: 1.5, h: 1.5)
gates.iec-gate-not(x: 3, y: 0, w: 1.5, h: 1.5, inverted: "all")
```, vertical: true)
#let group = example(```
element.group(
id: "g1", name: "Group 1", stroke: (dash: "dashed"),
@@ -147,4 +191,4 @@ wire.wire("w1", ((0, 0), (1, 1)), style: "zigzag")
wire.wire("w2", ((0, 0), (1, -.5)),
style: "zigzag", zigzag-ratio: 80%)
wire.intersection("w1.zig")
```)
```)

View File

@@ -1,40 +0,0 @@
#!/bin/bash
PDFS=false
while getopts "p" flag
do
case "${flag}" in
p) PDFS=true;;
esac
done
echo "Generating gallery images"
set -- ./gallery/*.typ
cnt="$#"
i=1
for f
do
f2="${f/typ/png}"
echo "($i/$cnt) $f -> $f2"
typst c --root ./ "$f" "$f2"
i=$((i+1))
done
if [ "$PDFS" = true ]
then
echo
echo "Generating gallery PDFs"
set -- ./gallery/*.typ
cnt="$#"
i=1
for f
do
f2="${f/typ/pdf}"
echo "($i/$cnt) $f -> $f2"
typst c --root ./ "$f" "$f2"
i=$((i+1))
done
fi

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -1,4 +1,4 @@
#import "../src/lib.typ": *
#import "@preview/circuiteria:0.2.1": *
#set page(width: auto, height: auto, margin: .5cm)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -1,4 +1,4 @@
#import "../src/lib.typ": circuit, element, util, wire
#import "@preview/circuiteria:0.2.1": circuit, element, util, wire
#set page(width: auto, height: auto, margin: .5cm)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -1,4 +1,4 @@
#import "../src/lib.typ": circuit, element, util, wire
#import "@preview/circuiteria:0.2.1": circuit, element, util, wire
#set page(width: auto, height: auto, margin: .5cm)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -1,5 +1,4 @@
#import "@preview/cetz:0.2.2": draw
#import "../src/lib.typ": circuit, element, util, wire
#import "@preview/circuiteria:0.2.1": circuit, element, util, wire
#set page(width: auto, height: auto, margin: .5cm)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 67 KiB

View File

@@ -1,5 +1,5 @@
#import "@preview/cetz:0.2.2": draw
#import "../src/lib.typ": *
#import "/src/cetz.typ": draw
#import "@preview/circuiteria:0.2.1": *
#set page(width: auto, height: auto, margin: .5cm)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

After

Width:  |  Height:  |  Size: 93 KiB

View File

@@ -1,5 +1,5 @@
#import "@preview/cetz:0.2.2": draw
#import "../src/lib.typ": *
#import "/src/cetz.typ": draw
#import "@preview/circuiteria:0.2.1": *
#set page(width: auto, height: auto, margin: .5cm)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -1,5 +1,5 @@
#import "@preview/cetz:0.2.2": draw, vector
#import "../src/lib.typ": *
#import "/src/cetz.typ": draw
#import "@preview/circuiteria:0.2.1": *
#set page(width: auto, height: auto, margin: .5cm)

BIN
gallery/test7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

97
gallery/test7.typ Normal file
View File

@@ -0,0 +1,97 @@
#import "@preview/circuiteria:0.2.1": circuit, element, util, wire
#set page(width: auto, height: auto, margin: .5cm)
#circuit({
element.iec-gate-buf(
x: 0,
y: 0,
w: 2,
h: 2,
id: "iec-buf",
inputs: 1,
)
wire.stub("iec-buf-port-in0", "west")
element.iec-gate-not(
x: 3,
y: 0,
w: 2,
h: 2,
id: "iec-not",
inputs: 1,
)
wire.stub("iec-not-port-in0", "west")
element.iec-gate-and(
id: "iec-and",
x: 0,
y: -3,
w: 2,
h: 2,
inputs: 2,
)
for i in range(2) {
wire.stub("iec-and-port-in" + str(i), "west")
}
element.iec-gate-nand(
id: "iec-nand",
x: 3,
y: -3,
w: 2,
h: 2,
inputs: 2,
)
for i in range(2) {
wire.stub("iec-nand-port-in" + str(i), "west")
}
element.iec-gate-or(
id: "iec-or",
x: 0,
y: -6,
w: 2,
h: 2,
inputs: 2,
)
for i in range(2) {
wire.stub("iec-or-port-in" + str(i), "west")
}
element.iec-gate-nor(
id: "iec-nor",
x: 3,
y: -6,
w: 2,
h: 2,
inputs: 2,
)
for i in range(2) {
wire.stub("iec-nor-port-in" + str(i), "west")
}
element.iec-gate-xor(
id: "iec-xor",
x: 0,
y: -9,
w: 2,
h: 2,
inputs: 2,
)
for i in range(2) {
wire.stub("iec-xor-port-in" + str(i), "west")
}
element.iec-gate-xnor(
id: "iec-nxor",
x: 3,
y: -9,
w: 2,
h: 2,
inputs: 2,
)
for i in range(2) {
wire.stub("iec-nxor-port-in" + str(i), "west")
}
})

11
justfile Normal file
View File

@@ -0,0 +1,11 @@
# Local Variables:
# mode: makefile
# End:
gallery_dir := "./gallery"
set shell := ["bash", "-uc"]
manual:
typst c manual.typ manual.pdf
gallery:
for f in "{{gallery_dir}}"/*.typ; do typst c --root . "$f" "${f%typ}png"; done

Binary file not shown.

View File

@@ -1,5 +1,5 @@
#import "@preview/tidy:0.3.0"
#import "@preview/cetz:0.2.2": draw, canvas
#import "@preview/tidy:0.4.3"
#import "/src/cetz.typ": draw, canvas
#import "src/lib.typ"
#import "doc/examples.typ"
#import "src/circuit.typ": circuit
@@ -12,7 +12,7 @@
numbering("1.1", ..num)
})
#{
outline(indent: true, depth: 3)
outline(indent: auto, depth: 3)
}
#show link: set text(blue)
@@ -47,7 +47,7 @@
#set page(numbering: "1/1", header: align(right)[circuiteria #sym.dash.em v#lib.version])
#set page(
header: locate(loc => {
header: context {
let txt = [circuiteria #sym.dash.em v#lib.version]
let cnt = counter(heading)
let cnt-val = cnt.get()
@@ -65,8 +65,8 @@
#rect(width: 100%, height: .5em, radius: .25em, stroke: none, fill: util.colors.values().at(i))
]
)
}),
footer: locate(loc => {
},
footer: context {
let cnt = counter(heading)
let cnt-val = cnt.get()
if cnt-val.len() < 2 { return }
@@ -80,12 +80,12 @@
],
counter(page).display("1/1", both: true)
)
})
}
)
#let doc-ref(target, full: false, var: false) = {
let (module, func) = target.split(".")
let label-name = module + func
let label-name = module + "-" + func
let display-name = func
if full {
display-name = target
@@ -94,7 +94,7 @@
label-name += "()"
display-name += "()"
}
link(label(label-name))[#display-name]
link(label(label-name), raw(display-name))
}
= Introduction
@@ -103,11 +103,21 @@ This package provides a way to make beautiful block circuit diagrams using the C
= Usage
Simply import #link("src/lib.typ") and call the `circuit` function:
Simply import Circuiteria and call the `circuit` function:
#pad(left: 1em)[```typ
#import "src/lib.typ"
#lib.circuit({
import lib: *
#import "@preview/circuiteria:0.2.1"
#circuiteria.circuit({
import circuiteria: *
...
})
```]
== Project installation
If you have installed Circuiteria directly in your project, import #link("src/lib.typ") and call the `circuit` function:
#pad(left: 1em)[```typ
#import "src/lib.typ" as circuiteria
#circuiteria.circuit({
import circuiteria: *
...
})
```]
@@ -117,6 +127,7 @@ Simply import #link("src/lib.typ") and call the `circuit` function:
#let circuit-docs = tidy.parse-module(
read("src/circuit.typ"),
name: "circuit",
old-syntax: true,
require-all-parameters: true
)
#tidy.show-module(circuit-docs)
@@ -126,6 +137,7 @@ Simply import #link("src/lib.typ") and call the `circuit` function:
#let util-docs = tidy.parse-module(
read("src/util.typ"),
name: "util",
old-syntax: true,
require-all-parameters: true,
scope: (
util: util,
@@ -140,6 +152,7 @@ Simply import #link("src/lib.typ") and call the `circuit` function:
#let wire-docs = tidy.parse-module(
read("src/wire.typ"),
name: "wire",
old-syntax: true,
require-all-parameters: true,
scope: (
wire: wire,
@@ -161,6 +174,7 @@ Simply import #link("src/lib.typ") and call the `circuit` function:
read("src/elements/multiplexer.typ") + "\n" +
read("src/elements/group.typ"),
name: "element",
old-syntax: true,
scope: (
element: element,
circuit: circuit,
@@ -181,8 +195,14 @@ Simply import #link("src/lib.typ") and call the `circuit` function:
read("src/elements/logic/and.typ") + "\n" +
read("src/elements/logic/buf.typ") + "\n" +
read("src/elements/logic/or.typ") + "\n" +
read("src/elements/logic/xor.typ"),
read("src/elements/logic/xor.typ") + "\n" +
read("src/elements/logic/iec_gate.typ") + "\n" +
read("src/elements/logic/iec_and.typ") + "\n" +
read("src/elements/logic/iec_buf.typ") + "\n" +
read("src/elements/logic/iec_or.typ") + "\n" +
read("src/elements/logic/iec_xor.typ"),
name: "gates",
old-syntax: true,
scope: (
element: element,
circuit: circuit,

1
src/cetz.typ Normal file
View File

@@ -0,0 +1 @@
#import "@preview/cetz:0.5.2": *

View File

@@ -1,5 +1,5 @@
#import "@preview/cetz:0.2.2": canvas
#import "@preview/tidy:0.3.0"
#import "/src/cetz.typ": canvas
#import "@preview/tidy:0.4.3"
/// Draws a block circuit diagram
///
@@ -9,6 +9,5 @@
/// - length (length, ratio): Optional base unit
/// -> none
#let circuit(body, length: 2em) = {
set text(font: "Source Sans 3")
canvas(length: length, body)
}
}

View File

@@ -11,5 +11,10 @@
#import "elements/logic/or.typ": gate-or, gate-nor
#import "elements/logic/xor.typ": gate-xor, gate-xnor
#import "elements/logic/buf.typ": gate-buf, gate-not
#import "elements/logic/iec_gate.typ": iec-gate
#import "elements/logic/iec_and.typ": iec-gate-and, iec-gate-nand
#import "elements/logic/iec_buf.typ": iec-gate-buf, iec-gate-not
#import "elements/logic/iec_or.typ": iec-gate-or, iec-gate-nor
#import "elements/logic/iec_xor.typ": iec-gate-xor, iec-gate-xnor
#import "elements/group.typ": group

View File

@@ -1,4 +1,4 @@
#import "@preview/cetz:0.2.2": draw
#import "/src/cetz.typ": draw
#import "element.typ"
#import "ports.typ": add-port

View File

@@ -1,9 +1,9 @@
#import "@preview/cetz:0.2.2": draw
#import "/src/cetz.typ": draw
#import "element.typ"
#let draw-shape(id, tl, tr, br, bl, fill, stroke) = {
#let draw-shape(id, tl, tr, br, bl, fill, stroke, radius: 0.5em) = {
let f = draw.rect(
radius: 0.5em,
radius: radius,
inset: 0.5em,
fill: fill,
stroke: stroke,
@@ -16,7 +16,8 @@
/// Draws a block element
///
/// #examples.block
/// For 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
#let block(
x: none,
y: none,
@@ -27,13 +28,14 @@
ports: (),
ports-margins: (),
fill: none,
radius: 0.5em,
stroke: black + 1pt,
id: "",
debug: (
ports: false
)
) = element.elmt(
draw-shape: draw-shape,
draw-shape: draw-shape.with(radius: radius),
x: x,
y: y,
w: w,
@@ -46,4 +48,4 @@
stroke: stroke,
id: id,
debug: debug
)
)

View File

@@ -1,4 +1,4 @@
#import "@preview/cetz:0.2.2": draw, coordinate
#import "/src/cetz.typ": draw, coordinate
#import "ports.typ": add-ports, add-port
#import "../util.typ"
@@ -88,15 +88,26 @@
if to-side in ports-margins {
margins = ports-margins.at(to-side)
}
let used-pct = 100% - margins.at(0) - margins.at(1)
let used-height = height * used-pct / 100%
let top-margin = height * margins.at(0) / 100%
let dy = used-height * (i + 1) / (ports.at(to-side).len() + 1)
if not auto-ports {
let dy
let top-margin
if to-side in ("east", "west") {
let used-pct = 100% - margins.at(0) - margins.at(1)
let used-height = height * used-pct / 100%
top-margin = height * margins.at(0) / 100%
dy = used-height * (i + 1) / (ports.at(to-side).len() + 1)
if not auto-ports {
top-margin = 0
dy = ports-y.at(to)(height)
}
} else if to-side == "north" {
dy = 0
top-margin = 0
} else if to-side == "south" {
dy = height
top-margin = 0
dy = ports-y.at(to)(height)
}
let (ctx, from-pos) = coordinate.resolve(ctx, from)

View File

@@ -1,4 +1,4 @@
#import "@preview/cetz:0.2.2": draw
#import "/src/cetz.typ": draw
#import "element.typ"
#import "ports.typ": add-port

View File

@@ -1,4 +1,4 @@
#import "@preview/cetz:0.2.2": draw, coordinate
#import "/src/cetz.typ": draw, coordinate
#import "../util.typ"
/// Draws a group of elements

View File

@@ -1,4 +1,4 @@
#import "@preview/cetz:0.2.2": draw
#import "/src/cetz.typ": draw
#import "gate.typ"
#let draw-shape(id, tl, tr, br, bl, fill, stroke) = {

View File

@@ -1,4 +1,4 @@
#import "@preview/cetz:0.2.2": draw
#import "/src/cetz.typ": draw
#import "gate.typ"
#let draw-shape(id, tl, tr, br, bl, fill, stroke) = {

View File

@@ -1,4 +1,4 @@
#import "@preview/cetz:0.2.2": draw, coordinate
#import "/src/cetz.typ": draw, coordinate
#import "../ports.typ": add-ports, add-port
#import "../element.typ"
@@ -63,6 +63,12 @@
if to == "out" {
dy = height / 2
} else {
let all-ports = range(inputs).map(i => "in" + str(i))
let i = all-ports.position(p => p == to)
assert(
i != none,
message: "Unknown port '" + y.to + "' must be one of " + repr(all-ports)
)
dy = height * (i + 0.5) / inputs
}

View File

@@ -0,0 +1,70 @@
#import "/src/cetz.typ": draw
// #import "iec_gate.typ" as iec-gate
#import "iec_gate.typ" as iec-gate
/// Draws an IEC-AND gate. This function is also available as `element.iec-gate-and()`
///
/// For parameters, see #doc-ref("gates.iec-gate")
/// #examples.iec-gate-and
#let iec-gate-and(
x: none,
y: none,
w: none,
h: none,
inputs: 2,
fill: none,
stroke: black + 1pt,
id: "",
inverted: (),
debug: (
ports: false
),
) = {
iec-gate.iec-gate(
x: x,
y: y,
w: w,
h: h,
inputs: inputs,
fill: fill,
stroke: stroke,
id: id,
inverted: inverted,
debug: debug,
symbol: $amp$,
)
}
/// Draws an IEC-NAND gate. This function is also available as `element.iec-gate-nand()`
///
/// For parameters, see #doc-ref("gates.iec-gate")
/// #examples.iec-gate-nand
#let iec-gate-nand(
x: none,
y: none,
w: none,
h: none,
inputs: 2,
fill: none,
stroke: black + 1pt,
id: "",
inverted: (),
debug: (
ports: false
),
) = {
iec-gate-and(
x: x,
y: y,
w: w,
h: h,
inputs: inputs,
fill: fill,
stroke: stroke,
id: id,
inverted: if inverted != "all" {inverted + ("out",)} else {inverted},
debug: debug,
)
}

View File

@@ -0,0 +1,68 @@
#import "/src/cetz.typ": draw
#import "iec_gate.typ" as iec-gate
/// Draws an IEC buffer gate. This function is also available as `element.iec-gate-buf()`
///
/// For parameters, see #doc-ref("gates.iec-gate")
/// #examples.iec-gate-buf
#let iec-gate-buf(
x: none,
y: none,
w: none,
h: none,
inputs: 2,
fill: none,
stroke: black + 1pt,
id: "",
inverted: (),
debug: (
ports: false,
),
) = {
iec-gate.iec-gate(
x: x,
y: y,
w: w,
h: h,
inputs: inputs,
fill: fill,
stroke: stroke,
id: id,
inverted: inverted,
debug: debug,
symbol: "1",
)
}
/// Draws an IEC NOT gate. This function is also available as `element.iec-gate-not()`
///
/// For parameters, see #doc-ref("gates.iec-gate")
/// #examples.iec-gate-not
#let iec-gate-not(
x: none,
y: none,
w: none,
h: none,
inputs: 2,
fill: none,
stroke: black + 1pt,
id: "",
inverted: (),
debug: (
ports: false,
),
) = {
iec-gate-buf(
x: x,
y: y,
w: w,
h: h,
inputs: inputs,
fill: fill,
stroke: stroke,
id: id,
inverted: if inverted != "all" { inverted + ("out",) } else { inverted },
debug: debug,
)
}

View File

@@ -0,0 +1,125 @@
#import "/src/cetz.typ": draw, coordinate
#import "../ports.typ": add-ports, add-port
#import "../element.typ"
#let default-draw-shape(id, tl, tr, br, bl, fill, stroke, symbol) = {
let shapes = draw.rect(
inset: 0.5em,
fill: fill,
stroke: stroke,
name: id,
bl, tr
)
shapes += draw.content(
id + ".center",
[*$ symbol $*]
)
return (shapes, tl, tr, br, bl)
}
/// Draws a logic gate. This function is also available as `element.iec-gate()`
///
/// - draw-shape (function): see #doc-ref("element.elmt")
/// - x (number, dictionary): see #doc-ref("element.elmt")
/// - y (number, dictionary): see #doc-ref("element.elmt")
/// - w (number): see #doc-ref("element.elmt")
/// - h (number): see #doc-ref("element.elmt")
/// - inputs (int): The number of inputs
/// - fill (none, color): see #doc-ref("element.elmt")
/// - stroke (stroke): see #doc-ref("element.elmt")
/// - id (str): see #doc-ref("element.elmt")
/// - inverted (str, array): Either "all" or an array of port ids to display as inverted
/// - inverted-radius (number): The radius of inverted ports dot
/// - debug (dictionary): see #doc-ref("element.elmt")
/// - symbol (str): The symbol to display at the center of the gate
#let iec-gate(
draw-shape: default-draw-shape,
x: none,
y: none,
w: none,
h: none,
inputs: 2,
fill: none,
stroke: black + 1pt,
id: "",
inverted: (),
inverted-radius: 0.1,
debug: (
ports: false
),
symbol: "",
) = draw.get-ctx(ctx => {
let width = w
let height = h
let x = x
let y = y
if x == none { panic("Parameter x must be set") }
if y == none { panic("Parameter y must be set") }
if w == none { panic("Parameter w must be set") }
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)
}
if (type(y) == dictionary) {
let from = y.from
let to = y.to
let dy
if to == "out" {
dy = height / 2
} else {
dy = height * (i + 0.5) / inputs
}
let (ctx, from-pos) = coordinate.resolve(ctx, from)
y = from-pos.at(1) + dy - height
}
let tl = (x, y + height)
let tr = (x + width, y + height)
let br = (x + width, y)
let bl = (x, y)
// Workaround because CeTZ needs to have all draw functions in the body
let func = {}
(func, tl, tr, br, bl) = draw-shape(id, tl, tr, br, bl, fill, stroke, symbol)
func
let space = 100% / inputs
for i in range(inputs) {
let pct = (i + 0.5) * space
let port-pos = (tl, pct, bl)
let port-name = "in" + str(i)
if inverted == "all" or port-name in inverted {
draw.circle(
port-pos,
radius: inverted-radius,
anchor: "east",
stroke: stroke
)
port-pos = (rel: (-2 * inverted-radius, 0), to: port-pos)
}
add-port(
id, "west",
(id: port-name), port-pos,
debug: debug.ports
)
}
let out-pos = id + ".east"
if inverted == "all" or "out" in inverted {
draw.circle(out-pos, radius: inverted-radius, anchor: "west", stroke: stroke)
out-pos = (rel: (2 * inverted-radius, 0), to: out-pos)
}
add-port(
id, "east",
(id: "out"), out-pos,
debug: debug.ports
)
})

View File

@@ -0,0 +1,67 @@
#import "/src/cetz.typ": draw
#import "iec_gate.typ" as iec-gate
/// Draws an IEC-OR gate. This function is also available as `element.iec-gate-or()`
///
/// For parameters, see #doc-ref("gates.iec-gate")
/// #examples.iec-gate-or
#let iec-gate-or(
x: none,
y: none,
w: none,
h: none,
inputs: 2,
fill: none,
stroke: black + 1pt,
id: "",
inverted: (),
debug: (
ports: false
)
) = {
iec-gate.iec-gate(
x: x,
y: y,
w: w,
h: h,
inputs: inputs,
fill: fill,
stroke: stroke,
id: id,
inverted: inverted,
debug: debug,
symbol: $>= 1$,
)
}
/// Draws an IEC-NOR gate. This function is also available as `element.iec-gate-nor()`
///
/// For parameters, see #doc-ref("gates.iec-gate")
/// #examples.iec-gate-nor
#let iec-gate-nor(
x: none,
y: none,
w: none,
h: none,
inputs: 2,
fill: none,
stroke: black + 1pt,
id: "",
inverted: (),
debug: (
ports: false
)
) = {
iec-gate-or(
x: x,
y: y,
w: w,
h: h,
inputs: inputs,
fill: fill,
stroke: stroke,
id: id,
inverted: if inverted != "all" {inverted + ("out",)} else {inverted},
debug: debug
)
}

View File

@@ -0,0 +1,67 @@
#import "/src/cetz.typ": draw
#import "iec_gate.typ" as iec-gate
/// Draws an IEC-XOR gate. This function is also available as `element.iec-gate-xor()`
///
/// For parameters, see #doc-ref("gates.iec-gate")
/// #examples.iec-gate-xor
#let iec-gate-xor(
x: none,
y: none,
w: none,
h: none,
inputs: 2,
fill: none,
stroke: black + 1pt,
id: "",
inverted: (),
debug: (
ports: false
)
) = {
iec-gate.iec-gate(
x: x,
y: y,
w: w,
h: h,
inputs: inputs,
fill: fill,
stroke: stroke,
id: id,
inverted: inverted,
debug: debug,
symbol: $= 1$,
)
}
/// Draws an IEC-XNOR gate. This function is also available as `element.iec-gate-xnor()`
///
/// For parameters, see #doc-ref("gates.iec-gate")
/// #examples.iec-gate-xnor
#let iec-gate-xnor(
x: none,
y: none,
w: none,
h: none,
inputs: 2,
fill: none,
stroke: black + 1pt,
id: "",
inverted: (),
debug: (
ports: false
)
) = {
iec-gate-xor(
x: x,
y: y,
w: w,
h: h,
inputs: inputs,
fill: fill,
stroke: stroke,
id: id,
inverted: if inverted != "all" {inverted + ("out",)} else {inverted},
debug: debug
)
}

View File

@@ -1,4 +1,4 @@
#import "@preview/cetz:0.2.2": draw
#import "/src/cetz.typ": draw
#import "gate.typ"
#let draw-shape(id, tl, tr, br, bl, fill, stroke) = {

View File

@@ -1,4 +1,4 @@
#import "@preview/cetz:0.2.2": draw
#import "/src/cetz.typ": draw
#import "gate.typ"
#let space = 10%

View File

@@ -1,4 +1,4 @@
#import "@preview/cetz:0.2.2": draw
#import "/src/cetz.typ": draw
#import "../util.typ"
#import "element.typ"
#import "ports.typ": add-port

View File

@@ -1,4 +1,4 @@
#import "@preview/cetz:0.2.2": draw
#import "/src/cetz.typ": draw
#import "../util.typ": rotate-anchor
#let add-port(

View File

@@ -2,4 +2,9 @@
#import "elements/logic/and.typ": gate-and, gate-nand
#import "elements/logic/or.typ": gate-or, gate-nor
#import "elements/logic/xor.typ": gate-xor, gate-xnor
#import "elements/logic/buf.typ": gate-buf, gate-not
#import "elements/logic/buf.typ": gate-buf, gate-not
#import "elements/logic/iec_gate.typ": iec-gate
#import "elements/logic/iec_and.typ": iec-gate-and, iec-gate-nand
#import "elements/logic/iec_or.typ": iec-gate-or, iec-gate-nor
#import "elements/logic/iec_buf.typ": iec-gate-buf, iec-gate-not
#import "elements/logic/iec_xor.typ": iec-gate-xor, iec-gate-xnor

View File

@@ -1,4 +1,5 @@
#let version = version(0, 1, 0)
#let version = version(0, 2, 1)
#let name = "circuiteria"
#import "circuit.typ": circuit
#import "element.typ"

View File

@@ -73,4 +73,45 @@
#let valid-anchors = (
"center", "north", "east", "west", "south",
"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
}

View File

@@ -1,9 +1,9 @@
#import "@preview/cetz:0.2.2": draw, coordinate
#import "util.typ": opposite-anchor
#import "/src/cetz.typ": draw, coordinate
#import "util.typ": opposite-anchor, normalize-coord
/// List of valid wire styles
/// #examples.wires
#let wire-styles = ("direct", "zigzag", "dodge")
#let wire-styles = ("direct", "zigzag", "dodge", "guided")
#let signal-width = 1pt
#let bus-width = 1.5pt
@@ -68,8 +68,8 @@
let (ctx, p0) = coordinate.resolve(ctx, start)
let (ctx, p3) = coordinate.resolve(ctx, end)
p0 = (x: p0.first(), y: p0.last())
p3 = (x: p3.first(), y: p3.last())
p0 = (x: p0.first(), y: p0.at(1))
p3 = (x: p3.first(), y: p3.at(1))
let dx1 = margin-start
let dx2 = margin-end
@@ -86,13 +86,13 @@
if side-end == "east" {
dx2 *= -1
}
p1 = (p0.x + dx1, p0.y)
p2 = (p3.x - dx2, p0.y)
p1 = (p0.x + dx1, p0.y, 0)
p2 = (p3.x - dx2, p0.y, 0)
let points = (
start,
(horizontal: p1, vertical: ()),
(horizontal: (), vertical: (0, dodge-y)),
(horizontal: (), vertical: (0, dodge-y, 0)),
(horizontal: p2, vertical: ()),
(horizontal: (), vertical: end),
end
@@ -109,6 +109,88 @@
return (points, anchors)
}
#let get-guided-wire(pts, margins, sides, center-guides, ctx) = {
let start = pts.first()
let end = pts.last()
let (margin-start, margin-end) = margins
let (side-start, side-end) = sides
let (center_horizontal, center_vertical) = center-guides
let (ctx, p0) = coordinate.resolve(ctx, start)
let (ctx, p6) = coordinate.resolve(ctx, end)
p0 = (x: p0.first(), y: p0.at(1))
p6 = (x: p6.first(), y: p6.at(1))
let box_width = calc.abs(p6.x - p0.x)
let box_height = calc.abs(p6.y - p0.y)
// finding correct dx and dy
let dx1 = box_width * margin-start / 100%
if side-start == "west" {
dx1 *= -1
} else if side-start == "north" or side-start == "south" { dx1 = 0}
let dx2 = box_width * margin-end / 100%
if side-end == "west" {
dx2 *= -1
} else if side-end == "north" or side-end == "south" { dx2 = 0}
let dy1 = box_height * margin-start / 100%
if side-start == "south" {
dy1 *= -1
} else if side-start == "west" or side-start == "east" { dy1 = 0}
let dy2 = box_height * margin-end / 100%
if side-end == "south" {
dy2 *= -1
} else if side-end == "west" or side-end == "east" { dy2 = 0}
// points that are closest to the edge points
let p1 = (p0.x + dx1, p0.y + dy1, 0)
let p5 = (p6.x + dx2, p6.y + dy2, 0)
// middle point
let center_x = p0.x + box_width * center_horizontal / 100%
let center_y = p0.y + box_height * center_vertical / 100%
let p3 = (center_x, center_y, 0)
// setting up the points for that touch the guides
let p2 = (0, 0, 0)
let p4 = (0, 0, 0)
if side-start in ("north", "south") {
p2 = (horizontal: p3, vertical: p1)
} else {
p2 = (horizontal: p1, vertical: p3)
}
if side-end in ("north", "south") {
p4 = (horizontal: p3, vertical: p5)
} else if side-end in ("east", "west") {
p4 = (horizontal: p5, vertical: p3)
}
// returning
let points = (
start,
p1,
p2,
p3,
p4,
p5,
end
)
let anchors = (
"start": start,
"start2": points.at(1),
"guide-start": points.at(2),
"center": points.at(3),
"guide-end": points.at(4),
"end2": points.at(5),
"end": end
)
return (points, anchors)
}
/// Draws a wire between two points
/// - id (str): The wire's id, for future reference (anchors)
/// - pts (array): The two points (as CeTZ compatible coordinates, i.e. XY, relative positions, ids, etc.)
@@ -127,6 +209,9 @@
/// - 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-margins (array): The start and end margins (i.e. space before dodging) of the wire (only with style "dodge")
/// - guided-center (array): the horizontal and vertical guides of the center guides (only with style "guided")
/// - guided-margins (array): the start and end of guided margins of the wire (only with style "guided")
/// - guided-sides (array): the side of start and end array (must be either "north", "south", "west", "east") (only work with style "guided")
#let wire(
id, pts,
bus: false,
@@ -137,6 +222,9 @@
dashed: false,
style: "direct",
reverse: false,
guided-center: (50%, 50%),
guided-margins: (5%, 5%),
guided-sides: ("east", "west"),
directed: false,
rotate-name: true,
zigzag-ratio: 50%,
@@ -152,6 +240,9 @@
if pts.len() != 2 {
panic("Wrong number of points (got " + str(pts.len()) + " instead of 2)")
}
// Normalize user-provided coordinates: (x, y) -> (x, y, 0)
let pts = pts.map(normalize-coord)
let stroke = (
paint: color,
@@ -178,6 +269,14 @@
dodge-sides,
ctx
)
} else if style == "guided" {
(points, anchors) = get-guided-wire(
pts,
guided-margins,
guided-sides,
guided-center,
ctx
)
}
let mark = (fill: color)
@@ -303,4 +402,4 @@
name
)
}
}
}

View File

@@ -1,7 +1,7 @@
[package]
name = "circuiteria"
version = "0.1.0"
compiler = "0.11.0"
version = "0.2.1"
compiler = "0.15.0"
repository = "https://git.kb28.ch/HEL/circuiteria"
entrypoint = "src/lib.typ"
authors = [
@@ -9,6 +9,6 @@ authors = [
]
categories = [ "visualization" ]
license = "Apache-2.0"
description = "Drawing block circuits with Typst made easy, using CeTZ"
description = "Drawing block circuits made easy, using CeTZ"
keywords = [ "circuit", "block", "draw" ]
exclude = [ "gallery", "gallery.bash", "doc" ]
exclude = [ "gallery", "justfile", "doc" ]