diff --git a/gallery/target_api.pdf b/gallery/target_api.pdf index 840b756..d1a9600 100644 Binary files a/gallery/target_api.pdf and b/gallery/target_api.pdf differ diff --git a/gallery/target_api.typ b/gallery/target_api.typ index 6b810ef..b856434 100644 --- a/gallery/target_api.typ +++ b/gallery/target_api.typ @@ -17,7 +17,7 @@ wire.stub("PCBuf.CLK", name: "CLK") wire.stub("PCBuf.EN", name: "PCWrite") - /* + element.multiplexer( pos: ( 3, (align: "in0", with: "PCBuf.PC") @@ -25,9 +25,10 @@ size: (1, 2), id: "AdrSrc-MP", fill: util.colors.orange, - entries: 2 + entries: 2, + debug: (ports: true) ) - wire.wire( + /*wire.wire( "PCBuf.PC", "AdrSrc-MP.in0", id: "wPCBuf-InstDataMgr", diff --git a/src/elements/block.typ b/src/elements/block.typ index 58a1b6f..8df321d 100644 --- a/src/elements/block.typ +++ b/src/elements/block.typ @@ -19,6 +19,7 @@ #let block( ..args ) = element.elmt( + cls: "block", draw-shape: draw-shape, ..args ) \ No newline at end of file diff --git a/src/elements/element.typ b/src/elements/element.typ index d83c4b5..d38a824 100644 --- a/src/elements/element.typ +++ b/src/elements/element.typ @@ -1,5 +1,5 @@ #import "@preview/cetz:0.3.2": 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" #let find-port(ports, id) = { @@ -10,7 +10,7 @@ } } } - panic("Could not find port with id " + str(id)) + panic("Could not find port with id '" + str(id) + "'") } #let local-to-global(origin, u, v, points) = { @@ -42,7 +42,7 @@ return pos.at(axis) } -#let resolve-align(ctx, elmt, align, with, 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 { @@ -67,12 +67,12 @@ 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) - - if not elmt.auto-ports { + //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(with)(len) - } + dl = elmt.ports-pos.at(align)(len) + }*/ } else if align-side == ortho-sides.first() { dl = 0 start-margin = 0 @@ -89,7 +89,7 @@ return with-pos.at(axis) - dl + start-margin } -#let resolve-coordinate(ctx, elmt, coord, axis) = { +#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) @@ -105,7 +105,7 @@ return resolve-offset(ctx, offset, from, axis) } else if none not in (align, with) { - return resolve-align(ctx, elmt, align, with, axis) + return resolve-align(ctx, elmt, bounds, align, with, axis) } else { panic("Dictionnary must either provide both 'offset' and 'from', or 'align' and 'with'") } @@ -116,11 +116,11 @@ return coord } -#let make-bounds(x, y, w, h) = { +#let make-bounds(elmt, x, y, w, h) = { let w2 = w / 2 let h2 = h / 2 - return ( + let bounds = ( bl: (x, y), tl: (x, y + h), tr: (x + w, y + h), @@ -131,6 +131,33 @@ l: (x, y + h2), r: (x + w, y + h2), ) + bounds += ( + 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 = ports-margins.at(side) + a = (pt0, margins.at(0), pt1) + b = (pt0, 100% - margins.at(1), pt1) + props2 = (a, b) + } + bounds.ports.insert(side, props2) + } + return bounds } #let render(draw-shape, elmt) = draw.group(name: elmt.id, ctx => { @@ -140,10 +167,10 @@ let x = elmt.pos.first() let y = elmt.pos.last() - x = resolve-coordinate(ctx, elmt, x, 0) - y = resolve-coordinate(ctx, elmt, y, 1) - - let bounds = make-bounds(x, y, width, height) + 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 = {} @@ -171,15 +198,7 @@ ) } - if elmt.auto-ports { - add-ports( - elmt.id, - bounds, - elmt.ports, - elmt.ports-margins, - debug: elmt.debug.ports - ) - } + add-ports(elmt, bounds) }) /// Draws an element @@ -210,6 +229,7 @@ /// Supported fields include: /// - `ports`: if true, shows dots on all ports of the element #let elmt( + cls: "element", draw-shape: default-draw-shape, pre-process: default-pre-process, pos: (0, 0), @@ -221,11 +241,11 @@ fill: none, stroke: black + 1pt, id: auto, - auto-ports: true, - ports-y: (:), + ports-pos: auto, debug: ( ports: false - ) + ), + extra: (:) ) = { for (key, side-ports) in ports.pairs() { if type(side-ports) == str { @@ -249,6 +269,7 @@ return (( + cls: cls, id: id, draw: render.with(draw-shape), pre-process: pre-process, @@ -260,8 +281,7 @@ ports-margins: ports-margins, fill: fill, stroke: stroke, - auto-ports: auto-ports, - ports-y: ports-y, + ports-pos: ports-pos, debug: debug - ),) + ) + extra,) } diff --git a/src/elements/multiplexer.typ b/src/elements/multiplexer.typ index d7c29eb..8e55173 100644 --- a/src/elements/multiplexer.typ +++ b/src/elements/multiplexer.typ @@ -3,29 +3,29 @@ #import "element.typ" #import "ports.typ": add-port -#let draw-shape(id, tl, tr, br, bl, fill, stroke, h-ratio: 60%) = { - let margin = (100% - h-ratio) / 2 - let tr2 = (tr, margin, br) - let br2 = (br, margin, tr) - let f = draw.group(name: id, { +#let draw-shape(elmt, bounds) = { + let margin = (100% - elmt.l-ratio) / 2 + let tr2 = (bounds.tr, margin, bounds.br) + let br2 = (bounds.br, margin, bounds.tr) + let f = draw.group(name: elmt.id, { draw.merge-path( inset: 0.5em, - fill: fill, - stroke: stroke, + fill: elmt.fill, + stroke: elmt.stroke, close: true, - draw.line(tl, tr2, br2, bl) + draw.line(bounds.tl, tr2, br2, bounds.bl) ) - draw.anchor("north", (tl, 50%, tr2)) - draw.anchor("south", (bl, 50%, br2)) - draw.anchor("west", (tl, 50%, bl)) + draw.anchor("north", (bounds.tl, 50%, tr2)) + draw.anchor("south", (bounds.bl, 50%, br2)) + draw.anchor("west", (bounds.tl, 50%, bounds.bl)) draw.anchor("east", (tr2, 50%, br2)) - draw.anchor("north-west", tl) + draw.anchor("north-west", bounds.tl) draw.anchor("north-east", tr2) draw.anchor("south-east", br2) - draw.anchor("south-west", bl) + draw.anchor("south-west", bounds.bl) }) - return (f, tl, tr, br, bl) + return (f, bounds) } /// Draws a multiplexer @@ -35,64 +35,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. /// - h-ratio (ratio): The height ratio of the right side relative to the full height #let multiplexer( - x: none, - y: none, - w: none, - h: none, - name: none, - name-anchor: "center", entries: 2, - h-ratio: 60%, - fill: none, - stroke: black + 1pt, - id: "", - debug: ( - ports: false - ) + l-ratio: 60%, + ..args ) = { - let ports = () - let ports-y = ( - out: (h) => {h * 0.5} + let in-ports = () + let ports-pos = ( + "east": auto, ) if (type(entries) == int) { let nbits = calc.ceil(calc.log(entries, base: 2)) for i in range(entries) { 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 { 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 l = ports.len() - for (i, port) in ports.enumerate() { - ports-y.insert(port.id, (h) => {h * (i + 0.5) / l}) - } + let n = in-ports.len() + ports-pos.insert("west", (l, i) => {l * (i + 0.5) / n}) element.elmt( - draw-shape: draw-shape.with(h-ratio: h-ratio), - x: x, - y: y, - w: w, - h: h, - name: name, - 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 + cls: "multiplexer", + draw-shape: draw-shape, + ports: (west: in-ports, east: ((id: "out"),)), + ports-pos: ports-pos, + extra: (l-ratio: l-ratio), + ..args ) + /* for (i, port) in ports.enumerate() { let pct = (i + 0.5) * space 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")) + */ } \ No newline at end of file diff --git a/src/elements/ports.typ b/src/elements/ports.typ index 66d1f0c..eecd300 100644 --- a/src/elements/ports.typ +++ b/src/elements/ports.typ @@ -1,6 +1,74 @@ #import "@preview/cetz:0.3.2": 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 " + element.id) +} + +#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, @@ -54,46 +122,41 @@ } #let add-ports( - elmt-id, - bounds, - ports, - ports-margins, - debug: false + elmt, + bounds ) = { - let sides = ( - "north": (bounds.tl, bounds.tr), - "east": (bounds.tr, bounds.br), - "south": (bounds.bl, bounds.br), - "west": (bounds.tl, bounds.bl) - ) + let debug = elmt.debug.ports - if type(ports) != dictionary { + if type(elmt.ports) != dictionary { return } - for (side, props) in sides { - let side-ports = ports.at(side, default: ()) + 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 pct = (i + 1) * space - let pt0 = props.at(0) - let pt1 = props.at(1) + let offset = get-port-pos(elmt, bounds, side, port, i) - if side in ports-margins { - let (a, b) = (pt0, pt1) - let margins = ports-margins.at(side) - a = (pt0, margins.at(0), pt1) - b = (pt0, 100% - margins.at(1), pt1) - pt0 = a - pt1 = b + let pos = (pt0, offset, pt1) + let offset-prev = if type(offset) == ratio { + offset - space / 2 + } else { + offset - space * side-len / 200% } - - let pos = (pt0, pct, pt1) - let pct-prev = (i + 0.5) * space - let pct-next = (i + 1.5) * space - let pos-prev = (pt0, pct-prev, pt1) - let pos-next = (pt0, pct-next, pt1) + 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) @@ -101,7 +164,7 @@ } add-port( - elmt-id, + elmt.id, side, port, pos, diff --git a/src/util.typ b/src/util.typ index 037ae67..a444434 100644 --- a/src/util.typ +++ b/src/util.typ @@ -73,15 +73,4 @@ #let valid-anchors = ( "center", "north", "east", "west", "south", "north-east", "north-west", "south-east", "south-west" -) - -#let get-port-side(element, port) = { - for (side, ports) in element.ports { - for p in ports { - if p.id == port { - return side - } - } - } - panic("Unknown port " + port + " on element " + element.id) -} \ No newline at end of file +) \ No newline at end of file diff --git a/src/wire.typ b/src/wire.typ index 661faed..899e4c1 100644 --- a/src/wire.typ +++ b/src/wire.typ @@ -1,5 +1,6 @@ #import "@preview/cetz:0.3.2": draw, coordinate -#import "util.typ": opposite-anchor, get-port-side +#import "util.typ": opposite-anchor +#import "elements/ports.typ": get-port-side #import "elements/element.typ" /// List of valid wire styles