forked from HEL/circuiteria
120 lines
3.4 KiB
Typst
120 lines
3.4 KiB
Typst
#import "@preview/cetz:0.2.2": draw, coordinate
|
|
#import "../ports.typ": add-ports, add-port
|
|
#import "../element.typ"
|
|
|
|
#let default-draw-shape(id, tl, tr, br, bl, fill, stroke) = {
|
|
let f = {draw.rect(tl, br, fill: fill, stroke: stroke)}
|
|
return (f, tl, tr, br, bl)
|
|
}
|
|
|
|
|
|
/// Draws a logic gate. This function is also available as `element.gate()`
|
|
///
|
|
/// - draw-shape (function): see #doc-ref("element.elmt")
|
|
/// - x (number, dictionary): see #doc-ref("element.elmt")
|
|
/// - y (number, dictionary): see #doc-ref("element.elmt")
|
|
/// - w (number): see #doc-ref("element.elmt")
|
|
/// - h (number): see #doc-ref("element.elmt")
|
|
/// - inputs (int): The number of inputs
|
|
/// - fill (none, color): see #doc-ref("element.elmt")
|
|
/// - stroke (stroke): see #doc-ref("element.elmt")
|
|
/// - id (str): see #doc-ref("element.elmt")
|
|
/// - inverted (str, array): Either "all" or an array of port ids to display as inverted
|
|
/// - inverted-radius (number): The radius of inverted ports dot
|
|
/// - debug (dictionary): see #doc-ref("element.elmt")
|
|
#let gate(
|
|
draw-shape: default-draw-shape,
|
|
x: none,
|
|
y: none,
|
|
w: none,
|
|
h: none,
|
|
inputs: 2,
|
|
fill: none,
|
|
stroke: black + 1pt,
|
|
id: "",
|
|
inverted: (),
|
|
inverted-radius: 0.2,
|
|
debug: (
|
|
ports: false
|
|
)
|
|
) = draw.get-ctx(ctx => {
|
|
let width = w
|
|
let height = h
|
|
|
|
let x = x
|
|
let y = y
|
|
if x == none { panic("Parameter x must be set") }
|
|
if y == none { panic("Parameter y must be set") }
|
|
if w == none { panic("Parameter w must be set") }
|
|
if h == none { panic("Parameter h must be set") }
|
|
|
|
if (type(x) == dictionary) {
|
|
let offset = x.rel
|
|
let to = x.to
|
|
let (ctx, to-pos) = coordinate.resolve(ctx, (rel: (offset, 0), to: to))
|
|
x = to-pos.at(0)
|
|
}
|
|
|
|
if (type(y) == dictionary) {
|
|
let from = y.from
|
|
let to = y.to
|
|
let (to-side, i) = find-port(ports, to)
|
|
let margins = (0%, 0%)
|
|
if to-side in ports-margins {
|
|
margins = ports-margins.at(to-side)
|
|
}
|
|
let used-pct = 100% - margins.at(0) - margins.at(1)
|
|
let used-height = height * used-pct / 100%
|
|
let top-margin = height * margins.at(0) / 100%
|
|
|
|
let dy = used-height * (i + 1) / (ports.at(to-side).len() + 1)
|
|
|
|
let (ctx, from-pos) = coordinate.resolve(ctx, from)
|
|
y = from-pos.at(1) + dy - height + top-margin
|
|
}
|
|
|
|
let tl = (x, y + height)
|
|
let tr = (x + width, y + height)
|
|
let br = (x + width, y)
|
|
let bl = (x, y)
|
|
|
|
// Workaround because CeTZ needs to have all draw functions in the body
|
|
let func = {}
|
|
(func, tl, tr, br, bl) = draw-shape(id, tl, tr, br, bl, fill, stroke)
|
|
func
|
|
|
|
let space = 100% / inputs
|
|
for i in range(inputs) {
|
|
let pct = (i + 0.5) * space
|
|
let a = (tl, pct, bl)
|
|
let b = (tr, pct, br)
|
|
let int-name = id + "i" + str(i)
|
|
draw.intersections(
|
|
int-name,
|
|
func,
|
|
draw.hide(draw.line(a, b))
|
|
)
|
|
let port-name = "in" + str(i)
|
|
let port-pos = int-name + ".0"
|
|
if inverted == "all" or port-name in inverted {
|
|
draw.circle(port-pos, radius: inverted-radius, anchor: "east", stroke: stroke)
|
|
port-pos = (rel: (-2 * inverted-radius, 0), to: port-pos)
|
|
}
|
|
add-port(
|
|
id, "west",
|
|
(id: port-name), port-pos,
|
|
debug: debug.ports
|
|
)
|
|
}
|
|
|
|
let out-pos = id + ".east"
|
|
if inverted == "all" or "out" in inverted {
|
|
draw.circle(out-pos, radius: inverted-radius, anchor: "west", stroke: stroke)
|
|
out-pos = (rel: (2 * inverted-radius, 0), to: out-pos)
|
|
}
|
|
add-port(
|
|
id, "east",
|
|
(id: "out"), out-pos,
|
|
debug: debug.ports
|
|
)
|
|
}) |