forked from HEL/circuiteria
179 lines
4.2 KiB
Typst
179 lines
4.2 KiB
Typst
#import "/src/cetz.typ": draw
|
|
#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(
|
|
elmt-id, side, port, pos,
|
|
prev: none,
|
|
next: none,
|
|
debug: false
|
|
) = {
|
|
let name = port.at("name", default: "")
|
|
let name-rotate = port.at("vertical", default: false)
|
|
|
|
if (port.at("clock", default: false)) {
|
|
if prev == none or next == none {
|
|
panic("Clock port must have previous and next positions")
|
|
}
|
|
|
|
let size = if port.at("small", default: false) {8pt} else {1em}
|
|
let offset
|
|
if (side == "north") { offset = ( 0, -size) }
|
|
else if (side == "east") { offset = (-size, 0) }
|
|
else if (side == "south") { offset = ( 0, size) }
|
|
else if (side == "west") { offset = ( size, 0) }
|
|
|
|
let pos1 = (rel: offset, to: pos)
|
|
|
|
// TODO: use context or vectors to have the height relative to the width
|
|
draw.line(prev, pos1, next)
|
|
}
|
|
draw.content(
|
|
pos,
|
|
anchor: if name-rotate {rotate-anchor(side)} else {side},
|
|
padding: 2pt,
|
|
angle: if name-rotate {90deg} else {0deg},
|
|
name
|
|
)
|
|
|
|
if debug {
|
|
draw.circle(
|
|
pos,
|
|
radius: .1,
|
|
stroke: none,
|
|
fill: red
|
|
)
|
|
|
|
} else {
|
|
draw.hide(draw.circle(
|
|
pos,
|
|
radius: 0,
|
|
stroke: none
|
|
))
|
|
}
|
|
draw.anchor(port.id, pos)
|
|
}
|
|
|
|
#let add-ports(
|
|
elmt,
|
|
bounds
|
|
) = {
|
|
let debug = elmt.debug.at("ports", default: false)
|
|
|
|
if type(elmt.ports) != dictionary {
|
|
return
|
|
}
|
|
|
|
for (side, props) in bounds.ports {
|
|
let side-ports = elmt.ports.at(side, default: ())
|
|
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() {
|
|
let offset = get-port-pos(elmt, bounds, side, port.id, i)
|
|
|
|
let pos = (pt0, offset, pt1)
|
|
let offset-prev = if type(offset) == ratio {
|
|
offset - space / 2
|
|
} else {
|
|
offset - space * side-len / 200%
|
|
}
|
|
let offset-next = if type(offset) == ratio {
|
|
offset + space / 2
|
|
} else {
|
|
offset + space * side-len / 200%
|
|
}
|
|
let pos-prev = (pt0, offset-prev, pt1)
|
|
let pos-next = (pt0, offset-next, pt1)
|
|
|
|
if port.at("small", default: false) {
|
|
pos-prev = (pos, 4pt, pt0)
|
|
pos-next = (pos, 4pt, pt1)
|
|
}
|
|
|
|
add-port(
|
|
elmt.id,
|
|
side,
|
|
port,
|
|
pos,
|
|
prev: pos-prev,
|
|
next: pos-next,
|
|
debug: debug
|
|
)
|
|
}
|
|
}
|
|
} |