diff --git a/packages/preview/zap/0.6.0/LICENSE b/packages/preview/zap/0.6.0/LICENSE new file mode 100644 index 0000000000..5f740bf37b --- /dev/null +++ b/packages/preview/zap/0.6.0/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Louis Grange + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/packages/preview/zap/0.6.0/README.md b/packages/preview/zap/0.6.0/README.md new file mode 100644 index 0000000000..86e38ef329 --- /dev/null +++ b/packages/preview/zap/0.6.0/README.md @@ -0,0 +1,47 @@ +# Zap for Typst + +**Zap** โšก is a Typst package that makes drawing electronic circuits simple and intuitive ๐Ÿ’ฅ. It's the first circuit library inspired by widely recognized standards ๐Ÿงท like **IEC** and **IEEE/ANSI**. Unlike circuitikz in LaTeX (2007), its design philosophy balances ease of use with powerful customization, avoiding any awkward syntax. + +[Repository](https://codeberg.org/grangelouis/zap) โ€“ [Documentation](https://zap.grangelouis.ch) โ€“ [Examples](https://codeberg.org/grangelouis/zap/src/branch/main/examples) + +## Examples + +You can find the full list of examples [here](https://codeberg.org/grangelouis/zap/src/branch/main/examples). + +### Operational amplifier + +![Operational amplifier example](https://codeberg.org/grangelouis/zap/raw/branch/main/examples/example1.svg) + +### MicroController Unit + +![MicroController Unit example](https://codeberg.org/grangelouis/zap/raw/branch/main/examples/example2.svg) + +### Logic circuit + +![Logic circuit example](https://codeberg.org/grangelouis/zap/raw/branch/main/examples/example3.svg) + +## Quick usage + +```typst +#import "@preview/zap:0.6.0" + +#zap.circuit({ + import zap: * + + // Here is a minimalist example + node("B", (0, 0)) + resistor("r1", "B", (rel: (0, 4)), i: $i_1$) +}) +``` + +## Online documentation + +You can find the full documentation ๐Ÿ“š [available online](https://zap.grangelouis.ch). It provides comprehensive guides, a detailed list of components, styling options, and example codes to get you started easily. + +## Contributing + +I highly welcome contributions ๐ŸŒฑ! Creating and maintaining Zap takes time and love. If you'd like to help, check out the [contribution procedure](https://codeberg.org/grangelouis/zap/src/branch/main/CONTRIBUTING.md) and join the journey ๐Ÿคฉ! + +## Legal disclaimer + +This project is an independent, open-source library licensed under [MIT](https://codeberg.org/grangelouis/zap/src/branch/main/LICENSE) with symbols generated algorithmically from scratch. While inspired by international conventions (IEC, IEEE) for compatibility, Zap is **not affiliated with or certified by** any standards body. These are stylistic approximations for illustration. For safety-critical or regulatory compliance, please consult official standards and [create custom symbols](https://zap.grangelouis.ch/#custom). \ No newline at end of file diff --git a/packages/preview/zap/0.6.0/src/circuit.typ b/packages/preview/zap/0.6.0/src/circuit.typ new file mode 100644 index 0000000000..680813835b --- /dev/null +++ b/packages/preview/zap/0.6.0/src/circuit.typ @@ -0,0 +1,19 @@ +#import "/src/deps.typ": cetz +#import "/src/styles.typ": default + +/// CeTZ native canvas wrapper +/// +/// https://zap.grangelouis.ch/#getting-started +/// +/// - drawing (none, array, element): canvas content, typically containing the circuit elements +/// -> content +#let circuit(drawing, ..params) = { + cetz.canvas(..params, { + // Init style directory + cetz.draw.set-ctx(ctx => { + ctx.style.insert("zap", default) + return ctx + }) + drawing + }) +} diff --git a/packages/preview/zap/0.6.0/src/decorations.typ b/packages/preview/zap/0.6.0/src/decorations.typ new file mode 100644 index 0000000000..6f256396a3 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/decorations.typ @@ -0,0 +1,119 @@ +#import "/src/deps.typ": cetz +#import cetz.draw: bezier-through, catmull, circle, content, get-ctx, hobby, line, mark +#import cetz.styles: merge + +#let resolve-directions(direction) = { + let vertical = "north" + let horizontal = "east" + if "south" in direction { + vertical = "south" + } + if "west" in direction { + horizontal = "west" + } + return (x: horizontal, y: vertical) +} + +#let resolve-decoration(ctx, deco, decor-type) = { + if type(deco) == dictionary and deco.at("content", default: none) == none { panic("decoration dictionary needs at least the 'content' key") } + + let style = cetz.styles.resolve(ctx.style.zap.decoration, merge: if type(deco) == dictionary { deco } else { (content: deco) }, root: decor-type) + style.size = cetz.util.measure(ctx, style.content) + style.position = resolve-directions(style.anchor) + style.side = if style.position.y == "north" { 1 } else { -1 } + style.label-distance = style.at("label-distance", default: (0.1 + style.size.last()) * style.side) + + return style +} + +/// Symbol current decoration +/// +/// - label (content | dict): label content +/// -> content +#let current(label) = { + get-ctx(ctx => { + let style = resolve-decoration(ctx, label, "current") + + let mark-position = if style.position.x == "west" { + (("in", style.distance, "symbol.west"), "in") + } else { + (("symbol.east", style.distance, "out"), "out") + } + + mark(..mark-position, symbol: style.symbol, reverse: style.invert, anchor: "center", fill: cetz.util.resolve-stroke(style.stroke).paint, stroke: 0pt, scale: style.scale) + content((rel: (0, style.label-distance), to: mark-position.at(0)), style.content) + }) +} + +/// Symbol flow decoration +/// +/// - label (content | dict): label content +/// -> content +#let flow(label) = { + get-ctx(ctx => { + let style = resolve-decoration(ctx, label, "flow") + + let west = style.position.x == "west" + let a-start = (to: ("symbol." + style.position.x, style.distance, if west { "in" } else { "out" }), rel: (0, style.indent * style.side)) + let a-end = (to: a-start, rel: (style.length * if west { -1 } else { 1 }, 0)) + + line( + a-start, + a-end, + mark: ( + (if style.invert { "start" } else { "end" }): style.symbol, + stroke: 0pt, + fill: cetz.util.resolve-stroke(style.stroke).paint, + scale: style.scale, + ), + stroke: style.stroke, + ) + content((rel: (0, style.label-distance), to: (a-start, style.label-ratio, a-end)), style.content) + }) +} + +/// Symbol voltage decoration +/// +/// - label (content | dict): label content +/// -> content +#let voltage(label, p-rotate) = { + get-ctx(ctx => { + let style = resolve-decoration(ctx, label, "voltage") + + let r-distance = cetz.util.resolve-number(ctx, style.distance) + let a-start = (rel: (style.start.at(0), (r-distance + style.start.at(1)) * style.side), to: "symbol." + style.position.y + "-west") + let a-end = (rel: (style.end.at(0), (r-distance + style.end.at(1)) * style.side), to: "symbol." + style.position.y + "-east") + let a-center = (rel: (style.center.at(0), (r-distance + style.center.at(1)) * style.side), to: "symbol." + style.position.y) + + let (a-start, a-end) = if style.position.x == "west" { (a-end, a-start) } else { (a-start, a-end) } + content((rel: (0, style.label-distance), to: a-center), style.content) + if (style.shape == "curved") { + hobby( + a-start, + a-center, + a-end, + mark: ( + (if style.invert { "start" } else { "end" }): style.symbol, + stroke: 0pt, + fill: cetz.util.resolve-stroke(style.stroke).paint, + scale: style.scale, + ), + stroke: style.stroke, + ) + } else if (style.shape == "straight") { + line( + a-start, + a-end, + mark: ( + (if style.invert { "start" } else { "end" }): style.symbol, + stroke: 0pt, + fill: cetz.util.resolve-stroke(style.stroke).paint, + scale: style.scale, + ), + stroke: style.stroke, + ) + } else { + panic("Only 'curved' and 'straight' variants are supported for voltage arrows") + } + }) +} diff --git a/packages/preview/zap/0.6.0/src/deps.typ b/packages/preview/zap/0.6.0/src/deps.typ new file mode 100644 index 0000000000..82d328faeb --- /dev/null +++ b/packages/preview/zap/0.6.0/src/deps.typ @@ -0,0 +1 @@ +#import "@preview/cetz:0.5.2" diff --git a/packages/preview/zap/0.6.0/src/lib.typ b/packages/preview/zap/0.6.0/src/lib.typ new file mode 100644 index 0000000000..152e30ca7a --- /dev/null +++ b/packages/preview/zap/0.6.0/src/lib.typ @@ -0,0 +1,44 @@ +// Export dependencies +#import "deps.typ": cetz + +// Export circuit +#import "circuit.typ": circuit + +// Export decorations +#import "decorations.typ": current, flow, voltage + +// Export styles +#import "styles.typ" + +// Export core +#import "symbol.typ": interface, symbol + +// Export symbols +#import "symbols/antenna.typ": antenna +#import "symbols/transformer.typ": transformer +#import "symbols/stub.typ": estub, nstub, sstub, stub, wstub +#import "symbols/wire.typ": swire, wire, zwire +#import "symbols/circulator.typ": circulator +#import "symbols/node.typ": node +#import "symbols/capacitor.typ": capacitor, pcapacitor +#import "symbols/diode.typ": diode, led, photodiode, schottky, tunnel, zener +#import "symbols/switch.typ": switch +#import "symbols/fuse.typ": afuse, fuse +#import "symbols/supply.typ": earth, frame, ground, rground, vcc, vee +#import "symbols/inductor.typ": inductor +#import "symbols/lamp.typ": lamp +#import "symbols/piezo.typ": piezo +#import "symbols/logic.typ": land, lnand, lnor, lnot, lor, lxnor, lxor +#import "symbols/resistor.typ": heater, potentiometer, resistor, rheostat +#import "symbols/source.typ": acvsource, disource, dvsource, isource, vsource +#import "symbols/battery.typ": battery, cell, multicell +#import "symbols/motor.typ": acmotor, dcmotor +#import "symbols/transistors/bjt.typ": bjt, npn, pnp +#import "symbols/transistors/mosfet.typ": mosfet, nmos, nmosd, pmos, pmosd +#import "symbols/transistors/jfet.typ": jfet, njfet, pjfet +#import "symbols/integrated/opamp.typ": opamp +#import "symbols/integrated/mcu.typ": mcu +#import "symbols/integrated/converter.typ": adc, dac +#import "symbols/instruments/round-meter.typ": ammeter, ohmmeter, round-meter, voltmeter, wattmeter +#import "symbols/button.typ": button, ncbutton, ncibutton, nobutton, noibutton +#import "symbols/integrated/flipflop.typ": dflipflop, flipflop, jkflipflop, srlatch diff --git a/packages/preview/zap/0.6.0/src/mini.typ b/packages/preview/zap/0.6.0/src/mini.typ new file mode 100644 index 0000000000..aeb958139f --- /dev/null +++ b/packages/preview/zap/0.6.0/src/mini.typ @@ -0,0 +1,180 @@ +#import "deps.typ": cetz +#import cetz.draw: anchor, circle, hobby, line, merge-path, rotate, scope, set-origin, set-style +#import cetz.styles: merge + +/// Default symbol styling dictionary +#let center-mark(symbol: ">", ..end) = { + (end: ((pos: 50%, symbol: symbol, fill: black, anchor: "center", ..end.named()), (pos: 0%, symbol: ">", scale: 0))) +} + +/// Mini lamp symbol +#let lamp(pos, radius: .5, ..params) = { + circle(pos, radius: radius, ..params) + line((rel: (radius: radius, angle: 45deg), to: pos), (rel: (radius: -radius, angle: 45deg), to: pos), ..params) + line((rel: (radius: radius, angle: -45deg), to: pos), (rel: (radius: -radius, angle: -45deg), to: pos), ..params) +} + +/// Mini arrow displayed on top of resistors/... +#let adjust-arrow(type, ..params) = { + scope(ctx => { + let style = cetz.styles.resolve(ctx.style.zap.arrow, merge: params.named(), root: type) + + let origin = ( + -style.ratio.at(0) * calc.cos(style.angle) * style.length, + -style.ratio.at(1) * calc.sin(style.angle) * style.length, + ) + + if type == "sensor" { + anchor("label", origin) + anchor("wiper", (to: origin, rel: (-style.sensor-length, 0))) + } else { + anchor("wiper", origin) + } + + set-origin(origin) + rotate(style.angle) + + anchor("tip", (style.length, 0)) + + set-style( + stroke: style.stroke, + mark: ( + end: style.symbol, + scale: style.scale, + ), + ) + if type == "variable" { + line("wiper", "tip", mark: ( + stroke: (thickness: 0pt), + fill: cetz.util.resolve-stroke(style.stroke).paint, + )) + } else if type == "preset" { + line("wiper", "tip", mark: ( + stroke: style.stroke, + width: style.width, + )) + } else if type == "sensor" { + line("wiper", "label", "tip", mark: ( + stroke: style.stroke, + )) + } + }) + if type == "sensor" { + anchor("label", "label") + } + anchor("wiper", "wiper") + anchor("tip", "tip") +} + +/// Default symbol styling dictionary +#let radiation-arrows(origin, ..params) = { + scope(ctx => { + let style = cetz.styles.resolve(ctx.style.zap.arrow, merge: params.named(), root: "radiation") + + set-origin(origin) + rotate(style.angle) + set-style( + stroke: style.stroke, + mark: ( + stroke: (thickness: 0pt), + scale: style.scale, + fill: cetz.util.resolve-stroke(style.stroke).paint, + ), + ) + + let pos = if style.reversed { "start" } else { "end" } + line((style.length, -style.distance), (0, -style.distance), mark: ((pos): style.symbol)) + line((style.length, +style.distance), (0, +style.distance), mark: ((pos): style.symbol)) + }) +} + +/// Default symbol styling dictionary +#let adjustable-arrow(node, ..params) = { + scope(ctx => { + let style = cetz.styles.resolve(ctx.style.zap.arrow, merge: params.named(), root: "adjustable") + + anchor("adjust", (to: node, rel: (0, style.length))) + anchor("tip", node) + + line("adjust", "tip", stroke: style.stroke, mark: ( + stroke: (thickness: 0pt), + end: style.symbol, + scale: style.scale, + fill: cetz.util.resolve-stroke(style.stroke).paint, + )) + }) + anchor("adjust", "adjust") + anchor("a", "adjust") + anchor("tip", "tip") +} + +/// DC sign +#let dc-sign(ctx) = { + let width = ctx.style.zap.sign.width + let spacing = 1.5pt + let vspace = 3pt + let tick-width = (width - 2 * spacing) / 3 + + set-style(stroke: ctx.style.zap.sign.stroke) + + line((-width / 2, 0), (width / 2, 0)) + line((-width / 2, -vspace), (-width / 2 + tick-width, -vspace)) + line((-tick-width / 2, -vspace), (tick-width / 2, -vspace)) + line((width / 2, -vspace), (width / 2 - tick-width, -vspace)) +} + +/// AC sign with different waveforms +#let ac-sign(ctx, waveform: "default") = { + let width = ctx.style.zap.sign.width + let height = ctx.style.zap.sign.height + + set-style(stroke: ctx.style.zap.sign.stroke) + + if waveform == "rect" { + height *= 1.3 + width *= 0.8 + line( + (-width / 2, 0), + (rel: (0, height / 2)), + (rel: (width / 2, 0)), + (rel: (0, -height)), + (rel: (width / 2, 0)), + (rel: (0, height / 2)), + ) + } else if waveform == "tri" { + line( + (-width / 2, -height / 2), + (rel: (width / 3, height)), + (rel: (width / 3, -height)), + (rel: (width / 3, height)), + ) + } else if waveform == "saw" { + line( + (-width / 2, -height / 2), + (rel: (width / 2, height)), + (rel: (0, -height)), + (rel: (width / 2, height)), + ) + } else { + hobby( + (-width / 2, 0), + (-width / 4, height / 2), + (width / 4, -height / 2), + (width / 2, 0), + ) + } +} + +/// Default symbol styling dictionary +#let clock-wedge(size: 1) = { + let width = 5pt * size + let height = 10pt * size + let symbol-stroke = 0.55pt + + set-style(stroke: symbol-stroke) + + merge-path({ + line((0, height / 2), (width, 0)) + line((0, -height / 2), (width, 0)) + }) +} diff --git a/packages/preview/zap/0.6.0/src/styles.typ b/packages/preview/zap/0.6.0/src/styles.typ new file mode 100644 index 0000000000..b6fb6a5af0 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/styles.typ @@ -0,0 +1,475 @@ +/// Default symbol styling dictionary +#let default = ( + variant: "iec", + scale: (x: 1.0, y: 1.0), + stroke: .8pt, + fill: none, + background: white, + foreground: black, + label: ( + scale: auto, + content: none, + distance: 7pt, + anchor: "north", + ), + node: ( + stroke: .65pt, + fill: black, + nofill: white, + radius: .06, + ), + wire: ( + stroke: .6pt, + ), + arrow: ( + symbol: ">", + scale: 1.0, + angle: 55deg, + length: 40pt, + ratio: (0.5, 0.5), + stroke: 1pt, + variable: ( + symbol: auto, + scale: 1.0, + stroke: auto, + length: auto, + angle: auto, + ratio: auto, + ), + preset: ( + symbol: "|", + scale: 1.0, + stroke: auto, + length: auto, + angle: auto, + ratio: auto, + width: 10pt, + ), + sensor: ( + symbol: none, + scale: 1.0, + stroke: auto, + length: auto, + sensor-length: 12pt, + angle: auto, + ratio: auto, + ), + radiation: ( + symbol: auto, + scale: 1, + stroke: 0.55pt, + distance: 3pt, + length: 12pt, + angle: -120deg, + reversed: false, + ), + adjustable: ( + symbol: auto, + scale: 1.0, + stroke: auto, + length: 0.8, + ), + ), + decoration: ( + symbol: ">", + scale: 1.0, + stroke: .6pt, + content: none, + distance: 9pt, + position: (x: "east", y: "north"), + anchor: "north-east", + invert: false, + current: ( + symbol: auto, + scale: 1.0, + stroke: auto, + content: auto, + distance: auto, + position: auto, + anchor: auto, + invert: auto, + wire: ( + symbol: auto, + scale: auto, + stroke: auto, + content: auto, + distance: 7pt, + position: 50%, + anchor: "north", + invert: auto, + ), + ), + flow: ( + symbol: auto, + scale: 1.0, + stroke: 0.55pt, + content: auto, + distance: auto, + position: auto, + anchor: auto, + invert: auto, + length: 0.7, + indent: 0.2, + label-ratio: 50%, + ), + voltage: ( + symbol: auto, + scale: 1.0, + stroke: auto, + content: auto, + distance: 2pt, + position: auto, + anchor: auto, + invert: auto, + start: (-.4, .1), + end: (.4, .1), + center: (0, .3), + shape: "curved", + ), + ), + debug: ( + stroke: .2pt + red, + enabled: false, + radius: .7pt, + angle: -30deg, + shift: 3pt, + inset: 1pt, + font: 3pt, + fill: red, + ), + invert: ( + radius: .09, + wedge-width: 0.2, + wedge-height: 0.15, + stroke: .8pt, + ), + sign: ( + stroke: .65pt, + width: 20pt, + height: 8pt, + size: .14, + ), + // Components + stub: ( + scale: auto, + length: .8, + ), + capacitor: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + width: .8, + distance: .25, + radius: 0.6, + angle: 40deg, + ), + diode: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + radius: .3, + width: .25, + tunnel-length: .11, + ), + opamp: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + width: 1.8, + height: 1.75, + padding: .28, + sign-stroke: .55pt, + sign-size: .14, + sign-delta: .45, + label: ( + anchor: "label", + align: "east", + distance: 0, + ), + ), + lamp: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + radius: 8pt, + ), + switch: ( + variant: auto, + scale: auto, + stroke: auto, + width: .8, + angle: 35deg, + ), + fuse: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + width: .88, + height: .88 / 2.4, + asymmetry: 25%, + ), + ground: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + radius: 0.22, + distance: 0.28, + ), + rground: ( + variant: auto, + scale: auto, + stroke: auto, + width: .53, + distance: .28, + ), + frame: ( + variant: auto, + scale: auto, + stroke: auto, + number: 3, + width: 0.46, + angle: 20deg, + depth: 0.25, + distance: 0.28, + ), + earth: ( + variant: auto, + scale: auto, + stroke: auto, + width: .53, + delta: .09, + spacing: .11, + distance: .28, + ), + vcc: ( + variant: auto, + scale: auto, + stroke: auto, + angle: 35deg, + radius: .4, + distance: .6, + ), + vee: ( + variant: auto, + scale: auto, + stroke: auto, + angle: 35deg, + radius: .4, + distance: .6, + label: ( + anchor: "south", + align: "north", + ), + ), + inductor: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + width: 1.65, + height: 1.65 / 4, + bumps: 4, + ), + resistor: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + width: 1.41, + height: .47, + zigs: 3, + ), + vsource: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + radius: .53, + padding: .25, + sign: ( + stroke: auto, + size: .14, + delta: .07, + ), + ), + isource: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + radius: .53, + padding: .25, + arrow-scale: 1, + ), + battery: ( + variant: auto, + scale: auto, + stroke: auto, + distance: .18, + plus-width: 1, + minus-width: .5, + polarity: ( + padding: (.15, .1), + side: 1, + ), + multi: ( + distance: 0.7, + number: 5, + ), + ), + acmotor: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + radius: .58, + ), + dcmotor: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + radius: .58, + ), + bjt: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + radius: .65, + base-height: .6, + base-distance: .12, + aperture: 50deg, + ), + mosfet: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + height: 0.7, + width: 1, + base-width: 1.2, + base-spacing: 0.16, + base-distance: 0.16, + radius: 0.85, + ), + jfet: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + height: 0.7, + width: 1, + base-width: 1.2, + base-spacing: 0.16, + base-distance: 0.16, + radius: 0.85, + ), + antenna: ( + variant: auto, + scale: auto, + stroke: auto, + fill: none, + number: 3, + distance: 0.8, + spacing: 0.35, + length: 0.5, + ), + transformer: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + radius: 0.35, + distance: 0.45, + ), + converter: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + width: 1.7, + height: 0.7, + arrow-width: 0.4, + label: ( + align: "center", + anchor: "label", + ), + ), + mcu: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + width: 3, + min-height: 1, + padding: 0.2, + spacing: 0.4, + ), + round-meter: ( + variant: auto, + scale: auto, + stroke: auto, + radius: .47, + ), + button: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + width: .8, + angle: 23deg, + overlap: .1, + distance: .7, + lamp-distance: .12, + button-width: .33, + button-height: .16, + button-omega: 1, + latch-size: 0.1, + latch-padding: 0.15, + ), + circulator: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + radius: 0.53, + arrow-radius: 0.35, + ), + piezo: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + width: 0.4, + height: 0.7, + spacing: 0.15, + ), + logic: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + width: 0.95, + min-height: 0.85, + padding: 0.2, + xor-spacing: 0.13, + spacing: .4, + ), + flipflop: ( + variant: auto, + scale: auto, + stroke: auto, + fill: auto, + width: 1.8, + height: 2, + padding: 0.2, + spacing: 0.5, + ), +) diff --git a/packages/preview/zap/0.6.0/src/symbol.typ b/packages/preview/zap/0.6.0/src/symbol.typ new file mode 100644 index 0000000000..3472e9cad8 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbol.typ @@ -0,0 +1,162 @@ +#import "deps.typ": cetz +#import "decorations.typ": current, flow, voltage +#import "symbols/node.typ": node +#import "symbols/wire.typ": wire +#import "styles.typ": default +#import "utils.typ": get-label-anchor, opposite-anchor +#import cetz.styles: merge +#import cetz.draw: anchor, circle, copy-anchors, for-each-anchor, get-ctx, group, hide, move-to, on-layer, rect, scope + +#let typst-angle = angle + +/// Low-level function for creating circuit symbols. Used internally for all built-in symbols and can also be used to create custom ones. +/// +/// https://zap.grangelouis.ch/#custom +/// +/// - draw (func): drawing function +/// - label (content | dict): label content +/// - i (content | dict): current decoration +/// - f (content | dict): flow decoration +/// - u (content | dict): voltage decoration +/// - n (str): ends nodes types +/// - position (ratio): position of the symbol +/// - scale (float): scaling factor +/// - angle (angle): rotation angle +/// - debug (bool): debug mode (displays anchors) +/// -> content +#let symbol( + uid, + name, + draw: none, + label: none, + i: none, + f: none, + u: none, + n: none, + position: 50%, + angle: 0deg, + debug: none, + ..params, +) = { + assert(params.pos().len() in (1, 2), message: "only 1 or 2 nodes are accepted") + assert(params.pos().len() == 1 or angle == 0deg, message: "cannot use rotate argument with 2 nodes") + assert(type(name) == str, message: "identifier must be a string") + assert(type(angle) == typst-angle, message: "rotation must an angle") + assert(label == none or type(label) in (content, str, dictionary), message: "label must content, dictionary or string") + assert("variant" not in params.named() or params.named().variant in ("ieee", "iec", "alt", auto), message: "variant must be 'iec', 'ieee', 'alt' or auto") + assert(n in (none, "*-", "*-*", "-*", "o-*", "*-o", "o-", "-o", "o-o"), message: "nodes must be none, *-*, o-*, o-o, o-, etc.") + + group(name: name, ctx => { + let keep-style = ctx.style + let style = cetz.styles.resolve(ctx.style.zap, merge: params.named(), root: uid) + let stroke = style.at("stroke", default: default.stroke) + style.stroke = cetz.util.resolve-stroke(if stroke != none { stroke } else { 0pt }) + style.scale = style.at("scale", default: default.scale) + if type(style.scale) in (float, int) { style.scale = (x: style.scale, y: style.scale) } + let (ctx, ..nodes) = cetz.coordinate.resolve(ctx, ..params.pos()) + let origin = nodes.first() + let params-angle = angle + if nodes.len() == 2 { + anchor("in", nodes.first()) + anchor("out", nodes.last()) + params-angle = cetz.vector.angle2(..nodes) + origin = (nodes.first(), position, nodes.last()) + } + cetz.draw.set-origin(origin) + cetz.draw.rotate(params-angle) + ctx.insert("rotation", params-angle) + + // Symbol + on-layer(1, { + group(name: "symbol", { + // Scaling + cetz.draw.scale(..style.scale) + + // Symbol's draw function + draw(ctx, nodes, style) + copy-anchors("bounds") + }) + }) + + copy-anchors("symbol") + + // Label + on-layer(0, { + if label != none { + let label-style = cetz.styles.resolve( + ctx.style.zap.at(uid, default: (label: (:))), + base: ctx.style.zap.label, + merge: if type(label) == dictionary { label } else { (content: label) }, + root: "label", + ) + let anchor = get-label-anchor(params-angle) + let resolved-anchor = if type(label-style.anchor) == str and "south" in label-style.anchor { opposite-anchor(anchor) } else { anchor } + cetz.draw.content( + if type(label-style.anchor) == str { "symbol." + label-style.anchor } else { label-style.anchor }, + anchor: label-style.at("align", default: resolved-anchor), + label-style.content, + padding: label-style.distance, + ) + } + }) + + // Symbol decorations + if nodes.len() == 2 { + wire("in", "symbol.west") + wire("symbol.east", "out") + + if i != none { current(i) } + if f != none { flow(f) } + if u != none { voltage(u, params-angle) } + if n != none { + if "*-" in n { + node("", "in") + } else if "o-" in n { + node("", "in", fill: false) + } + if "-*" in n { + node("", "out") + } else if "-o" in n { + node("", "out", fill: false) + } + } + } + + // Bringing back the current style + cetz.draw.set-style(..keep-style) + }) + + // Show symbol anchors in debug mode + get-ctx(ctx => { + let debug = if debug == none { ctx.style.zap.debug.enabled } else { debug } + if (debug) { + on-layer(1, ctx => { + let style = ctx.style.zap.debug + for-each-anchor(name, exclude: ("start", "end", "mid", "symbol", "line", "bounds", "gl", "0", "1"), name => { + circle((), radius: style.radius, stroke: style.stroke) + cetz.draw.content((rel: (0, style.shift)), box(inset: style.inset, text(style.font, name, fill: style.fill)), angle: style.angle) + }) + }) + } + }) + + // Set previous coordinate + move-to(params.pos().last()) +} + +// TODO: update this to more modern and resilient function (with "wirein" and "wireout" anchors) +/// Low-level symbol interface to automate wiring and positioning. Will be replaced in the future. +#let interface(node1, node2, ..params, io: false) = { + hide(rect(node1, node2, name: "bounds")) + if io { + let (node3, node4) = (0, 0) + if params.pos().len() == 2 { + (node3, node4) = params.pos() + } else { + (node3, node4) = ("bounds.west", "bounds.east") + } + + anchor("in", node3) + anchor("out", node4) + } +} diff --git a/packages/preview/zap/0.6.0/src/symbols/antenna.typ b/packages/preview/zap/0.6.0/src/symbols/antenna.typ new file mode 100644 index 0000000000..79e3dc8063 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/antenna.typ @@ -0,0 +1,35 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import cetz.draw: anchor, line, merge-path +#import "/src/symbols/wire.typ": wire + +#let antenna(name, node, closed: false, ..params) = { + // Drawing function + let draw(ctx, position, style) = { + assert(style.number > 1, message: "number must be at least 2") + + wire((0, 0), (0, style.distance)) + let width = style.spacing * (style.number - 1) + let left = -(style.number - 1) / 2 + + merge-path( + close: true, + fill: if closed { style.fill } else { none }, + stroke: if closed { style.stroke } else { none }, + { + line((0, style.distance), (rel: (style.spacing * left, style.length))) + line((rel: (width, 0)), (0, style.distance)) + }, + ) + if not closed { + for i in range(style.number) { + line((0, style.distance), (rel: (style.spacing * (i + left), style.length)), stroke: style.stroke) + } + } + interface((-width / 2, style.distance), (rel: (width, style.length))) + anchor("default", (0, 0)) + } + + // Constructor call + symbol("antenna", name, node, draw: draw, ..params) +} diff --git a/packages/preview/zap/0.6.0/src/symbols/battery.typ b/packages/preview/zap/0.6.0/src/symbols/battery.typ new file mode 100644 index 0000000000..be5f6a2d73 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/battery.typ @@ -0,0 +1,58 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import cetz.draw: line, on-layer, rect, set-style + +/// Battery symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// - cells (int | 'multi'): number of battery cells +/// - show-polarity (bool): displays polarity +/// -> content +#let battery(name, node, cells: 2, polarity: false, ..params) = { + assert(type(cells) == str and cells == "multi" or type(cells) == int and cells >= 1, message: "cells must be 'multi' or at least 1 number") + + // Drawing function + let draw(ctx, position, style) = { + set-style(stroke: style.stroke) + + let width + if cells == "multi" { + width = style.distance * 2 + style.multi.distance + + line((-width / 2, -style.minus-width / 2), (rel: (0, style.minus-width))) + line((-width / 2 + style.distance, -style.plus-width / 2), (rel: (0, style.plus-width))) + + let step = style.multi.distance / style.multi.number / cetz.util.resolve-number(ctx, 1pt) * 1pt + line((-width / 2 + style.distance, 0), (rel: (style.multi.distance, 0)), stroke: (dash: (step, step))) + + line((width / 2 - style.distance, -style.minus-width / 2), (rel: (0, style.minus-width))) + line((width / 2, -style.plus-width / 2), (rel: (0, style.plus-width))) + } else { + width = style.distance * (cells * 2 - 1) + + for i in range(cells * 2, step: 2) { + line((-width / 2 + style.distance * i, -style.minus-width / 2), (rel: (0, style.minus-width))) + line((-width / 2 + style.distance * (i + 1), -style.plus-width / 2), (rel: (0, style.plus-width))) + } + } + interface((-width / 2, -style.plus-width / 2), (width / 2, style.plus-width / 2), io: position.len() < 2) + + if polarity { + set-style(stroke: ctx.style.zap.sign.stroke) + line((-width / 2 - style.polarity.padding.at(0), (style.plus-width / 2 + style.polarity.padding.at(1) + ctx.style.zap.sign.size) * style.polarity.side), ( + rel: (-2 * ctx.style.zap.sign.size, 0), + )) + line((width / 2 + style.polarity.padding.at(0) + ctx.style.zap.sign.size, (style.plus-width / 2 + style.polarity.padding.at(1)) * style.polarity.side), ( + rel: (0, 2 * ctx.style.zap.sign.size), + )) + line((rel: (ctx.style.zap.sign.size, -ctx.style.zap.sign.size)), (rel: (-2 * ctx.style.zap.sign.size, 0))) + } + } + + // Constructor call + symbol("battery", name, node, draw: draw, ..params) +} + +#let cell(name, node, ..params) = battery(name, node, ..params, cells: 1) +#let multicell(name, node, ..params) = battery(name, node, ..params, cells: "multi") diff --git a/packages/preview/zap/0.6.0/src/symbols/button.typ b/packages/preview/zap/0.6.0/src/symbols/button.typ new file mode 100644 index 0000000000..bf005cffed --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/button.typ @@ -0,0 +1,81 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import cetz.draw: anchor, circle, hide, hobby, line, mark, merge-path, rect +#import "/src/symbols/wire.typ": wire +#import "/src/mini.typ": lamp + +/// Button symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// - nc (bool): closed by default +/// - illuminated (bool): draws a light source on top +/// - head ('standard' | 'mushroom'): button head shape +/// - latching (bool): displays a string +/// -> content +#let button(name, node, nc: false, illuminated: false, head: "standard", latching: false, ..params) = { + // Drawing function + let draw(ctx, position, style) = { + interface((-style.width / 2, -0.2), (style.width / 2, 0.2), io: position.len() < 2) + + let angle = 180deg - if nc { -1 } else { 1 } * style.angle + line((style.width / 2, 0), (rel: (radius: style.width / calc.cos(style.angle), angle: angle)), stroke: style.stroke, name: "support") + line((), (rel: (radius: style.overlap, angle: angle)), stroke: style.stroke) + if nc { + line((-style.width / 2, 0), (rel: (0, -style.overlap - style.width * calc.tan(style.angle))), stroke: style.stroke) + } + if latching { + line( + "support.50%", + (rel: (0, -style.latch-padding - 2 * style.latch-size), to: (0, style.distance - style.button-height)), + stroke: (..style.stroke, dash: (array: (6.5pt, 3pt))), + ) + line( + (), + (rel: (0, style.latch-padding / 2)), + (rel: (1.5 * style.latch-size, style.latch-size)), + (rel: (-1.5 * style.latch-size, style.latch-size)), + (rel: (0, style.latch-padding / 2)), + if head == "standard" { (rel: (0, style.button-height)) } else { () }, + stroke: style.stroke, + ) + } else { + line("support.50%", (0, style.distance - if head == "mushroom" { style.button-height } else { 0 }), stroke: (..style.stroke, dash: (array: (6.5pt, 3pt)))) + } + + merge-path( + stroke: style.stroke, + fill: if head == "mushroom" { style.fill } else { none }, + close: head == "mushroom", + { + if head == "mushroom" { + line((-style.button-width / 2, style.distance - style.button-height), (rel: (style.button-width, 0)), name: "top") + hobby((), (rel: (-style.button-width / 2, style.button-height)), (rel: (-style.button-width / 2, -style.button-height)), omega: style.button-omega) + anchor("top", (to: "top.50%", rel: (y: style.button-height))) + } else if head == "standard" { + line( + (-style.button-width / 2, style.distance - style.button-height), + (rel: (0, style.button-height)), + (rel: (style.button-width, 0)), + (rel: (0, -style.button-height)), + name: "line", + ) + anchor("top", "line.50%") + } + }, + ) + + if illuminated { + line("top", (rel: (0, style.lamp-distance), to: "top"), stroke: style.stroke) + lamp((rel: (0, style.button-width / 2 + style.lamp-distance), to: "top"), radius: style.button-width / 2, fill: style.fill, stroke: style.stroke) + } + } + + // Constructor call + symbol("button", name, node, draw: draw, ..params) +} + +#let nobutton(name, node, ..params) = button(name, node, ..params) +#let noibutton(name, node, ..params) = button(name, node, ..params, illuminated: true) +#let ncbutton(name, node, ..params) = button(name, node, ..params, nc: true) +#let ncibutton(name, node, ..params) = button(name, node, ..params, nc: true, illuminated: true) diff --git a/packages/preview/zap/0.6.0/src/symbols/capacitor.typ b/packages/preview/zap/0.6.0/src/symbols/capacitor.typ new file mode 100644 index 0000000000..699a7842e5 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/capacitor.typ @@ -0,0 +1,51 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import cetz.draw: anchor, arc, line, merge-path, rect, set-style +#import "/src/mini.typ": adjust-arrow + +#let capacitor(name, node, variable: false, preset: false, sensor: false, polarized: false, ..params) = { + assert(type(variable) == bool, message: "variable must be of type bool") + + // Drawing function + let draw(ctx, position, style) = { + interface((-style.distance / 2, -style.width / 2), (style.distance / 2, style.width / 2), io: position.len() < 2) + + let plates() = { + if polarized { + arc( + (-style.distance / 2 - style.radius + style.radius * calc.cos(style.angle), style.radius * calc.sin(style.angle)), + radius: style.radius, + start: style.angle, + stop: -style.angle, + ) + } else { + line((-style.distance / 2, style.width / 2), (-style.distance / 2, -style.width / 2)) + } + line((style.distance / 2, -style.width / 2), (style.distance / 2, style.width / 2)) + } + + if style.fill != none { + merge-path( + stroke: none, + fill: style.fill, + plates(), + ) + } + + set-style(stroke: style.stroke) + plates() + + if variable { + adjust-arrow("variable") + } else if preset { + adjust-arrow("preset") + } else if sensor { + adjust-arrow("sensor") + } + } + + // Constructor call + symbol("capacitor", name, node, draw: draw, ..params) +} + +#let pcapacitor(name, node, ..params) = capacitor(name, node, ..params, polarized: true) diff --git a/packages/preview/zap/0.6.0/src/symbols/circulator.typ b/packages/preview/zap/0.6.0/src/symbols/circulator.typ new file mode 100644 index 0000000000..3aaaf87d18 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/circulator.typ @@ -0,0 +1,22 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/mini.typ": ac-sign +#import cetz.draw: anchor, arc, circle, rotate + +#let circulator(name, node, ..params) = { + // Drawing function + let draw(ctx, position, style) = { + interface((-style.radius, -style.radius), (style.radius, style.radius), io: false) + + circle((0, 0), radius: style.radius, fill: white, ..style, name: "circle") + anchor("port1", "circle.west") + anchor("port2", "circle.east") + anchor("port3", "circle.north") + + let mark = (end: ">", fill: black, stroke: 0pt, anchor: "tip", inset: -.04) + arc((0, 0), radius: style.arrow-radius, start: -45deg, delta: 280deg, anchor: "origin", stroke: ctx.style.zap.arrow.stroke, mark: mark) + } + + // Constructor call + symbol("circulator", name, node, draw: draw, ..params) +} diff --git a/packages/preview/zap/0.6.0/src/symbols/diode.typ b/packages/preview/zap/0.6.0/src/symbols/diode.typ new file mode 100644 index 0000000000..cebf271e0d --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/diode.typ @@ -0,0 +1,71 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/mini.typ": radiation-arrows +#import "/src/symbols/wire.typ": wire +#import cetz.draw: anchor, circle, line, merge-path, polygon, scope, set-style, translate + +/// Diode symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// - type ('emitting' | 'receiving' | 'tunnel' | 'zener' | 'schottky'): subvariants of diode +/// -> content +#let diode(name, node, type: none, ..params) = { + assert((type in ("emitting", "receiving", "tunnel", "zener", "schottky") or type == none), message: "type must be tunnel, zener, schottky, ...") + + // Drawing function + let draw(ctx, position, style) = { + translate((-style.radius / 4, 0)) + interface((-style.radius / 2, -style.radius), (style.radius, style.radius), io: position.len() < 2) + + set-style(stroke: style.stroke) + polygon((0, 0), 3, radius: style.radius, fill: if style.variant == "ieee" { cetz.util.resolve-stroke(style.stroke).paint } else { none }) + if style.variant == "iec" { + wire((0deg, style.radius), (180deg, style.radius / 2)) + } + + // Diode specific lines - horizontal lines orthogonal to cathode + if (type in ("tunnel", "zener", "schottky")) { + // Calculate extension to account for cathode line thickness + merge-path({ + // Shottky specific line + if (type == "schottky") { + line((style.radius + style.tunnel-length, style.width), (style.radius + style.tunnel-length, style.width - style.tunnel-length)) + } + if (type == "tunnel") { + line((style.radius - style.tunnel-length, style.width), (style.radius, style.width)) + } else { + line((style.radius + style.tunnel-length, style.width), (style.radius, style.width)) + } + + // Main cathode line (vertical) + line((style.radius, style.width), (style.radius, -style.width)) + + // Lower line toward anode + line((style.radius, -style.width), (style.radius - style.tunnel-length, -style.width)) + + // Shottky specific line + if (type == "schottky") { + line((style.radius - style.tunnel-length, -style.width), (style.radius - style.tunnel-length, -style.width + style.tunnel-length)) + } + }) + } else { + // Main cathode line (vertical) + line((style.radius, style.width), (style.radius, -style.width)) + } + + if (type in ("emitting", "receiving")) { + let reversed = (type == "receiving") + radiation-arrows((to: (0, 0), rel: (0.25, 0.65)), reversed: reversed) + } + } + + // Constructor call + symbol("diode", name, node, draw: draw, ..params) +} + +#let led(name, node, ..params) = diode(name, node, ..params, type: "emitting") +#let photodiode(name, node, ..params) = diode(name, node, ..params, type: "receiving") +#let tunnel(name, node, ..params) = diode(name, node, ..params, type: "tunnel") +#let zener(name, node, ..params) = diode(name, node, ..params, type: "zener") +#let schottky(name, node, ..params) = diode(name, node, ..params, type: "schottky") diff --git a/packages/preview/zap/0.6.0/src/symbols/fuse.typ b/packages/preview/zap/0.6.0/src/symbols/fuse.typ new file mode 100644 index 0000000000..e67d620806 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/fuse.typ @@ -0,0 +1,31 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/symbols/wire.typ": wire +#import cetz.draw: anchor, circle, floating, line, rect, set-style + +/// Fuse symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// - asymmetric (bool): displays a filled rectangle inside +/// -> content +#let fuse(name, node, asymmetric: false, ..params) = { + assert(type(asymmetric) == bool, message: "asymmetric must be of type bool") + + // Drawing function + let draw(ctx, position, style) = { + interface((-style.width / 2, -style.height / 2), (style.width / 2, style.height / 2), io: position.len() < 2) + + rect((-style.width / 2, -style.height / 2), (style.width / 2, style.height / 2), stroke: style.stroke, fill: style.fill) + wire((-style.width / 2, 0), (style.width / 2, 0)) + + if (asymmetric) { + rect((-style.width / 2, -style.height / 2), (-style.width / 2 + float(style.asymmetry * style.width), style.height / 2), stroke: none, fill: style.stroke.paint) + } + } + + // Constructor call + symbol("fuse", name, node, draw: draw, ..params) +} + +#let afuse(name, node, ..params) = fuse(name, node, ..params, asymmetric: true) diff --git a/packages/preview/zap/0.6.0/src/symbols/inductor.typ b/packages/preview/zap/0.6.0/src/symbols/inductor.typ new file mode 100644 index 0000000000..c7797676f5 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/inductor.typ @@ -0,0 +1,45 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/mini.typ": adjust-arrow +#import cetz.draw: anchor, arc, line, merge-path, rect, set-style + +/// Inductor symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// - variable (bool): displays a diagonal arrow +/// - preset (bool): displays a diagonal arrow with flat end +/// - sensor (bool): displays a diagonal arrow with flat line +/// -> content +#let inductor(name, node, variable: false, preset: false, sensor: false, ..params) = { + // Drawing function + let draw(ctx, position, style) = { + interface((-style.width / 2, -style.height / 2), (style.width / 2, style.height / 2), io: position.len() < 2) + + set-style(stroke: style.stroke) + + let bump-radius = style.width / style.bumps / 2 + merge-path({ + let sgn = if position.last().at(0) < position.first().at(0) { -1 } else { 1 } + let start = (-style.width / 2 - bump-radius, 0) + for i in range(style.bumps) { + let arc-center-x = ( + start.at(0) + bump-radius + i * 2 * bump-radius + ) + let arc-center = (arc-center-x, 0) + arc(arc-center, radius: bump-radius, start: sgn * 180deg, stop: 0deg) + } + }) + + if variable { + adjust-arrow("variable") + } else if preset { + adjust-arrow("preset") + } else if sensor { + adjust-arrow("sensor") + } + } + + // Constructor call + symbol("inductor", name, node, draw: draw, ..params) +} diff --git a/packages/preview/zap/0.6.0/src/symbols/instruments/round-meter.typ b/packages/preview/zap/0.6.0/src/symbols/instruments/round-meter.typ new file mode 100644 index 0000000000..8a7c71adcc --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/instruments/round-meter.typ @@ -0,0 +1,21 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/mini.typ": ac-sign +#import cetz.draw: anchor, circle, content, line, mark, polygon, rect + +#let round-meter(name, node, measurand: str, ..params) = { + // Drawing function + let draw(ctx, position, style) = { + interface((-style.radius, -style.radius), (style.radius, style.radius), io: position.len() < 2) + circle((0, 0), radius: style.radius, fill: white, ..style) + content((0, 0), measurand) + } + + // Constructor call + symbol("round-meter", name, node, draw: draw, ..params) +} + +#let voltmeter(name, node, ..params) = round-meter(name, node, measurand: "V", ..params) +#let ammeter(name, node, ..params) = round-meter(name, node, measurand: "A", ..params) +#let ohmmeter(name, node, ..params) = round-meter(name, node, measurand: $Omega$, ..params) +#let wattmeter(name, node, ..params) = round-meter(name, node, measurand: "W", ..params) diff --git a/packages/preview/zap/0.6.0/src/symbols/integrated/converter.typ b/packages/preview/zap/0.6.0/src/symbols/integrated/converter.typ new file mode 100644 index 0000000000..e380a528cf --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/integrated/converter.typ @@ -0,0 +1,49 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import cetz.draw: anchor, content, line, merge-path, on-layer, polygon, rect, scale, scope, translate + +/// Analog-digital converter symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// - input ('a' | 'd'): analog/digital input +/// -> content +#let adc(name, node, input: "a", ..params) = { + assert(input in ("a", "d"), message: "input can only be d or a") + + // Drawing function + let draw(ctx, position, style) = { + interface((-style.width / 2, -style.height / 2), (style.width / 2, style.height / 2), io: position.len() < 2) + + let inverse = if input == "d" { -1 } else { 1 } + on-layer(0, { + scope({ + scale(x: inverse) + merge-path( + close: true, + { + line( + (-style.width / 2, style.height / 2), + (style.width / 2 - style.arrow-width, style.height / 2), + (style.width / 2, 0), + (style.width / 2 - style.arrow-width, -style.height / 2), + (-style.width / 2, -style.height / 2), + (-style.width / 2, style.height / 2), + ) + }, + stroke: style.stroke, + fill: style.fill, + ) + }) + }) + + anchor("vcc", (0, style.height / 2)) + anchor("gnd", (0, -style.height / 2)) + anchor("label", (-0.15 * if input == "d" { -1 } else { 1 }, 0)) + } + + // Constructor call + symbol("converter", name, node, draw: draw, ..params) +} + +#let dac(name, node, ..params) = adc(name, node, input: "d", ..params) diff --git a/packages/preview/zap/0.6.0/src/symbols/integrated/flipflop.typ b/packages/preview/zap/0.6.0/src/symbols/integrated/flipflop.typ new file mode 100644 index 0000000000..720d414f3b --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/integrated/flipflop.typ @@ -0,0 +1,104 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/mini.typ": clock-wedge +#import cetz.draw: anchor, content, line, polygon, rect, scope, translate + +#let flipflop(name, node, pins: (:), ..params) = { + assert(params.pos().len() == 0, message: "flipflop supports only one node") + assert(type(pins) == dictionary, message: "pins should be a dictionnary") + assert( + pins.keys().all(pin => pin in ("pin1", "pin2", "pin3", "pin4", "pin5", "pin6", "pinup", "pindown")), + message: "pins should be one of : pin1, pin2, pin3, pin4, pin5, pin6, pinup, pindown", + ) + + // Drawing function + let draw(ctx, position, style) = { + interface((-style.width / 2, -style.height / 2), (style.width / 2, style.height / 2)) + + for i in range(1, 4) { + anchor("pin" + str(i), (-1 * style.width / 2, style.height / 2 - i * style.spacing)) + } + for i in range(4, 7) { + anchor("pin" + str(i), (1 * style.width / 2, -style.height / 2 + (i - 3) * style.spacing)) + } + + anchor("pinup", (0, style.height / 2)) + anchor("pindown", (0, -style.height / 2)) + + rect((-style.width / 2, -style.height / 2), (style.width / 2, style.height / 2), fill: style.fill, stroke: style.stroke) + + for pin_name in pins.keys() { + let pin = pins.at(pin_name) + assert(type(pin) == dictionary, message: "pin must be dictionnaries") + assert( + pin.keys().all(pin => pin in ("content", "clock")), + message: "pins should be one of : content, clock", + ) + + let side = if pin_name in ("pin1", "pin2", "pin3") { + "west" + } else if pin_name == "pinup" { + "north" + } else if pin_name == "pindown" { + "south" + } else { + "east" + } + + content(pin_name, pin.at("content", default: ""), anchor: side, padding: style.padding) + + if pin.at("clock", default: false) { + let (new_side, angle) = if side == "west" { + ("west", 0deg) + } else if side == "east" { + ("west", 180deg) + } else if side == "north" { + ("west", -90deg) + } else { + ("west", 90deg) + } + content(pin_name, [#cetz.canvas({ clock-wedge() })], anchor: new_side, angle: angle) + } + } + } + + // Constructor call + symbol("flipflop", name, node, draw: draw, ..params) +} + +#let srlatch(name, node, ..params) = flipflop( + name, + node, + ..params, + pins: ( + pin1: (content: "S"), + pin3: (content: "R"), + pin4: (content: overline("Q")), + pin6: (content: "Q"), + ), +) + +#let dflipflop(name, node, ..params) = flipflop( + name, + node, + ..params, + pins: ( + pin1: (content: "D"), + pin3: (clock: true), + pin4: (content: overline("Q")), + pin6: (content: "Q"), + ), +) + +#let jkflipflop(name, node, ..params) = flipflop( + name, + node, + ..params, + pins: ( + pin1: (content: "J"), + pin2: (clock: true), + pin3: (content: "K"), + pin4: (content: overline("Q")), + pin6: (content: "Q"), + ), +) diff --git a/packages/preview/zap/0.6.0/src/symbols/integrated/mcu.typ b/packages/preview/zap/0.6.0/src/symbols/integrated/mcu.typ new file mode 100644 index 0000000000..9003a100fb --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/integrated/mcu.typ @@ -0,0 +1,52 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import cetz.draw: anchor, content, line, polygon, rect, scope, translate + +#let opposite-anchor(side) = { + if "west" in side { "east" } else if "north" in side { "south" } else if "east" in side { "west" } else if "south" in side { "north" } +} + +#let mcu(name, node, pins: (), invert: false, ..params) = { + assert(params.pos().len() == 0, message: "mcu supports only one node") + assert(type(pins) == array or type(pins) == int, message: "pins should be an array or integer") + + // Drawing function + let draw(ctx, position, style) = { + let pins = if type(pins) == int { + let pins_west = calc.ceil(pins / 2) + let pins_east = pins - pins_west + let max_pins = calc.max(pins_west, pins_east) + range(pins).map(i => ( + content: str(i + 1), + side: if i < pins_west { "west" } else { "east" }, + )) + } else { + pins + } + let west-count = pins.filter(p => p.side == "west").len() + let east-count = pins.filter(p => p.side == "east").len() + let height = calc.max(style.min-height, (calc.max(west-count, east-count)) * style.spacing + 2 * style.padding) + interface((-style.width / 2, -height / 2), (style.width / 2, height / 2)) + + rect((-style.width / 2, -height / 2), (style.width / 2, height / 2), fill: style.fill, stroke: style.stroke) + + let (wi, ei) = (0, 0) + for pin in pins { + assert(type(pin) == dictionary, message: "pins must be dictionnaries") + let is_west = "west" in pin.at("side", default: "west") + let (reverse, counter) = if is_west { + wi += 1 + (-1, wi) + } else { + ei += 1 + (1, ei) + } + let pin-number = wi + ei + anchor("pin" + str(pin-number), (reverse * style.width / 2, height / 2 - counter * style.spacing)) + content("pin" + str(pin-number), pin.at("content", default: ""), anchor: pin.at("side", default: "west"), padding: style.padding) + } + } + + // Constructor call + symbol("mcu", name, node, draw: draw, ..params) +} diff --git a/packages/preview/zap/0.6.0/src/symbols/integrated/opamp.typ b/packages/preview/zap/0.6.0/src/symbols/integrated/opamp.typ new file mode 100644 index 0000000000..4891083c84 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/integrated/opamp.typ @@ -0,0 +1,41 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import cetz.draw: anchor, content, line, polygon, rect, scope, set-style, translate + +#let opamp(name, node, invert: false, ..params) = { + assert(params.pos().len() == 0, message: "opamp supports only one node") + + // Drawing function + let draw(ctx, position, style) = { + interface((-style.width / 2, -style.height / 2), (style.width / 2, style.height / 2), io: true) + + let sgn = if invert { -1 } else { 1 } + anchor("minus", (-style.width / 2, sgn * style.sign-delta)) + anchor("plus", (-style.width / 2, -sgn * style.sign-delta)) + + if style.variant == "iec" { + rect((-style.width / 2, -style.height / 2), (style.width / 2, style.height / 2), fill: style.fill, stroke: style.stroke, name: "shape") + anchor("v", "shape.north") + anchor("g", "shape.south") + } else { + scope({ + if style.variant == "ieee" { translate((-style.width / 6, 0)) } + polygon((0, 0), 3, radius: style.width * 2 / 3, fill: style.fill, stroke: style.stroke, name: "shape") + }) + anchor("v", "shape.edge-0") + anchor("g", "shape.edge-2") + } + + set-style(stroke: style.sign-stroke) + line((rel: (style.padding - style.sign-size, 0), to: "minus"), (rel: (2 * style.sign-size, 0))) + line((rel: (style.padding - style.sign-size, 0), to: "plus"), (rel: (2 * style.sign-size, 0))) + line((rel: (style.padding, -style.sign-size), to: "plus"), (rel: (0, 2 * style.sign-size))) + + anchor("label", (style.width / 2 - if style.variant == "ieee" { .45 } else { .15 }, 0)) + } + + // Constructor call + symbol("opamp", name, node, draw: draw, ..params) +} + +#let iopamp(name, node, ..params) = opamp(name, node, ..params, invert: true) diff --git a/packages/preview/zap/0.6.0/src/symbols/lamp.typ b/packages/preview/zap/0.6.0/src/symbols/lamp.typ new file mode 100644 index 0000000000..868b664c23 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/lamp.typ @@ -0,0 +1,24 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/mini.typ": adjust-arrow +#import cetz.draw: anchor, arc, circle, line, merge-path, rect, set-style + +/// Lamp symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// -> content +#let lamp(name, node, ..params) = { + // Drawing function + let draw(ctx, position, style) = { + interface((-style.radius, -style.radius), (style.radius, style.radius), io: position.len() < 2) + + set-style(stroke: style.stroke) + circle((0, 0), radius: style.radius) + line((45deg, style.radius), (-135deg, style.radius)) + line((135deg, style.radius), (-45deg, style.radius)) + } + + // Constructor call + symbol("lamp", name, node, draw: draw, ..params) +} diff --git a/packages/preview/zap/0.6.0/src/symbols/logic.typ b/packages/preview/zap/0.6.0/src/symbols/logic.typ new file mode 100644 index 0000000000..cd5a2c8224 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/logic.typ @@ -0,0 +1,123 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import cetz.draw: anchor, arc-through, bezier, circle, content, line, merge-path, rect, rotate, set-style + +/// IEC (international)-inspired variant drawing function +#let logic-iec(ctx, position, style, text, invert, inputs, ..params) = { + let height = calc.max(style.min-height, (inputs - 1) * style.spacing + 2 * style.padding) + let inner-height = height - 2 * style.padding + interface((-style.width / 2, -height / 2), (style.width / 2, height / 2), io: false) + + rect((-style.width / 2, -height / 2), (rel: (style.width, height)), fill: style.fill, stroke: style.stroke) + content((0, 0), text, anchor: "center") + + for input in range(1, inputs + 1) { + let y = if inputs == 1 { + 0 + } else { + height / 2 - style.padding - (input - 1) * inner-height / (inputs - 1) + } + anchor("in" + str(input), (-style.width / 2, y)) + } + + if invert == true { + let radius = ctx.style.zap.invert.radius + circle((style.width / 2 + radius, 0), radius: radius, fill: style.fill, stroke: style.stroke, name: "bubble") + anchor("out", "bubble.east") + } else if invert == "wedge" { + let invert = ctx.style.zap.invert + line((style.width / 2, invert.wedge-height), (rel: (invert.wedge-width, -invert.wedge-height))) + line((style.width / 2, 0), (rel: (invert.wedge-width, 0))) + anchor("out", (style.width / 2 + invert.wedge-width, 0)) + } else { + anchor("out", (style.width / 2, 0)) + } +} + +/// IEEE/ANSI-inspired variant drawing function +#let logic-ieee(ctx, position, style, text, invert, inputs, ..params) = { + let is-or = text in ($>=1$, $=1$) + let is-xor = text in ($=1$,) + let is-triangle = text in ($1$,) + + let inputs = if is-triangle { 1 } else { inputs } + + let spread = (inputs - 1) * style.spacing + let h = calc.max(style.min-height, spread + 2 * style.padding) / 2 + let w = if is-triangle { style.width * 0.8 } else { style.width } + + interface((-w / 2, -h), (w / 2, h), io: false) + set-style(stroke: style.stroke) + + merge-path(fill: style.fill, close: true, { + if is-triangle { + line((-w / 2, w / 2), (-w / 2, -w / 2), (w / 2, 0)) + } else if is-or { + bezier((-w / 2, h), (w / 2, 0), (w / 8, h), (w / 2, h / 2)) + bezier((w / 2, 0), (-w / 2, -h), (w / 2, -h / 2), (w / 8, -h)) + bezier((-w / 2, -h), (-w / 2, h), (-w / 4, -h / 2), (-w / 4, h / 2)) + } else { + line((-w / 2, h), (0, h)) + bezier((0, h), (0, -h), (w * 0.65, h), (w * 0.65, -h)) + line((0, -h), (-w / 2, -h), (-w / 2, h)) + } + }) + + if is-xor { + bezier((-w / 2 - style.xor-spacing, h), (-w / 2 - style.xor-spacing, -h), (-w / 4 - style.xor-spacing, h / 2), (-w / 4 - style.xor-spacing, -h / 2)) + } + + if invert == true { + let radius = ctx.style.zap.invert.radius + circle((w / 2 + radius, 0), radius: radius, fill: style.fill, stroke: ctx.style.zap.invert.stroke, name: "bubble") + anchor("out", "bubble.east") + } else { + anchor("out", (w / 2, 0)) + } + + for i in range(0, inputs) { + let y-pos = (spread / 2) - (i * style.spacing) + + let x-indent = if is-or { (1 - calc.pow(y-pos / h, 2)) * 0.18 } else { 0 } + + let input-x = if is-xor { + -w / 2 - 0.15 + x-indent + } else if is-or { + -w / 2 + x-indent + } else { + -w / 2 + } + + anchor("in" + str(i + 1), (input-x, y-pos)) + } +} + +/// Logic symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// - text (content): content displayed inside the rectangle +/// - invert (bool): display an inverted output +/// - inputs (int | array): number of inputs or list of input dictionaries +/// -> content +#let logic(name, node, text: $"&"$, invert: false, inputs: 2, ..params) = { + assert(inputs >= 1, message: "logic supports minimum one input") + assert(invert in (true, false, "wedge"), message: "invert should be boolean or 'wedge'") + + // Drawing function + let draw(ctx, position, style) = { + let func = if style.variant == "iec" { logic-iec } else { logic-ieee } + func(ctx, position, style, text, invert, inputs, ..params) + } + + // Constructor call + symbol("logic", name, node, draw: draw, ..params) +} + +#let lnot(name, node, ..params) = logic(name, node, inputs: 1, ..params, text: $1$, invert: true) +#let land(name, node, ..params) = logic(name, node, ..params, text: $"&"$) +#let lnand(name, node, ..params) = logic(name, node, ..params, text: $"&"$, invert: true) +#let lor(name, node, ..params) = logic(name, node, ..params, text: $>=1$) +#let lnor(name, node, ..params) = logic(name, node, ..params, text: $>=1$, invert: true) +#let lxor(name, node, ..params) = logic(name, node, ..params, text: $=1$) +#let lxnor(name, node, ..params) = logic(name, node, ..params, text: $=1$, invert: true) diff --git a/packages/preview/zap/0.6.0/src/symbols/motor.typ b/packages/preview/zap/0.6.0/src/symbols/motor.typ new file mode 100644 index 0000000000..ae6b04a143 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/motor.typ @@ -0,0 +1,35 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/mini.typ": ac-sign, dc-sign +#import cetz.draw: anchor, arc, circle, content, rect + +/// Motor symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// - current ("dc" | "ac"): type of current +/// -> content +#let motor(uid, name, node, current: "dc", ..params) = { + assert(current in ("dc", "ac"), message: "current must be ac or dc") + + // Drawing function + let draw(ctx, position, style) = { + interface((-style.radius, -style.radius), (style.radius, style.radius), io: position.len() < 2) + + circle((0, 0), radius: style.radius, fill: style.fill, stroke: style.stroke) + content((0, 0), anchor: "south", "M", padding: .03) + let symbol = if current == "dc" { dc-sign } else { ac-sign } + cetz.draw.scope({ + cetz.draw.rotate(-ctx.rotation) + cetz.draw.translate(y: -0.2) + cetz.draw.scale(0.7) + symbol(ctx) + }) + } + + // Constructor call + symbol(uid, name, node, draw: draw, ..params) +} + +#let dcmotor(name, node, ..params) = motor("dcmotor", name, node, ..params, current: "dc") +#let acmotor(name, node, ..params) = motor("acmotor", name, node, ..params, current: "ac") diff --git a/packages/preview/zap/0.6.0/src/symbols/node.typ b/packages/preview/zap/0.6.0/src/symbols/node.typ new file mode 100644 index 0000000000..b9f910e406 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/node.typ @@ -0,0 +1,33 @@ +#import "/src/deps.typ": cetz +#import "/src/utils.typ": opposite-anchor +#import cetz.draw: circle, content, get-ctx, on-layer + +/// Node symbol to use on a canvas +/// +/// - name (str): symbole unique identifier +/// - position (coordinate): symbol position coordinate +/// - fill (bool): whether to fill the node circle +/// -> content +#let node(name, position, fill: true, label: none, ..params) = { + assert(type(name) == str, message: "node name must be a string") + + on-layer(1, ctx => { + let style = cetz.styles.resolve(ctx.style.zap, root: "node") + circle(position, radius: style.radius, fill: if fill { style.fill } else { style.nofill }, name: name, stroke: style.stroke, ..params) + }) + + // Label + on-layer(0, ctx => { + if label != none { + if type(label) == dictionary and label.at("content", default: none) == none { panic("label dictionary needs at least content key") } + let label-style = ctx.style.zap.label + label-style = cetz.styles.merge(label-style, if type(label) == dictionary { label } else { (content: label) }) + content( + if type(label-style.anchor) == str { name + "." + label-style.anchor } else { label-style.anchor }, + anchor: opposite-anchor(label-style.anchor), + label-style.content, + padding: label-style.distance, + ) + } + }) +} diff --git a/packages/preview/zap/0.6.0/src/symbols/piezo.typ b/packages/preview/zap/0.6.0/src/symbols/piezo.typ new file mode 100644 index 0000000000..d6ec2d4cd6 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/piezo.typ @@ -0,0 +1,25 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/mini.typ": ac-sign, dc-sign +#import cetz.draw: anchor, arc, circle, content, line, rect, stroke + +/// Piezoelectric crystal unit symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// - current ("dc" | "ac"): type of current +/// -> content +#let piezo(name, node, ..params) = { + // Drawing function + let draw(ctx, position, style) = { + interface((-style.width / 2 - style.spacing, -style.height / 2), (style.width / 2 + style.spacing, style.height / 2), io: position.len() < 2) + + stroke(style.stroke) + rect((-style.width / 2, -style.height / 2), (style.width / 2, style.height / 2)) + line((-style.width / 2 - style.spacing, -style.height * 0.8 / 2), (rel: (0, style.height * 0.8))) + line((style.width / 2 + style.spacing, -style.height * 0.8 / 2), (rel: (0, style.height * 0.8))) + } + + // Constructor call + symbol("piezo", name, node, draw: draw, ..params) +} diff --git a/packages/preview/zap/0.6.0/src/symbols/resistor.typ b/packages/preview/zap/0.6.0/src/symbols/resistor.typ new file mode 100644 index 0000000000..a6cda414c1 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/resistor.typ @@ -0,0 +1,71 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/mini.typ": adjust-arrow, adjustable-arrow +#import cetz.draw: anchor, line, rect, set-style + +/// Resistor symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// - variable (bool): displays a diagonal arrow +/// - heatable (bool): transform to heater device +/// - adjustable (bool): display an orthogonal arrow +/// - preset (bool): display a diagonal arrow with flat tip +/// - sensor (bool): display a diagonal arrow with flat line +/// -> content +#let resistor(name, node, variable: false, heatable: false, adjustable: false, preset: false, sensor: false, ..params) = { + assert(type(variable) == bool, message: "variable must be of type bool") + assert(type(adjustable) == bool, message: "adjustable must be of type bool") + + // Drawing function + let draw(ctx, position, style) = { + interface((-style.width / 2, -style.height / 2), (style.width / 2, style.height / 2), io: position.len() < 2) + + set-style(stroke: style.stroke) + if style.variant == "iec" { + rect( + (-style.width / 2, -style.height / 2), + ( + style.width / 2, + style.height / 2, + ), + fill: style.fill, + ) + } else { + let step = style.width / (style.zigs * 2) + let sign = -1 + let x = style.width / 2 + line( + (-x, 0), + (rel: (step / 2, style.height / 2)), + ..for _ in range(style.zigs * 2 - 1) { + ((rel: (step, style.height * sign)),) + sign *= -1 + }, + (x, 0), + ) + } + if variable { + adjust-arrow("variable") + } else if preset { + adjust-arrow("preset") + } else if sensor { + adjust-arrow("sensor") + } else if adjustable { + adjustable-arrow((0, style.height / 2)) + } + if heatable { + for i in range(3) { + let x = style.width / 4 * (i + 1) - style.width / 2 + line((x, -style.height / 2), (x, style.height / 2)) + } + } + } + + // Constructor call + symbol("resistor", name, node, draw: draw, ..params) +} + +#let rheostat(name, node, ..params) = resistor(name, node, ..params, variable: true) +#let potentiometer(name, node, ..params) = resistor(name, node, ..params, adjustable: true) +#let heater(name, node, ..params) = resistor(name, node, ..params, heatable: true) diff --git a/packages/preview/zap/0.6.0/src/symbols/source.typ b/packages/preview/zap/0.6.0/src/symbols/source.typ new file mode 100644 index 0000000000..13a85b5ee8 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/source.typ @@ -0,0 +1,89 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/mini.typ": ac-sign +#import cetz.draw: anchor, circle, content, line, mark, polygon, rect, set-style + +/// Current source symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// - dependent (bool): whether this source depends on some value +/// -> content +#let isource(name, node, dependent: false, ..params) = { + assert(type(dependent) == bool, message: "dependent must be boolean") + + // Drawing function + let draw(ctx, position, style) = { + let factor = if dependent { 1.1 } else { 1 } + interface((-style.radius * factor, -style.radius * factor), (style.radius * factor, style.radius * factor), io: position.len() < 2) + + set-style(stroke: style.stroke) + if dependent { + polygon((0, 0), 4, fill: style.fill, radius: style.radius * factor) + } else { + circle((0, 0), fill: style.fill, radius: style.radius) + } + if style.variant == "iec" { + line((0, -style.radius * factor), (rel: (0, 2 * style.radius * factor))) + } else { + line( + (-style.radius + style.padding, 0), + (rel: (2 * style.radius - 1.85 * style.padding, 0)), + mark: (end: ">", scale: style.arrow-scale * style.scale.at("x", default: 1.0)), + fill: cetz.util.resolve-stroke(ctx.style.zap.arrow.stroke).paint, + stroke: ctx.style.zap.arrow.stroke, + ) + } + } + + // Constructor call + symbol("isource", name, node, draw: draw, ..params) +} + +#let disource(name, node, ..params) = isource(name, node, dependent: true, ..params) +#let acisource(name, node, ..params) = isource(name, node, current: "ac", ..params) + +/// Voltage source symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// - dependent (bool): whether this source depends on some value +/// - current ('dc' | 'ac' | 'tri' | 'rect' | 'saw' | 'sin'): type of current flowing through +/// -> content +#let vsource(name, node, dependent: false, current: "dc", ..params) = { + assert(current in ("dc", "ac", "tri", "rect", "saw", "sin"), message: "current must be 'dc', 'ac', 'tri', 'rect', 'saw' or 'sin'") + + // Drawing function + let draw(ctx, position, style) = { + let factor = if dependent { 1.1 } else { 1 } + interface((-style.radius * factor, -style.radius * factor), (style.radius * factor, style.radius * factor), io: position.len() < 2) + + set-style(stroke: style.stroke) + if dependent { + polygon((0, 0), 4, fill: style.fill, radius: style.radius * factor) + } else { + circle((0, 0), fill: style.fill, radius: style.radius) + } + if style.variant == "iec" { + if current != "dc" { + cetz.draw.scope({ + cetz.draw.rotate(-ctx.rotation) + ac-sign(ctx, waveform: current) + }) + } else { + line((-style.radius * factor, 0), (rel: (2 * style.radius * factor, 0))) + } + } else { + set-style(stroke: ctx.style.zap.sign.stroke) + line((rel: (-style.radius + style.padding, -style.sign.size)), (rel: (0, 2 * style.sign.size))) + line((style.radius - style.padding - style.sign.delta, -style.sign.size), (rel: (0, 2 * style.sign.size))) + line((rel: (style.sign.size, -style.sign.size)), (rel: (-2 * style.sign.size, 0))) + } + } + + // Constructor call + symbol("vsource", name, node, draw: draw, ..params) +} + +#let dvsource(name, node, ..params) = vsource(name, node, ..params, dependent: true) +#let acvsource(name, node, ..params) = vsource(name, node, ..params, current: "ac") diff --git a/packages/preview/zap/0.6.0/src/symbols/stub.typ b/packages/preview/zap/0.6.0/src/symbols/stub.typ new file mode 100644 index 0000000000..3854bee2ae --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/stub.typ @@ -0,0 +1,71 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/mini.typ": ac-sign +#import "/src/utils.typ": opposite-anchor +#import "/src/symbols/wire.typ": wire +#import cetz.draw: anchor, circle, content, line, mark, polygon, rect, set-style + +/// Stub symbol to use inside a circuit +/// +/// - node (coordinate): stub position coordinates +/// - dir ('north' | 'east' | 'south' | 'west'): direction of the stub +/// - invert (bool | 'wedge'): displays a bubble/wedge at the origin +/// -> content +#let stub(node, dir: "north", invert: false, ..params) = { + assert(params.pos().len() == 0, message: "stub must have exactly one node") + assert(invert in (true, false, "wedge"), message: "invert should be boolean or 'wedge'") + + let args = params.named() + if "label" in args and args.label != none { + if type(args.label) == dictionary { + args = args + (label: args.label + (anchor: dir)) + } else { + args = args + (label: (content: args.label, anchor: dir, align: opposite-anchor(dir))) + } + } + + // Drawing function + let draw(ctx, position, style) = { + let diff = { + if dir == "north" { + ((0, style.length), (0.0001, style.length)) + } else if dir == "south" { + ((0, -style.length), (0.0001, -style.length)) + } else if dir == "east" { + ((style.length, 0), (style.length, 0.0001)) + } else if dir == "west" { + ((-style.length, 0), (-style.length, 0.0001)) + } + } + + interface((0, 0), diff.at(1), io: false) + + wire((0, 0), diff.at(0)) + + if invert == true { + let r = ctx.style.zap.invert.radius + let cx = if dir == "east" { r } else if dir == "west" { -r } else { 0 } + let cy = if dir == "north" { r } else if dir == "south" { -r } else { 0 } + circle((cx, cy), radius: r, fill: white, stroke: ctx.style.zap.invert.stroke) + } else if invert == "wedge" { + let invert = ctx.style.zap.invert + let (a, b) = (-1, -1) + if dir == "east" { + (a, b) = (1, 1) + } else if dir == "west" { + (a, b) = (1, -1) + } else if dir == "north" { + (a, b) = (-1, 1) + } + line((invert.wedge-width * b, 0), (0, invert.wedge-height * a), stroke: ctx.style.zap.invert.stroke) + } + } + + // Constructor call + symbol("stub", "l", node, draw: draw, ..args) +} + +#let nstub(node, ..params) = stub(node, ..params, dir: "north") +#let sstub(node, ..params) = stub(node, ..params, dir: "south") +#let estub(node, ..params) = stub(node, ..params, dir: "east") +#let wstub(node, ..params) = stub(node, ..params, dir: "west") diff --git a/packages/preview/zap/0.6.0/src/symbols/supply.typ b/packages/preview/zap/0.6.0/src/symbols/supply.typ new file mode 100644 index 0000000000..0477666c9b --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/supply.typ @@ -0,0 +1,142 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/symbols/wire.typ": wire +#import cetz.draw: anchor, line, polygon, scale, scope, set-style + +/// Ground symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// -> content +#let ground(name, node, ..params) = { + assert(params.pos().len() == 0, message: "ground supports only one node") + + // Drawing function + let draw(ctx, position, style) = { + wire((0, 0), (0, -style.distance)) + polygon( + (0, -style.distance), + 3, + anchor: "north", + radius: style.radius, + angle: -90deg, + name: "polygon", + stroke: style.stroke, + fill: style.fill, + ) + + let (width, height) = cetz.util.measure(ctx, "polygon") + interface((-width / 2, -height / 2), (width / 2, height / 2)) + anchor("default", (0, 0)) + } + + // Constructor call + symbol("ground", name, node, draw: draw, ..params) +} + +/// Frame symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// -> content +#let frame(name, node, ..params) = { + assert(params.pos().len() == 0, message: "frame supports only one node") + + // Drawing function + let draw(ctx, position, style) = { + wire((0, 0), (0, -style.distance)) + let delta = style.width / 2 + + set-style(stroke: style.stroke) + line((-style.width / 2, -style.distance), (style.width / 2, -style.distance)) + for i in (0, 1, 2) { + line((-style.width / 2 + (1 - i) * .01 + i * delta, -style.distance), ( + rel: (angle: -style.angle - 90deg, radius: style.depth), + )) + } + + interface((-style.width / 2, style.distance), (style.width / 2, -style.distance)) + anchor("default", (0, 0)) + } + + // Constructor call + symbol("frame", name, node, draw: draw, ..params) +} + +/// Earth symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// -> content +#let earth(name, node, ..params) = { + assert(params.pos().len() == 0, message: "earth supports only one node") + + // Drawing function + let draw(ctx, position, style) = { + wire((0, 0), (0, -style.distance)) + for i in (0, 1, 2) { + line( + (-style.width / 2 + i * style.delta, -style.distance - i * style.spacing), + (style.width / 2 - i * style.delta, -style.distance - i * style.spacing), + ..style, + ) + } + + interface((-style.width / 2, -style.distance - style.spacing * 2), (style.width / 2, -style.distance)) + anchor("default", (0, 0)) + } + + // Constructor call + symbol("earth", name, node, draw: draw, ..params) +} + +/// Voltage supply symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// - invert (bool): whether to vertically invert the symbol +/// -> content +#let vsupply(uid, name, node, invert: false, ..params) = { + assert(params.pos().len() == 0, message: "vsupply supports only one node") + + // Drawing function + let draw(ctx, position, style) = { + let direction = if invert { -1 } else { 1 } + let cos = calc.cos(style.angle) * style.radius + let sin = calc.sin(style.angle) * style.radius + scope({ + scale(y: direction) + wire((0, 0), (0, style.distance)) + line((rel: (-sin, -cos), to: (0, style.distance)), (0, style.distance), (rel: (sin, -cos)), stroke: style.stroke) + }) + interface((-sin, (style.distance - cos) * direction), (sin, style.distance * direction), (0, 0), (0, 0), io: false) + anchor("default", (0, 0)) + } + + // Constructor call + symbol(uid, name, node, draw: draw, ..params) +} + +/// Rectangular ground symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// -> content +#let rground(name, node, ..params) = { + assert(params.pos().len() == 0, message: "rground supports only one node") + + // Drawing function + let draw(ctx, position, style) = { + wire((0, 0), (0, -style.distance)) + line((-style.width / 2, -style.distance), (style.width / 2, -style.distance)) + + interface((-style.width / 2, style.distance), (style.width / 2, -style.distance)) + anchor("default", (0, 0)) + } + + // Constructor call + symbol("rground", name, node, draw: draw, ..params) +} + +#let vcc(name, node, ..params) = vsupply("vcc", name, node, ..params) +#let vee(name, node, ..params) = vsupply("vee", name, node, ..params, invert: true) diff --git a/packages/preview/zap/0.6.0/src/symbols/switch.typ b/packages/preview/zap/0.6.0/src/symbols/switch.typ new file mode 100644 index 0000000000..d7413e0437 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/switch.typ @@ -0,0 +1,22 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/symbols/wire.typ": wire +#import cetz.draw: anchor, circle, hide, line, mark, rect + +/// Switch symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// - closed (bool): switch state +/// -> content +#let switch(name, node, closed: false, ..params) = { + // Drawing function + let draw(ctx, position, style) = { + interface((-style.width / 2, -0.2), (style.width / 2, 0.2), io: position.len() < 2) + + wire((-style.width / 2, 0), (radius: style.width / 2, angle: if closed { 0deg } else { style.angle })) + } + + // Constructor call + symbol("switch", name, node, draw: draw, ..params) +} diff --git a/packages/preview/zap/0.6.0/src/symbols/transformer.typ b/packages/preview/zap/0.6.0/src/symbols/transformer.typ new file mode 100644 index 0000000000..8fbb68798e --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/transformer.typ @@ -0,0 +1,33 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import cetz.draw: anchor, circle, hide, line, mark, merge-path, rect, set-style + +/// Transformer symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// -> content +#let transformer(name, node, ..params) = { + // Drawing function + let draw(ctx, position, style) = { + interface((-style.distance / 2 - style.radius, -style.radius), (style.distance / 2 + style.radius, style.radius), io: position.len() < 2) + + set-style(circle: (radius: style.radius)) + merge-path( + join: false, + stroke: none, + fill: style.fill, + { + circle((-style.distance / 2, 0)) + circle((style.distance / 2, 0)) + }, + ) + + set-style(stroke: style.stroke) + circle((-style.distance / 2, 0)) + circle((style.distance / 2, 0)) + } + + // Constructor call + symbol("transformer", name, node, draw: draw, ..params) +} diff --git a/packages/preview/zap/0.6.0/src/symbols/transistors/bjt.typ b/packages/preview/zap/0.6.0/src/symbols/transistors/bjt.typ new file mode 100644 index 0000000000..b644c6dbaf --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/transistors/bjt.typ @@ -0,0 +1,57 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/mini.typ": center-mark +#import "/src/symbols/wire.typ": wire +#import cetz.draw: anchor, circle, content, hide, line, mark, set-style, translate + +/// Bipolar Junction Transistor symbol to use inside a circuit +/// +/// - name (str): symbole unique identifier +/// - node (coordinate): symbol position coordinates +/// - polarisation ('npn' | 'pnp'): transistor polarisation +/// - envelope (bool): displays a circle envelope around the symbol +/// -> content +#let bjt(name, node, polarisation: "npn", envelope: false, ..params) = { + assert(polarisation in ("npn", "pnp"), message: "polarisation must `npn` or `pnp`") + assert(type(envelope) == bool, message: "envelope must be of type bool") + assert(params.pos().len() == 0, message: "bjt supports only one node") + + // Drawing function + let draw(ctx, position, style) = { + interface((-style.radius, -style.radius), (style.radius, style.radius)) + + translate((-calc.cos(style.aperture) * style.radius, 0)) + + let sgn = if polarisation == "npn" { 1 } else { -1 } + anchor("base", ((-style.radius, 0), 30%, (style.radius, 0))) + anchor("e", (-style.aperture * sgn, style.radius)) + anchor("c", (style.aperture * sgn, style.radius)) + anchor("b", if envelope { (-style.radius, 0) } else { "base" }) + + set-style(stroke: style.stroke) + if envelope { + circle((0, 0), radius: style.radius, fill: style.fill, name: "circle") + wire("base", (-style.radius, 0)) + } else { + hide(circle((0, 0), radius: style.radius, name: "circle")) + } + + line((to: "base", rel: (0, -style.base-height / 2)), (to: "base", rel: (0, style.base-height / 2))) + line((to: "base", rel: (0, -style.base-distance * sgn)), "e", stroke: cetz.util.resolve-stroke(ctx.style.zap.wire.stroke).thickness, mark: center-mark( + symbol: if sgn == -1 { "<" } else { ">" }, + stroke: 0pt, + fill: cetz.util.resolve-stroke(style.stroke).paint, + )) + wire((to: "base", rel: (0, style.base-distance * sgn)), "c") + + if params.named().at("label", default: none) != none { + content((style.radius, 0), params.named().at("label"), anchor: "west", padding: if envelope { 0.2 } else { -0.1 }) + } + } + + // Constructor call + symbol("bjt", name, node, draw: draw, ..params, label: none) +} + +#let pnp(name, node, ..params) = bjt(name, node, polarisation: "pnp", ..params) +#let npn(name, node, ..params) = bjt(name, node, polarisation: "npn", ..params) diff --git a/packages/preview/zap/0.6.0/src/symbols/transistors/jfet.typ b/packages/preview/zap/0.6.0/src/symbols/transistors/jfet.typ new file mode 100644 index 0000000000..f4d164a332 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/transistors/jfet.typ @@ -0,0 +1,52 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/symbols/wire.typ": wire +#import cetz.draw: anchor, circle, content, floating, hide, line, mark, scale, set-origin, set-style, translate + +#let jfet( + name, + node, + channel: "n", + envelope: false, + ..params, +) = { + assert(type(envelope) == bool, message: "envelope must be of type bool") + assert(channel in ("p", "n"), message: "channel must be `p` or `n`") + + // Drawing function + let draw(ctx, position, style) = { + let (height, width, base-width, base-spacing, radius) = style + interface((-height / 2, -width / 2), (height / 2, width / 2)) + + let center = (-height / 2, 0) + + anchor("d", (height / 2, width / 2)) + anchor("s", (height / 2, -width / 2)) + + set-style(stroke: style.stroke) + if envelope { + circle(center, radius: radius, fill: style.fill, name: "c") + } else { + hide(circle((0, 0), radius: radius, fill: style.fill, name: "c")) + } + + line((-height / 2, -base-width / 2), (rel: (0, base-width))) + wire("d", (rel: (0, 0)), (rel: (-height, 0))) + wire("s", (rel: (0, 0)), (rel: (-height, 0))) + wire((rel: (0, -width / 2), to: center), (rel: (-height, 0))) + anchor("g", (rel: (-height, -width / 2), to: center)) + mark( + (-height, -width / 2), + (rel: (height, 0)), + symbol: if (channel == "n") { ">" } else { "<" }, + fill: black, + anchor: "center", + ) + } + + // Constructor call + symbol("jfet", name, node, draw: draw, ..params) +} + +#let pjfet(name, node, ..params) = jfet(name, node, channel: "p", ..params) +#let njfet(name, node, ..params) = jfet(name, node, channel: "n", ..params) diff --git a/packages/preview/zap/0.6.0/src/symbols/transistors/mosfet.typ b/packages/preview/zap/0.6.0/src/symbols/transistors/mosfet.typ new file mode 100644 index 0000000000..66b5ff12f1 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/transistors/mosfet.typ @@ -0,0 +1,86 @@ +#import "/src/symbol.typ": interface, symbol +#import "/src/deps.typ": cetz +#import "/src/symbols/wire.typ": wire +#import cetz.draw: anchor, circle, content, floating, hide, line, mark, scale, set-origin, set-style, translate + +#let mosfet( + name, + node, + channel: "n", + envelope: false, + mode: "enhancement", + bulk: "internal", + ..params, +) = { + assert(type(envelope) == bool, message: "envelope must be of type bool") + assert(mode in ("enhancement", "depletion"), message: "mode must be `enhancement` or `depletion`") + assert(channel in ("p", "n"), message: "channel must be `p` or `n`") + assert(bulk in ("internal", "external", none), message: "substrate must be `internal`, `external` or none") + + // Drawing function + let draw(ctx, position, style) = { + let (height, width, base-width, base-spacing, radius) = style + interface((-height, -width / 2), (0, width / 2)) + + let center = (-height / 2, 0) + + anchor("d", (0, width / 2)) + anchor("s", (0, -width / 2)) + if bulk == "external" { + anchor("bulk", (0, 0)) + } + + set-style(stroke: style.stroke) + if envelope { + circle(center, radius: radius, fill: style.fill, name: "c") + } else { + hide(circle((0, 0), radius: radius, fill: style.fill, name: "c")) + } + + if mode == "enhancement" { + let bar-length = (base-width - 2 * base-spacing) / 3 + for i in range(3) { + line((-height, -base-width / 2 + i * (bar-length + base-spacing)), (rel: (0, bar-length))) + } + } else { + line((-height, -base-width / 2), (rel: (0, base-width))) + } + if bulk == "internal" { + wire((0, 0), (0, -width / 2)) + } + wire("d", (rel: (0, 0)), (rel: (-height, 0))) + wire("s", (rel: (0, 0)), (rel: (-height, 0))) + + anchor("gl", (rel: (-3 * height / 4, width / 2), to: center)) + + if bulk != none { + wire((-height, 0), (rel: (height, 0))) + mark(((-height, 0), 50%, (rel: (height, 0))), (-height, 0), symbol: if (channel == "n") { ">" } else { "<" }, fill: black, anchor: "center") + wire("gl", (rel: (0, -width)), (rel: (-height / 4, 0))) + anchor("g", (rel: (-height, -width / 2), to: center)) + } else { + wire("gl", (rel: (0, -width / 2)), (rel: (0, -width / 2))) + wire((rel: (0, -width / 2)), (rel: (-height / 2, 0))) + anchor("g", (rel: (-height / 2, -width / 2))) + + mark( + ( + -height / 2, + if (channel == "n") { -width / 2 } else { width / 2 }, + ), + (rel: (height, 0)), + symbol: if (channel == "n") { ">" } else { "<" }, + fill: black, + anchor: "center", + ) + } + } + + // Constructor call + symbol("mosfet", name, node, draw: draw, ..params) +} + +#let pmos(name, node, ..params) = mosfet(name, node, channel: "p", ..params) +#let nmos(name, node, ..params) = mosfet(name, node, channel: "n", ..params) +#let pmosd(name, node, ..params) = mosfet(name, node, channel: "p", mode: "depletion", ..params) +#let nmosd(name, node, ..params) = mosfet(name, node, channel: "n", mode: "depletion", ..params) diff --git a/packages/preview/zap/0.6.0/src/symbols/wire.typ b/packages/preview/zap/0.6.0/src/symbols/wire.typ new file mode 100644 index 0000000000..8195f8b9e8 --- /dev/null +++ b/packages/preview/zap/0.6.0/src/symbols/wire.typ @@ -0,0 +1,57 @@ +#import "/src/deps.typ": cetz +#import "/src/utils.typ": zigzag +#import "/src/decorations.typ": current +#import cetz.draw: anchor, circle, content, group, hide, line, mark, set-style +#import cetz.styles: merge + +// Save native function +#let typst-ratio = ratio + +/// Electrical wire symbol to use inside a circuit +/// +/// - bits (int): number of bit marks +/// - shape (str): wire routing style +/// - ratio (ratio): position of wire segments +/// - axis (str): axis of the shape +/// - i (str | dict): current decoration +/// - name (str): symbol identifier +/// -> content +#let wire(bits: 0, shape: "direct", ratio: 50%, axis: "x", i: none, name: none, ..params) = { + assert(type(bits) == int, message: "bits must be an int") + assert(params.pos().len() >= 2, message: "wires need at least two nodes") + assert(type(ratio) in (typst-ratio, int, float, length), message: "ratio must be a ratio, a number or a length") + assert(shape in ("direct", "zigzag", "dodge"), message: "shape must be 'direct', 'zigzag' or 'dodge'") + + group(name: name, ctx => { + let style = cetz.styles.resolve(ctx.style.zap, merge: params.named(), root: "wire") + let (ctx, ..nodes) = cetz.coordinate.resolve(ctx, ..params.pos()) + cetz.draw.stroke(style.stroke) + + // Drawing the wire using the shape parameter + anchor("in", nodes.first()) + anchor("out", nodes.last()) + for (index, node) in nodes.enumerate() { anchor("p" + str(index), node) } + if shape == "direct" { + line(..nodes, name: "line") + } else if shape == "zigzag" { + if nodes.len() < 2 { return } + let generated = zigzag(ctx, nodes, axis, ratio) + cetz.draw.line(..generated, nodes.last(), name: "line") + } + + // Multi-bits wiring by displaying a slash with a number + for i in range(bits) { + let delta = i * 0.4 + line((rel: (0, -0.2), to: "line.50%"), (rel: (0, 0.2), to: "line.50%")) + } + + // Current decoration + cetz.draw.set-style(zap: (decoration: (current: (distance: 0pt)))) + cetz.draw.hide(cetz.draw.rect("line.50%", "line.51%", name: "symbol")) + if i != none { current(i) } + }) +} + + +#let zwire(..params) = wire(shape: "zigzag", ..params) +#let swire(..params) = wire(shape: "zigzag", ratio: 100%, ..params) diff --git a/packages/preview/zap/0.6.0/src/utils.typ b/packages/preview/zap/0.6.0/src/utils.typ new file mode 100644 index 0000000000..7f7e19993b --- /dev/null +++ b/packages/preview/zap/0.6.0/src/utils.typ @@ -0,0 +1,85 @@ +#import "/src/deps.typ": cetz +#import cetz.draw: anchor + +/// Gives the label anchor depending on the symbol orientation in degrees +/// +/// - angle (angle): symbol rotation angle +/// -> str +#let get-label-anchor(angle) = { + let angle = angle.deg() + let normalized-angle = calc.rem(if angle < 0 { angle + 360 } else { angle }, 360) + let tolerance = 15 + if calc.abs(normalized-angle) < tolerance { + return "south" + } else if calc.abs(normalized-angle - 90) < tolerance { + return "east" + } else if calc.abs(normalized-angle - 180) < tolerance { + return "north" + } else if calc.abs(normalized-angle - 270) < tolerance { + return "west" + } else { + if normalized-angle > 0 and normalized-angle < 90 { + return "south-east" + } else if normalized-angle > 90 and normalized-angle < 180 { + return "north-east" + } else if normalized-angle > 180 and normalized-angle < 270 { + return "north-west" + } else { + return "south-west" + } + } +} + +/// Gives the opposite anchor +/// +/// - anchor (str): original anchor +/// -> str +#let opposite-anchor(anchor) = { + if anchor == "north" { + "south" + } else if anchor == "south" { + "north" + } else if anchor == "east" { + "west" + } else if anchor == "west" { + "east" + } else if anchor == "north-east" { + "south-west" + } else if anchor == "north-west" { + "south-east" + } else if anchor == "south-east" { + "north-west" + } else if anchor == "south-west" { + "north-east" + } else if anchor == "center" { + "center" + } else { + panic("anchor not recognized: " + anchor) + } +} + +/// Returns an array of points forming a zigzag path from a list of nodes +/// +/// - ctx (context): current CeTZ context +/// - nodes (array): list of nodes from which to create the path +/// - axis ('x' | 'y'): which axis to start and end with +/// - ratio (ratio): position of turn +/// -> array +#let zigzag(ctx, nodes, axis, ratio, ..params) = { + let generated = () + for i in range(nodes.len() - 1) { + let p1 = nodes.at(i) + let p2 = nodes.at(i + 1) + let (ctx, p-mid) = cetz.coordinate.resolve(ctx, (p1, ratio, p2)) + + let p-mid1 = if axis == "x" { (p-mid.at(0), p1.at(1)) } else { (p1.at(0), p-mid.at(1)) } + let p-mid2 = if axis == "x" { (p-mid.at(0), p2.at(1)) } else { (p2.at(0), p-mid.at(1)) } + + cetz.draw.group(name: "p" + str(i) + "-p" + str(i + 1), { + anchor("a", p-mid1) + anchor("b", p-mid2) + }) + generated = (..generated, p1, p-mid1, p-mid2) + } + return generated +} diff --git a/packages/preview/zap/0.6.0/typst.toml b/packages/preview/zap/0.6.0/typst.toml new file mode 100644 index 0000000000..b8407c96f1 --- /dev/null +++ b/packages/preview/zap/0.6.0/typst.toml @@ -0,0 +1,14 @@ +[package] +name = "zap" +version = "0.6.0" +compiler = "0.14.0" +entrypoint = "src/lib.typ" +authors = ["Louis Grange "] +license = "MIT" +homepage = "https://zap.grangelouis.ch" +description = "A package to draw standards-inspired electronic circuits, powered by CeTZ" +repository = "https://codeberg.org/grangelouis/zap" +categories = ["visualization", "components"] +disciplines = ["engineering", "drawing", "physics"] +keywords = ["zap", "electronic", "circuit", "cetz", "circuitikz", "voltage", "draw", "schema", "engineering"] +exclude = [".forgejo", "docs", "examples", "tests"] \ No newline at end of file