diff --git a/packages/preview/zap/0.2.1/.gitignore b/packages/preview/zap/0.2.1/.gitignore new file mode 100644 index 0000000000..a66b35a006 --- /dev/null +++ b/packages/preview/zap/0.2.1/.gitignore @@ -0,0 +1,5 @@ +/.idea/ +/main.typ +*.pdf +/doc/**/*.svg +/main.svg diff --git a/packages/preview/zap/0.2.1/LICENSE b/packages/preview/zap/0.2.1/LICENSE new file mode 100644 index 0000000000..65c5ca88a6 --- /dev/null +++ b/packages/preview/zap/0.2.1/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/packages/preview/zap/0.2.1/README.md b/packages/preview/zap/0.2.1/README.md new file mode 100644 index 0000000000..1feb8eefa2 --- /dev/null +++ b/packages/preview/zap/0.2.1/README.md @@ -0,0 +1,52 @@ +![](https://badgers.space/github/release/l0uisgrange/zap?theme=tailwind) +![](https://badgers.space/github/checks/l0uisgrange/zap?theme=tailwind) +![](https://badgers.space/github/contributors/l0uisgrange/zap?theme=tailwind) +![](https://badgers.space/github/open-issues/l0uisgrange/zap?theme=tailwind) + +# âšĄī¸ Zap for Typst + +**Zap** is a lightweight đŸĒļ Typst package that makes drawing electronic circuits simple and intuitive. It's the first Typst library designed to align with widely recognized standards like **IEC** and **IEEE/ANSI** 📜. + +[Documentation](https://l0uisgrange.github.io/zap/) — [Examples](https://l0uisgrange.github.io/zap/examples) — [Forum](https://github.com/l0uisgrange/zap/discussions/categories/q-a) + +## Simple examples + +You can find the full list of examples [here](https://l0uisgrange.github.io/zap/examples). + + + + + + + + + + +
+ Example 1 + + Example 2 +
Simple exampleWheatstone bridge
+ + +## Quick usage + +```typst +#import "@preview/zap:0.2.1" + +#zap.canvas({ + import zap: * + + isource("i1", (0,0), (5,0)) + resistor("r1", (5,5), (0,5)) + wire("r1.out", "i1.out") +}) +``` + +## Online documentation + +You can find the full documentation 📚 [available online](https://l0uisgrange.github.io/zap/). It provides comprehensive guides, a detailed list of components, full API references, 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://github.com/l0uisgrange/zap/blob/main/CONTRIBUTING.md) and join the journey 🤩! diff --git a/packages/preview/zap/0.2.1/src/component.typ b/packages/preview/zap/0.2.1/src/component.typ new file mode 100644 index 0000000000..f0147e6cbe --- /dev/null +++ b/packages/preview/zap/0.2.1/src/component.typ @@ -0,0 +1,140 @@ +#import "dependencies.typ": cetz +#import "styles.typ": default-style +#import "decorations.typ": current, flow, voltage +#import "components/nodes.typ": node +#import "components/nodes.typ": node +#import "utils.typ": get-label-anchor + +#let component( + draw: none, + label: none, + i: none, + f: none, + u: none, + n: none, + position: 50%, + scale: 1.0, + rotate: 0deg, + debug: false, + style: none, + ..params, +) = { + let p-position = position + let (uid, name, ..position) = params.pos() + if position.at(1, default: none) == none { + position = (position.first(),) + } + assert(position.len() in (1, 2), message: "accepts only 2 or 3 (for 2 nodes components only) positional arguments") + assert(position.at(1, default: none) == none or rotate == 0deg, message: "cannot use rotate argument with 2 nodes") + assert(type(name) == str, message: "component name must be a string") + assert(type(scale) == float, message: "scale must be a float") + assert(type(rotate) == angle, message: "rotate must an angle") + assert(label == none or type(label) in (content, str, dictionary), message: "label must content, dictionary or string") + assert(params.at("variant", default: default-style.variant) in ("ieee", "iec", "pretty"), message: "variant must be 'iec', 'ieee' or 'pretty'") + assert(n in (none, "*-", "*-*", "-*", "o-*", "*-o", "o-", "-o", "o-o")) + + let p-rotate = rotate + let p-scale = scale + let p-draw = draw + let p-style = style + import cetz.draw: * + + group(name: name, ctx => { + let pre-style = cetz.styles.resolve(ctx.style, root: "zap", base: default-style) + let base-style = p-style + pre-style + pre-style.at(uid, default: (something: none)) + let style = cetz.styles.resolve(base-style, merge: params.named()) + let p-rotate = p-rotate + let (ctx, ..position) = cetz.coordinate.resolve(ctx, ..position) + let p-origin = position.first() + if position.len() == 2 { + anchor("in", position.first()) + anchor("out", position.last()) + p-rotate = cetz.vector.angle2(..position) + p-origin = (position.first(), p-position, position.last()) + } + set-origin(p-origin) + rotate(p-rotate) + + // Component + on-layer(1, { + group(name: "component", { + scale(p-scale * style.at("scale", default: 1)) + draw(ctx, position, style) + copy-anchors("bounds") + }) + }) + + copy-anchors("component") + + // Label + on-layer(0, { + if label != none { + if type(label) == dictionary and label.at("content", default: none) == none { panic("Label dictionary needs at least content key") } + let (label, distance, width, height, anchor) = if type(label) == dictionary { + (label.at("content", default: none), label.at("distance", default: 7pt), ..cetz.util.measure(ctx, label.at("content")), label.at("anchor", default: "north")) + } else { + (label, 7pt, ..cetz.util.measure(ctx, label), "north") + } + let reverse = "south" in anchor + let new-position = (0.5 * width * calc.abs(calc.sin(p-rotate)) + 0.5 * height * calc.abs(calc.cos(p-rotate))) + content("component."+anchor, anchor: get-label-anchor(p-rotate).at(if reverse { 1 } else { 0 }), label, padding: distance) + } + }) + + // Decorations + if position.len() == 2 { + line("in", "component.west", ..pre-style.at("wires")) + line("component.east", "out", ..pre-style.at("wires")) + + if i != none { + current(ctx, i) + } + if f != none { + flow(ctx, f) + } + if u != none { + voltage(ctx, u, p-rotate) + } + if n != none { + if "*-" in n { + node("", "in") + } + if "-*" in n { + node("", "out") + } + if "o-" in n { + node("", "in", fill: false) + } + if "-o" in n { + node("", "out", fill: false) + } + } + } + }) + + if (debug) { + on-layer(1, { + for-each-anchor(name, exclude: ("start", "end", "mid", "component", "line", "bounds", "gl", "0", "1"), name => { + circle((), radius: .7pt, stroke: red + .2pt) + content((rel: (0, 3pt)), box(inset: 1pt, text(3pt, name, fill: red)), angle: -30deg) + }) + }) + } +} + +#let interface(node1, node2, ..params, io: false) = { + import cetz.draw: * + + 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.2.1/src/components/capacitors.typ b/packages/preview/zap/0.2.1/src/components/capacitors.typ new file mode 100644 index 0000000000..027a1384fd --- /dev/null +++ b/packages/preview/zap/0.2.1/src/components/capacitors.typ @@ -0,0 +1,28 @@ +#import "/src/component.typ": component, interface +#import "/src/dependencies.typ": cetz +#import cetz.draw: anchor, line +#import "/src/mini.typ": variable-arrow + +#let capacitor(name, node, variable: false, ..params) = { + assert(type(variable) == bool, message: "variable must be of type bool") + + // Capacitor style + let style = ( + width: .8, + distance: .25, + ) + + // Drawing function + let draw(ctx, position, style) = { + interface((-style.distance / 2, -style.width / 2), (style.distance / 2, style.width / 2), io: position.len() < 2) + + line((-style.distance / 2, -style.width / 2), (-style.distance / 2, style.width / 2), ..style) + line((style.distance / 2, -style.width / 2), (style.distance / 2, style.width / 2), ..style) + if (variable) { + variable-arrow() + } + } + + // Componant call + component("capacitor", name, node, draw: draw, style: style, ..params) +} diff --git a/packages/preview/zap/0.2.1/src/components/diodes.typ b/packages/preview/zap/0.2.1/src/components/diodes.typ new file mode 100644 index 0000000000..868556a21f --- /dev/null +++ b/packages/preview/zap/0.2.1/src/components/diodes.typ @@ -0,0 +1,34 @@ +#import "/src/component.typ": component, interface +#import "/src/dependencies.typ": cetz +#import cetz.draw: anchor, circle, line, polygon, scope, translate +#import "/src/mini.typ": radiation-arrows + +#let diode(name, node, emitting: false, receiving: false, ..params) = { + assert(type(emitting) == bool, message: "emitting must be of type bool") + assert(type(receiving) == bool, message: "receiving must be of type bool") + + // Diode style + let style = ( + radius: .3, + line: .25, + ) + + // 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) + + polygon((0, 0), 3, radius: style.radius, fill: white, ..style) + line((0deg, style.radius), (180deg, style.radius / 2), ..style.at("wires")) + line((style.radius, -style.line), (style.radius, style.line), ..style) + if (emitting or receiving) { + radiation-arrows((to: (0, 0), rel: (0.25, 0.65)), reversed: receiving) + } + } + + // Componant call + component("diode", name, node, draw: draw, style: style, ..params) +} + +#let led(name, node, ..params) = diode(name, node, emitting: true, ..params) +#let photodiode(name, node, ..params) = diode(name, node, receiving: true, ..params) diff --git a/packages/preview/zap/0.2.1/src/components/fuses.typ b/packages/preview/zap/0.2.1/src/components/fuses.typ new file mode 100644 index 0000000000..9a882cc2a0 --- /dev/null +++ b/packages/preview/zap/0.2.1/src/components/fuses.typ @@ -0,0 +1,30 @@ +#import "/src/component.typ": component, interface +#import "/src/dependencies.typ": cetz +#import cetz.draw: anchor, circle, floating, line, rect + +#let fuse(name, node, asymmetric: false, ..params) = { + assert(type(asymmetric) == bool, message: "asymmetric must be of type bool") + + // Fuses style + let style = ( + width: .88, + height: .88 / 2.4, + asymmetry: 25%, + ) + + // 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), fill: white, ..style) + line((-style.width / 2, 0), (style.width / 2, 0), ..style.at("wires")) + if (asymmetric) { + rect((-style.width / 2, -style.height / 2), (-style.width / 2 + float(style.asymmetry * style.width), style.height / 2), fill: black) + } + } + + // Componant call + component("fuse", name, node, draw: draw, style: style, ..params) +} + +#let afuse(name, node, ..params) = fuse(name, node, asymmetric: true, ..params) diff --git a/packages/preview/zap/0.2.1/src/components/grounds.typ b/packages/preview/zap/0.2.1/src/components/grounds.typ new file mode 100644 index 0000000000..dec6464ed8 --- /dev/null +++ b/packages/preview/zap/0.2.1/src/components/grounds.typ @@ -0,0 +1,102 @@ +#import "/src/component.typ": component, interface +#import "/src/dependencies.typ": cetz +#import cetz.draw: anchor, line, polygon + +#let ground(name, node, ..params) = { + assert(params.pos().len() == 0, message: "ground supports only one node") + + // Ground style + let style = ( + radius: 0.22, + distance: 0.28, + ) + + // Drawing function + let draw(ctx, position, style) = { + line((0, 0), (0, -style.distance), ..style.at("wires")) + polygon((0, -style.distance), 3, anchor: "north", radius: style.radius, angle: -90deg, name: "polygon", ..style) + + let (width, height) = cetz.util.measure(ctx, "polygon") + interface((-width / 2, -height / 2), (width / 2, height / 2)) + } + + // Componant call + component("ground", name, node, draw: draw, style: style, ..params) +} + +#let frame(name, node, ..params) = { + assert(params.pos().len() == 0, message: "earth supports only one node") + + // Earth style + let style = ( + width: 0.46, + angle: 20deg, + depth: 0.25, + distance: 0.28, + ) + + // Drawing function + let draw(ctx, position, style) = { + line((0, 0), (0, -style.distance), ..style.at("wires")) + let delta = style.width / 2 + line((-style.width / 2, -style.distance), (style.width / 2, -style.distance), ..style) + 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)), ..style) + } + interface((-style.width / 2, style.distance), (style.width / 2, -style.distance)) + } + + // Componant call + component("frame", name, node, draw: draw, style: style, ..params) +} + +#let earth(name, node, ..params) = { + assert(params.pos().len() == 0, message: "earth supports only one node") + + // Earth style + let style = ( + width: .53, + delta: .09, + spacing: .11, + distance: .28, + ) + + // Drawing function + let draw(ctx, position, style) = { + line((0, 0), (0, -style.distance), ..style.at("wires")) + 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)) + } + + // Componant call + component("earth", name, node, draw: draw, style: style, ..params) +} + +#let vcc(name, node, ..params) = { + // VCC style + let style = ( + angle: 35deg, + radius: .4, + distance: .6, + ) + + // Drawing function + let draw(ctx, position, style) = { + line((0, 0), (0, style.distance), ..style.at("wires")) + line((rel: (radius: style.radius, angle: -90deg - style.angle), to: (0, style.distance)), (0, style.distance), ( + rel: (radius: style.radius, angle: -90deg + style.angle), + )) + + let (width, height) = (calc.sin(style.angle) * style.radius, calc.cos(style.angle) * style.radius) + interface((-width / 2, -height / 2), (width / 2, height / 2)) + } + + // Componant call + component("vcc", name, node, draw: draw, style: style, ..params) +} diff --git a/packages/preview/zap/0.2.1/src/components/inductors.typ b/packages/preview/zap/0.2.1/src/components/inductors.typ new file mode 100644 index 0000000000..3f11e91399 --- /dev/null +++ b/packages/preview/zap/0.2.1/src/components/inductors.typ @@ -0,0 +1,35 @@ +#import "/src/component.typ": component, interface +#import "/src/dependencies.typ": cetz +#import cetz.draw: anchor, arc, line, rect + +#let inductor(name, node, ..params) = { + // Inductor style + let style = ( + width: 1.41, + height: 1.41 / 3, + bumps: 3, + ) + + // 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 bump-radius = style.width / style.bumps / 2 + if (style.variant == "iec") { + rect((-style.width / 2, -style.height / 2), (style.width / 2, style.height / 2), fill: black, ..style) + } else { + 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, ..style) + } + } + } + + // Componant call + component("inductor", name, node, draw: draw, style: style, ..params) +} diff --git a/packages/preview/zap/0.2.1/src/components/motors.typ b/packages/preview/zap/0.2.1/src/components/motors.typ new file mode 100644 index 0000000000..0dcbe8585f --- /dev/null +++ b/packages/preview/zap/0.2.1/src/components/motors.typ @@ -0,0 +1,36 @@ +#import "/src/component.typ": component, interface +#import "/src/dependencies.typ": cetz +#import cetz.draw: anchor, arc, circle, content, rect +#import "/src/mini.typ": ac-sign, dc-sign + +#let motor(uid, name, node, current: "dc", magnet: false, ..params) = { + assert(current in ("dc", "ac"), message: "current must be ac or dc") + assert(type(magnet) == bool, message: "magnet must be bool") + assert(not (magnet and current == "ac"), message: "magnet only with dcmotor") + + // DCmotor style + let style = ( + radius: .49, + magnet-width: 1.23, + magnet-height: 1.23 / 4, + ) + + // Drawing function + let draw(ctx, position, style) = { + interface((-style.radius, -style.radius), (style.radius, style.radius), io: position.len() < 2) + + if (magnet) { + rect((-style.magnet-width / 2, -style.magnet-height / 2), (style.magnet-width / 2, style.magnet-height / 2), fill: black) + } + circle((0, 0), radius: style.radius, fill: white, ..style) + content((0, 0), anchor: "south", "M", padding: .03) + let symbol = if current == "dc" { dc-sign } else { ac-sign } + content((0, 0), [#cetz.canvas({ symbol() })], anchor: "north", padding: .13) + } + + // Componant call + component(uid, name, node, draw: draw, style: style, ..params) +} + +#let dcmotor(name, node, ..params) = motor("dcmotor", name, node, current: "dc", ..params) +#let acmotor(name, node, ..params) = motor("acmotor", name, node, current: "ac", ..params) diff --git a/packages/preview/zap/0.2.1/src/components/nodes.typ b/packages/preview/zap/0.2.1/src/components/nodes.typ new file mode 100644 index 0000000000..bbcf2ea297 --- /dev/null +++ b/packages/preview/zap/0.2.1/src/components/nodes.typ @@ -0,0 +1,10 @@ +#import "../dependencies.typ": cetz +#import cetz.draw: circle, on-layer + +#let node(name, position, fill: true, ..params) = { + assert(type(name) == str, message: "node name must be a string") + + on-layer(1, { + circle(position, radius: .05, fill: if fill { black } else { white }, name: name, stroke: .4pt, ..params) + }) +} \ No newline at end of file diff --git a/packages/preview/zap/0.2.1/src/components/opamp.typ b/packages/preview/zap/0.2.1/src/components/opamp.typ new file mode 100644 index 0000000000..183564c395 --- /dev/null +++ b/packages/preview/zap/0.2.1/src/components/opamp.typ @@ -0,0 +1,47 @@ +#import "/src/component.typ": component, interface +#import "/src/dependencies.typ": cetz +#import cetz.draw: anchor, content, line, polygon, rect, scope, translate + +#let opamp(name, node, invert: false, label: none, ..params) = { + assert(params.pos().len() == 0, message: "opamp supports only one node") + + // Capacitor style + let style = ( + width: 1.8, + height: 1.75, + padding: .28, + sign-stroke: .55pt, + sign-size: .14, + sign-delta: .45, + ) + + + // 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: white, ..style) + } else { + scope({ + if style.variant == "ieee" { translate((-style.width / 6, 0)) } + polygon((0, 0), 3, radius: style.width * 2 / 3, ..style) + }) + } + + line((rel: (style.padding - style.sign-size, 0), to: "minus"), (rel: (2 * style.sign-size, 0)), stroke: style.sign-stroke) + line((rel: (style.padding - style.sign-size, 0), to: "plus"), (rel: (2 * style.sign-size, 0)), stroke: style.sign-stroke) + line((rel: (style.padding, -style.sign-size), to: "plus"), (rel: (0, 2 * style.sign-size)), stroke: style.sign-stroke) + + if label != none { + content((style.width / 2, 0), label, anchor: "east", padding: if style.variant == "ieee" { .45 } else { .15 }) + } + } + + // Componant call + component("opamp", name, node, draw: draw, style: style, ..params, label: none) +} diff --git a/packages/preview/zap/0.2.1/src/components/resistors.typ b/packages/preview/zap/0.2.1/src/components/resistors.typ new file mode 100644 index 0000000000..a9537e7d3b --- /dev/null +++ b/packages/preview/zap/0.2.1/src/components/resistors.typ @@ -0,0 +1,61 @@ +#import "/src/component.typ": component, interface +#import "/src/dependencies.typ": cetz +#import cetz.draw: anchor, line, rect +#import "/src/mini.typ": variable-arrow + +#let resistor(name, node, variable: false, adjustable: false, ..params) = { + assert(type(variable) == bool, message: "variable must be of type bool") + assert(type(adjustable) == bool, message: "adjustable must be of type bool") + + // Resistor style + let style = ( + width: 1.41, + height: .47, + zigs: 3, + ) + + // Drawing function + let draw(ctx, position, style) = { + interface((-style.width / 2, -style.height / 2), (style.width / 2, style.height / 2), io: position.len() < 2) + + if style.variant == "iec" { + rect( + (-style.width / 2, -style.height / 2), + ( + style.width / 2, + style.height / 2, + ), + fill: white, + ..style, + ) + } 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), + ..style, + fill: none, + ) + } + if variable { + variable-arrow() + } else if adjustable { + let arrow-length = .8 + anchor("a", (0, style.height / 2 + arrow-length)) + line("a", (0, style.height / 2), mark: (end: ">", fill: black), fill: none) + } + } + + // Componant call + component("resistor", name, node, draw: draw, style: style, ..params) +} + +#let rheostat(name, node, ..params) = resistor(name, node, variable: true, ..params) +#let potentiometer(name, node, ..params) = resistor(name, node, adjustable: true, ..params) diff --git a/packages/preview/zap/0.2.1/src/components/sources.typ b/packages/preview/zap/0.2.1/src/components/sources.typ new file mode 100644 index 0000000000..1859e0ce81 --- /dev/null +++ b/packages/preview/zap/0.2.1/src/components/sources.typ @@ -0,0 +1,70 @@ +#import "/src/component.typ": component, interface +#import "/src/dependencies.typ": cetz +#import cetz.draw: anchor, circle, line, mark, rect + +#let isource(name, node, current: "dc", ..params) = { + assert(current in ("dc", "ac"), message: "current must be ac or dc") + + // Isource style + let style = ( + radius: .53, + padding: .25, + arrow-scale: 3, + ) + + // 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) + if (style.variant == "iec") { + line((0, -style.radius), (rel: (0, 2 * style.radius)), ..style, fill: none) + } else { + line((-style.radius + style.padding, 0), (rel: (2 * style.radius - 1.85 * style.padding, 0)), mark: (end: ">"), fill: black) + } + } + + // Componant call + component("isource", name, node, draw: draw, style: style, ..params) +} + +#let acisource(name, node, ..params) = isource(name, node, current: "ac", ..params) + +#let vsource(name, node, current: "dc", ..params) = { + assert(current in ("dc", "ac"), message: "current must be ac or dc") + + // Vsource style + let style = ( + radius: .53, + padding: .25, + sign-stroke: .55pt, + sign-size: .14, + sign-delta: .07, + ) + + // 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) + if (style.variant == "iec") { + line((-style.radius, 0), (rel: (2 * style.radius, 0)), ..style) + } else { + line((rel: (-style.radius + style.padding, -style.sign-size)), (rel: (0, 2 * style.sign-size)), stroke: style.sign-stroke) + line( + ( + style.radius - style.padding - style.sign-delta, + -style.sign-size, + ), + (rel: (0, 2 * style.sign-size)), + stroke: style.sign-stroke, + ) + line((rel: (style.sign-size, -style.sign-size)), (rel: (-2 * style.sign-size, 0)), stroke: style.sign-stroke) + } + } + + // Componant call + component("vsource", name, node, draw: draw, style: style, ..params) +} + +#let acvsource(name, node, ..params) = vsource(name, node, current: "ac", ..params) diff --git a/packages/preview/zap/0.2.1/src/components/transistors/bjts.typ b/packages/preview/zap/0.2.1/src/components/transistors/bjts.typ new file mode 100644 index 0000000000..ee1198368c --- /dev/null +++ b/packages/preview/zap/0.2.1/src/components/transistors/bjts.typ @@ -0,0 +1,48 @@ +#import "/src/component.typ": component, interface +#import "/src/dependencies.typ": cetz +#import cetz.draw: anchor, circle, hide, line, mark, translate +#import "/src/mini.typ": center-mark + +#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: "ground supports only one node") + + // BJT style + let style = ( + radius: .65, + base-height: .6, + base-distance: .12, + aperture: 50deg, + ) + + // 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" }) + + if envelope { + circle((0, 0), radius: style.radius, ..style, name: "circle") + line("base", (-style.radius, 0), ..style.at("wires")) + } else { + hide(circle((0, 0), radius: style.radius, ..style, name: "circle")) + } + + line((to: "base", rel: (0, -style.base-height / 2)), (to: "base", rel: (0, style.base-height / 2)), ..style) + line((to: "base", rel: (0, -style.base-distance * sgn)), "e", ..style.at("wires"), mark: center-mark(symbol: if sgn == -1 { "<" } else { ">" })) + line((to: "base", rel: (0, style.base-distance * sgn)), "c", ..style.at("wires")) + } + + // Componant call + component("bjt", name, node, draw: draw, style: style, ..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.2.1/src/components/transistors/mosfets.typ b/packages/preview/zap/0.2.1/src/components/transistors/mosfets.typ new file mode 100644 index 0000000000..94f9afb3f9 --- /dev/null +++ b/packages/preview/zap/0.2.1/src/components/transistors/mosfets.typ @@ -0,0 +1,94 @@ +#import "/src/component.typ": component, interface +#import "/src/dependencies.typ": cetz +#import cetz.draw: anchor, circle, content, floating, hide, line, mark, scale, set-origin, 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") + + // Mosfet style + let style = ( + height: 0.795, + width: 1.065, + base-width: 1.35, + base-spacing: 0.165, + base-distance: 0.165, + radius: 1.05, + ) + + // 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)) + } + + 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)), ..style) + } + } else { + line((-height, -base-width / 2), (rel: (0, base-width)), ..style) + } + if bulk == "internal" { + line((0, 0), (0, -width / 2), ..style.at("wires")) + } + line("d", (rel: (0, 0)), (rel: (-height, 0)), ..style.at("wires")) + line("s", (rel: (0, 0)), (rel: (-height, 0)), ..style.at("wires")) + + if envelope { + circle(center, radius: radius, ..style, name: "c") + } else { + hide(circle((0, 0), radius: radius, ..style, name: "c")) + } + + anchor("gl", (rel: (-3 * height / 4, width / 2), to: center)) + + if bulk != none { + line((-height, 0), (rel: (height, 0)), name: "line", ..style.at("wires")) + mark("line.centroid", (-height, 0), symbol: if (channel == "n") { ">" } else { "<" }, fill: black, anchor: "center") + line("gl", (rel: (0, -width)), (rel: (-height / 4, 0)), ..style.at("wires")) + anchor("g", ()) + } else { + line("gl", (rel: (0, -width / 2)), (rel: (0, -width / 2)), ..style.at("wires")) + line((rel: (0, width / 2)), (rel: (-height / 2, 0)), ..style.at("wires")) + anchor("g", ()) + + mark( + ( + -height / 2, + if (channel == "n") { -width / 2 } else { width / 2 }, + ), + (rel: (height, 0)), + symbol: if (channel == "n") { ">" } else { "<" }, + fill: black, + anchor: "center", + ) + } + } + + // Componant call + component("mosfet", name, node, draw: draw, style: style, ..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.2.1/src/components/wires.typ b/packages/preview/zap/0.2.1/src/components/wires.typ new file mode 100644 index 0000000000..a3a7a47e0a --- /dev/null +++ b/packages/preview/zap/0.2.1/src/components/wires.typ @@ -0,0 +1,11 @@ +#import "../dependencies.typ": cetz +#import cetz.draw: line + +#let wire(multi: 1, ..params) = { + assert(type(multi) == int, message: "multi must be an int") + + line(stroke: 0.4pt, ..params) + /*if multi > 10 { + line(, stroke: 0.6pt) + }*/ +} diff --git a/packages/preview/zap/0.2.1/src/decorations.typ b/packages/preview/zap/0.2.1/src/decorations.typ new file mode 100644 index 0000000000..e10dabee53 --- /dev/null +++ b/packages/preview/zap/0.2.1/src/decorations.typ @@ -0,0 +1,77 @@ +#import "/src/dependencies.typ": cetz +#import cetz.draw: bezier-through, catmull, circle, content, hobby, line, mark + +#let get-label(label) = { + let p-label = if type(label) == dictionary and "label" in label { + label.label + } else { + label + } + let p-invert = label.at("invert", default: false) + let p-position = if type(label) == dictionary and "position" in label and type(label.position) == alignment { + label.position + } else { + end + top + } + (p-label, p-position, p-invert, label.at("distance", default: 50%)) +} + +#let current(ctx, label) = { + let (p-label, p-position, p-invert, p-distance) = get-label(label) + + let (width, height) = cetz.util.measure(ctx, p-label) + let side = if p-position.y == top { (1, ">", "<") } else { (-1, ">", "<") } + + if p-position.x == start { + mark(("in", p-distance, "component.west"), "in", symbol: if p-invert { "<" } else { ">" }, anchor: "center", fill: black, scale: 0.8) + content((rel: (0, (.2 + height) * side.first()), to: ("in", p-distance, "component.west")), p-label) + } else { + mark(("component.east", p-distance, "out"), "out", symbol: if p-invert { "<" } else { ">" }, anchor: "center", fill: black, scale: 0.8) + content((rel: (0, (.2 + height) * side.first()), to: ("component.east", p-distance, "out")), p-label) + } +} + +#let flow(ctx, label) = { + let (p-label, p-position, p-invert, p-distance) = get-label(label) + + let (width, height) = cetz.util.measure(ctx, p-label) + + let bottom = p-position.y == bottom + + let (a-start, a-end) = if p-position.x == start { + let first = ("component.west", p-distance, "in") + (first, (rel: (-.7, 0), to: first)) + } else { + let first = ("component.east", p-distance, "out") + (first, (rel: (.7, 0), to: first)) + } + + line( + (rel: (0, if bottom { -.2 } else { .2 }), to: if p-invert { a-end } else { a-start }), + (rel: (0, if bottom { -.2 } else { .2 }), to: if p-invert { a-start } else { a-end }), + mark: (end: ">"), + fill: black, + stroke: 0.55pt, + scale: 0.8, + ) + content((rel: (0, height * if bottom { -2 } else { 2 }), to: (a-start, 50%, a-end)), p-label) +} + +#let voltage(ctx, label, p-rotate) = { + let (p-label, p-position, ..params) = get-label(label) + + let (width, height) = cetz.util.measure(ctx, p-label) + let side = if p-position.y == top { (1, "north") } else { (-1, "south") } + + let a-start = (rel: (-.4, .1 * side.first()), to: "component." + side.last() + "-west") + let a-end = (rel: (.4, .1 * side.first()), to: "component." + side.last() + "-east") + let a-center = (rel: (0, .3 * side.first()), to: "component." + side.last()) + let a-label = (width / 2 * calc.abs(calc.sin(p-rotate)) + height / 2 * calc.abs(calc.cos(p-rotate))) + + content((rel: (0, a-label), to: (rel: (0, 5pt * side.first()), to: a-center)), p-label) + if p-position.x == start { + hobby(a-end, a-center, a-start, mark: (end: ">", fill: black), scale: 0.8, stroke: 0.55pt) + } else { + hobby(a-start, a-center, a-end, mark: (end: ">", fill: black), scale: 0.8, stroke: 0.55pt) + } +} diff --git a/packages/preview/zap/0.2.1/src/dependencies.typ b/packages/preview/zap/0.2.1/src/dependencies.typ new file mode 100644 index 0000000000..e5b088c07e --- /dev/null +++ b/packages/preview/zap/0.2.1/src/dependencies.typ @@ -0,0 +1 @@ +#import "@preview/cetz:0.4.0" diff --git a/packages/preview/zap/0.2.1/src/lib.typ b/packages/preview/zap/0.2.1/src/lib.typ new file mode 100644 index 0000000000..51281da5fc --- /dev/null +++ b/packages/preview/zap/0.2.1/src/lib.typ @@ -0,0 +1,25 @@ +// Export dependencies +#import "dependencies.typ": cetz +#import cetz: canvas +#import cetz.draw as draw +#import cetz.draw: set-style + +// Export components +#import "component.typ": component, interface + +// Export components +#import "components/wires.typ": wire +#import "components/nodes.typ": node +#import "components/capacitors.typ": capacitor +#import "components/diodes.typ": diode, led, photodiode +#import "components/opamp.typ": opamp +#import "components/fuses.typ": afuse, fuse +#import "components/grounds.typ": earth, frame, ground, vcc +#import "components/inductors.typ": inductor +#import "components/resistors.typ": potentiometer, resistor, rheostat +#import "components/sources.typ": isource, vsource +#import "components/motors.typ": acmotor, dcmotor + +// Export transistors +#import "components/transistors/bjts.typ": bjt, npn, pnp +#import "components/transistors/mosfets.typ": mosfet, nmos, nmosd, pmos, pmosd diff --git a/packages/preview/zap/0.2.1/src/mini.typ b/packages/preview/zap/0.2.1/src/mini.typ new file mode 100644 index 0000000000..a05cc9b160 --- /dev/null +++ b/packages/preview/zap/0.2.1/src/mini.typ @@ -0,0 +1,82 @@ +#import "dependencies.typ": cetz +#import cetz.draw: anchor, hobby, line, rotate, scope, set-origin, set-style + +#let center-mark(symbol: ">") = { + (end: ((pos: 50%, symbol: symbol, fill: black, anchor: "center"), (pos: 0%, symbol: ">", scale: 0))) +} + +#let variable-arrow() = { + scope({ + let arrow-length = 40pt + let arrow-angle = 55deg + let arrow-origin = ( + -0.5 * calc.cos(arrow-angle) * arrow-length, + -0.5 * calc.sin(arrow-angle) * arrow-length, + ) + anchor("adjust", arrow-origin) + + set-origin(arrow-origin) + rotate(arrow-angle) + line((0, 0), (arrow-length, 0), mark: (end: ">", fill: black)) + }) +} + +#let radiation-arrows(origin, angle: -120deg, reversed: false, length: 12pt) = { + scope({ + let arrows-distance = 3pt + let arrows-length = length + let arrows-scale = 0.8 + + set-origin(origin) + set-style(stroke: 0.55pt) + rotate(angle) + if (reversed) { + line((arrows-length, -arrows-distance), (0, -arrows-distance), mark: ( + start: ">", + scale: arrows-scale, + fill: black, + )) + line((arrows-length, arrows-distance), (0, arrows-distance), mark: ( + start: ">", + scale: arrows-scale, + fill: black, + )) + } else { + line((arrows-length, -arrows-distance), (0, -arrows-distance), mark: ( + end: ">", + scale: arrows-scale, + fill: black, + )) + line((arrows-length, arrows-distance), (0, arrows-distance), mark: ( + end: ">", + scale: arrows-scale, + fill: black, + )) + } + }) +} + +#let dc-sign() = { + let width = 10pt + let spacing = 1.5pt + let vspace = 3pt + let symbol-stroke = 0.55pt + let tick-width = (width - 2 * spacing) / 3 + + set-style(stroke: symbol-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)) +} + +#let ac-sign() = { + let width = 10pt + let height = 4pt + let symbol-stroke = 0.55pt + + set-style(stroke: symbol-stroke) + + hobby((-width / 2, 0), (-width / 4, height / 2), (width / 4, -height / 2), (width / 2, 0)) +} diff --git a/packages/preview/zap/0.2.1/src/styles.typ b/packages/preview/zap/0.2.1/src/styles.typ new file mode 100644 index 0000000000..3d870a00b1 --- /dev/null +++ b/packages/preview/zap/0.2.1/src/styles.typ @@ -0,0 +1,5 @@ +#let default-style = ( + variant: "iec", + wires: (stroke: 0.4pt), + stroke: .8pt, +) diff --git a/packages/preview/zap/0.2.1/src/utils.typ b/packages/preview/zap/0.2.1/src/utils.typ new file mode 100644 index 0000000000..8682cc874f --- /dev/null +++ b/packages/preview/zap/0.2.1/src/utils.typ @@ -0,0 +1,28 @@ +#let get-label-anchor(angle-deg, ) = { + let angle = angle-deg.deg() + let normalized-angle = calc.rem(if angle < 0 { angle + 360 } else { angle }, 360) + + let tolerance = 15 + + //panic(repr(normalized-angle)) + + if calc.abs(normalized-angle) < tolerance { + return ("south", "north") + } else if calc.abs(normalized-angle - 90) < tolerance { + return ("east", "west") + } else if calc.abs(normalized-angle - 180) < tolerance { + return ("north", "south") + } else if calc.abs(normalized-angle - 270) < tolerance { + return ("west", "east") + } else { + if normalized-angle > 0 and normalized-angle < 90 { + return ("south-east", "north-west") + } else if normalized-angle > 90 and normalized-angle < 180 { + return ("north-east", "south-west") + } else if normalized-angle > 180 and normalized-angle < 270 { + return ("north-west", "south-east") + } else { + return ("south-west", "north-east") + } + } +} diff --git a/packages/preview/zap/0.2.1/typst.toml b/packages/preview/zap/0.2.1/typst.toml new file mode 100644 index 0000000000..013746a4fb --- /dev/null +++ b/packages/preview/zap/0.2.1/typst.toml @@ -0,0 +1,14 @@ +[package] +name = "zap" +version = "0.2.1" +compiler = "0.13.0" +entrypoint = "src/lib.typ" +authors = ["Louis Grange "] +license = "LGPL-3.0-or-later" +homepage = "https://l0uisgrange.github.io/zap/" +description = "A package to draw amazing electronic circuits using CeTZ superpowers" +repository = "https://github.com/l0uisgrange/zap" +categories = ["visualization", "components"] +disciplines = ["engineering"] +keywords = ["zap", "electronic", "circuit", "cetz", "voltage", "draw", "schema", "engineering"] +exclude = [".github", "docs", "examples"] \ No newline at end of file