Compare commits
	
		
			15 Commits
		
	
	
		
			60898d1e60
			...
			feat/draw-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						9ab454045e
	
				 | 
					
					
						|||
| 
						
						
							
						
						aba66ed442
	
				 | 
					
					
						|||
| 
						
						
							
						
						0a108fdc73
	
				 | 
					
					
						|||
| 
						
						
							
						
						cd8784fcee
	
				 | 
					
					
						|||
| 
						
						
							
						
						4733f69b51
	
				 | 
					
					
						|||
| 
						
						
							
						
						33d79f35fa
	
				 | 
					
					
						|||
| 
						
						
							
						
						3e9272255c
	
				 | 
					
					
						|||
| 
						
						
							
						
						4930b53c84
	
				 | 
					
					
						|||
| 
						
						
							
						
						26231b2f48
	
				 | 
					
					
						|||
| 
						
						
							
						
						5616544707
	
				 | 
					
					
						|||
| 
						
						
							
						
						358de4825d
	
				 | 
					
					
						|||
| 2bb7e3b5a9 | |||
| 
						
						
							
						
						371caf094c
	
				 | 
					
					
						|||
| 841f53e76c | |||
| 
						
						
							
						
						ff0b91e683
	
				 | 
					
					
						
@@ -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.1.0"
 | 
					#import "@preview/circuiteria:0.2.0"
 | 
				
			||||||
#circuiteria.circuit({
 | 
					#circuiteria.circuit({
 | 
				
			||||||
  import circuiteria: *
 | 
					  import circuiteria: *
 | 
				
			||||||
  ...
 | 
					  ...
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw
 | 
					#import "/src/cetz.typ": draw
 | 
				
			||||||
#import "../src/circuit.typ": circuit
 | 
					#import "../src/circuit.typ": circuit
 | 
				
			||||||
#import "../src/util.typ"
 | 
					#import "../src/util.typ"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -148,21 +148,3 @@ wire.wire("w2", ((0, 0), (1, -.5)),
 | 
				
			|||||||
          style: "zigzag", zigzag-ratio: 80%)
 | 
					          style: "zigzag", zigzag-ratio: 80%)
 | 
				
			||||||
wire.intersection("w1.zig")
 | 
					wire.intersection("w1.zig")
 | 
				
			||||||
```)
 | 
					```)
 | 
				
			||||||
 | 
					 | 
				
			||||||
#let capacitor = example(```
 | 
					 | 
				
			||||||
electrical.capacitor(
 | 
					 | 
				
			||||||
  x: 0, y: 0, w: 2, h: 1, id: "a",
 | 
					 | 
				
			||||||
  scales: (100%, 80%), gap: 0.3
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
electrical.capacitor(
 | 
					 | 
				
			||||||
  x: 4, y: -0.5, w: 1, h: 2, id: "b",
 | 
					 | 
				
			||||||
  vertical: true, symbols: ([+], none)
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
```, vertical: true)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#let resistor = example(```
 | 
					 | 
				
			||||||
electrical.resistor(x: 0, y: 0, w: 2, h: 0.5, id: "a", zigzags: 8)
 | 
					 | 
				
			||||||
electrical.resistor(x: 4, y: -0.5, w: 0.5, h: 2, id: "b", vertical: true)
 | 
					 | 
				
			||||||
electrical.resistor(x: 6.5, y: 0, w: 2, h: 0.5, id: "c", zigzags: none)
 | 
					 | 
				
			||||||
```, vertical: true)
 | 
					 | 
				
			||||||
							
								
								
									
										40
									
								
								gallery.bash
									
									
									
									
									
								
							
							
						
						@@ -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
 | 
					 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								gallery/target_api.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										286
									
								
								gallery/target_api.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,286 @@
 | 
				
			|||||||
 | 
					#import "../src/lib.typ": *
 | 
				
			||||||
 | 
					#import "/src/cetz.typ": draw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#set text(font: "Source Sans 3")
 | 
				
			||||||
 | 
					#set page(width: auto, height: auto, margin: .5cm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#circuit({
 | 
				
			||||||
 | 
					  element.block(
 | 
				
			||||||
 | 
					    size: (1.5, 2.2),
 | 
				
			||||||
 | 
					    id: "PCBuf",
 | 
				
			||||||
 | 
					    fill: util.colors.orange,
 | 
				
			||||||
 | 
					    ports: (
 | 
				
			||||||
 | 
					      west: "PCNext",
 | 
				
			||||||
 | 
					      north: (id: "CLK", clock: true),
 | 
				
			||||||
 | 
					      east: "PC",
 | 
				
			||||||
 | 
					      south: (("EN", "EN"),)
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  wire.stub("PCBuf.CLK", name: "CLK")
 | 
				
			||||||
 | 
					  wire.stub("PCBuf.EN", name: "PCWrite")
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  element.multiplexer(
 | 
				
			||||||
 | 
					    pos: (
 | 
				
			||||||
 | 
					      3, (align: "in0", with: "PCBuf.PC")
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    size: (1, 2),
 | 
				
			||||||
 | 
					    id: "AdrSrc-MP",
 | 
				
			||||||
 | 
					    fill: util.colors.orange,
 | 
				
			||||||
 | 
					    entries: 2
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    "PCBuf.PC",
 | 
				
			||||||
 | 
					    "AdrSrc-MP.in0",
 | 
				
			||||||
 | 
					    id: "wPCBuf-InstDataMgr",
 | 
				
			||||||
 | 
					    name: "PC",
 | 
				
			||||||
 | 
					    bus: true
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  wire.stub("AdrSrc-MP.north", side: "north", name: "AdrSrc")
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  element.block(
 | 
				
			||||||
 | 
					    pos: (
 | 
				
			||||||
 | 
					      6, (align: "A", with: "AdrSrc-MP.out")
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    size: (3, 4),
 | 
				
			||||||
 | 
					    id: "InstDataMgr",
 | 
				
			||||||
 | 
					    fill: util.colors.yellow,
 | 
				
			||||||
 | 
					    ports: (
 | 
				
			||||||
 | 
					      west: (
 | 
				
			||||||
 | 
					        ("A", "A"),
 | 
				
			||||||
 | 
					        ("WD", "WD")
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      north: (
 | 
				
			||||||
 | 
					        (id: "CLK", clock: true),
 | 
				
			||||||
 | 
					        (id: "WE", name: "WE", vertical: true),
 | 
				
			||||||
 | 
					        (id: "IRWrite", name: "IRWrite", vertical: true)
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      east: (
 | 
				
			||||||
 | 
					        ("Instr", "Instr."),
 | 
				
			||||||
 | 
					        ("RD", "RD")
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    ports-margins: (
 | 
				
			||||||
 | 
					      west: (30%, 0%),
 | 
				
			||||||
 | 
					      east: (40%, 0%)
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    "AdrSrc-MP.out",
 | 
				
			||||||
 | 
					    "InstDataMgr.A",
 | 
				
			||||||
 | 
					    id: "wAdrSrcMP-InstDataMgr",
 | 
				
			||||||
 | 
					    name: (none, "Adr"),
 | 
				
			||||||
 | 
					    bus: true
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  wire.stub("InstDataMgr.CLK", name: "CLK")
 | 
				
			||||||
 | 
					  wire.stub("InstDataMgr.WE")
 | 
				
			||||||
 | 
					  wire.stub("InstDataMgr.IRWrite")
 | 
				
			||||||
 | 
					  wire.stub("InstDataMgr.WD")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  element.block(
 | 
				
			||||||
 | 
					    pos: (
 | 
				
			||||||
 | 
					      15, (align: "WD3", with: "InstDataMgr.RD")
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    size: (3, 4),
 | 
				
			||||||
 | 
					    id: "RegFile",
 | 
				
			||||||
 | 
					    fill: util.colors.pink,
 | 
				
			||||||
 | 
					    ports: (
 | 
				
			||||||
 | 
					      west: (
 | 
				
			||||||
 | 
					        ("A1", "A1"),
 | 
				
			||||||
 | 
					        ("A2", "A2"),
 | 
				
			||||||
 | 
					        ("A3", "A3"),
 | 
				
			||||||
 | 
					        ("WD3", "WD3"),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      north: (
 | 
				
			||||||
 | 
					        (id: "CLK", clock: true),
 | 
				
			||||||
 | 
					        (id: "WE3", name: "WE3", vertical: true)
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      east: (
 | 
				
			||||||
 | 
					        ("RD1", "RD1"),
 | 
				
			||||||
 | 
					        ("RD2", "RD2"),
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    ports-margins: (
 | 
				
			||||||
 | 
					      east: (20%, 20%)
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  wire.stub("RegFile.CLK", name: "CLK")
 | 
				
			||||||
 | 
					  wire.stub("RegFile.WE3", name: "Regwrite", name-offset: 0.6)
 | 
				
			||||||
 | 
					  wire.stub("RegFile.A2")
 | 
				
			||||||
 | 
					  wire.stub("RegFile.RD2")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  element.extender(
 | 
				
			||||||
 | 
					    pos: (15, -3.5),
 | 
				
			||||||
 | 
					    size: (3, 1),
 | 
				
			||||||
 | 
					    id: "Extender",
 | 
				
			||||||
 | 
					    fill: util.colors.green
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    "Extender.north",
 | 
				
			||||||
 | 
					    (18, -2),
 | 
				
			||||||
 | 
					    id: "wExtender-ImmSrc",
 | 
				
			||||||
 | 
					    style: "zigzag",
 | 
				
			||||||
 | 
					    zigzag-ratio: 0%,
 | 
				
			||||||
 | 
					    name: (none, "ImmSrc"),
 | 
				
			||||||
 | 
					    bus: true
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  let mid = ("InstDataMgr.east", 50%, "RegFile.west")
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    "InstDataMgr.Instr",
 | 
				
			||||||
 | 
					    (vertical: (), horizontal: mid),
 | 
				
			||||||
 | 
					    id: "wInstDataMgr-Bus",
 | 
				
			||||||
 | 
					    name: ("Instr", none),
 | 
				
			||||||
 | 
					    bus: true
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    (v => (v.at(0), -3.5), mid),
 | 
				
			||||||
 | 
					    (horizontal: (), vertical: (0, 3.5)),
 | 
				
			||||||
 | 
					    id: "wBus",
 | 
				
			||||||
 | 
					    bus: true
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    "RegFile.A1",
 | 
				
			||||||
 | 
					    (horizontal: mid, vertical: ()),
 | 
				
			||||||
 | 
					    id: "wBus-RegFile-A1",
 | 
				
			||||||
 | 
					    name: (none, "RS1"),
 | 
				
			||||||
 | 
					    slice: (19, 15),
 | 
				
			||||||
 | 
					    reverse: true,
 | 
				
			||||||
 | 
					    bus: true
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    "RegFile.A3",
 | 
				
			||||||
 | 
					    (horizontal: mid, vertical: ()),
 | 
				
			||||||
 | 
					    id: "wBus-RegFile-A3",
 | 
				
			||||||
 | 
					    name: (none, "RD"),
 | 
				
			||||||
 | 
					    slice: (11, 7),
 | 
				
			||||||
 | 
					    reverse: true,
 | 
				
			||||||
 | 
					    bus: true
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    "Extender.in",
 | 
				
			||||||
 | 
					    (horizontal: mid, vertical: ()),
 | 
				
			||||||
 | 
					    id: "wBus-Extender",
 | 
				
			||||||
 | 
					    slice: (31, 7),
 | 
				
			||||||
 | 
					    reverse: true,
 | 
				
			||||||
 | 
					    bus: true
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  element.alu(
 | 
				
			||||||
 | 
					    pos: (
 | 
				
			||||||
 | 
					      22, (align: "in1", with: "RegFile.RD1")
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    size: (1, 2),
 | 
				
			||||||
 | 
					    id: "ALU",
 | 
				
			||||||
 | 
					    fill: util.colors.purple
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    "RegFile.RD1",
 | 
				
			||||||
 | 
					    "ALU.in1",
 | 
				
			||||||
 | 
					    id: "wRegFile-ALU",
 | 
				
			||||||
 | 
					    name: ("A", "SrcA"),
 | 
				
			||||||
 | 
					    bus: true
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  wire.stub("ALU.north", side: "north")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  element.block(
 | 
				
			||||||
 | 
					    pos: (
 | 
				
			||||||
 | 
					      26, (align: "in", with: "ALU.out")
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    size: (1.5, 2),
 | 
				
			||||||
 | 
					    id: "OutBuf",
 | 
				
			||||||
 | 
					    fill: util.colors.orange,
 | 
				
			||||||
 | 
					    ports: (
 | 
				
			||||||
 | 
					      west: "in",
 | 
				
			||||||
 | 
					      north: (id: "CLK", clock: true),
 | 
				
			||||||
 | 
					      east: "out"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  wire.stub("OutBuf.CLK", name: "CLK")
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    "ALU.out",
 | 
				
			||||||
 | 
					    "OutBuf.in",
 | 
				
			||||||
 | 
					    id: "wALU-OutBuf",
 | 
				
			||||||
 | 
					    name: "ALUResult",
 | 
				
			||||||
 | 
					    bus: true
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  element.multiplexer(
 | 
				
			||||||
 | 
					    pos: (
 | 
				
			||||||
 | 
					      30, (align: "in0", with: "OutBuf.out")
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    size: (1, 2.5),
 | 
				
			||||||
 | 
					    id: "Res-MP",
 | 
				
			||||||
 | 
					    fill: util.colors.orange,
 | 
				
			||||||
 | 
					    entries: 3
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  wire.stub("Res-MP.north", side: "north", name: "ResultSrc")
 | 
				
			||||||
 | 
					  wire.stub("Res-MP.in2")
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    "OutBuf.out",
 | 
				
			||||||
 | 
					    "Res-MP.in0",
 | 
				
			||||||
 | 
					    id: "wOutBuf-ResMP",
 | 
				
			||||||
 | 
					    name: "ALUOut",
 | 
				
			||||||
 | 
					    bus: true
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    "Extender.out",
 | 
				
			||||||
 | 
					    "ALU.in2",
 | 
				
			||||||
 | 
					    id: "wExt-ALU",
 | 
				
			||||||
 | 
					    name: ("ImmExt", "SrcB"),
 | 
				
			||||||
 | 
					    bus: true,
 | 
				
			||||||
 | 
					    style: "zigzag",
 | 
				
			||||||
 | 
					    zigzag-ratio: 60%
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    "InstDataMgr.RD",
 | 
				
			||||||
 | 
					    "Res-MP.in1",
 | 
				
			||||||
 | 
					    id: "wInstDataMgr-ResMP",
 | 
				
			||||||
 | 
					    style: "dodge",
 | 
				
			||||||
 | 
					    dodge-y: -4,
 | 
				
			||||||
 | 
					    dodge-sides: ("east", "west"),
 | 
				
			||||||
 | 
					    name: ("Data", none),
 | 
				
			||||||
 | 
					    bus: true
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    "Res-MP.out",
 | 
				
			||||||
 | 
					    "AdrSrc-MP.in1",
 | 
				
			||||||
 | 
					    id: "wResMP-AdrSrc",
 | 
				
			||||||
 | 
					    style: "dodge",
 | 
				
			||||||
 | 
					    dodge-y: -5,
 | 
				
			||||||
 | 
					    dodge-sides: ("east", "west"),
 | 
				
			||||||
 | 
					    dodge-margins: (0.5, 1),
 | 
				
			||||||
 | 
					    bus: true
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    "Res-MP.out",
 | 
				
			||||||
 | 
					    "RegFile.WD3",
 | 
				
			||||||
 | 
					    id: "wResMP-RegFile",
 | 
				
			||||||
 | 
					    style: "dodge",
 | 
				
			||||||
 | 
					    dodge-y: -5,
 | 
				
			||||||
 | 
					    dodge-sides: ("east", "west"),
 | 
				
			||||||
 | 
					    dodge-margins: (0.5, 1),
 | 
				
			||||||
 | 
					    bus: true
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  wire.wire(
 | 
				
			||||||
 | 
					    "Res-MP.out",
 | 
				
			||||||
 | 
					    "PCBuf.PCNext",
 | 
				
			||||||
 | 
					    id: "wResMP-PCBuf",
 | 
				
			||||||
 | 
					    style: "dodge",
 | 
				
			||||||
 | 
					    dodge-y: -5,
 | 
				
			||||||
 | 
					    dodge-sides: ("east", "west"),
 | 
				
			||||||
 | 
					    dodge-margins: (0.5, 1.5),
 | 
				
			||||||
 | 
					    name: (none, "PCNext"),
 | 
				
			||||||
 | 
					    bus: true
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  wire.intersection("wResMP-RegFile.dodge-end", radius: .2)
 | 
				
			||||||
 | 
					  wire.intersection("wResMP-AdrSrc.dodge-end", radius: .2)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								gallery/test.png
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB  | 
| 
		 Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB  | 
| 
		 Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB  | 
@@ -1,4 +1,4 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw
 | 
					#import "/src/cetz.typ": draw
 | 
				
			||||||
#import "../src/lib.typ": 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: 159 KiB After Width: | Height: | Size: 159 KiB  | 
@@ -1,4 +1,4 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw
 | 
					#import "/src/cetz.typ": draw
 | 
				
			||||||
#import "../src/lib.typ": *
 | 
					#import "../src/lib.typ": *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#set page(width: auto, height: auto, margin: .5cm)
 | 
					#set page(width: auto, height: auto, margin: .5cm)
 | 
				
			||||||
 
 | 
				
			|||||||
| 
		 Before Width: | Height: | Size: 276 KiB After Width: | Height: | Size: 275 KiB  | 
@@ -1,4 +1,4 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw
 | 
					#import "/src/cetz.typ": draw
 | 
				
			||||||
#import "../src/lib.typ": *
 | 
					#import "../src/lib.typ": *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#set page(width: auto, height: auto, margin: .5cm)
 | 
					#set page(width: auto, height: auto, margin: .5cm)
 | 
				
			||||||
 
 | 
				
			|||||||
| 
		 Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB  | 
@@ -1,4 +1,4 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw, vector
 | 
					#import "/src/cetz.typ": draw, vector
 | 
				
			||||||
#import "../src/lib.typ": *
 | 
					#import "../src/lib.typ": *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#set page(width: auto, height: auto, margin: .5cm)
 | 
					#set page(width: auto, height: auto, margin: .5cm)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								justfile
									
									
									
									
									
										Normal 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
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								manual.pdf
									
									
									
									
									
								
							
							
						
						
							
								
								
									
										62
									
								
								manual.typ
									
									
									
									
									
								
							
							
						
						@@ -1,10 +1,9 @@
 | 
				
			|||||||
#import "@preview/tidy:0.3.0"
 | 
					#import "@preview/tidy:0.4.1"
 | 
				
			||||||
#import "@preview/cetz:0.2.2": draw, canvas
 | 
					#import "/src/cetz.typ": draw, canvas
 | 
				
			||||||
#import "src/lib.typ"
 | 
					#import "src/lib.typ"
 | 
				
			||||||
#import "doc/examples.typ"
 | 
					#import "doc/examples.typ"
 | 
				
			||||||
#import "src/circuit.typ": circuit
 | 
					#import "src/circuit.typ": circuit
 | 
				
			||||||
#import "src/element.typ"
 | 
					#import "src/element.typ"
 | 
				
			||||||
#import "src/electrical.typ"
 | 
					 | 
				
			||||||
#import "src/gates.typ"
 | 
					#import "src/gates.typ"
 | 
				
			||||||
#import "src/util.typ"
 | 
					#import "src/util.typ"
 | 
				
			||||||
#import "src/wire.typ"
 | 
					#import "src/wire.typ"
 | 
				
			||||||
@@ -13,7 +12,7 @@
 | 
				
			|||||||
  numbering("1.1", ..num)
 | 
					  numbering("1.1", ..num)
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
#{
 | 
					#{
 | 
				
			||||||
  outline(indent: true, depth: 3)
 | 
					  outline(indent: auto, depth: 3)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#show link: set text(blue)
 | 
					#show link: set text(blue)
 | 
				
			||||||
@@ -48,7 +47,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#set page(numbering: "1/1", header: align(right)[circuiteria #sym.dash.em v#lib.version])
 | 
					#set page(numbering: "1/1", header: align(right)[circuiteria #sym.dash.em v#lib.version])
 | 
				
			||||||
#set page(
 | 
					#set page(
 | 
				
			||||||
  header: locate(loc => {
 | 
					  header: context {
 | 
				
			||||||
    let txt = [circuiteria #sym.dash.em v#lib.version]
 | 
					    let txt = [circuiteria #sym.dash.em v#lib.version]
 | 
				
			||||||
    let cnt = counter(heading)
 | 
					    let cnt = counter(heading)
 | 
				
			||||||
    let cnt-val = cnt.get()
 | 
					    let cnt-val = cnt.get()
 | 
				
			||||||
@@ -66,8 +65,8 @@
 | 
				
			|||||||
        #rect(width: 100%, height: .5em, radius: .25em, stroke: none, fill: util.colors.values().at(i))
 | 
					        #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 = counter(heading)
 | 
				
			||||||
    let cnt-val = cnt.get()
 | 
					    let cnt-val = cnt.get()
 | 
				
			||||||
    if cnt-val.len() < 2 { return }
 | 
					    if cnt-val.len() < 2 { return }
 | 
				
			||||||
@@ -81,12 +80,12 @@
 | 
				
			|||||||
      ],
 | 
					      ],
 | 
				
			||||||
      counter(page).display("1/1", both: true)
 | 
					      counter(page).display("1/1", both: true)
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  })
 | 
					  }
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let doc-ref(target, full: false, var: false) = {
 | 
					#let doc-ref(target, full: false, var: false) = {
 | 
				
			||||||
  let (module, func) = target.split(".")
 | 
					  let (module, func) = target.split(".")
 | 
				
			||||||
  let label-name = module + func
 | 
					  let label-name = module + "-" + func
 | 
				
			||||||
  let display-name = func
 | 
					  let display-name = func
 | 
				
			||||||
  if full {
 | 
					  if full {
 | 
				
			||||||
    display-name = target
 | 
					    display-name = target
 | 
				
			||||||
@@ -95,7 +94,7 @@
 | 
				
			|||||||
    label-name += "()"
 | 
					    label-name += "()"
 | 
				
			||||||
    display-name += "()"
 | 
					    display-name += "()"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  link(label(label-name))[#display-name]
 | 
					  link(label(label-name), raw(display-name))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
= Introduction
 | 
					= Introduction
 | 
				
			||||||
@@ -104,11 +103,21 @@ This package provides a way to make beautiful block circuit diagrams using the C
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
= Usage
 | 
					= Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Simply import #link("src/lib.typ") and call the `circuit` function:
 | 
					Simply import Circuiteria and call the `circuit` function:
 | 
				
			||||||
#pad(left: 1em)[```typ
 | 
					#pad(left: 1em)[```typ
 | 
				
			||||||
#import "src/lib.typ"
 | 
					#import "@preview/circuiteria:0.2.0"
 | 
				
			||||||
#lib.circuit({
 | 
					#circuiteria.circuit({
 | 
				
			||||||
  import lib: *
 | 
					  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: *
 | 
				
			||||||
  ...
 | 
					  ...
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
```]
 | 
					```]
 | 
				
			||||||
@@ -118,6 +127,7 @@ Simply import #link("src/lib.typ") and call the `circuit` function:
 | 
				
			|||||||
#let circuit-docs = tidy.parse-module(
 | 
					#let circuit-docs = tidy.parse-module(
 | 
				
			||||||
  read("src/circuit.typ"),
 | 
					  read("src/circuit.typ"),
 | 
				
			||||||
  name: "circuit",
 | 
					  name: "circuit",
 | 
				
			||||||
 | 
					  old-syntax: true,
 | 
				
			||||||
  require-all-parameters: true
 | 
					  require-all-parameters: true
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
#tidy.show-module(circuit-docs)
 | 
					#tidy.show-module(circuit-docs)
 | 
				
			||||||
@@ -127,6 +137,7 @@ Simply import #link("src/lib.typ") and call the `circuit` function:
 | 
				
			|||||||
#let util-docs = tidy.parse-module(
 | 
					#let util-docs = tidy.parse-module(
 | 
				
			||||||
  read("src/util.typ"),
 | 
					  read("src/util.typ"),
 | 
				
			||||||
  name: "util",
 | 
					  name: "util",
 | 
				
			||||||
 | 
					  old-syntax: true,
 | 
				
			||||||
  require-all-parameters: true,
 | 
					  require-all-parameters: true,
 | 
				
			||||||
  scope: (
 | 
					  scope: (
 | 
				
			||||||
    util: util,
 | 
					    util: util,
 | 
				
			||||||
@@ -141,6 +152,7 @@ Simply import #link("src/lib.typ") and call the `circuit` function:
 | 
				
			|||||||
#let wire-docs = tidy.parse-module(
 | 
					#let wire-docs = tidy.parse-module(
 | 
				
			||||||
  read("src/wire.typ"),
 | 
					  read("src/wire.typ"),
 | 
				
			||||||
  name: "wire",
 | 
					  name: "wire",
 | 
				
			||||||
 | 
					  old-syntax: true,
 | 
				
			||||||
  require-all-parameters: true,
 | 
					  require-all-parameters: true,
 | 
				
			||||||
  scope: (
 | 
					  scope: (
 | 
				
			||||||
    wire: wire,
 | 
					    wire: wire,
 | 
				
			||||||
@@ -162,6 +174,7 @@ Simply import #link("src/lib.typ") and call the `circuit` function:
 | 
				
			|||||||
  read("src/elements/multiplexer.typ") + "\n" +
 | 
					  read("src/elements/multiplexer.typ") + "\n" +
 | 
				
			||||||
  read("src/elements/group.typ"),
 | 
					  read("src/elements/group.typ"),
 | 
				
			||||||
  name: "element",
 | 
					  name: "element",
 | 
				
			||||||
 | 
					  old-syntax: true,
 | 
				
			||||||
  scope: (
 | 
					  scope: (
 | 
				
			||||||
    element: element,
 | 
					    element: element,
 | 
				
			||||||
    circuit: circuit,
 | 
					    circuit: circuit,
 | 
				
			||||||
@@ -184,6 +197,7 @@ Simply import #link("src/lib.typ") and call the `circuit` function:
 | 
				
			|||||||
  read("src/elements/logic/or.typ") + "\n" +
 | 
					  read("src/elements/logic/or.typ") + "\n" +
 | 
				
			||||||
  read("src/elements/logic/xor.typ"),
 | 
					  read("src/elements/logic/xor.typ"),
 | 
				
			||||||
  name: "gates",
 | 
					  name: "gates",
 | 
				
			||||||
 | 
					  old-syntax: true,
 | 
				
			||||||
  scope: (
 | 
					  scope: (
 | 
				
			||||||
    element: element,
 | 
					    element: element,
 | 
				
			||||||
    circuit: circuit,
 | 
					    circuit: circuit,
 | 
				
			||||||
@@ -197,23 +211,3 @@ Simply import #link("src/lib.typ") and call the `circuit` function:
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#tidy.show-module(gates-docs, sort-functions: false)
 | 
					#tidy.show-module(gates-docs, sort-functions: false)
 | 
				
			||||||
 | 
					 | 
				
			||||||
#pagebreak()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#let electrical-docs = tidy.parse-module(
 | 
					 | 
				
			||||||
  read("src/elements/electrical/capacitor.typ") + "\n" +
 | 
					 | 
				
			||||||
  read("src/elements/electrical/resistor.typ") + "\n",
 | 
					 | 
				
			||||||
  name: "electrical",
 | 
					 | 
				
			||||||
  scope: (
 | 
					 | 
				
			||||||
    element: element,
 | 
					 | 
				
			||||||
    circuit: circuit,
 | 
					 | 
				
			||||||
    electrical: electrical,
 | 
					 | 
				
			||||||
    draw: draw,
 | 
					 | 
				
			||||||
    wire: wire,
 | 
					 | 
				
			||||||
    tidy: tidy,
 | 
					 | 
				
			||||||
    examples: examples,
 | 
					 | 
				
			||||||
    doc-ref: doc-ref
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#tidy.show-module(electrical-docs, sort-functions: false)
 | 
					 | 
				
			||||||
							
								
								
									
										1
									
								
								src/cetz.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					#import "@preview/cetz:0.3.4": *
 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": canvas
 | 
					#import "/src/cetz.typ": canvas
 | 
				
			||||||
#import "@preview/tidy:0.3.0"
 | 
					#import "@preview/tidy:0.3.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Draws a block circuit diagram
 | 
					/// Draws a block circuit diagram
 | 
				
			||||||
@@ -9,6 +9,52 @@
 | 
				
			|||||||
/// - length (length, ratio): Optional base unit
 | 
					/// - length (length, ratio): Optional base unit
 | 
				
			||||||
/// -> none
 | 
					/// -> none
 | 
				
			||||||
#let circuit(body, length: 2em) = {
 | 
					#let circuit(body, length: 2em) = {
 | 
				
			||||||
  set text(font: "Source Sans 3")
 | 
					  let next-id = 0
 | 
				
			||||||
  canvas(length: length, body)
 | 
					  let elements = (:)
 | 
				
			||||||
 | 
					  let ids = ()
 | 
				
			||||||
 | 
					  for e in body {
 | 
				
			||||||
 | 
					    if type(e) == dictionary and "id" in e {
 | 
				
			||||||
 | 
					      ids.push(e.id)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for element in body {
 | 
				
			||||||
 | 
					    let internal = type(element) == dictionary and "id" in element
 | 
				
			||||||
 | 
					    let eid = if internal {element.id} else {auto}
 | 
				
			||||||
 | 
					    if eid == auto {
 | 
				
			||||||
 | 
					      while str(next-id) in ids {
 | 
				
			||||||
 | 
					        next-id += 1
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      eid = str(next-id)
 | 
				
			||||||
 | 
					      ids.push(eid)
 | 
				
			||||||
 | 
					      if internal {
 | 
				
			||||||
 | 
					        element.id = eid
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      next-id += 1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if eid in elements {
 | 
				
			||||||
 | 
					      panic("An element with the id '" + eid + "' already exists. Please use a different id")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    elements.insert(eid, element)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for element in elements.values() {
 | 
				
			||||||
 | 
					    if type(element) == dictionary and "pre-process" in element {
 | 
				
			||||||
 | 
					      elements = (element.pre-process)(elements, element)
 | 
				
			||||||
 | 
					      assert(
 | 
				
			||||||
 | 
					        type(elements) == dictionary,
 | 
				
			||||||
 | 
					        message: "The `pre-process` method of element '" + element.id + "' did not return the elements dictionary"
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  canvas(length: length, {
 | 
				
			||||||
 | 
					    for element in elements.values() {
 | 
				
			||||||
 | 
					      if type(element) == dictionary and "draw" in element {
 | 
				
			||||||
 | 
					        (element.draw)(element)
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        (element,)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,2 +0,0 @@
 | 
				
			|||||||
#import "elements/electrical/capacitor.typ": capacitor
 | 
					 | 
				
			||||||
#import "elements/electrical/resistor.typ": resistor
 | 
					 | 
				
			||||||
@@ -12,7 +12,4 @@
 | 
				
			|||||||
#import "elements/logic/xor.typ": gate-xor, gate-xnor
 | 
					#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/electrical/resistor.typ": resistor
 | 
					 | 
				
			||||||
#import "elements/electrical/capacitor.typ": capacitor
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#import "elements/group.typ": group
 | 
					#import "elements/group.typ": group
 | 
				
			||||||
@@ -1,45 +1,40 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw
 | 
					#import "/src/cetz.typ": draw
 | 
				
			||||||
#import "element.typ"
 | 
					#import "element.typ"
 | 
				
			||||||
#import "ports.typ": add-port
 | 
					#import "ports.typ": add-port
 | 
				
			||||||
 | 
					#import "../util.typ"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let draw-shape(elmt, bounds) = {
 | 
				
			||||||
 | 
					  let tl = bounds.tl
 | 
				
			||||||
 | 
					  let tr = bounds.tr
 | 
				
			||||||
 | 
					  let bl = bounds.bl
 | 
				
			||||||
 | 
					  let br = bounds.br
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let draw-shape(id, tl, tr, br, bl, fill, stroke) = {
 | 
					 | 
				
			||||||
  let p0 = tl
 | 
					  let p0 = tl
 | 
				
			||||||
  let p1 = (tr, 10%, br)
 | 
					  let p1 = util.lerp(tr, 10%, br)
 | 
				
			||||||
  let p2 = (tr, 90%, br)
 | 
					  let p2 = util.lerp(tr, 90%, br)
 | 
				
			||||||
  let p3 = bl
 | 
					  let p3 = bl
 | 
				
			||||||
  let p4 = (tl, 55%, bl)
 | 
					  let p4 = util.lerp(tl, 55%, bl)
 | 
				
			||||||
  let p5 = (tl, 50%, br)
 | 
					  let p5 = util.lerp(tl, 50%, br)
 | 
				
			||||||
  let p6 = (tl, 45%, bl)
 | 
					  let p6 = util.lerp(tl, 45%, bl)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let f1 = draw.group(name: id, {
 | 
					  let bounds2 = element.complete-bounds(elmt, (
 | 
				
			||||||
    
 | 
					    tl: p0,
 | 
				
			||||||
    draw.merge-path(
 | 
					    tr: p1,
 | 
				
			||||||
      inset: 0.5em,
 | 
					    br: p2,
 | 
				
			||||||
      fill: fill,
 | 
					    bl: p3
 | 
				
			||||||
      stroke: stroke,
 | 
					  ))
 | 
				
			||||||
      close: true,
 | 
					 | 
				
			||||||
      draw.line(p0, p1, p2, p3, p4, p5, p6)
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    draw.anchor("north", (p0, 50%, p1))
 | 
					 | 
				
			||||||
    draw.anchor("south", (p2, 50%, p3))
 | 
					 | 
				
			||||||
    draw.anchor("west", (p0, 50%, p3))
 | 
					 | 
				
			||||||
    draw.anchor("east", (p1, 50%, p2))
 | 
					 | 
				
			||||||
    draw.anchor("north-west", p0)
 | 
					 | 
				
			||||||
    draw.anchor("north-east", p1)
 | 
					 | 
				
			||||||
    draw.anchor("south-east", p2)
 | 
					 | 
				
			||||||
    draw.anchor("south-west", p3)
 | 
					 | 
				
			||||||
    draw.anchor("name", (p5, 50%, (p1, 50%, p2)))
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let f2 = add-port(id, "west", (id: "in1"), (p0, 50%, p6))
 | 
					 | 
				
			||||||
  let f3 = add-port(id, "west", (id: "in2"), (p3, 50%, p4))
 | 
					 | 
				
			||||||
  let f4 = add-port(id, "east", (id: "out"), (p1, 50%, p2))
 | 
					 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  let f = {
 | 
					  let f = {
 | 
				
			||||||
    f1; f2; f3; f4
 | 
					    draw.line(
 | 
				
			||||||
 | 
					      p0, p1, p2, p3, p4, p5, p6,
 | 
				
			||||||
 | 
					      fill: elmt.fill,
 | 
				
			||||||
 | 
					      stroke: elmt.stroke,
 | 
				
			||||||
 | 
					      close: true
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    draw.anchor("name", (p5, 50%, (p1, 50%, p2)))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (f, tl, tr, br, bl)
 | 
					  return (f, bounds2)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Draws an ALU with two inputs
 | 
					/// Draws an ALU with two inputs
 | 
				
			||||||
@@ -47,18 +42,7 @@
 | 
				
			|||||||
/// #examples.alu
 | 
					/// #examples.alu
 | 
				
			||||||
/// For parameters description, see #doc-ref("element.elmt")
 | 
					/// For parameters description, see #doc-ref("element.elmt")
 | 
				
			||||||
#let alu(
 | 
					#let alu(
 | 
				
			||||||
  x: none,
 | 
					  ..args
 | 
				
			||||||
  y: none,
 | 
					 | 
				
			||||||
  w: none,
 | 
					 | 
				
			||||||
  h: none,
 | 
					 | 
				
			||||||
  name: none,
 | 
					 | 
				
			||||||
  name-anchor: "center",
 | 
					 | 
				
			||||||
  fill: none,
 | 
					 | 
				
			||||||
  stroke: black + 1pt,
 | 
					 | 
				
			||||||
  id: "",
 | 
					 | 
				
			||||||
  debug: (
 | 
					 | 
				
			||||||
    ports: false
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
) = {
 | 
					) = {
 | 
				
			||||||
  let ports = (
 | 
					  let ports = (
 | 
				
			||||||
    west: (
 | 
					    west: (
 | 
				
			||||||
@@ -70,24 +54,18 @@
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  element.elmt(
 | 
					  return element.elmt(
 | 
				
			||||||
    draw-shape: draw-shape,
 | 
					    draw-shape: draw-shape,
 | 
				
			||||||
    x: x,
 | 
					 | 
				
			||||||
    y: y,
 | 
					 | 
				
			||||||
    w: w,
 | 
					 | 
				
			||||||
    h: h,
 | 
					 | 
				
			||||||
    name: name,
 | 
					 | 
				
			||||||
    name-anchor: name-anchor,
 | 
					 | 
				
			||||||
    ports: ports,
 | 
					    ports: ports,
 | 
				
			||||||
    fill: fill,
 | 
					    ports-pos: (
 | 
				
			||||||
    stroke: stroke,
 | 
					      west: (
 | 
				
			||||||
    id: id,
 | 
					        in1: l => l * 0.225,
 | 
				
			||||||
    auto-ports: false,
 | 
					        in2: l => l * 0.775,
 | 
				
			||||||
    ports-y: (
 | 
					      ),
 | 
				
			||||||
      in1: (h) => {h * 0.225},
 | 
					      east: (
 | 
				
			||||||
      in2: (h) => {h * 0.775},
 | 
					        out: l => l * 0.5
 | 
				
			||||||
      out: (h) => {h * 0.5}
 | 
					      )
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    debug: debug
 | 
					    ..args
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,16 +1,15 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw
 | 
					#import "/src/cetz.typ": draw
 | 
				
			||||||
#import "element.typ"
 | 
					#import "element.typ"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let draw-shape(id, tl, tr, br, bl, fill, stroke) = {
 | 
					#let draw-shape(elmt, bounds) = {
 | 
				
			||||||
  let f = draw.rect(
 | 
					  let f = draw.rect(
 | 
				
			||||||
    radius: 0.5em,
 | 
					    radius: 0.5em,
 | 
				
			||||||
    inset: 0.5em,
 | 
					    inset: 0.5em,
 | 
				
			||||||
    fill: fill,
 | 
					    fill: elmt.fill,
 | 
				
			||||||
    stroke: stroke,
 | 
					    stroke: elmt.stroke,
 | 
				
			||||||
    name: id,
 | 
					    bounds.bl, bounds.tr
 | 
				
			||||||
    bl, tr
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
  return (f, tl, tr, br, bl)
 | 
					  return (f, bounds)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Draws a block element
 | 
					/// Draws a block element
 | 
				
			||||||
@@ -18,32 +17,9 @@
 | 
				
			|||||||
/// #examples.block
 | 
					/// #examples.block
 | 
				
			||||||
/// For parameters description, see #doc-ref("element.elmt")
 | 
					/// For parameters description, see #doc-ref("element.elmt")
 | 
				
			||||||
#let block(
 | 
					#let block(
 | 
				
			||||||
  x: none,
 | 
					  ..args
 | 
				
			||||||
  y: none,
 | 
					 | 
				
			||||||
  w: none,
 | 
					 | 
				
			||||||
  h: none,
 | 
					 | 
				
			||||||
  name: none,
 | 
					 | 
				
			||||||
  name-anchor: "center",
 | 
					 | 
				
			||||||
  ports: (),
 | 
					 | 
				
			||||||
  ports-margins: (),
 | 
					 | 
				
			||||||
  fill: none,
 | 
					 | 
				
			||||||
  stroke: black + 1pt,
 | 
					 | 
				
			||||||
  id: "",
 | 
					 | 
				
			||||||
  debug: (
 | 
					 | 
				
			||||||
    ports: false
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
) = element.elmt(
 | 
					) = element.elmt(
 | 
				
			||||||
 | 
					  cls: "block",
 | 
				
			||||||
  draw-shape: draw-shape,
 | 
					  draw-shape: draw-shape,
 | 
				
			||||||
  x: x,
 | 
					  ..args
 | 
				
			||||||
  y: y,
 | 
					 | 
				
			||||||
  w: w,
 | 
					 | 
				
			||||||
  h: h,
 | 
					 | 
				
			||||||
  name: name,
 | 
					 | 
				
			||||||
  name-anchor: name-anchor,
 | 
					 | 
				
			||||||
  ports: ports,
 | 
					 | 
				
			||||||
  ports-margins: ports-margins,
 | 
					 | 
				
			||||||
  fill: fill,
 | 
					 | 
				
			||||||
  stroke: stroke,
 | 
					 | 
				
			||||||
  id: id,
 | 
					 | 
				
			||||||
  debug: debug
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -1,138 +0,0 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw
 | 
					 | 
				
			||||||
#import "../element.typ"
 | 
					 | 
				
			||||||
#import "../ports.typ": add-port
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#let draw-shape(
 | 
					 | 
				
			||||||
  id, tl, tr, br, bl,
 | 
					 | 
				
			||||||
  fill, stroke,
 | 
					 | 
				
			||||||
  vertical: false,
 | 
					 | 
				
			||||||
  gap: 0.2,
 | 
					 | 
				
			||||||
  scales: (100%, 100%),
 | 
					 | 
				
			||||||
  symbols: (none, none)
 | 
					 | 
				
			||||||
) = {
 | 
					 | 
				
			||||||
  let (x0, y0) = tl
 | 
					 | 
				
			||||||
  let (x1, y1) = br
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let w = x1 - x0
 | 
					 | 
				
			||||||
  let h = y1 - y0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let (o0, s0) = if vertical {(y0, h)} else {(x0, w)}
 | 
					 | 
				
			||||||
  let (o1, s1) = if vertical {(x0, w)} else {(y0, h)}
 | 
					 | 
				
			||||||
  let m1 = o1 + s1 / 2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let pt(i, j) = if vertical {
 | 
					 | 
				
			||||||
    (j, i)
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    (i, j)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let size0 = s1 * scales.first() / 100%
 | 
					 | 
				
			||||||
  let size1 = s1 * scales.last() / 100%
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if type(gap) == ratio {
 | 
					 | 
				
			||||||
    gap = gap / 100%
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    gap = gap / calc.abs(s0)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  let r0 = 0.5 - gap / 2
 | 
					 | 
				
			||||||
  let r1 = 0.5 + gap / 2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Coordinates in (main axis, secondary axis) format
 | 
					 | 
				
			||||||
  let p0 = pt(o0, m1)
 | 
					 | 
				
			||||||
  let p1 = pt(o0 + r0 * s0, m1)
 | 
					 | 
				
			||||||
  let p2 = pt(o0 + r1 * s0, m1)
 | 
					 | 
				
			||||||
  let p3 = pt(o0 + s0, m1)
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  let p4 = pt(o0 + r0 * s0, m1 - size0 / 2)
 | 
					 | 
				
			||||||
  let p5 = pt(o0 + r0 * s0, m1 + size0 / 2)
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  let p6 = pt(o0 + r1 * s0, m1 - size1 / 2)
 | 
					 | 
				
			||||||
  let p7 = pt(o0 + r1 * s0, m1 + size1 / 2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let line = draw.line.with(stroke: stroke)
 | 
					 | 
				
			||||||
  let f = draw.group(name: id, {
 | 
					 | 
				
			||||||
    line(p0, p1)
 | 
					 | 
				
			||||||
    line(p2, p3)
 | 
					 | 
				
			||||||
    line(p4, p5)
 | 
					 | 
				
			||||||
    line(p6, p7)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if symbols.first() != none {
 | 
					 | 
				
			||||||
      draw.content(
 | 
					 | 
				
			||||||
        p1,
 | 
					 | 
				
			||||||
        symbols.first(),
 | 
					 | 
				
			||||||
        anchor: if vertical {"south-west"} else {"south-east"},
 | 
					 | 
				
			||||||
        padding: 2pt
 | 
					 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if symbols.last() != none {
 | 
					 | 
				
			||||||
      draw.content(
 | 
					 | 
				
			||||||
        p2,
 | 
					 | 
				
			||||||
        symbols.last(),
 | 
					 | 
				
			||||||
        anchor: if vertical {"north-west"} else {"south-west"},
 | 
					 | 
				
			||||||
        padding: 2pt
 | 
					 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (f, tl, tr, br, bl)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Draws a capacitor
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// #examples.capacitor
 | 
					 | 
				
			||||||
/// For other parameters description, see #doc-ref("element.elmt")
 | 
					 | 
				
			||||||
/// - vertical (bool): Whether the element is vertical or horizontal.
 | 
					 | 
				
			||||||
///   - If false, port 0 is placed on the west side and port 1 on the east.\
 | 
					 | 
				
			||||||
///   - If true, they are on the north, respectively the south sides
 | 
					 | 
				
			||||||
/// - gap (number, ratio): The gap between both sides
 | 
					 | 
				
			||||||
///   - if it is a number (int or float), it is interpreted as an absolute canvas-unit length
 | 
					 | 
				
			||||||
///   - if it is a ratio, it is interpreted as proportional to the capacitor's length (width if horizontal, height if vertical)
 | 
					 | 
				
			||||||
/// - scales (array): A pair of ratios, the sizes of the sides relative to the capacitor's height (width if vertical).
 | 
					 | 
				
			||||||
/// - symbols (array): A pair of content or strings (or none values) to attach on the sides of the capacitor
 | 
					 | 
				
			||||||
#let capacitor(
 | 
					 | 
				
			||||||
  x: none,
 | 
					 | 
				
			||||||
  y: none,
 | 
					 | 
				
			||||||
  w: none,
 | 
					 | 
				
			||||||
  h: none,
 | 
					 | 
				
			||||||
  name: none,
 | 
					 | 
				
			||||||
  name-anchor: "center",
 | 
					 | 
				
			||||||
  vertical: false,
 | 
					 | 
				
			||||||
  gap: 0.2,
 | 
					 | 
				
			||||||
  scales: (100%, 100%),
 | 
					 | 
				
			||||||
  symbols: (none, none),
 | 
					 | 
				
			||||||
  fill: none,
 | 
					 | 
				
			||||||
  stroke: black + 1pt,
 | 
					 | 
				
			||||||
  id: "",
 | 
					 | 
				
			||||||
  debug: (
 | 
					 | 
				
			||||||
    ports: false
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
) = {
 | 
					 | 
				
			||||||
  let ports = if vertical {(
 | 
					 | 
				
			||||||
    north: ((id: "0"),),
 | 
					 | 
				
			||||||
    south: ((id: "1"),)
 | 
					 | 
				
			||||||
  )} else {(
 | 
					 | 
				
			||||||
    west: ((id: "0"),),
 | 
					 | 
				
			||||||
    east: ((id: "1"),)
 | 
					 | 
				
			||||||
  )}
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  element.elmt(
 | 
					 | 
				
			||||||
    draw-shape: draw-shape.with(
 | 
					 | 
				
			||||||
      vertical: vertical,
 | 
					 | 
				
			||||||
      gap: gap,
 | 
					 | 
				
			||||||
      scales: scales,
 | 
					 | 
				
			||||||
      symbols: symbols
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    x: x,
 | 
					 | 
				
			||||||
    y: y,
 | 
					 | 
				
			||||||
    w: w,
 | 
					 | 
				
			||||||
    h: h,
 | 
					 | 
				
			||||||
    name: name,
 | 
					 | 
				
			||||||
    name-anchor: name-anchor,
 | 
					 | 
				
			||||||
    ports: ports,
 | 
					 | 
				
			||||||
    fill: fill,
 | 
					 | 
				
			||||||
    stroke: stroke,
 | 
					 | 
				
			||||||
    id: id,
 | 
					 | 
				
			||||||
    debug: debug
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,106 +0,0 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw
 | 
					 | 
				
			||||||
#import "../element.typ"
 | 
					 | 
				
			||||||
#import "../ports.typ": add-port
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#let draw-shape(
 | 
					 | 
				
			||||||
  id, tl, tr, br, bl,
 | 
					 | 
				
			||||||
  fill, stroke,
 | 
					 | 
				
			||||||
  zigzags: 6,
 | 
					 | 
				
			||||||
  vertical: false
 | 
					 | 
				
			||||||
) = {
 | 
					 | 
				
			||||||
  let (x0, y0) = tl
 | 
					 | 
				
			||||||
  let (x1, y1) = br
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let w = x1 - x0
 | 
					 | 
				
			||||||
  let h = y1 - y0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let (o0, s0) = if vertical {(y0, h)} else {(x0, w)}
 | 
					 | 
				
			||||||
  let (o1, s1) = if vertical {(x0, w)} else {(y0, h)}
 | 
					 | 
				
			||||||
  let m1 = o1 + s1 / 2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let pt(i, j) = if vertical {
 | 
					 | 
				
			||||||
    (j, i)
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    (i, j)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let p0 = pt(o0, m1)
 | 
					 | 
				
			||||||
  let p1 = pt(o0 + 0.2 * s0, m1)
 | 
					 | 
				
			||||||
  let p2 = pt(o0 + 0.8 * s0, m1)
 | 
					 | 
				
			||||||
  let p3 = pt(o0 + s0, m1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if zigzags == none {
 | 
					 | 
				
			||||||
    let p4 = pt(o0 + 0.2 * s0, o1)
 | 
					 | 
				
			||||||
    let p5 = pt(o0 + 0.8 * s0, o1 + s1)
 | 
					 | 
				
			||||||
    let f = draw.group(name: id, {
 | 
					 | 
				
			||||||
      draw.line(p0, p1)
 | 
					 | 
				
			||||||
      draw.line(p2, p3)
 | 
					 | 
				
			||||||
      draw.rect(p4, p5, stroke: stroke, fill: fill)
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    return (f, tl, tr, br, bl)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  let pts = (p0, p1)
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  for i in range(zigzags) {
 | 
					 | 
				
			||||||
    let r = ((i+0.5) / zigzags * 0.6 + 0.2)
 | 
					 | 
				
			||||||
    let pos = pt(o0 + r * s0, o1 + s1 * calc.rem(i, 2))
 | 
					 | 
				
			||||||
    pts.push(pos)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  pts += (p2, p3)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let f = draw.group(name: id, {
 | 
					 | 
				
			||||||
    draw.line(..pts, stroke: stroke)
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
  return (f, tl, tr, br, bl)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Draws a resistor
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// #examples.resistor
 | 
					 | 
				
			||||||
/// For other parameters description, see #doc-ref("element.elmt")
 | 
					 | 
				
			||||||
/// - vertical (bool): Whether the element is vertical or horizontal. If false, port 0 is placed on the west side and port 1 on the east. If true, they are on the north, respectively the south sides
 | 
					 | 
				
			||||||
/// - zigzags (number, none): Number of zigzags to draw. If none, a rectangle is drawn
 | 
					 | 
				
			||||||
#let resistor(
 | 
					 | 
				
			||||||
  x: none,
 | 
					 | 
				
			||||||
  y: none,
 | 
					 | 
				
			||||||
  w: none,
 | 
					 | 
				
			||||||
  h: none,
 | 
					 | 
				
			||||||
  name: none,
 | 
					 | 
				
			||||||
  name-anchor: "center",
 | 
					 | 
				
			||||||
  vertical: false,
 | 
					 | 
				
			||||||
  zigzags: 6,
 | 
					 | 
				
			||||||
  fill: none,
 | 
					 | 
				
			||||||
  stroke: black + 1pt,
 | 
					 | 
				
			||||||
  id: "",
 | 
					 | 
				
			||||||
  debug: (
 | 
					 | 
				
			||||||
    ports: false
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
) = {
 | 
					 | 
				
			||||||
  let ports = if vertical {(
 | 
					 | 
				
			||||||
    north: ((id: "0"),),
 | 
					 | 
				
			||||||
    south: ((id: "1"),)
 | 
					 | 
				
			||||||
  )} else {(
 | 
					 | 
				
			||||||
    west: ((id: "0"),),
 | 
					 | 
				
			||||||
    east: ((id: "1"),)
 | 
					 | 
				
			||||||
  )}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  element.elmt(
 | 
					 | 
				
			||||||
    draw-shape: draw-shape.with(
 | 
					 | 
				
			||||||
      vertical: vertical,
 | 
					 | 
				
			||||||
      zigzags: zigzags
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    x: x,
 | 
					 | 
				
			||||||
    y: y,
 | 
					 | 
				
			||||||
    w: w,
 | 
					 | 
				
			||||||
    h: h,
 | 
					 | 
				
			||||||
    name: name,
 | 
					 | 
				
			||||||
    name-anchor: name-anchor,
 | 
					 | 
				
			||||||
    ports: ports,
 | 
					 | 
				
			||||||
    fill: fill,
 | 
					 | 
				
			||||||
    stroke: stroke,
 | 
					 | 
				
			||||||
    id: id,
 | 
					 | 
				
			||||||
    debug: debug
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw, coordinate
 | 
					#import "/src/cetz.typ": draw, coordinate, matrix, vector
 | 
				
			||||||
#import "ports.typ": add-ports, add-port
 | 
					#import "ports.typ": add-ports, add-port, get-port-pos, get-port-idx
 | 
				
			||||||
#import "../util.typ"
 | 
					#import "../util.typ"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let find-port(ports, id) = {
 | 
					#let find-port(ports, id) = {
 | 
				
			||||||
@@ -10,13 +10,234 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  panic("Could not find port with id " + str(id))
 | 
					  panic("Could not find port with id '" + str(id) + "' in ports " + repr(ports))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let default-draw-shape(id, tl, tr, br, bl, fill, stroke) = {
 | 
					#let local-to-global(origin, u, v, points) = {
 | 
				
			||||||
  return ({}, tl, tr, br, bl)
 | 
					  return points-real = points.map(p => {
 | 
				
			||||||
 | 
					    let (pu, pv) = p
 | 
				
			||||||
 | 
					    return vector.add(
 | 
				
			||||||
 | 
					      origin,
 | 
				
			||||||
 | 
					      vector.add(
 | 
				
			||||||
 | 
					        vector.scale(u, pu),
 | 
				
			||||||
 | 
					        vector.scale(v, pv)
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let default-draw-shape(elmt, bounds) = {
 | 
				
			||||||
 | 
					  return ({}, bounds)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let default-pre-process(elements, element) = {
 | 
				
			||||||
 | 
					  return elements
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let resolve-offset(ctx, offset, from, axis) = {
 | 
				
			||||||
 | 
					  let (ctx, pos) = coordinate.resolve(
 | 
				
			||||||
 | 
					    ctx,
 | 
				
			||||||
 | 
					    (rel: offset, to: from)
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  return pos.at(axis)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let resolve-align(ctx, elmt, bounds, align, with, axis) = {
 | 
				
			||||||
 | 
					  let (align-side, i) = find-port(elmt.ports, align)
 | 
				
			||||||
 | 
					  let margins = (0%, 0%)
 | 
				
			||||||
 | 
					  if align-side in elmt.ports-margins {
 | 
				
			||||||
 | 
					    margins = elmt.ports-margins.at(align-side)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let parallel-sides = (
 | 
				
			||||||
 | 
					    ("north", "south"),
 | 
				
			||||||
 | 
					    ("west", "east")
 | 
				
			||||||
 | 
					  ).at(axis)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let ortho-sides = (
 | 
				
			||||||
 | 
					    ("west", "east"),
 | 
				
			||||||
 | 
					    ("north", "south")
 | 
				
			||||||
 | 
					  ).at(axis)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let dl
 | 
				
			||||||
 | 
					  let start-margin
 | 
				
			||||||
 | 
					  let len = elmt.size.at(axis)
 | 
				
			||||||
 | 
					  if align-side in parallel-sides {
 | 
				
			||||||
 | 
					    let used-pct = 100% - margins.at(0) - margins.at(1)
 | 
				
			||||||
 | 
					    let used-len = len * used-pct / 100%
 | 
				
			||||||
 | 
					    start-margin = len * margins.at(0) / 100%
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    //dl = used-len * (i + 1) / (elmt.ports.at(align-side).len() + 1)
 | 
				
			||||||
 | 
					    dl = get-port-pos(elmt, bounds, align-side, align, get-port-idx(elmt, align, side: align-side))
 | 
				
			||||||
 | 
					    /*if not elmt.auto-ports {
 | 
				
			||||||
 | 
					      start-margin = 0
 | 
				
			||||||
 | 
					      dl = elmt.ports-pos.at(align)(len)
 | 
				
			||||||
 | 
					    }*/
 | 
				
			||||||
 | 
					  } else if align-side == ortho-sides.first() {
 | 
				
			||||||
 | 
					    dl = 0
 | 
				
			||||||
 | 
					    start-margin = 0
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    dl = len
 | 
				
			||||||
 | 
					    start-margin = 0
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if axis == 1 {
 | 
				
			||||||
 | 
					    dl = len - dl
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  let (ctx, with-pos) = coordinate.resolve(ctx, with)
 | 
				
			||||||
 | 
					  return with-pos.at(axis) - dl + start-margin
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let resolve-coordinate(ctx, elmt, bounds, coord, axis) = {
 | 
				
			||||||
 | 
					  if type(coord) == dictionary {
 | 
				
			||||||
 | 
					    let offset = coord.at("offset", default: none)
 | 
				
			||||||
 | 
					    let from = coord.at("from", default: none)
 | 
				
			||||||
 | 
					    let align = coord.at("align", default: none)
 | 
				
			||||||
 | 
					    let with = coord.at("with", default: none)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if none not in (offset, from) {
 | 
				
			||||||
 | 
					      if type(offset) != array {
 | 
				
			||||||
 | 
					        let a = (0, 0)
 | 
				
			||||||
 | 
					        a.at(axis) = offset
 | 
				
			||||||
 | 
					        offset = a
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return resolve-offset(ctx, offset, from, axis)
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					    } else if none not in (align, with) {
 | 
				
			||||||
 | 
					      return resolve-align(ctx, elmt, bounds, align, with, axis)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      panic("Dictionnary must either provide both 'offset' and 'from', or 'align' and 'with'")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if type(coord) not in (int, float, length) {
 | 
				
			||||||
 | 
					    panic("Invalid " + "xy".at(axis) + " coordinate: " + repr(coord))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return coord
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let complete-bounds(elmt, bounds) = {
 | 
				
			||||||
 | 
					  let b = bounds
 | 
				
			||||||
 | 
					  bounds += (
 | 
				
			||||||
 | 
					    center: (
 | 
				
			||||||
 | 
					      (b.br.at(0) + b.tl.at(0))/2,
 | 
				
			||||||
 | 
					      (b.br.at(1) + b.tl.at(1))/2
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    b: (
 | 
				
			||||||
 | 
					      (b.br.at(0) + b.bl.at(0))/2,
 | 
				
			||||||
 | 
					      (b.br.at(1) + b.bl.at(1))/2
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    t: (
 | 
				
			||||||
 | 
					      (b.tr.at(0) + b.tl.at(0))/2,
 | 
				
			||||||
 | 
					      (b.tr.at(1) + b.tl.at(1))/2
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    l: (
 | 
				
			||||||
 | 
					      (b.bl.at(0) + b.tl.at(0))/2,
 | 
				
			||||||
 | 
					      (b.bl.at(1) + b.tl.at(1))/2
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    r: (
 | 
				
			||||||
 | 
					      (b.br.at(0) + b.tr.at(0))/2,
 | 
				
			||||||
 | 
					      (b.br.at(1) + b.tr.at(1))/2
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    sides: (
 | 
				
			||||||
 | 
					      north: (bounds.tl, bounds.tr),
 | 
				
			||||||
 | 
					      south: (bounds.bl, bounds.br),
 | 
				
			||||||
 | 
					      west: (bounds.tl, bounds.bl),
 | 
				
			||||||
 | 
					      east: (bounds.tr, bounds.br),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    lengths: (
 | 
				
			||||||
 | 
					      north: (bounds.tr.at(0) - bounds.tl.at(0)),
 | 
				
			||||||
 | 
					      south: (bounds.br.at(0) - bounds.bl.at(0)),
 | 
				
			||||||
 | 
					      west: (bounds.tl.at(1) - bounds.bl.at(1)),
 | 
				
			||||||
 | 
					      east: (bounds.tr.at(1) - bounds.br.at(1)),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    ports: (:)
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  for (side, props) in bounds.sides.pairs() {
 | 
				
			||||||
 | 
					    let props2 = props
 | 
				
			||||||
 | 
					    if side in elmt.ports-margins {
 | 
				
			||||||
 | 
					      let (pt0, pt1) = props
 | 
				
			||||||
 | 
					      let margins = elmt.ports-margins.at(side)
 | 
				
			||||||
 | 
					      let a = util.lerp(pt0, margins.at(0), pt1)
 | 
				
			||||||
 | 
					      let b = util.lerp(pt0, 100% - margins.at(1), pt1)
 | 
				
			||||||
 | 
					      props2 = (a, b)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    bounds.ports.insert(side, props2)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return bounds
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let make-bounds(elmt, x, y, w, h) = {
 | 
				
			||||||
 | 
					  let w2 = w / 2
 | 
				
			||||||
 | 
					  let h2 = h / 2
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  let bounds = (
 | 
				
			||||||
 | 
					    bl: (x, y),
 | 
				
			||||||
 | 
					    tl: (x, y + h),
 | 
				
			||||||
 | 
					    tr: (x + w, y + h),
 | 
				
			||||||
 | 
					    br: (x + w, y),
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  return complete-bounds(elmt, bounds)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let render(draw-shape, elmt) = draw.group(name: elmt.id, ctx => {
 | 
				
			||||||
 | 
					  let width = elmt.size.first()
 | 
				
			||||||
 | 
					  let height = elmt.size.last()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let x = elmt.pos.first()
 | 
				
			||||||
 | 
					  let y = elmt.pos.last()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let bounds = make-bounds(elmt, 0, 0, width, height)
 | 
				
			||||||
 | 
					  x = resolve-coordinate(ctx, elmt, bounds, x, 0)
 | 
				
			||||||
 | 
					  y = resolve-coordinate(ctx, elmt, bounds, y, 1)
 | 
				
			||||||
 | 
					  bounds = make-bounds(elmt, x, y, width, height)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Workaround because CeTZ needs to have all draw functions in the body
 | 
				
			||||||
 | 
					  let func = {}
 | 
				
			||||||
 | 
					  let res = draw-shape(elmt, bounds)
 | 
				
			||||||
 | 
					  assert(
 | 
				
			||||||
 | 
					    type(res) == array and res.len() == 2,
 | 
				
			||||||
 | 
					    message: "The drawing function of element '" + elmt.id + "' did not return a function and new bounds"
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  (func, bounds) = res
 | 
				
			||||||
 | 
					  if type(func) == function {
 | 
				
			||||||
 | 
					    func = (func,)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  assert(
 | 
				
			||||||
 | 
					    type(bounds) == dictionary,
 | 
				
			||||||
 | 
					    message: "The drawing function of element '" + elmt.id + "' did not return the correct bounds dictionary"
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  func
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  draw.anchor("north", bounds.t)
 | 
				
			||||||
 | 
					  draw.anchor("south", bounds.b)
 | 
				
			||||||
 | 
					  draw.anchor("west", bounds.l)
 | 
				
			||||||
 | 
					  draw.anchor("east", bounds.r)
 | 
				
			||||||
 | 
					  draw.anchor("north-west", bounds.tl)
 | 
				
			||||||
 | 
					  draw.anchor("north-east", bounds.tr)
 | 
				
			||||||
 | 
					  draw.anchor("south-east", bounds.br)
 | 
				
			||||||
 | 
					  draw.anchor("south-west", bounds.bl)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if elmt.name != none {
 | 
				
			||||||
 | 
					    draw.content(
 | 
				
			||||||
 | 
					      (name: elmt.id, anchor: elmt.name-anchor),
 | 
				
			||||||
 | 
					      anchor: if elmt.name-anchor in util.valid-anchors {elmt.name-anchor} else {"center"},
 | 
				
			||||||
 | 
					      padding: 0.5em,
 | 
				
			||||||
 | 
					      align(center)[*#elmt.name*]
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  add-ports(elmt, bounds)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if elmt.debug.bounds {
 | 
				
			||||||
 | 
					    draw.line(
 | 
				
			||||||
 | 
					      bounds.tl, bounds.tr, bounds.br, bounds.bl,
 | 
				
			||||||
 | 
					      stroke: red,
 | 
				
			||||||
 | 
					      close: true
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Draws an element
 | 
					/// Draws an element
 | 
				
			||||||
/// - draw-shape (function): Draw function
 | 
					/// - draw-shape (function): Draw function
 | 
				
			||||||
/// - x (number, dictionary): The x position (bottom-left corner).
 | 
					/// - x (number, dictionary): The x position (bottom-left corner).
 | 
				
			||||||
@@ -45,101 +266,59 @@
 | 
				
			|||||||
///   Supported fields include:
 | 
					///   Supported fields include:
 | 
				
			||||||
///     - `ports`: if true, shows dots on all ports of the element
 | 
					///     - `ports`: if true, shows dots on all ports of the element
 | 
				
			||||||
#let elmt(
 | 
					#let elmt(
 | 
				
			||||||
 | 
					  cls: "element",
 | 
				
			||||||
  draw-shape: default-draw-shape,
 | 
					  draw-shape: default-draw-shape,
 | 
				
			||||||
  x: none,
 | 
					  pre-process: default-pre-process,
 | 
				
			||||||
  y: none,
 | 
					  pos: (0, 0),
 | 
				
			||||||
  w: none,
 | 
					  size: (1, 1),
 | 
				
			||||||
  h: none,
 | 
					 | 
				
			||||||
  name: none,
 | 
					  name: none,
 | 
				
			||||||
  name-anchor: "center",
 | 
					  name-anchor: "center",
 | 
				
			||||||
  ports: (:),
 | 
					  ports: (:),
 | 
				
			||||||
  ports-margins: (:),
 | 
					  ports-margins: (:),
 | 
				
			||||||
  fill: none,
 | 
					  fill: none,
 | 
				
			||||||
  stroke: black + 1pt,
 | 
					  stroke: black + 1pt,
 | 
				
			||||||
  id: "",
 | 
					  id: auto,
 | 
				
			||||||
  auto-ports: true,
 | 
					  ports-pos: auto,
 | 
				
			||||||
  ports-y: (:),
 | 
					 | 
				
			||||||
  debug: (
 | 
					  debug: (
 | 
				
			||||||
 | 
					    bounds: false,
 | 
				
			||||||
    ports: false
 | 
					    ports: false
 | 
				
			||||||
  )
 | 
					  ),
 | 
				
			||||||
) = draw.get-ctx(ctx => {
 | 
					  extra: (:)
 | 
				
			||||||
  let width = w
 | 
					) = {
 | 
				
			||||||
  let height = h
 | 
					  for (key, side-ports) in ports.pairs() {
 | 
				
			||||||
 | 
					    if type(side-ports) == str {
 | 
				
			||||||
  let x = x
 | 
					      side-ports = ((id: side-ports),)
 | 
				
			||||||
  let y = y
 | 
					    } else if type(side-ports) == dictionary {
 | 
				
			||||||
  if x == none { panic("Parameter x must be set") }
 | 
					      side-ports = (side-ports,)
 | 
				
			||||||
  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 (to-side, i) = find-port(ports, to)
 | 
					 | 
				
			||||||
    let margins = (0%, 0%)
 | 
					 | 
				
			||||||
    if to-side in ports-margins {
 | 
					 | 
				
			||||||
      margins = ports-margins.at(to-side)
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let dy
 | 
					    for (i, port) in side-ports.enumerate() {
 | 
				
			||||||
    let top-margin
 | 
					      if type(port) == array {
 | 
				
			||||||
    if to-side in ("east", "west") {
 | 
					        side-ports.at(i) = (
 | 
				
			||||||
      let used-pct = 100% - margins.at(0) - margins.at(1)
 | 
					          id: port.at(0, default: ""),
 | 
				
			||||||
      let used-height = height * used-pct / 100%
 | 
					          name: port.at(1, default: "")
 | 
				
			||||||
      top-margin = height * margins.at(0) / 100%
 | 
					        )
 | 
				
			||||||
      
 | 
					      } else if type(port) == str {
 | 
				
			||||||
      dy = used-height * (i + 1) / (ports.at(to-side).len() + 1)
 | 
					        side-ports.at(i) = (id: port)
 | 
				
			||||||
 | 
					 | 
				
			||||||
      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
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    ports.at(key) = side-ports
 | 
				
			||||||
    let (ctx, from-pos) = coordinate.resolve(ctx, from)
 | 
					 | 
				
			||||||
    y = from-pos.at(1) + dy - height + top-margin
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let tl = (x, y + height)
 | 
					  return ((
 | 
				
			||||||
  let tr = (x + width, y + height)
 | 
					    cls: cls,
 | 
				
			||||||
  let br = (x + width, y)
 | 
					    id: id,
 | 
				
			||||||
  let bl = (x, y)
 | 
					    draw: render.with(draw-shape),
 | 
				
			||||||
 | 
					    pre-process: pre-process,
 | 
				
			||||||
  // Workaround because CeTZ needs to have all draw functions in the body
 | 
					    pos: pos,
 | 
				
			||||||
  let func = {}
 | 
					    size: size,
 | 
				
			||||||
  (func, tl, tr, br, bl) = draw-shape(id, tl, tr, br, bl, fill, stroke)
 | 
					    name: name,
 | 
				
			||||||
  func
 | 
					    name-anchor: name-anchor,
 | 
				
			||||||
 | 
					    ports: ports,
 | 
				
			||||||
  if (name != none) {
 | 
					    ports-margins: ports-margins,
 | 
				
			||||||
    draw.content(
 | 
					    fill: fill,
 | 
				
			||||||
      (name: id, anchor: name-anchor),
 | 
					    stroke: stroke,
 | 
				
			||||||
      anchor: if name-anchor in util.valid-anchors {name-anchor} else {"center"},
 | 
					    ports-pos: ports-pos,
 | 
				
			||||||
      padding: 0.5em,
 | 
					    debug: debug
 | 
				
			||||||
      align(center)[*#name*]
 | 
					  ) + extra,)
 | 
				
			||||||
    )
 | 
					}
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if auto-ports {
 | 
					 | 
				
			||||||
    add-ports(
 | 
					 | 
				
			||||||
      id,
 | 
					 | 
				
			||||||
      tl, tr, br, bl,
 | 
					 | 
				
			||||||
      ports,
 | 
					 | 
				
			||||||
      ports-margins,
 | 
					 | 
				
			||||||
      debug: debug.ports
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,41 +1,25 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw
 | 
					#import "/src/cetz.typ": draw
 | 
				
			||||||
#import "element.typ"
 | 
					#import "element.typ"
 | 
				
			||||||
#import "ports.typ": add-port
 | 
					#import "ports.typ": add-port
 | 
				
			||||||
 | 
					#import "../util.typ"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let draw-shape(id, tl, tr, br, bl, fill, stroke, h-ratio: 75%, align-out: true) = {
 | 
					#let draw-shape(elmt, bounds) = {
 | 
				
			||||||
  let (x, y) = bl
 | 
					  let ratio = elmt.l-ratio / 100%
 | 
				
			||||||
  let (width, height) = (tr.at(0) - x, tr.at(1) - y)
 | 
					  let tl2 = util.lerp(bounds.bl, elmt.l-ratio, bounds.tl)
 | 
				
			||||||
 | 
					  let bounds2 = element.complete-bounds(elmt, (
 | 
				
			||||||
 | 
					    tl: tl2,
 | 
				
			||||||
 | 
					    tr: bounds.tr,
 | 
				
			||||||
 | 
					    br: bounds.br,
 | 
				
			||||||
 | 
					    bl: bounds.bl,
 | 
				
			||||||
 | 
					  ))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let ratio = h-ratio / 100%
 | 
					  let f = draw.line(
 | 
				
			||||||
 | 
					    bounds2.tl, bounds2.tr, bounds2.br, bounds2.bl,
 | 
				
			||||||
  tl = (x, y + height * ratio)
 | 
					    fill: elmt.fill,
 | 
				
			||||||
  let tr2 = (x + width, y + height * ratio)
 | 
					    stroke: elmt.stroke,
 | 
				
			||||||
  let br = (x + width, y)
 | 
					    close: true
 | 
				
			||||||
  
 | 
					  )
 | 
				
			||||||
  if align-out {
 | 
					  return (f, bounds2)
 | 
				
			||||||
    (tr, tr2) = (tr2, tr)
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    (tr, tr2) = (tr, tr)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let f = draw.group(name: id, {
 | 
					 | 
				
			||||||
    draw.merge-path(
 | 
					 | 
				
			||||||
      inset: 0.5em,
 | 
					 | 
				
			||||||
      fill: fill,
 | 
					 | 
				
			||||||
      stroke: stroke,
 | 
					 | 
				
			||||||
      close: true,
 | 
					 | 
				
			||||||
      draw.line(tl, tr2, br, bl)
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    draw.anchor("north", (tl, 50%, tr2))
 | 
					 | 
				
			||||||
    draw.anchor("south", (bl, 50%, br))
 | 
					 | 
				
			||||||
    draw.anchor("west", (tl, 50%, bl))
 | 
					 | 
				
			||||||
    draw.anchor("east", (tr2, 50%, br))
 | 
					 | 
				
			||||||
    draw.anchor("north-west", tl)
 | 
					 | 
				
			||||||
    draw.anchor("north-east", tr2)
 | 
					 | 
				
			||||||
    draw.anchor("south-east", br)
 | 
					 | 
				
			||||||
    draw.anchor("south-west", bl)
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
  return (f, tl, tr, br, bl)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Draws a bit extender
 | 
					/// Draws a bit extender
 | 
				
			||||||
@@ -45,54 +29,28 @@
 | 
				
			|||||||
/// - h-ratio (ratio): The height ratio of the left side relative to the full height
 | 
					/// - h-ratio (ratio): The height ratio of the left side relative to the full height
 | 
				
			||||||
/// - align-out (bool): If true, the output and input ports are aligned, otherwise, the output port is centered on the right side
 | 
					/// - align-out (bool): If true, the output and input ports are aligned, otherwise, the output port is centered on the right side
 | 
				
			||||||
#let extender(
 | 
					#let extender(
 | 
				
			||||||
  x: none,
 | 
					  l-ratio: 75%,
 | 
				
			||||||
  y: none,
 | 
					 | 
				
			||||||
  w: none,
 | 
					 | 
				
			||||||
  h: none,
 | 
					 | 
				
			||||||
  name: none,
 | 
					 | 
				
			||||||
  name-anchor: "center",
 | 
					 | 
				
			||||||
  fill: none,
 | 
					 | 
				
			||||||
  stroke: black + 1pt,
 | 
					 | 
				
			||||||
  id: "",
 | 
					 | 
				
			||||||
  h-ratio: 75%,
 | 
					 | 
				
			||||||
  align-out: true,
 | 
					  align-out: true,
 | 
				
			||||||
  debug: (
 | 
					  ..args
 | 
				
			||||||
    ports: false
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
) = {
 | 
					) = {
 | 
				
			||||||
  let ports = (
 | 
					  let ports = (
 | 
				
			||||||
    west: (
 | 
					    west: ((id: "in"),),
 | 
				
			||||||
      (id: "in"),
 | 
					    east: ((id: "out"),)
 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    east: (
 | 
					 | 
				
			||||||
      (id: "out"),
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
  let out-pct = if align-out {h-ratio / 2} else {50%}
 | 
					  let out-pct = if align-out {l-ratio / 2} else {50%}
 | 
				
			||||||
  let ports-y = (
 | 
					  let ports-pos = (
 | 
				
			||||||
    "in": (h) => {h - h * (h-ratio / 200%)},
 | 
					    "east": (l, i) => {l - l * (out-pct / 100%)}
 | 
				
			||||||
    "out": (h) => {h - h * (out-pct / 100%)}
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  element.elmt(
 | 
					  return element.elmt(
 | 
				
			||||||
    draw-shape: draw-shape.with(h-ratio: h-ratio, align-out: align-out),
 | 
					    cls: "extender",
 | 
				
			||||||
    x: x,
 | 
					    draw-shape: draw-shape,
 | 
				
			||||||
    y: y,
 | 
					 | 
				
			||||||
    w: w,
 | 
					 | 
				
			||||||
    h: h,
 | 
					 | 
				
			||||||
    name: name,
 | 
					 | 
				
			||||||
    name-anchor: name-anchor,
 | 
					 | 
				
			||||||
    ports: ports,
 | 
					    ports: ports,
 | 
				
			||||||
    auto-ports: false,
 | 
					    ports-pos: ports-pos,
 | 
				
			||||||
    ports-y: ports-y,
 | 
					    extra: (
 | 
				
			||||||
    fill: fill,
 | 
					      l-ratio: l-ratio,
 | 
				
			||||||
    stroke: stroke,
 | 
					      align-out: align-out
 | 
				
			||||||
    id: id,
 | 
					    ),
 | 
				
			||||||
    debug: debug
 | 
					    ..args
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					 | 
				
			||||||
  let in-pos = (rel: (0, h * (h-ratio / 200%)), to: id+".south-west")
 | 
					 | 
				
			||||||
  let out-pos = (id+".south-east", out-pct, id+".north-east")
 | 
					 | 
				
			||||||
  add-port(id, "west", ports.west.first(), in-pos)
 | 
					 | 
				
			||||||
  add-port(id, "east", ports.east.first(), out-pos)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw, coordinate
 | 
					#import "/src/cetz.typ": draw, coordinate
 | 
				
			||||||
#import "../util.typ"
 | 
					#import "../util.typ"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Draws a group of elements
 | 
					/// Draws a group of elements
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw
 | 
					#import "/src/cetz.typ": draw
 | 
				
			||||||
#import "gate.typ"
 | 
					#import "gate.typ"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let draw-shape(id, tl, tr, br, bl, fill, stroke) = {
 | 
					#let draw-shape(id, tl, tr, br, bl, fill, stroke) = {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw
 | 
					#import "/src/cetz.typ": draw
 | 
				
			||||||
#import "gate.typ"
 | 
					#import "gate.typ"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let draw-shape(id, tl, tr, br, bl, fill, stroke) = {
 | 
					#let draw-shape(id, tl, tr, br, bl, fill, stroke) = {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 "../ports.typ": add-ports, add-port
 | 
				
			||||||
#import "../element.typ"
 | 
					#import "../element.typ"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw
 | 
					#import "/src/cetz.typ": draw
 | 
				
			||||||
#import "gate.typ"
 | 
					#import "gate.typ"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let draw-shape(id, tl, tr, br, bl, fill, stroke) = {
 | 
					#let draw-shape(id, tl, tr, br, bl, fill, stroke) = {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw
 | 
					#import "/src/cetz.typ": draw
 | 
				
			||||||
#import "gate.typ"
 | 
					#import "gate.typ"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let space = 10%
 | 
					#let space = 10%
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,31 +1,26 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw
 | 
					#import "/src/cetz.typ": draw
 | 
				
			||||||
#import "../util.typ"
 | 
					#import "../util.typ"
 | 
				
			||||||
#import "element.typ"
 | 
					#import "element.typ"
 | 
				
			||||||
#import "ports.typ": add-port
 | 
					#import "ports.typ": add-port
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let draw-shape(id, tl, tr, br, bl, fill, stroke, h-ratio: 60%) = {
 | 
					#let draw-shape(elmt, bounds) = {
 | 
				
			||||||
  let margin = (100% - h-ratio) / 2
 | 
					  let margin = (100% - elmt.l-ratio) / 2
 | 
				
			||||||
  let tr2 = (tr, margin, br)
 | 
					  let tr2 = util.lerp(bounds.tr, margin, bounds.br)
 | 
				
			||||||
  let br2 = (br, margin, tr)
 | 
					  let br2 = util.lerp(bounds.br, margin, bounds.tr)
 | 
				
			||||||
  let f = draw.group(name: id, {
 | 
					  let bounds2 = element.complete-bounds(elmt, (
 | 
				
			||||||
    draw.merge-path(
 | 
					    tl: bounds.tl,
 | 
				
			||||||
      inset: 0.5em,
 | 
					    bl: bounds.bl,
 | 
				
			||||||
      fill: fill,
 | 
					    tr: tr2,
 | 
				
			||||||
      stroke: stroke,
 | 
					    br: br2,
 | 
				
			||||||
      close: true,
 | 
					  ))
 | 
				
			||||||
      draw.line(tl, tr2, br2, bl)
 | 
					  let f = draw.line(
 | 
				
			||||||
    )
 | 
					    bounds2.tl, bounds2.tr, bounds2.br, bounds2.bl,
 | 
				
			||||||
    draw.anchor("north", (tl, 50%, tr2))
 | 
					    close: true,
 | 
				
			||||||
    draw.anchor("south", (bl, 50%, br2))
 | 
					    fill: elmt.fill,
 | 
				
			||||||
    draw.anchor("west", (tl, 50%, bl))
 | 
					    stroke: elmt.stroke
 | 
				
			||||||
    draw.anchor("east", (tr2, 50%, br2))
 | 
					  )
 | 
				
			||||||
    draw.anchor("north-west", tl)
 | 
					 | 
				
			||||||
    draw.anchor("north-east", tr2)
 | 
					 | 
				
			||||||
    draw.anchor("south-east", br2)
 | 
					 | 
				
			||||||
    draw.anchor("south-west", bl)
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (f, tl, tr, br, bl)
 | 
					  return (f, bounds2)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Draws a multiplexer
 | 
					/// Draws a multiplexer
 | 
				
			||||||
@@ -35,64 +30,44 @@
 | 
				
			|||||||
/// - entries (int, array): If it is an integer, it defines the number of input ports (automatically named with their binary index). If it is an array of strings, it defines the name of each input.
 | 
					/// - entries (int, array): If it is an integer, it defines the number of input ports (automatically named with their binary index). If it is an array of strings, it defines the name of each input.
 | 
				
			||||||
/// - h-ratio (ratio): The height ratio of the right side relative to the full height
 | 
					/// - h-ratio (ratio): The height ratio of the right side relative to the full height
 | 
				
			||||||
#let multiplexer(
 | 
					#let multiplexer(
 | 
				
			||||||
  x: none,
 | 
					 | 
				
			||||||
  y: none,
 | 
					 | 
				
			||||||
  w: none,
 | 
					 | 
				
			||||||
  h: none,
 | 
					 | 
				
			||||||
  name: none,
 | 
					 | 
				
			||||||
  name-anchor: "center",
 | 
					 | 
				
			||||||
  entries: 2,
 | 
					  entries: 2,
 | 
				
			||||||
  h-ratio: 60%,
 | 
					  l-ratio: 60%,
 | 
				
			||||||
  fill: none,
 | 
					  ..args
 | 
				
			||||||
  stroke: black + 1pt,
 | 
					 | 
				
			||||||
  id: "",
 | 
					 | 
				
			||||||
  debug: (
 | 
					 | 
				
			||||||
    ports: false
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
) = {
 | 
					) = {
 | 
				
			||||||
  let ports = ()
 | 
					  let in-ports = ()
 | 
				
			||||||
  let ports-y = (
 | 
					  let ports-pos = (
 | 
				
			||||||
    out: (h) => {h * 0.5}
 | 
					    "east": auto,
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (type(entries) == int) {
 | 
					  if (type(entries) == int) {
 | 
				
			||||||
    let nbits = calc.ceil(calc.log(entries, base: 2))
 | 
					    let nbits = calc.ceil(calc.log(entries, base: 2))
 | 
				
			||||||
    for i in range(entries) {
 | 
					    for i in range(entries) {
 | 
				
			||||||
      let bits = util.lpad(str(i, base: 2), nbits)
 | 
					      let bits = util.lpad(str(i, base: 2), nbits)
 | 
				
			||||||
      ports.push((id: "in" + str(i), name: bits))
 | 
					      in-ports.push((id: "in" + str(i), name: bits))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    for (i, port) in entries.enumerate() {
 | 
					    for (i, port) in entries.enumerate() {
 | 
				
			||||||
      ports.push((id: "in" + str(i), name: port))
 | 
					      in-ports.push((id: "in" + str(i), name: port))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let space = 100% / ports.len()
 | 
					  let n = in-ports.len()
 | 
				
			||||||
  let l = ports.len()
 | 
					  ports-pos.insert("west", (l, i) => {l * (i + 0.5) / n})
 | 
				
			||||||
  for (i, port) in ports.enumerate() {
 | 
					 | 
				
			||||||
    ports-y.insert(port.id, (h) => {h * (i + 0.5) / l})
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  element.elmt(
 | 
					  element.elmt(
 | 
				
			||||||
    draw-shape: draw-shape.with(h-ratio: h-ratio),
 | 
					    cls: "multiplexer",
 | 
				
			||||||
    x: x,
 | 
					    draw-shape: draw-shape,
 | 
				
			||||||
    y: y,
 | 
					    ports: (west: in-ports, east: ((id: "out"),)),
 | 
				
			||||||
    w: w,
 | 
					    ports-pos: ports-pos,
 | 
				
			||||||
    h: h,
 | 
					    extra: (l-ratio: l-ratio),
 | 
				
			||||||
    name: name,
 | 
					    ..args
 | 
				
			||||||
    name-anchor: name-anchor,
 | 
					 | 
				
			||||||
    ports: (west: ports, east: ((id: "out"),)),
 | 
					 | 
				
			||||||
    fill: fill,
 | 
					 | 
				
			||||||
    stroke: stroke,
 | 
					 | 
				
			||||||
    id: id,
 | 
					 | 
				
			||||||
    ports-y: ports-y,
 | 
					 | 
				
			||||||
    auto-ports: false,
 | 
					 | 
				
			||||||
    debug: debug
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
  for (i, port) in ports.enumerate() {
 | 
					  for (i, port) in ports.enumerate() {
 | 
				
			||||||
    let pct = (i + 0.5) * space
 | 
					    let pct = (i + 0.5) * space
 | 
				
			||||||
    add-port(id, "west", port, (id+".north-west", pct, id+".south-west"))
 | 
					    add-port(id, "west", port, (id+".north-west", pct, id+".south-west"))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  add-port(id, "east", (id: "out"), (id+".north-east", 50%, id+".south-east"))
 | 
					  add-port(id, "east", (id: "out"), (id+".north-east", 50%, id+".south-east"))
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,76 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw
 | 
					#import "/src/cetz.typ": draw
 | 
				
			||||||
#import "../util.typ": rotate-anchor
 | 
					#import "../util.typ": rotate-anchor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let get-port-side(elmt, port) = {
 | 
				
			||||||
 | 
					  for (side, ports) in elmt.ports {
 | 
				
			||||||
 | 
					    for p in ports {
 | 
				
			||||||
 | 
					      if p.id == port {
 | 
				
			||||||
 | 
					        return side
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  panic(
 | 
				
			||||||
 | 
					    "Unknown port '" + port + "' on element '" + elmt.id + "', could not automatically determine side"
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let get-port-idx(elmt, port, side: auto) = {
 | 
				
			||||||
 | 
					  if side == auto {
 | 
				
			||||||
 | 
					    side = get-port-side(elmt, port)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  assert(
 | 
				
			||||||
 | 
					    side in elmt.ports,
 | 
				
			||||||
 | 
					    message: "No ports on side '" + side + "' of element '" + elmt.id + "'"
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  let i = elmt.ports.at(side).position(p => p.id == port)
 | 
				
			||||||
 | 
					  assert(
 | 
				
			||||||
 | 
					    i != none,
 | 
				
			||||||
 | 
					    message: "Could not find port '" + port + "' on side '" + side + "' of element '" + elmt.id + "'"
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  return i
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let get-port-pos(elmt, bounds, side, port, port-i) = {
 | 
				
			||||||
 | 
					  let (pt0, pt1) = bounds.ports.at(side)
 | 
				
			||||||
 | 
					  let side-len = if side in ("north", "south") {
 | 
				
			||||||
 | 
					    pt1.at(0) - pt0.at(0)
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    pt0.at(1) - pt1.at(1)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let offset = if (
 | 
				
			||||||
 | 
					    elmt.ports-pos == auto or
 | 
				
			||||||
 | 
					    elmt.ports-pos.at(side, default: auto) == auto
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    let space = 100% / (elmt.ports.at(side, default: ()).len() + 1)
 | 
				
			||||||
 | 
					    (port-i + 1) * space
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    assert(
 | 
				
			||||||
 | 
					      side in elmt.ports-pos,
 | 
				
			||||||
 | 
					      message: "Could not reliably compute port position (missing side)"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    let side-pos = elmt.ports-pos.at(side)
 | 
				
			||||||
 | 
					    if type(side-pos) == function {
 | 
				
			||||||
 | 
					      (side-pos)(side-len, port-i)
 | 
				
			||||||
 | 
					    } else if type(side-pos) == array {
 | 
				
			||||||
 | 
					      (side-pos.at(i))(side-len)
 | 
				
			||||||
 | 
					    } else if type(side-pos) == dictionary {
 | 
				
			||||||
 | 
					      assert(
 | 
				
			||||||
 | 
					        port in side-pos,
 | 
				
			||||||
 | 
					        message: "Could not reliably compute port position (missing port)"
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      (side-pos.at(port))(side-len)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      panic("Could not reliably compute port position (invalid type)")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if type(offset) == ratio {
 | 
				
			||||||
 | 
					    offset = offset * side-len / 100%
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return offset
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let add-port(
 | 
					#let add-port(
 | 
				
			||||||
  elmt-id, side, port, pos,
 | 
					  elmt-id, side, port, pos,
 | 
				
			||||||
  prev: none,
 | 
					  prev: none,
 | 
				
			||||||
@@ -34,12 +104,10 @@
 | 
				
			|||||||
    angle: if name-rotate {90deg} else {0deg},
 | 
					    angle: if name-rotate {90deg} else {0deg},
 | 
				
			||||||
    name
 | 
					    name
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
  let id = elmt-id + "-port-" + port.at("id")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if debug {
 | 
					  if debug {
 | 
				
			||||||
    draw.circle(
 | 
					    draw.circle(
 | 
				
			||||||
      pos,
 | 
					      pos,
 | 
				
			||||||
      name: id,
 | 
					 | 
				
			||||||
      radius: .1,
 | 
					      radius: .1,
 | 
				
			||||||
      stroke: none,
 | 
					      stroke: none,
 | 
				
			||||||
      fill: red
 | 
					      fill: red
 | 
				
			||||||
@@ -49,53 +117,48 @@
 | 
				
			|||||||
    draw.hide(draw.circle(
 | 
					    draw.hide(draw.circle(
 | 
				
			||||||
      pos,
 | 
					      pos,
 | 
				
			||||||
      radius: 0,
 | 
					      radius: 0,
 | 
				
			||||||
      stroke: none,
 | 
					      stroke: none
 | 
				
			||||||
      name: id
 | 
					 | 
				
			||||||
    ))
 | 
					    ))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  draw.anchor(port.id, pos)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let add-ports(
 | 
					#let add-ports(
 | 
				
			||||||
  elmt-id,
 | 
					  elmt,
 | 
				
			||||||
  tl, tr, br, bl,
 | 
					  bounds
 | 
				
			||||||
  ports,
 | 
					 | 
				
			||||||
  ports-margins,
 | 
					 | 
				
			||||||
  debug: false
 | 
					 | 
				
			||||||
) = {
 | 
					) = {
 | 
				
			||||||
  let sides = (
 | 
					  let debug = elmt.debug.at("ports", default: false)
 | 
				
			||||||
    "north": (tl, tr),
 | 
					 | 
				
			||||||
    "east": (tr, br),
 | 
					 | 
				
			||||||
    "south": (bl, br),
 | 
					 | 
				
			||||||
    "west": (tl, bl)
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if type(ports) != dictionary {
 | 
					  if type(elmt.ports) != dictionary {
 | 
				
			||||||
    return
 | 
					    return
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (side, props) in sides {
 | 
					  for (side, props) in bounds.ports {
 | 
				
			||||||
    let side-ports = ports.at(side, default: ())
 | 
					    let side-ports = elmt.ports.at(side, default: ())
 | 
				
			||||||
    let space = 100% / (side-ports.len() + 1)
 | 
					    let space = 100% / (side-ports.len() + 1)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    let (pt0, pt1) = props
 | 
				
			||||||
 | 
					    let side-len = if side in ("north", "south") {
 | 
				
			||||||
 | 
					      pt1.at(0) - pt0.at(0)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      pt0.at(1) - pt1.at(1)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    for (i, port) in side-ports.enumerate() {
 | 
					    for (i, port) in side-ports.enumerate() {
 | 
				
			||||||
      let pct = (i + 1) * space
 | 
					      let offset = get-port-pos(elmt, bounds, side, port.id, i)
 | 
				
			||||||
      let pt0 = props.at(0)
 | 
					 | 
				
			||||||
      let pt1 = props.at(1)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if side in ports-margins {
 | 
					      let pos = (pt0, offset, pt1)
 | 
				
			||||||
        let (a, b) = (pt0, pt1)
 | 
					      let offset-prev = if type(offset) == ratio {
 | 
				
			||||||
        let margins = ports-margins.at(side)
 | 
					        offset - space / 2
 | 
				
			||||||
        a = (pt0, margins.at(0), pt1)
 | 
					      } else {
 | 
				
			||||||
        b = (pt0, 100% - margins.at(1), pt1)
 | 
					        offset - space * side-len / 200%
 | 
				
			||||||
        pt0 = a
 | 
					 | 
				
			||||||
        pt1 = b
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      
 | 
					      let offset-next = if type(offset) == ratio {
 | 
				
			||||||
      let pos = (pt0, pct, pt1)
 | 
					        offset + space / 2
 | 
				
			||||||
      let pct-prev = (i + 0.5) * space
 | 
					      } else {
 | 
				
			||||||
      let pct-next = (i + 1.5) * space
 | 
					        offset + space * side-len / 200%
 | 
				
			||||||
      let pos-prev = (pt0, pct-prev, pt1)
 | 
					      }
 | 
				
			||||||
      let pos-next = (pt0, pct-next, pt1)
 | 
					      let pos-prev = (pt0, offset-prev, pt1)
 | 
				
			||||||
 | 
					      let pos-next = (pt0, offset-next, pt1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if port.at("small", default: false) {
 | 
					      if port.at("small", default: false) {
 | 
				
			||||||
        pos-prev = (pos, 4pt, pt0)
 | 
					        pos-prev = (pos, 4pt, pt0)
 | 
				
			||||||
@@ -103,7 +166,7 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      add-port(
 | 
					      add-port(
 | 
				
			||||||
        elmt-id,
 | 
					        elmt.id,
 | 
				
			||||||
        side,
 | 
					        side,
 | 
				
			||||||
        port,
 | 
					        port,
 | 
				
			||||||
        pos,
 | 
					        pos,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,6 @@
 | 
				
			|||||||
#let version = version(0, 1, 0)
 | 
					#let version = version(0, 2, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#import "circuit.typ": circuit
 | 
					#import "circuit.typ": circuit
 | 
				
			||||||
#import "electrical.typ"
 | 
					 | 
				
			||||||
#import "element.typ"
 | 
					#import "element.typ"
 | 
				
			||||||
#import "gates.typ"
 | 
					#import "gates.typ"
 | 
				
			||||||
#import "util.typ"
 | 
					#import "util.typ"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,3 +74,10 @@
 | 
				
			|||||||
  "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"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let lerp(pt0, ratio, pt1) = {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    (pt1.at(0) - pt0.at(0)) * ratio / 100% + pt0.at(0),
 | 
				
			||||||
 | 
					    (pt1.at(1) - pt0.at(1)) * ratio / 100% + pt0.at(1)
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										124
									
								
								src/wire.typ
									
									
									
									
									
								
							
							
						
						@@ -1,5 +1,7 @@
 | 
				
			|||||||
#import "@preview/cetz:0.2.2": draw, coordinate
 | 
					#import "/src/cetz.typ": draw, coordinate
 | 
				
			||||||
#import "util.typ": opposite-anchor
 | 
					#import "util.typ": opposite-anchor
 | 
				
			||||||
 | 
					#import "elements/ports.typ": get-port-side
 | 
				
			||||||
 | 
					#import "elements/element.typ"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// List of valid wire styles
 | 
					/// List of valid wire styles
 | 
				
			||||||
/// #examples.wires
 | 
					/// #examples.wires
 | 
				
			||||||
@@ -128,10 +130,11 @@
 | 
				
			|||||||
/// - 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")
 | 
				
			||||||
#let wire(
 | 
					#let wire(
 | 
				
			||||||
  id, pts,
 | 
					  pt0,
 | 
				
			||||||
 | 
					  pt1,
 | 
				
			||||||
 | 
					  id: none,
 | 
				
			||||||
  bus: false,
 | 
					  bus: false,
 | 
				
			||||||
  name: none,
 | 
					  name: none,
 | 
				
			||||||
  name-pos: "middle",
 | 
					 | 
				
			||||||
  slice: none,
 | 
					  slice: none,
 | 
				
			||||||
  color: black,
 | 
					  color: black,
 | 
				
			||||||
  dashed: false,
 | 
					  dashed: false,
 | 
				
			||||||
@@ -143,15 +146,14 @@
 | 
				
			|||||||
  zigzag-dir: "vertical",
 | 
					  zigzag-dir: "vertical",
 | 
				
			||||||
  dodge-y: 0,
 | 
					  dodge-y: 0,
 | 
				
			||||||
  dodge-sides: ("east", "west"),
 | 
					  dodge-sides: ("east", "west"),
 | 
				
			||||||
  dodge-margins: (5%, 5%)
 | 
					  dodge-margins: (5%, 5%),
 | 
				
			||||||
 | 
					  ..args
 | 
				
			||||||
) = draw.get-ctx(ctx => {
 | 
					) = draw.get-ctx(ctx => {
 | 
				
			||||||
  if not style in wire-styles {
 | 
					  if not style in wire-styles {
 | 
				
			||||||
    panic("Invalid wire style '" + style + "'")
 | 
					    panic("Invalid wire style '" + style + "'")
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if pts.len() != 2 {
 | 
					  let pts = (pt0, pt1)
 | 
				
			||||||
    panic("Wrong number of points (got " + str(pts.len()) + " instead of 2)")
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  let stroke = (
 | 
					  let stroke = (
 | 
				
			||||||
    paint: color,
 | 
					    paint: color,
 | 
				
			||||||
@@ -216,7 +218,7 @@
 | 
				
			|||||||
    let names = ()
 | 
					    let names = ()
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    if type(name) == str {
 | 
					    if type(name) == str {
 | 
				
			||||||
      names = ((name, name-pos),)
 | 
					      names = ((name, "middle"),)
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
    } else if type(name) == array {
 | 
					    } else if type(name) == array {
 | 
				
			||||||
      names = (
 | 
					      names = (
 | 
				
			||||||
@@ -226,6 +228,9 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (name, pos) in names {
 | 
					    for (name, pos) in names {
 | 
				
			||||||
 | 
					      if name == none {
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      let point
 | 
					      let point
 | 
				
			||||||
      let anchor
 | 
					      let anchor
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
@@ -267,40 +272,73 @@
 | 
				
			|||||||
/// - vertical (bool): Whether the name should be displayed vertically
 | 
					/// - vertical (bool): Whether the name should be displayed vertically
 | 
				
			||||||
/// - length (number): The length of the stub
 | 
					/// - length (number): The length of the stub
 | 
				
			||||||
/// - 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(anchor, side: auto, name: none, vertical: false, length: 1em, name-offset: 0) = {
 | 
				
			||||||
  let end-offset = (
 | 
					  if "." not in anchor {
 | 
				
			||||||
    north: (0, length),
 | 
					    panic("`anchor` must be a valid anchor of an element")
 | 
				
			||||||
    east: (length, 0),
 | 
					 | 
				
			||||||
    south: (0, -length),
 | 
					 | 
				
			||||||
    west: (-length, 0)
 | 
					 | 
				
			||||||
  ).at(side)
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  let name-offset = (
 | 
					 | 
				
			||||||
    north: (name-offset, length),
 | 
					 | 
				
			||||||
    east: (length, name-offset),
 | 
					 | 
				
			||||||
    south: (name-offset, -length),
 | 
					 | 
				
			||||||
    west: (-length, name-offset)
 | 
					 | 
				
			||||||
  ).at(side)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  draw.line(
 | 
					 | 
				
			||||||
    port-id,
 | 
					 | 
				
			||||||
    (rel: end-offset, to: port-id)
 | 
					 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
  if name != none {
 | 
					 | 
				
			||||||
    let text-anchor = if vertical {
 | 
					 | 
				
			||||||
      (
 | 
					 | 
				
			||||||
        "north": "west",
 | 
					 | 
				
			||||||
        "south": "east",
 | 
					 | 
				
			||||||
        "west": "south",
 | 
					 | 
				
			||||||
        "east": "north"
 | 
					 | 
				
			||||||
      ).at(side)
 | 
					 | 
				
			||||||
    } else { opposite-anchor(side) }
 | 
					 | 
				
			||||||
    draw.content(
 | 
					 | 
				
			||||||
      anchor: text-anchor,
 | 
					 | 
				
			||||||
      padding: 0.2em,
 | 
					 | 
				
			||||||
      angle: if vertical {90deg} else {0deg},
 | 
					 | 
				
			||||||
      (rel: name-offset, to: port-id),
 | 
					 | 
				
			||||||
      name
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  let parts = anchor.split(".")
 | 
				
			||||||
 | 
					  let port-id = parts.last()
 | 
				
			||||||
 | 
					  let port-elmt-id = parts.slice(0, -1).join(".")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let pre-process = (elements, elmt) => {
 | 
				
			||||||
 | 
					    let eid = elmt.id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let side = side
 | 
				
			||||||
 | 
					    if side == auto {
 | 
				
			||||||
 | 
					      if port-elmt-id not in elements {
 | 
				
			||||||
 | 
					        panic("Unknown element " + port-elmt-id)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      let port-elmt = elements.at(port-elmt-id)
 | 
				
			||||||
 | 
					      side = get-port-side(port-elmt, port-id)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    elements.at(eid).insert("side", side)
 | 
				
			||||||
 | 
					    return elements
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let draw-func(elmt, bounds) = {
 | 
				
			||||||
 | 
					    let side = elmt.side
 | 
				
			||||||
 | 
					    let end-offset = (
 | 
				
			||||||
 | 
					      north: (0, length),
 | 
				
			||||||
 | 
					      east: (length, 0),
 | 
				
			||||||
 | 
					      south: (0, -length),
 | 
				
			||||||
 | 
					      west: (-length, 0)
 | 
				
			||||||
 | 
					    ).at(side)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    let name-offset = (
 | 
				
			||||||
 | 
					      north: (name-offset, length),
 | 
				
			||||||
 | 
					      east: (length, name-offset),
 | 
				
			||||||
 | 
					      south: (name-offset, -length),
 | 
				
			||||||
 | 
					      west: (-length, name-offset)
 | 
				
			||||||
 | 
					    ).at(side)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let shapes = ()
 | 
				
			||||||
 | 
					    shapes += draw.line(
 | 
				
			||||||
 | 
					      anchor,
 | 
				
			||||||
 | 
					      (rel: end-offset, to: anchor)
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    if name != none {
 | 
				
			||||||
 | 
					      let text-anchor = if vertical {
 | 
				
			||||||
 | 
					        (
 | 
				
			||||||
 | 
					          "north": "west",
 | 
				
			||||||
 | 
					          "south": "east",
 | 
				
			||||||
 | 
					          "west": "south",
 | 
				
			||||||
 | 
					          "east": "north"
 | 
				
			||||||
 | 
					        ).at(side)
 | 
				
			||||||
 | 
					      } else { opposite-anchor(side) }
 | 
				
			||||||
 | 
					      shapes += draw.content(
 | 
				
			||||||
 | 
					        anchor: text-anchor,
 | 
				
			||||||
 | 
					        padding: 0.2em,
 | 
				
			||||||
 | 
					        angle: if vertical {90deg} else {0deg},
 | 
				
			||||||
 | 
					        (rel: name-offset, to: anchor),
 | 
				
			||||||
 | 
					        name
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (shapes, bounds)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return element.elmt(
 | 
				
			||||||
 | 
					    draw-shape: draw-func,
 | 
				
			||||||
 | 
					    pre-process: pre-process
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
[package]
 | 
					[package]
 | 
				
			||||||
name = "circuiteria"
 | 
					name = "circuiteria"
 | 
				
			||||||
version = "0.1.0"
 | 
					version = "0.2.0"
 | 
				
			||||||
compiler = "0.11.0"
 | 
					compiler = "0.13.0"
 | 
				
			||||||
repository = "https://git.kb28.ch/HEL/circuiteria"
 | 
					repository = "https://git.kb28.ch/HEL/circuiteria"
 | 
				
			||||||
entrypoint = "src/lib.typ"
 | 
					entrypoint = "src/lib.typ"
 | 
				
			||||||
authors = [
 | 
					authors = [
 | 
				
			||||||
@@ -11,4 +11,4 @@ categories = [ "visualization" ]
 | 
				
			|||||||
license = "Apache-2.0"
 | 
					license = "Apache-2.0"
 | 
				
			||||||
description = "Drawing block circuits with Typst made easy, using CeTZ"
 | 
					description = "Drawing block circuits with Typst made easy, using CeTZ"
 | 
				
			||||||
keywords = [ "circuit", "block", "draw" ]
 | 
					keywords = [ "circuit", "block", "draw" ]
 | 
				
			||||||
exclude = [ "gallery", "gallery.bash", "doc" ]
 | 
					exclude = [ "gallery", "justfile", "doc" ]
 | 
				
			||||||