diff --git a/gallery/test3.pdf b/gallery/test3.pdf new file mode 100644 index 0000000..bed94cd Binary files /dev/null and b/gallery/test3.pdf differ diff --git a/gallery/test3.typ b/gallery/test3.typ new file mode 100644 index 0000000..f3d5c76 --- /dev/null +++ b/gallery/test3.typ @@ -0,0 +1,89 @@ +#import "@preview/cetz:0.2.2": draw +#import "../src/lib.typ": circuit, element, util, wire + +#set page(flipped: true) +#let debug = false + +#circuit({ + element.block( + x: 0, y: 0, w: 2, h: 3, id: "block", + name: "Test", + ports: ( + east: ( + (id: "out0"), + (id: "out1"), + (id: "out2"), + ) + ), + debug: ( + ports: debug + + ) + ) + element.gate-and( + x: 4, y: 0, w: 2, h: 2, id: "and1", debug: (ports: debug), + inverted: ("in1") + ) + element.gate-or( + x: 7, y: 0, w: 2, h: 2, id: "or1", debug: (ports: debug), + inverted: ("in0", "out") + ) + + wire.wire( + "w1", + ("block-port-out0", "and1-port-in0"), + style: "dodge", + dodge-y: 3, + dodge-margins: (20%, 20%) + ) + wire.wire( + "w2", + ("block-port-out1", "and1-port-in1"), + style: "zigzag" + ) + wire.wire( + "w3", + ("and1-port-out", "or1-port-in0") + ) + + element.gate-and( + x: 11, y: 0, w: 2, h: 2, id: "and2", inputs: 3, debug: (ports: debug), + inverted: ("in0", "in2") + ) + for i in range(3) { + wire.stub("and2-port-in"+str(i), "west") + } + + element.gate-xor( + x: 14, y: 0, w: 2, h: 2, id: "xor", debug: (ports: debug), + inverted: ("in1") + ) + + element.gate-buf( + x: 0, y: -3, w: 2, h: 2, id: "buf", debug: (ports: debug) + ) + element.gate-not( + x: 0, y: -6, w: 2, h: 2, id: "not", debug: (ports: debug) + ) + + element.gate-and( + x: 3, y: -3, w: 2, h: 2, id: "and", debug: (ports: debug) + ) + element.gate-nand( + x: 3, y: -6, w: 2, h: 2, id: "nand", debug: (ports: debug) + ) + + element.gate-or( + x: 6, y: -3, w: 2, h: 2, id: "or", debug: (ports: debug) + ) + element.gate-nor( + x: 6, y: -6, w: 2, h: 2, id: "nor", debug: (ports: debug) + ) + + element.gate-xor( + x: 9, y: -3, w: 2, h: 2, id: "xor", debug: (ports: debug) + ) + element.gate-xnor( + x: 9, y: -6, w: 2, h: 2, id: "xnor", debug: (ports: debug) + ) +}) \ No newline at end of file diff --git a/src/element.typ b/src/element.typ index 331ad83..aee6cd1 100644 --- a/src/element.typ +++ b/src/element.typ @@ -4,4 +4,12 @@ #import "elements/alu.typ": alu #import "elements/block.typ": block #import "elements/extender.typ": extender -#import "elements/multiplexer.typ": multiplexer \ No newline at end of file +#import "elements/multiplexer.typ": multiplexer + +#import "elements/logic/gate.typ": gate +#import "elements/logic/and.typ": gate-and, gate-nand +#import "elements/logic/or.typ": gate-or, gate-nor +#import "elements/logic/xor.typ": gate-xor, gate-xnor +#import "elements/logic/buf.typ": gate-buf, gate-not +/* +*/ \ No newline at end of file diff --git a/src/elements/element.typ b/src/elements/element.typ index 924d211..1822f93 100644 --- a/src/elements/element.typ +++ b/src/elements/element.typ @@ -38,7 +38,7 @@ /// - stroke (stroke): Border stroke /// - id (str): The block id (for future reference) /// - auto-ports (bool): Whether to use auto port placements or not. If false, `draw-shape` is responsible for adding the appropiate ports -/// - ports-y (array): Array of the ports y offsets (used with `auto-ports: false`) +/// - ports-y (dictionary): Dictionary of the ports y offsets (used with `auto-ports: false`) /// - debug (dictionary): Dictionary of debug options. /// /// Supported fields include: diff --git a/src/elements/logic/and.typ b/src/elements/logic/and.typ new file mode 100644 index 0000000..3df22a4 --- /dev/null +++ b/src/elements/logic/and.typ @@ -0,0 +1,85 @@ +#import "@preview/cetz:0.2.2": draw +#import "gate.typ" + +#let draw-shape(id, tl, tr, br, bl, fill, stroke) = { + let (x, y) = bl + let (width, height) = (tr.at(0) - x, tr.at(1) - y) + + let t = (x + width / 4, y + height) + let b = (x + width / 4, y) + + let f = draw.group(name: id, { + draw.merge-path( + inset: 0.5em, + fill: fill, + stroke: stroke, + name: id + "-path", + close: true, { + draw.line(bl, tl, t) + draw.bezier((), b, tr, br) + draw.line((), b) + } + ) + + draw.anchor("north", t) + draw.anchor("south", b) + }) + return (f, tl, tr, br, bl) +} + +#let gate-and( + x: none, + y: none, + w: none, + h: none, + inputs: 2, + fill: none, + stroke: black + 1pt, + id: "", + inverted: (), + debug: ( + ports: false + ) +) = { + gate.gate( + draw-shape: draw-shape, + x: x, + y: y, + w: w, + h: h, + inputs: inputs, + fill: fill, + stroke: stroke, + id: id, + inverted: inverted, + debug: debug + ) +} + +#let gate-nand( + x: none, + y: none, + w: none, + h: none, + inputs: 2, + fill: none, + stroke: black + 1pt, + id: "", + inverted: (), + debug: ( + ports: false + ) +) = { + gate-and( + x: x, + y: y, + w: w, + h: h, + inputs: inputs, + fill: fill, + stroke: stroke, + id: id, + inverted: inverted + ("out",), + debug: debug + ) +} \ No newline at end of file diff --git a/src/elements/logic/buf.typ b/src/elements/logic/buf.typ new file mode 100644 index 0000000..87d8dc9 --- /dev/null +++ b/src/elements/logic/buf.typ @@ -0,0 +1,81 @@ +#import "@preview/cetz:0.2.2": draw +#import "gate.typ" + +#let draw-shape(id, tl, tr, br, bl, fill, stroke) = { + let (x, y) = bl + let (width, height) = (tr.at(0) - x, tr.at(1) - y) + + let r = (x + width, y + height / 2) + + let f = draw.group(name: id, { + draw.merge-path( + inset: 0.5em, + fill: fill, + stroke: stroke, + name: id + "-path", + close: true, { + draw.line(bl, tl, r) + } + ) + + draw.anchor("north", (tl, 50%, r)) + draw.anchor("south", (bl, 50%, r)) + }) + return (f, tl, tr, br, bl) +} + +#let gate-buf( + x: none, + y: none, + w: none, + h: none, + inputs: 2, + fill: none, + stroke: black + 1pt, + id: "", + inverted: (), + debug: ( + ports: false + ) +) = { + gate.gate( + draw-shape: draw-shape, + x: x, + y: y, + w: w, + h: h, + inputs: inputs, + fill: fill, + stroke: stroke, + id: id, + inverted: inverted, + debug: debug + ) +} + +#let gate-not(x: none, + y: none, + w: none, + h: none, + inputs: 2, + fill: none, + stroke: black + 1pt, + id: "", + inverted: (), + debug: ( + ports: false + ) +) = { + gate-buf( + x: x, + y: y, + w: w, + h: h, + inputs: inputs, + fill: fill, + stroke: stroke, + id: id, + inverted: inverted + ("out",), + debug: debug + ) +} \ No newline at end of file diff --git a/src/elements/logic/gate.typ b/src/elements/logic/gate.typ new file mode 100644 index 0000000..5fb282a --- /dev/null +++ b/src/elements/logic/gate.typ @@ -0,0 +1,127 @@ +#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) +} + +#let gate( + draw-shape: default-draw-shape, + x: none, + y: none, + w: none, + h: none, + inputs: 2, + fill: none, + stroke: black + 1pt, + id: "", + inverted: (), + 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) + { + //draw.rect(tl, br) + func + } + + let space = 100% / (inputs + 1) + for i in range(inputs) { + let pct = (i + 1) * 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: .2, anchor: "east", stroke: stroke) + port-pos = (rel: (-.4, 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: .2, anchor: "west", stroke: stroke) + out-pos = (rel: (.4, 0), to: out-pos) + } + add-port( + id, "east", + (id: "out"), out-pos, + debug: debug.ports + ) +}) +/* { + let ports = (west: (), east: ((id: "out"),)) + for i in range(inputs) { + ports.west.push((id: "in"+str(i))) + } + + element.elmt( + draw-shape: draw-shape, + x: x, + y: y, + w: w, + h: h, + ports: ports, + fill: fill, + stroke: stroke, + id: id, + auto-ports: true, + debug: debug + ) +} \ No newline at end of file diff --git a/src/elements/logic/or.typ b/src/elements/logic/or.typ new file mode 100644 index 0000000..4f44fb7 --- /dev/null +++ b/src/elements/logic/or.typ @@ -0,0 +1,96 @@ +#import "@preview/cetz:0.2.2": draw +#import "gate.typ" + +#let draw-shape(id, tl, tr, br, bl, fill, stroke) = { + let (x, y) = bl + let (width, height) = (tr.at(0) - x, tr.at(1) - y) + + let t = (x + width / 2, y + height) + let b = (x + width / 2, y) + + let ctrl-bl = (x + width / 2, y) + let ctrl-br = (x + width * 0.8, y + height * 0.1) + let ctrl-tl = (x + width / 2, y + height) + let ctrl-tr = (x + width * 0.8, y + height * 0.9) + + let l = (x + width * 0.2, y + height / 2) + let r = (x + width, y + height / 2) + + let f = draw.group(name: id, { + draw.merge-path( + inset: 0.5em, + fill: fill, + stroke: stroke, + name: id + "-path", { + draw.bezier-through(bl, l, tl) + draw.bezier((), r, ctrl-tl, ctrl-tr) + draw.bezier((), bl, ctrl-br, ctrl-bl) + } + ) + + draw.intersections("i", + id + "-path", + draw.hide(draw.line(t, b)) + ) + draw.anchor("north", "i.0") + draw.anchor("south", "i.1") + }) + return (f, tl, tr, br, bl) +} + +#let gate-or( + x: none, + y: none, + w: none, + h: none, + inputs: 2, + fill: none, + stroke: black + 1pt, + id: "", + inverted: (), + debug: ( + ports: false + ) +) = { + gate.gate( + draw-shape: draw-shape, + x: x, + y: y, + w: w, + h: h, + inputs: inputs, + fill: fill, + stroke: stroke, + id: id, + inverted: inverted, + debug: debug + ) +} + +#let gate-nor( + x: none, + y: none, + w: none, + h: none, + inputs: 2, + fill: none, + stroke: black + 1pt, + id: "", + inverted: (), + debug: ( + ports: false + ) +) = { + gate-or( + x: x, + y: y, + w: w, + h: h, + inputs: inputs, + fill: fill, + stroke: stroke, + id: id, + inverted: inverted + ("out",), + debug: debug + ) +} \ No newline at end of file diff --git a/src/elements/logic/xor.typ b/src/elements/logic/xor.typ new file mode 100644 index 0000000..3b4eb70 --- /dev/null +++ b/src/elements/logic/xor.typ @@ -0,0 +1,103 @@ +#import "@preview/cetz:0.2.2": draw +#import "gate.typ" + +#let space = 10% + +#let draw-shape(id, tl, tr, br, bl, fill, stroke) = { + let (x, y) = bl + let (width, height) = (tr.at(0) - x, tr.at(1) - y) + + let tl2 = (tl, space, tr) + let bl2 = (bl, space, br) + + let t = (x + width / 2, y + height) + let b = (x + width / 2, y) + + let ctrl-bl = (x + width / 2, y) + let ctrl-br = (x + width * 0.8, y + height * 0.1) + let ctrl-tl = (x + width / 2, y + height) + let ctrl-tr = (x + width * 0.8, y + height * 0.9) + + let l = (x + width * 0.2, y + height / 2) + let l2 = (x + width * (0.2 + space / 100%), y + height / 2) + let r = (x + width, y + height / 2) + + let f = draw.group(name: id, { + draw.bezier-through(bl, l, tl, stroke: stroke) + draw.merge-path( + inset: 0.5em, + fill: fill, + stroke: stroke, + name: id + "-path", { + draw.bezier-through(bl2, l2, tl2) + draw.bezier((), r, ctrl-tl, ctrl-tr) + draw.bezier((), bl2, ctrl-br, ctrl-bl) + } + ) + + draw.intersections("i", + id + "-path", + draw.hide(draw.line(t, b)) + ) + draw.anchor("north", "i.0") + draw.anchor("south", "i.1") + }) + return (f, tl, tr, br, bl) +} + +#let gate-xor( + x: none, + y: none, + w: none, + h: none, + inputs: 2, + fill: none, + stroke: black + 1pt, + id: "", + inverted: (), + debug: ( + ports: false + ) +) = { + gate.gate( + draw-shape: draw-shape, + x: x, + y: y, + w: w, + h: h, + inputs: inputs, + fill: fill, + stroke: stroke, + id: id, + inverted: inverted, + debug: debug + ) +} + +#let gate-xnor( + x: none, + y: none, + w: none, + h: none, + inputs: 2, + fill: none, + stroke: black + 1pt, + id: "", + inverted: (), + debug: ( + ports: false + ) +) = { + gate-xor( + x: x, + y: y, + w: w, + h: h, + inputs: inputs, + fill: fill, + stroke: stroke, + id: id, + inverted: inverted + ("out",), + debug: debug + ) +} \ No newline at end of file diff --git a/src/elements/ports.typ b/src/elements/ports.typ index 602d0f5..51bf409 100644 --- a/src/elements/ports.typ +++ b/src/elements/ports.typ @@ -68,6 +68,10 @@ "west": (tl, bl) ) + if type(ports) != dictionary { + return + } + for (side, props) in sides { let side-ports = ports.at(side, default: ()) let space = 100% / (side-ports.len() + 1) diff --git a/src/lib.typ b/src/lib.typ index e811c6d..d891428 100644 --- a/src/lib.typ +++ b/src/lib.typ @@ -1,4 +1,4 @@ -#let version = version((0,0,1)) +#let version = version((0,0,2)) #import "circuit.typ": circuit #import "element.typ" diff --git a/typst.toml b/typst.toml index 88e1ea5..55e8fb2 100644 --- a/typst.toml +++ b/typst.toml @@ -1,6 +1,6 @@ [package] name = "circuiteria" -version = "0.0.1" +version = "0.0.2" compiler = "0.11.0" repository = "https://git.kb28.ch/HEL/circuiteria" entrypoint = "src/lib.typ"