diff --git a/gallery/platypus.png b/gallery/platypus.png index 7e0a912..cd954d8 100644 Binary files a/gallery/platypus.png and b/gallery/platypus.png differ diff --git a/gallery/test.png b/gallery/test.png index c4e9480..2c5d0c6 100644 Binary files a/gallery/test.png and b/gallery/test.png differ diff --git a/gallery/test2.png b/gallery/test2.png index c4e9480..2c5d0c6 100644 Binary files a/gallery/test2.png and b/gallery/test2.png differ diff --git a/gallery/test3.png b/gallery/test3.png index 8af0ba5..ac6032a 100644 Binary files a/gallery/test3.png and b/gallery/test3.png differ diff --git a/gallery/test4.png b/gallery/test4.png index 4f36ffc..57ae67b 100644 Binary files a/gallery/test4.png and b/gallery/test4.png differ diff --git a/gallery/test5.png b/gallery/test5.png index e23a31f..ecb2eec 100644 Binary files a/gallery/test5.png and b/gallery/test5.png differ diff --git a/gallery/test6.png b/gallery/test6.png index 409663e..eb67604 100644 Binary files a/gallery/test6.png and b/gallery/test6.png differ diff --git a/gallery/test7.png b/gallery/test7.png index cfe3ee4..99d3505 100644 Binary files a/gallery/test7.png and b/gallery/test7.png differ diff --git a/gallery/test_yrel.png b/gallery/test_yrel.png new file mode 100644 index 0000000..8d902b5 Binary files /dev/null and b/gallery/test_yrel.png differ diff --git a/gallery/test_yrel.typ b/gallery/test_yrel.typ new file mode 100644 index 0000000..36a2cfb --- /dev/null +++ b/gallery/test_yrel.typ @@ -0,0 +1,46 @@ +// Demonstrates the `(rel: , to: )` shorthand for the `y` +// parameter (analogous to the existing shorthand for `x`). +#import "/src/lib.typ": circuit, element, util, wire + +#set page(width: auto, height: auto, margin: .5cm) + +#circuit({ + // Anchor block: absolute placement. + element.block( + x: 0, y: 0, w: 2, h: 1.5, + id: "A", + fill: util.colors.orange, + ports: (east: ((id: "out"),)) + ) + + // X relative, Y absolute (existing feature). + element.block( + x: (rel: 1, to: "A.east"), y: 0, w: 2, h: 1.5, + id: "B", + fill: util.colors.yellow, + ports: (west: ((id: "in"),), east: ((id: "out"),)) + ) + + // Both X and Y relative to another element's anchor (new feature). + element.block( + x: (rel: 1, to: "B.east"), + y: (rel: 2, to: "B.south-west"), + w: 2, h: 1.5, + id: "C", + fill: util.colors.green, + ports: (west: ((id: "in"),)) + ) + + // Y relative only, X absolute. + element.block( + x: 0, + y: (rel: -2.5, to: "A.south-west"), + w: 2, h: 1.5, + id: "D", + fill: util.colors.blue, + ports: (east: ((id: "out"),)) + ) + + wire.wire("wAB", ("A-port-out", "B-port-in")) + wire.wire("wBC", ("B-port-out", "C-port-in"), style: "zigzag") +}) diff --git a/manual.pdf b/manual.pdf index f4e1052..053db11 100644 Binary files a/manual.pdf and b/manual.pdf differ diff --git a/manual.typ b/manual.typ index ebe0c88..f1cfa11 100644 --- a/manual.typ +++ b/manual.typ @@ -1,4 +1,4 @@ -#import "@preview/tidy:0.4.1" +#import "@preview/tidy:0.4.3" #import "/src/cetz.typ": draw, canvas #import "src/lib.typ" #import "doc/examples.typ" diff --git a/src/cetz.typ b/src/cetz.typ index 980a619..adb796c 100644 --- a/src/cetz.typ +++ b/src/cetz.typ @@ -1 +1 @@ -#import "@preview/cetz:0.3.4": * \ No newline at end of file +#import "@preview/cetz:0.5.2": * \ No newline at end of file diff --git a/src/circuit.typ b/src/circuit.typ index f89389f..958fa6e 100644 --- a/src/circuit.typ +++ b/src/circuit.typ @@ -1,5 +1,5 @@ #import "/src/cetz.typ": canvas -#import "@preview/tidy:0.3.0" +#import "@preview/tidy:0.4.3" /// Draws a block circuit diagram /// diff --git a/src/elements/element.typ b/src/elements/element.typ index 09adfd3..92bc853 100644 --- a/src/elements/element.typ +++ b/src/elements/element.typ @@ -24,7 +24,9 @@ /// If it is a dictionary, it should be in the format `(rel: number, to: str)`, where `rel` is the offset and `to` the base anchor /// - y (number, dictionary): The y position (bottom-left corner). /// -/// If it is a dictionary, it should be in the format `(from: str, to: str)`, where `from` is the base anchor and `to` is the id of the port to align with the anchor +/// If it is a dictionary, it can take one of two forms: +/// - `(rel: number, to: str)`: `rel` is the y offset and `to` the base anchor (analogous to `x`) +/// - `(from: str, to: str)`: `from` is the base anchor and `to` is the id of the port to align with the anchor /// - w (number): Width of the element /// - h (number): Height of the element /// - name (none, str): Optional name of the block @@ -74,13 +76,12 @@ 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) + x = util.resolve-rel-coord(ctx, x, 0) } - if (type(y) == dictionary) { + if (type(y) == dictionary and "rel" in y) { + y = util.resolve-rel-coord(ctx, y, 1) + } else if (type(y) == dictionary) { let from = y.from let to = y.to let (to-side, i) = find-port(ports, to) diff --git a/src/elements/logic/gate.typ b/src/elements/logic/gate.typ index bd0fb23..5d9f4de 100644 --- a/src/elements/logic/gate.typ +++ b/src/elements/logic/gate.typ @@ -1,4 +1,5 @@ #import "/src/cetz.typ": draw, coordinate +#import "/src/util.typ" #import "../ports.typ": add-ports, add-port #import "../element.typ" @@ -49,13 +50,12 @@ 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) + x = util.resolve-rel-coord(ctx, x, 0) } - if (type(y) == dictionary) { + if (type(y) == dictionary and "rel" in y) { + y = util.resolve-rel-coord(ctx, y, 1) + } else if (type(y) == dictionary) { let from = y.from let to = y.to diff --git a/src/elements/logic/iec_gate.typ b/src/elements/logic/iec_gate.typ index 08d97f1..0656b05 100644 --- a/src/elements/logic/iec_gate.typ +++ b/src/elements/logic/iec_gate.typ @@ -1,4 +1,5 @@ #import "/src/cetz.typ": draw, coordinate +#import "/src/util.typ" #import "../ports.typ": add-ports, add-port #import "../element.typ" @@ -60,13 +61,12 @@ 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) + x = util.resolve-rel-coord(ctx, x, 0) } - if (type(y) == dictionary) { + if (type(y) == dictionary and "rel" in y) { + y = util.resolve-rel-coord(ctx, y, 1) + } else if (type(y) == dictionary) { let from = y.from let to = y.to diff --git a/src/util.typ b/src/util.typ index a444434..bee181f 100644 --- a/src/util.typ +++ b/src/util.typ @@ -1,3 +1,23 @@ +#import "cetz.typ": coordinate + +/// Resolves a relative coordinate shorthand of the form `(rel: , to: )` +/// to a single scalar along the requested axis. +/// +/// Used to implement the shared `(rel:, to:)` shorthand accepted by the `x` and +/// `y` parameters of `elmt`, `gate` and `iec-gate`. +/// +/// - ctx (dictionary): The current CeTZ context +/// - val (dictionary): A dictionary with fields `rel` (number) and `to` (anchor) +/// - axis (int): `0` for x, `1` for y +/// -> number +#let resolve-rel-coord(ctx, val, axis) = { + let offset = val.rel + let to = val.to + let delta = if axis == 0 { (offset, 0) } else { (0, offset) } + let (_, to-pos) = coordinate.resolve(ctx, (rel: delta, to: to)) + return to-pos.at(axis) +} + /// Predefined color palette /// #let w = 4 /// #box(width: 100%, align(center)[ @@ -73,4 +93,45 @@ #let valid-anchors = ( "center", "north", "east", "west", "south", "north-east", "north-west", "south-east", "south-west" -) \ No newline at end of file +) + +/// Normalizes a coordinate so that 2D points (x, y) are padded +/// to (x, y, 0) as required by CeTZ's coordinate resolver. +/// Also handles function-based and dictionary-based coordinates +/// recursively. +/// +/// - coord (any): A CeTZ-compatible coordinate +/// -> any +#let normalize-coord(coord) = { + if type(coord) == array { + if coord.len() == 2 { + let (a, b) = coord + // Raw 2D point: (x, y) -> (x, y, 0) + if type(a) in (int, float) and type(b) in (int, float) { + return (a, b, 0) + } + // Function-based coordinate: (fn, point) + if type(a) == function { + let orig = a + return (v => { + let result = orig(v) + if type(result) == array and result.len() == 2 { + let (rx, ry) = result + if type(rx) in (int, float) and type(ry) in (int, float) { + return (rx, ry, 0) + } + } + return result + }, b) + } + } + } + if type(coord) == dictionary { + let result = (:) + for (key, value) in coord { + result.insert(key, normalize-coord(value)) + } + return result + } + return coord +} \ No newline at end of file diff --git a/src/wire.typ b/src/wire.typ index cbf7c4b..f6fd158 100644 --- a/src/wire.typ +++ b/src/wire.typ @@ -1,5 +1,5 @@ #import "/src/cetz.typ": draw, coordinate -#import "util.typ": opposite-anchor +#import "util.typ": opposite-anchor, normalize-coord /// List of valid wire styles /// #examples.wires @@ -68,8 +68,8 @@ let (ctx, p0) = coordinate.resolve(ctx, start) let (ctx, p3) = coordinate.resolve(ctx, end) - p0 = (x: p0.first(), y: p0.last()) - p3 = (x: p3.first(), y: p3.last()) + p0 = (x: p0.first(), y: p0.at(1)) + p3 = (x: p3.first(), y: p3.at(1)) let dx1 = margin-start let dx2 = margin-end @@ -86,13 +86,13 @@ if side-end == "east" { dx2 *= -1 } - p1 = (p0.x + dx1, p0.y) - p2 = (p3.x - dx2, p0.y) + p1 = (p0.x + dx1, p0.y, 0) + p2 = (p3.x - dx2, p0.y, 0) let points = ( start, (horizontal: p1, vertical: ()), - (horizontal: (), vertical: (0, dodge-y)), + (horizontal: (), vertical: (0, dodge-y, 0)), (horizontal: p2, vertical: ()), (horizontal: (), vertical: end), end @@ -147,18 +147,18 @@ // points that are closest to the edge points - let p1 = (p0.x + dx1, p0.y + dy1) - let p5 = (p6.x + dx2, p6.y + dy2) + let p1 = (p0.x + dx1, p0.y + dy1, 0) + let p5 = (p6.x + dx2, p6.y + dy2, 0) // middle point let center_x = p0.x + box_width * center_horizontal / 100% let center_y = p0.y + box_height * center_vertical / 100% - let p3 = (center_x, center_y) + let p3 = (center_x, center_y, 0) // setting up the points for that touch the guides - let p2 = (0,0) - let p4 = (0,0) + let p2 = (0, 0, 0) + let p4 = (0, 0, 0) if side-start in ("north", "south") { p2 = (horizontal: p3, vertical: p1) } else { @@ -240,6 +240,9 @@ if pts.len() != 2 { panic("Wrong number of points (got " + str(pts.len()) + " instead of 2)") } + + // Normalize user-provided coordinates: (x, y) -> (x, y, 0) + let pts = pts.map(normalize-coord) let stroke = ( paint: color, diff --git a/typst.toml b/typst.toml index b83700a..fb35880 100644 --- a/typst.toml +++ b/typst.toml @@ -1,7 +1,7 @@ [package] name = "circuiteria" version = "0.2.1" -compiler = "0.13.1" +compiler = "0.15.0" repository = "https://git.kb28.ch/HEL/circuiteria" entrypoint = "src/lib.typ" authors = [