forked from HEL/circuiteria
		
	initial rewrite of drawing pipeline + positioning
This commit is contained in:
		
							
								
								
									
										
											BIN
										
									
								
								gallery/target_api.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gallery/target_api.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										297
									
								
								gallery/target_api.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										297
									
								
								gallery/target_api.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,297 @@ | |||||||
|  | #import "../src/lib.typ": * | ||||||
|  | #import "@preview/cetz:0.3.4": draw | ||||||
|  | #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"),) | ||||||
|  |     ), | ||||||
|  |     debug: (ports: true) | ||||||
|  |   ) | ||||||
|  |  | ||||||
|  |   element.block( | ||||||
|  |     size: (1, 2), | ||||||
|  |     ports: ( | ||||||
|  |       west: (("a", "A"), "e"), | ||||||
|  |       north: "b", | ||||||
|  |       east: "c", | ||||||
|  |       south: "d" | ||||||
|  |     ), | ||||||
|  |     pos: ( | ||||||
|  |       (offset: -1, from: "PCBuf.south"), | ||||||
|  |       2,//(align: "e", with: "PCBuf.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", 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 | ||||||
|  |   ) | ||||||
|  |  | ||||||
|  |   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", 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) | ||||||
|  |   */ | ||||||
|  | }) | ||||||
| @@ -9,6 +9,38 @@ | |||||||
| /// - 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 = (:) | ||||||
|  |  | ||||||
|  |   for element in body { | ||||||
|  |     let internal = type(element) == dictionary and "id" in element | ||||||
|  |     let eid = if internal {element.id} else {none} | ||||||
|  |     if eid == none { | ||||||
|  |       while str(next-id) in elements { | ||||||
|  |         next-id += 1 | ||||||
|  |       } | ||||||
|  |       eid = str(next-id) | ||||||
|  |       if internal { | ||||||
|  |         element.id = eid | ||||||
|  |       } | ||||||
|  |       next-id += 1 | ||||||
|  |     } | ||||||
|  |     elements.insert(eid, element) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   for element in elements.values() { | ||||||
|  |     if type(element) == dictionary and "pre-process" in element { | ||||||
|  |       elements = (element.pre-process)(elements, element) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   canvas(length: length, { | ||||||
|  |     for element in elements.values() { | ||||||
|  |       if type(element) == dictionary and "draw" in element { | ||||||
|  |         (element.draw)(element) | ||||||
|  |       } else { | ||||||
|  |         (element,) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }) | ||||||
| } | } | ||||||
| @@ -1,16 +1,15 @@ | |||||||
| #import "@preview/cetz:0.3.2": draw | #import "@preview/cetz:0.3.2": 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,8 @@ | |||||||
| /// #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( | ||||||
|   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,4 +1,4 @@ | |||||||
| #import "@preview/cetz:0.3.2": draw, coordinate | #import "@preview/cetz:0.3.2": draw, coordinate, matrix, vector | ||||||
| #import "ports.typ": add-ports, add-port | #import "ports.typ": add-ports, add-port | ||||||
| #import "../util.typ" | #import "../util.typ" | ||||||
|  |  | ||||||
| @@ -13,10 +13,163 @@ | |||||||
|   panic("Could not find port with id " + str(id)) |   panic("Could not find port with id " + str(id)) | ||||||
| } | } | ||||||
|  |  | ||||||
| #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, 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) | ||||||
|  |  | ||||||
|  |     if not elmt.auto-ports { | ||||||
|  |       start-margin = 0 | ||||||
|  |       dl = elmt.ports-pos.at(with)(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, 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, 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 make-bounds(x, y, w, h) = { | ||||||
|  |   let w2 = w / 2 | ||||||
|  |   let h2 = h / 2 | ||||||
|  |    | ||||||
|  |   return ( | ||||||
|  |     bl: (x, y), | ||||||
|  |     tl: (x, y + h), | ||||||
|  |     tr: (x + w, y + h), | ||||||
|  |     br: (x + w, y), | ||||||
|  |     center: (x + w2, y + h2), | ||||||
|  |     b: (x + w2, y), | ||||||
|  |     t: (x + w2, y + h), | ||||||
|  |     l: (x, y + h2), | ||||||
|  |     r: (x + w, y + h2), | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #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() | ||||||
|  |  | ||||||
|  |   x = resolve-coordinate(ctx, elmt, x, 0) | ||||||
|  |   y = resolve-coordinate(ctx, elmt, y, 1) | ||||||
|  |  | ||||||
|  |   let bounds = make-bounds(x, y, width, height) | ||||||
|  |  | ||||||
|  |   // Workaround because CeTZ needs to have all draw functions in the body | ||||||
|  |   let func = {} | ||||||
|  |   (func, bounds) = draw-shape(elmt, bounds) | ||||||
|  |   func | ||||||
|  |  | ||||||
|  |   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*] | ||||||
|  |     ) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if elmt.auto-ports { | ||||||
|  |     add-ports( | ||||||
|  |       elmt.id, | ||||||
|  |       bounds, | ||||||
|  |       elmt.ports, | ||||||
|  |       elmt.ports-margins, | ||||||
|  |       debug: elmt.debug.ports | ||||||
|  |     ) | ||||||
|  |   } | ||||||
|  | }) | ||||||
|  |  | ||||||
| /// 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). | ||||||
| @@ -46,10 +199,9 @@ | |||||||
| ///     - `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( | ||||||
|   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: (:), | ||||||
| @@ -62,84 +214,42 @@ | |||||||
|   debug: ( |   debug: ( | ||||||
|     ports: false |     ports: false | ||||||
|   ) |   ) | ||||||
| ) = draw.get-ctx(ctx => { | ) = { | ||||||
|   let width = w |   for (key, side-ports) in ports.pairs() { | ||||||
|   let height = h |     if type(side-ports) == str { | ||||||
|  |       side-ports = ((id: side-ports),) | ||||||
|   let x = x |     } else if type(side-ports) == dictionary { | ||||||
|   let y = y |       side-ports = (side-ports,) | ||||||
|   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 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) |  | ||||||
|   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 |   return (( | ||||||
|   let func = {} |     id: id, | ||||||
|   (func, tl, tr, br, bl) = draw-shape(id, tl, tr, br, bl, fill, stroke) |     draw: render.with(draw-shape), | ||||||
|   func |     pre-process: pre-process, | ||||||
|  |     pos: pos, | ||||||
|   if (name != none) { |     size: size, | ||||||
|     draw.content( |     name: name, | ||||||
|       (name: id, anchor: name-anchor), |     name-anchor: name-anchor, | ||||||
|       anchor: if name-anchor in util.valid-anchors {name-anchor} else {"center"}, |     ports: ports, | ||||||
|       padding: 0.5em, |     ports-margins: ports-margins, | ||||||
|       align(center)[*#name*] |     fill: fill, | ||||||
|     ) |     stroke: stroke, | ||||||
|   } |     auto-ports: auto-ports, | ||||||
|  |     ports-y: ports-y, | ||||||
|   if auto-ports { |     debug: debug | ||||||
|     add-ports( |   ),) | ||||||
|       id, | } | ||||||
|       tl, tr, br, bl, |  | ||||||
|       ports, |  | ||||||
|       ports-margins, |  | ||||||
|       debug: debug.ports |  | ||||||
|     ) |  | ||||||
|   } |  | ||||||
| }) |  | ||||||
|   | |||||||
| @@ -34,12 +34,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,24 +47,24 @@ | |||||||
|     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-id, | ||||||
|   tl, tr, br, bl, |   bounds, | ||||||
|   ports, |   ports, | ||||||
|   ports-margins, |   ports-margins, | ||||||
|   debug: false |   debug: false | ||||||
| ) = { | ) = { | ||||||
|   let sides = ( |   let sides = ( | ||||||
|     "north": (tl, tr), |     "north": (bounds.tl, bounds.tr), | ||||||
|     "east": (tr, br), |     "east": (bounds.tr, bounds.br), | ||||||
|     "south": (bl, br), |     "south": (bounds.bl, bounds.br), | ||||||
|     "west": (tl, bl) |     "west": (bounds.tl, bounds.bl) | ||||||
|   ) |   ) | ||||||
|  |  | ||||||
|   if type(ports) != dictionary { |   if type(ports) != dictionary { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user