add some other mantainer
You should probably nominate someone else to mantain this, I'm not talking about me but about someone who wrote a PR if they decide they want the role
Hi @Kreijstal,
I'm currently taking over and expanding on the original work by @fenjalien to publish it soon on Typst Universe. The updated repository is available here:
https://github.com/l0uisgrange/zap
The original repository hasn't been updated for about two years, before Typst Universe even launched. I initially wanted to contribute directly, but given the inactivity, I decided to start from there.
Of course, you are very welcome to join and contribute if interested — it would be a pleasure to collaborate!
tbh I found circetz not to my liking im using a custom one.
`components.typ`
#import "@preview/cetz:0.3.4"
#set page(width: auto, height: auto, margin: 8pt)
#let connect-orthogonal(start_anchor, end_anchor, style: "hv", ..styling) = {
assert(style in ("hv", "vh"), message: "Style must be 'hv' or 'vh'.")
let corner = if style == "hv" { (start_anchor, "|-", end_anchor) } else { (start_anchor, "-|", end_anchor) }
cetz.draw.line(start_anchor, corner, end_anchor, ..styling)
}
#let _draw_label(label, label_pos, label_offset, label_anchor, label_size, text_fill: black) = {
if label != none {
cetz.draw.content(
(rel: label_offset, to: label_pos),
text(size: label_size, fill: text_fill, label),
anchor: label_anchor,
)
}
}
#let _define_anchors(anchors) = {
for (name, pos) in anchors { cetz.draw.anchor(name, pos) }
}
#let _base_component(
position,
name,
scale: 1.0,
rotate: 0deg,
draw_content_func,
anchor_definitions,
label: none,
label_pos: "center",
label_anchor: "center",
label_offset: (0, 0),
label_size: 8pt,
text_fill: black,
..styling,
) = {
cetz.draw.group(
name: name,
..styling,
{
cetz.draw.set-origin(position)
cetz.draw.scale(scale)
cetz.draw.rotate(rotate)
draw_content_func(..styling)
_define_anchors(anchor_definitions)
_draw_label(label, label_pos, label_offset, label_anchor, label_size, text_fill: text_fill)
},
)
}
#let _transistor(
is_pmos: false,
position,
name,
label: none,
label_pos: auto,
label_anchor: auto,
label_offset: (0.05, 0.05),
label_size: 8pt,
show_pin_labels: false,
pin_label_size: 7pt,
show_gate_bubble: auto,
bubble_radius_factor: 0.08,
scale: 1.0,
rotate: 0deg,
width: 0.9,
height: 1.2,
gate_lead_factor: 0.3,
bulk_lead_factor: 0.3,
gate_pos_factor: 0.3,
channel_pos_factor: 0.4,
gate_v_extent_factor: 0.35,
channel_v_extent_factor: 0.35,
thick_factor: 2.0,
arrow_scale: 0.8,
arrow_fill: black,
..styling,
) = {
let final_label_pos = if label_pos == auto { if is_pmos { "S" } else { "D" } } else { label_pos }
let final_label_anchor = if label_anchor == auto { if is_pmos { "south-west" } else { "north-west" } } else {
label_anchor
}
let final_show_gate_bubble = if show_gate_bubble == auto { is_pmos } else { show_gate_bubble }
cetz.draw.group(
name: name,
..styling,
{
cetz.draw.set-origin(position)
cetz.draw.scale(scale)
cetz.draw.rotate(rotate)
let center_y = height / 2
let gate_line_x = gate_pos_factor * width
let channel_line_x = channel_pos_factor * width
let gate_v_extent = height * gate_v_extent_factor
let channel_v_extent = height * channel_v_extent_factor
let drain_term_rel = (width, if is_pmos { 0 } else { height })
let source_term_rel = (width, if is_pmos { height } else { 0 })
let gate_term_rel = (-gate_lead_factor * width, center_y)
let bulk_term_rel = (width + bulk_lead_factor * width, center_y)
let gate_line_top = (gate_line_x, center_y + gate_v_extent)
let gate_line_bottom = (gate_line_x, center_y - gate_v_extent)
let gate_conn_pt = (gate_line_x, center_y)
let channel_top = (channel_line_x, center_y + channel_v_extent)
let channel_bottom = (channel_line_x, center_y - channel_v_extent)
let bulk_conn_pt = (channel_line_x, center_y)
let horiz_top = (width, center_y + channel_v_extent)
let horiz_bottom = (width, center_y - channel_v_extent)
let line_thickness = 0.6pt * thick_factor
_define_anchors((
("G", gate_term_rel),
("D", drain_term_rel),
("S", source_term_rel),
("B", bulk_term_rel),
("center", (width / 2, height / 2)),
("bulk_conn", bulk_conn_pt),
("gate_conn", gate_conn_pt),
("north", (width / 2, height)),
("south", (width / 2, 0)),
("east", (width, height / 2)),
("west", (0, height / 2)),
("north-east", (width, height)),
("south-west", (0, 0)),
("south-east", (width, 0)),
("default", gate_term_rel),
))
cetz.draw.line(channel_bottom, channel_top, ..styling, thickness: line_thickness)
cetz.draw.line(gate_line_bottom, gate_line_top, ..styling, thickness: line_thickness)
cetz.draw.line(gate_term_rel, gate_conn_pt, ..styling)
cetz.draw.line(bulk_conn_pt, bulk_term_rel, ..styling)
if is_pmos {
cetz.draw.line(drain_term_rel, horiz_bottom, ..styling)
cetz.draw.line(horiz_bottom, channel_bottom, ..styling)
cetz.draw.line(source_term_rel, horiz_top, ..styling)
cetz.draw.line(horiz_top, channel_top, ..styling, mark: (end: "stealth", fill: arrow_fill, scale: arrow_scale))
if final_show_gate_bubble {
let bubble_radius = height * bubble_radius_factor
cetz.draw.circle((rel: (-bubble_radius*width/calc.abs(width), 0), to: "gate_conn"), radius: bubble_radius, ..styling, fill: white)
}
} else {
cetz.draw.line(drain_term_rel, horiz_top, ..styling)
cetz.draw.line(horiz_top, channel_top, ..styling)
cetz.draw.line(horiz_bottom, source_term_rel, ..styling)
cetz.draw.line(
channel_bottom,
horiz_bottom,
..styling,
mark: (end: "stealth", fill: arrow_fill, scale: arrow_scale),
)
}
if show_pin_labels {
cetz.draw.content((rel: (-0.05, 0), to: "G"), text(size: pin_label_size, $G$), anchor: "east")
cetz.draw.content(
(rel: (0.05, 0), to: "S"),
text(size: pin_label_size, $S$),
anchor: if is_pmos { "west" } else { "south" },
)
cetz.draw.content(
(rel: (0.05, 0), to: "D"),
text(size: pin_label_size, $D$),
anchor: if is_pmos { "west" } else { "north" },
)
cetz.draw.content((rel: (0.05, 0), to: "B"), text(size: pin_label_size, $B$), anchor: "west")
}
_draw_label(label, final_label_pos, label_offset, final_label_anchor, label_size)
},
)
}
#let nmos_transistor(..args) = _transistor(is_pmos: false, ..args)
#let pmos_transistor(..args) = _transistor(is_pmos: true, ..args)
#let gnd_symbol(
position,
name,
label: none,
label_pos: "north",
label_anchor: "south",
label_offset: (0, 0.1),
label_size: 8pt,
scale: 1.0,
rotate: 0deg,
lead_length: 0.3,
bar_width: 0.5,
bar_spacing: 0.05,
bar_width_factors: (1.0, 0.7, 0.4),
..styling,
) = {
assert(bar_width_factors.len() == 3, message: "bar_width_factors must have 3 elements.")
let draw_func(..styling) = {
let y_coords = (-lead_length, -lead_length - bar_spacing, -lead_length - 2 * bar_spacing)
cetz.draw.line((0, 0), (0, -lead_length), ..styling)
for (idx, y) in y_coords.enumerate() {
let half_w = bar_width * bar_width_factors.at(idx) / 2
cetz.draw.line((-half_w, y), (half_w, y), ..styling)
}
}
let south_y = -lead_length - 2 * bar_spacing
let anchors = (
("T", (0, 0)),
("north", (0, 0)),
("south", (0, south_y)),
("west", (-bar_width * bar_width_factors.at(0) / 2, -lead_length)),
("east", (bar_width * bar_width_factors.at(0) / 2, -lead_length)),
("center", (0, (-lead_length + south_y) / 2)),
("default", (0, 0)),
)
_base_component(
position,
name,
scale: scale,
rotate: rotate,
draw_func,
anchors,
label: label,
label_pos: label_pos,
label_anchor: label_anchor,
label_offset: label_offset,
label_size: label_size,
..styling,
)
}
#let resistor(
position,
name,
label: none,
label_pos: "south",
label_anchor: "north",
label_offset: (0, -0.1),
label_size: 8pt,
scale: 1.0,
rotate: 0deg,
width: 0.8,
height: 0.3,
zigs: 3,
lead_extension: 0.3,
..styling,
) = {
let draw_func(..styling) = {
let hw = width / 2
let hh = height / 2
let lead_start_x = -hw - lead_extension
let lead_end_x = hw + lead_extension
let zig_start_x = -hw
let num_segments = zigs * 2
let seg_h = width / num_segments
let sgn = 1
cetz.draw.line(
(lead_start_x, 0),
(zig_start_x, 0),
(rel: (seg_h / 2, hh * sgn)),
..for _ in range(num_segments - 1) {
sgn *= -1
((rel: (seg_h, hh * 2 * sgn)),)
},
(rel: (seg_h / 2, hh)),
(lead_end_x, 0),
..styling,
)
}
let hw = width / 2
let hh = height / 2
let lead_start_x = -hw - lead_extension
let lead_end_x = hw + lead_extension
let anchors = (
("L", (lead_start_x, 0)),
("R", (lead_end_x, 0)),
("center", (0, 0)),
("north", (0, hh)),
("south", (0, -hh)),
("east", (lead_end_x, 0)),
("west", (lead_start_x, 0)),
("T", (0, hh)),
("B", (0, -hh)),
("default", (lead_start_x, 0)),
)
_base_component(
position,
name,
scale: scale,
rotate: rotate,
draw_func,
anchors,
label: label,
label_pos: label_pos,
label_anchor: label_anchor,
label_offset: label_offset,
label_size: label_size,
..styling,
)
}
#let capacitor(
position,
name,
label: none,
label_pos: "south",
label_anchor: "north",
label_offset: (0, -0.1),
label_size: 8pt,
scale: 1.0,
rotate: 0deg,
plate_height: 0.6,
plate_gap: 0.2,
lead_extension: 0.55,
..styling,
) = {
let draw_func(..styling) = {
let hg = plate_gap / 2
let hh = plate_height / 2
let lead_start_x = -hg - lead_extension
let lead_end_x = hg + lead_extension
let plate_left_x = -hg
let plate_right_x = hg
cetz.draw.line((lead_start_x, 0), (plate_left_x, 0), ..styling)
cetz.draw.line((plate_left_x, -hh), (plate_left_x, hh), ..styling)
cetz.draw.line((plate_right_x, hh), (plate_right_x, -hh), ..styling)
cetz.draw.line((plate_right_x, 0), (lead_end_x, 0), ..styling)
}
let hg = plate_gap / 2
let hh = plate_height / 2
let lead_start_x = -hg - lead_extension
let lead_end_x = hg + lead_extension
let anchors = (
("L", (lead_start_x, 0)),
("R", (lead_end_x, 0)),
("center", (0, 0)),
("north", (0, hh)),
("south", (0, -hh)),
("east", (lead_end_x, 0)),
("west", (lead_start_x, 0)),
("T", (0, hh)),
("B", (0, -hh)),
("default", (lead_start_x, 0)),
)
_base_component(
position,
name,
scale: scale,
rotate: rotate,
draw_func,
anchors,
label: label,
label_pos: label_pos,
label_anchor: label_anchor,
label_offset: label_offset,
label_size: label_size,
..styling,
)
}
#let voltage_source(
position,
name,
label: none,
annotation_label_pos: "left",
annotation_label_anchor: auto,
annotation_label_offset: auto,
annotation_label_size: 8pt,
show_voltage_annotation: true,
voltage_arrow_pos: "left",
voltage_arrow_dir: "down",
voltage_arrow_length_factor: 2,
voltage_arrow_offset_factor: 0.7,
arrow_scale: 1.0,
arrow_fill: black,
stroke_thickness: 0.6pt,
scale: 1.0,
rotate: 0deg,
radius: 0.3,
lead_length: 0.3,
..styling,
) = {
let draw_func(..styling) = {
let top_y = radius
let bottom_y = -radius
let top_lead_y = top_y + lead_length
let bottom_lead_y = bottom_y - lead_length
cetz.draw.circle((0, 0), radius: radius, ..styling)
cetz.draw.line((0, top_y), (0, top_lead_y), ..styling)
cetz.draw.line((0, bottom_y), (0, bottom_lead_y), ..styling)
if show_voltage_annotation {
let arrow_x = if voltage_arrow_pos == "left" { -radius * (1 + voltage_arrow_offset_factor) } else {
radius * (1 + voltage_arrow_offset_factor)
}
let arrow_len = radius * voltage_arrow_length_factor
let arrow_half_len = arrow_len / 2
let (arrow_start_y, arrow_end_y) = if voltage_arrow_dir == "down" { (arrow_half_len, -arrow_half_len) } else {
(-arrow_half_len, arrow_half_len)
}
cetz.draw.line(
(arrow_x, arrow_start_y),
(arrow_x, arrow_end_y),
..styling,
mark: (
end: "stealth",
scale: arrow_scale * 0.4,
fill: arrow_fill,
stroke: (paint: black, thickness: stroke_thickness),
),
)
if label != none {
let (default_anchor, default_offset) = if annotation_label_pos == "left" { ("east", (-0.05, 0)) } else {
("west", (0.05, 0))
}
let final_anchor = if annotation_label_anchor == auto { default_anchor } else { annotation_label_anchor }
let final_offset = if annotation_label_offset == auto { default_offset } else { annotation_label_offset }
cetz.draw.content(
(rel: (0.1 * scale * arrow_x / calc.abs(arrow_x), 0), to: (arrow_x, 0)),
text(size: annotation_label_size, fill: arrow_fill, label),
anchor: final_anchor,
offset: final_offset,
)
}
}
}
let top_lead_y = radius + lead_length
let bottom_lead_y = -radius - lead_length
let anchors = (
("T", (0, top_lead_y)),
("B", (0, bottom_lead_y)),
("center", (0, 0)),
("north", (0, top_lead_y)),
("south", (0, bottom_lead_y)),
("east", (radius, 0)),
("west", (-radius, 0)),
("default", (0, top_lead_y)),
)
_base_component(
position,
name,
scale: scale,
rotate: rotate,
draw_func,
anchors,
label: none,
..styling,
)
}
#let node(
position,
name,
label: none,
radius: 0.05,
label_size: 8pt,
label_offset: (0, 0),
label_anchor: "center",
fill: white,
text_fill: black,
scale: 1.0,
rotate: 0deg,
..styling,
) = {
let draw_func(..styling) = {
cetz.draw.circle((0, 0), radius: radius, ..styling, fill: fill)
}
let diag_offset = radius * calc.cos(45deg)
let anchors = (
("center", (0, 0)),
("default", (0, 0)),
("north", (0, radius)),
("south", (0, -radius)),
("east", (radius, 0)),
("west", (-radius, 0)),
("north-east", (diag_offset, diag_offset)),
("north-west", (-diag_offset, diag_offset)),
("south-east", (diag_offset, -diag_offset)),
("south-west", (-diag_offset, -diag_offset)),
)
_base_component(
position,
name,
scale: scale,
rotate: rotate,
draw_func,
anchors,
label: label,
label_pos: "center",
label_anchor: label_anchor,
label_offset: label_offset,
label_size: label_size,
text_fill: text_fill,
..styling,
)
}
#let vdd_symbol(
position,
name,
label: none,
label_pos: "north",
label_anchor: "south",
label_offset: (0, 0.1),
label_size: 8pt,
scale: 1.0,
rotate: 0deg,
stem_length: 0.3,
bar_width: 0.5,
text_fill: black,
..styling,
) = {
let draw_func(..styling) = {
let stem_top_y = stem_length
let bar_half_width = bar_width / 2
cetz.draw.line((0, 0), (0, stem_top_y), ..styling)
cetz.draw.line((-bar_half_width, stem_top_y), (bar_half_width, stem_top_y), ..styling)
}
let stem_top_y = stem_length
let bar_half_width = bar_width / 2
let anchors = (
("B", (0, 0)),
("south", (0, 0)),
("default", (0, 0)),
("T", (0, stem_top_y)),
("north", (0, stem_top_y)),
("TL", (-bar_half_width, stem_top_y)),
("TR", (bar_half_width, stem_top_y)),
("west", (-bar_half_width, stem_top_y)),
("east", (bar_half_width, stem_top_y)),
("center", (0, stem_top_y / 2)),
)
_base_component(
position,
name,
scale: scale,
rotate: rotate,
draw_func,
anchors,
label: label,
label_pos: label_pos,
label_anchor: label_anchor,
label_offset: label_offset,
label_size: label_size,
text_fill: text_fill,
..styling,
)
}
#let current_source(
position,
name,
label: none,
label_pos: "west",
label_anchor: "west",
label_offset: (0.1, 0),
label_size: 8pt,
scale: 1.0,
rotate: 0deg,
radius: 0.3,
lead_length: 0.3,
arrow_dir: "up",
arrow_scale: 1.0,
arrow_fill: black,
..styling,
) = {
let draw_func(..styling) = {
let top_y = radius
let bottom_y = -radius
let top_lead_y = top_y + lead_length
let bottom_lead_y = bottom_y - lead_length
cetz.draw.circle((0, 0), radius: radius, ..styling)
cetz.draw.line((0, top_y), (0, top_lead_y), ..styling)
cetz.draw.line((0, bottom_y), (0, bottom_lead_y), ..styling)
let arrow_v_extent = radius * 0.7
assert(arrow_dir in ("up", "down"), message: "Arrow direction must be 'up' or 'down'.")
let (arrow_start_y, arrow_end_y) = if arrow_dir == "up" { (-arrow_v_extent, arrow_v_extent) } else {
(arrow_v_extent, -arrow_v_extent)
}
cetz.draw.line(
(0, arrow_start_y),
(0, arrow_end_y),
..styling,
mark: (end: "stealth", scale: arrow_scale * 0.4, fill: arrow_fill),
)
}
let top_lead_y = radius + lead_length
let bottom_lead_y = -radius - lead_length
let anchors = (
("T", (0, top_lead_y)),
("B", (0, bottom_lead_y)),
("center", (0, 0)),
("north", (0, top_lead_y)),
("south", (0, bottom_lead_y)),
("east", (radius, 0)),
("west", (-radius, 0)),
("default", (0, top_lead_y)),
)
_base_component(
position,
name,
scale: scale,
rotate: rotate,
draw_func,
anchors,
label: label,
label_pos: label_pos,
label_anchor: label_anchor,
label_offset: label_offset,
label_size: label_size,
..styling,
)
}
#let voltage_points(
position,
name,
label: none,
annotation_label_pos: "left",
annotation_label_anchor: auto,
annotation_label_offset: auto,
annotation_label_size: 8pt,
annotation_text_fill: black,
show_voltage_annotation: true,
voltage_arrow_pos: "left",
voltage_arrow_dir: "down",
arrow_length_factor: 1.0,
arrow_offset: 0.3,
arrow_scale: 1.0,
arrow_fill: black,
arrow_stroke: black,
arrow_stroke_thickness: 0.6pt,
point_separation: 0.6,
point_radius: 0.05,
point_fill: black,
point_stroke: none,
show_point_labels: false,
top_label: $[+]$,
bottom_label: $[-]$,
point_label_size: 7pt,
point_text_fill: black,
top_label_offset: (0, 0.05),
top_label_anchor: "south",
bottom_label_offset: (0, -0.05),
bottom_label_anchor: "north",
scale: 1.0,
rotate: 0deg,
..styling,
) = {
let draw_func(..styling) = {
let half_sep = point_separation / 2
let top_pos = (0, half_sep)
let bottom_pos = (0, -half_sep)
cetz.draw.circle(top_pos, radius: point_radius, fill: point_fill, stroke: point_stroke, ..styling)
cetz.draw.circle(bottom_pos, radius: point_radius, fill: point_fill, stroke: point_stroke, ..styling)
if show_voltage_annotation {
let arrow_x = if voltage_arrow_pos == "left" { -arrow_offset } else { arrow_offset }
let arrow_len = point_separation * arrow_length_factor
let arrow_half_len = arrow_len / 2
let (arrow_start_y, arrow_end_y) = if voltage_arrow_dir == "down" {
(arrow_half_len, -arrow_half_len)
} else {
(-arrow_half_len, arrow_half_len)
}
cetz.draw.line(
(arrow_x, arrow_start_y),
(arrow_x, arrow_end_y),
stroke: (paint: arrow_stroke, thickness: arrow_stroke_thickness),
..styling,
mark: (
end: "stealth",
scale: arrow_scale * 0.4,
fill: arrow_fill,
stroke: (paint: arrow_stroke, thickness: arrow_stroke_thickness),
),
)
if label != none {
let (default_anchor, default_offset) = if annotation_label_pos == "left" { ("east", (-0.05, 0)) } else {
("west", (0.05, 0))
}
let final_anchor = if annotation_label_anchor == auto { default_anchor } else { annotation_label_anchor }
let final_offset = if annotation_label_offset == auto { default_offset } else { annotation_label_offset }
cetz.draw.content(
(arrow_x, 0),
text(size: annotation_label_size, fill: annotation_text_fill, label),
anchor: final_anchor,
offset: final_offset,
)
}
}
if show_point_labels {
_draw_label(top_label, (0, half_sep), top_label_offset, top_label_anchor, point_label_size, text_fill: point_text_fill)
_draw_label(bottom_label, (0, -half_sep), bottom_label_offset, bottom_label_anchor, point_label_size, text_fill: point_text_fill)
}
}
let half_sep = point_separation / 2
let anchors = (
("T", (0, half_sep)),
("B", (0, -half_sep)),
("center", (0, 0)),
("north", (0, half_sep)),
("south", (0, -half_sep)),
("east", (point_radius, 0)),
("west", (-point_radius, 0)),
("default", (0, half_sep)),
)
_base_component(
position,
name,
scale: scale,
rotate: rotate,
draw_func,
anchors,
label: none,
..styling,
)
}
#let wire_hop(
wire1_start, wire1_end,
wire2_start, wire2_end,
name: none,
hopping_wire: 1,
hop_radius: 0.15,
hop_direction: 1,
..styling
) = {
assert(hopping_wire in (1, 2), message: "hopping_wire must be 1 or 2.")
assert(hop_direction in (1, -1), message: "hop_direction must be 1 or -1.")
cetz.draw.group(name: name, {
cetz.draw.get-ctx(ctx=>{
let a1=cetz.coordinate.resolve(ctx,wire1_start).at(1)
let a2=cetz.coordinate.resolve(ctx,wire1_end).at(1)
let b1=cetz.coordinate.resolve(ctx,wire2_start).at(1)
let b2=cetz.coordinate.resolve(ctx,wire2_end).at(1)
let intersection = cetz.intersection.line-line(
a1, a2,
b1, b2,
)
assert(intersection != none, message: "Wires do not intersect, cannot hop.")
let (straight_start, straight_end, hopping_start, hopping_end) = if hopping_wire == 1 {
(b1, b2, a1, a2)
} else {
(a1, a2, b1, b2)
}
cetz.draw.line(straight_start, straight_end, ..styling)
let hop_vec = cetz.vector.sub(hopping_end, hopping_start)
let wire_angle = calc.atan2(hop_vec.at(0), hop_vec.at(1))
let hop_unit_vec = cetz.vector.norm(hop_vec)
assert(hop_unit_vec != none, message: "Cannot get unit vector for zero-length hopping wire.")
let offset_vec_neg = cetz.vector.scale(hop_unit_vec, -hop_radius)
let offset_vec_pos = cetz.vector.scale(hop_unit_vec, hop_radius)
let arc_start_point = (rel: offset_vec_neg, to: intersection)
let arc_end_point = (rel: offset_vec_pos, to: intersection)
let arc_start_angle = wire_angle
let arc_stop_angle = wire_angle + (180deg * hop_direction)
cetz.draw.line(hopping_start, arc_start_point, ..styling)
cetz.draw.arc(
(rel: cetz.vector.scale((calc.cos(arc_start_angle), calc.sin(arc_start_angle)), hop_radius*2), to: arc_start_point),
start: arc_start_angle,
stop: arc_stop_angle,
radius: hop_radius,
..styling
)
cetz.draw.line(arc_end_point, hopping_end, ..styling)
// Define anchors
let diag_offset = hop_radius * calc.cos(45deg)
let anchors = (
("wire1_start", a1),
("wire1_end", a2),
("wire2_start", b1),
("wire2_end", b2),
("intersection", intersection),
("center", intersection),
("north", (intersection.at(0), intersection.at(1) + hop_radius)),
("south", (intersection.at(0), intersection.at(1) - hop_radius)),
("east", (intersection.at(0) + hop_radius, intersection.at(1))),
("west", (intersection.at(0) - hop_radius, intersection.at(1))),
("north-east", (intersection.at(0) + diag_offset, intersection.at(1) + diag_offset)),
("north-west", (intersection.at(0) - diag_offset, intersection.at(1) + diag_offset)),
("south-east", (intersection.at(0) + diag_offset, intersection.at(1) - diag_offset)),
("south-west", (intersection.at(0) - diag_offset, intersection.at(1) - diag_offset)),
("default", intersection),
)
_define_anchors(anchors)
})
})
}
#let capacitor_between(
start_point,
end_point,
name,
scale: 1.0,
plate_height: 0.6,
plate_gap: 0.2,
..capparams,
) = {
cetz.draw.get-ctx(ctx => {
let start_coord = cetz.coordinate.resolve(ctx, start_point).at(1)
let end_coord = cetz.coordinate.resolve(ctx, end_point).at(1)
let dx = end_coord.at(0) - start_coord.at(0)
let dy = end_coord.at(1) - start_coord.at(1)
let distance = calc.sqrt(dx*dx + dy*dy)
let angle = calc.atan2(dx, dy)
let scaled_plate_gap = plate_gap * scale
let min_distance = scaled_plate_gap
assert(
distance > min_distance,
message: "Distance between start and end points (" + repr(distance) + ") must be greater than the scaled plate gap (" + repr(min_distance) + "). Reduce scale or increase distance.",
)
let calculated_lead_extension = (distance / scale - plate_gap) / 2
assert( calculated_lead_extension >= 0,
message: "Internal error: Calculated lead extension is negative. Check scale and distance (" + repr(calculated_lead_extension) +").",
)
let mid_x = (start_coord.at(0) + end_coord.at(0)) / 2
let mid_y = (start_coord.at(1) + end_coord.at(1)) / 2
let calculated_position = (mid_x, mid_y)
capacitor(
calculated_position,
name,
scale: scale,
rotate: angle,
plate_height: plate_height,
plate_gap: plate_gap,
lead_extension: calculated_lead_extension,
..capparams
)
})
}
//*
// Example Usage: (Requires the necessary base functions to be defined)
#set page(width: auto, height: auto, margin: 10pt)
#cetz.canvas({
import cetz.draw: *
// Define two points
let p1 = (0, 0)
let p2 = (3, 1)
let p3 = (5, 1)
let p4 = (5, -1)
// Draw points for reference
circle(p1, radius: 0.05, fill: blue)
circle(p2, radius: 0.05, fill: blue)
circle(p3, radius: 0.05, fill: red)
circle(p4, radius: 0.05, fill: red)
// Use the wrapper function
capacitor_between(p1, p2, "C1", label: $C_1$, scale: 1.0)
// Use the wrapper function with different scale and parameters
capacitor_between(
p3, p4, // start, end
"C2", // name
label: $C_2$,
scale: 1, // Makes plates larger relative to leads
plate_gap: 0.15,
plate_height: 0.8,
stroke: red,
label_offset: (0.1, 0.1), // Adjust offset if needed
label_anchor: "south-west",
)
})
//*/
#let current_arrow(
start_point,
end_point,
name,
label: none,
label_anchor: "south", // Default anchor point ON the label text box
label_offset: (0, 0.1), // Default offset FROM the midpoint TO the anchor point
label_size: 8pt,
text_fill: black,
arrow_position:60%,
arrow_scale: 1.0, // Scale for the arrowhead size
arrow_fill: black,
arrow_stroke: black, // Stroke color for the arrowhead outline
// Use stroke argument directly for line styling:
// Use ..styling for any *other* Cetz styling options if needed
..styling
) = {
// Determine the final stroke for the main line, prioritizing the direct argument
// Ensure the arrow mark stroke thickness matches the line stroke
// Define the midpoint specification (used multiple times)
let mid_point_spec = (start_point, arrow_position, end_point)
cetz.draw.group(name: name, {// Pass styling to the group
// No get-ctx or resolve needed here anymore
// --- Draw func logic implementation ---
// 1. Draw the full line segment using direct points
cetz.draw.line(start_point, end_point, ..styling)
// 2. Draw the "invisible" line carrying the mark from start to midpoint
cetz.draw.line(
start_point,
mid_point_spec, // Use the relative midpoint specification
mark: (
end: "stealth",
scale: arrow_scale *0.7, // Use consistent scaling factor
fill: arrow_fill
),
..styling
)
// --- End of draw func logic ---
// --- Label Placement ---
if label != none {
cetz.draw.content(
(rel:label_offset,to:mid_point_spec), // Place relative to the midpoint specification
text(size: label_size, fill: text_fill, label),
anchor: label_anchor,
)
}
// --- Define Anchors Directly using specifications ---
cetz.draw.anchor("start", start_point)
cetz.draw.anchor("end", end_point)
cetz.draw.anchor("center", mid_point_spec) // Anchor at the relative midpoint
cetz.draw.anchor("default", mid_point_spec)
})
}
`example.typ`
#import "./components.typ" as comp
#import "@preview/cetz:0.3.4" as cetz
/*
; figure 1
; RC Low-Pass Filter (from Figure 1)
; Node (Vin) is the input voltage node (relative to GND).
; Node (Vout) is the output voltage node (relative to GND).
(Vin) -- R -- (Vout) -- C -- (GND)
*/
#figure(
cetz.canvas({
let default_stroke = (stroke: (thickness:0.6pt))
let node_styling = (radius: 0.06, fill: black, ..default_stroke)
let x_in = 0
let x_R = 1.5
let x_C = 3.0
let x_out = 4.0
let y_top = 1.0
let y_bottom = -1.0
let y_gnd = -1.5
// Input terminals and voltage label
comp.node((x_in, y_top), "in_T", ..node_styling)
comp.node((x_in, y_bottom), "in_B", ..node_styling)
/* comp.voltage_points(
(x_in, y_top),
(x_in, y_bottom),
"Vin",
label: $V_"in"$,
voltage_arrow_pos: "left",
voltage_arrow_dir: "down",
arrow_offset: 0.3,
show_points: false,
show_point_labels: false
)
*/
comp.voltage_points(
(x_in, (y_top +y_bottom)/2),
"Vin",
label: $V_"in"$,
point_separation: 2,
point_radius: 0.05,
point_fill: white,
point_stroke: black,
show_point_labels: true,
top_label: $$,
bottom_label: none,
point_text_fill: gray,
point_label_size: 9pt,
top_label_offset: (0, 0.1),
top_label_anchor: "south",
bottom_label_offset: (0, -0.1),
bottom_label_anchor: "north",
show_voltage_annotation: true,
voltage_arrow_pos: "left",
voltage_arrow_dir: "down",
arrow_offset: 0.35,
arrow_length_factor: 0.9,
arrow_stroke_thickness: 0.8pt,
annotation_label_size: 10pt
)
comp.voltage_points(
(x_out, (y_top +y_bottom)/2),
"Vout",
label: $V_"out"$,
point_separation: 2,
point_radius: 0.05,
point_fill: white,
point_stroke: black,
show_point_labels: true,
top_label: $$,
bottom_label: none,
point_text_fill: gray,
point_label_size: 9pt,
top_label_offset: (0, 0.1),
top_label_anchor: "south",
bottom_label_offset: (0, -0.1),
bottom_label_anchor: "north",
show_voltage_annotation: true,
voltage_arrow_pos: "left",
voltage_arrow_dir: "down",
arrow_offset: -0.35,
arrow_length_factor: 0.9,
arrow_stroke_thickness: 0.8pt,
annotation_label_size: 10pt
)
// Resistor
comp.resistor((x_R, y_top), "R", label: $R$, width: 1.2, ..default_stroke)
// Capacitor
comp.capacitor((x_C, (y_top+ y_bottom)/2), "C", label: $C$, rotate: 90deg, lead_extension: 0.9, ..default_stroke)
// Output terminals and voltage label
comp.node((x_out, y_top), "out_T", ..node_styling)
comp.node((x_out, y_bottom), "out_B", ..node_styling)
// Ground symbol
comp.gnd_symbol((x_C -1, y_gnd), "GND", ..default_stroke)
// Connections
cetz.draw.line("in_T", "R.L", ..default_stroke)
cetz.draw.line("R.R", "C.R", ..default_stroke)
cetz.draw.line("C.R", "out_T", ..default_stroke)
cetz.draw.line("in_B", (x_C, y_bottom), ..default_stroke) // Connect bottom input to ground line
cetz.draw.line("C.L", (x_C, y_bottom), ..default_stroke) // Connect capacitor bottom to ground line
cetz.draw.line("out_B", (x_C, y_bottom), ..default_stroke) // Connect bottom output to ground line
cetz.draw.line((x_C -1, y_bottom), "GND.T", ..default_stroke) // Connect ground line to GND symbol
}),
caption: [RC filter.]
)
/*
VoltageSource VinSrc
Resistor R1
Nmos M1
Resistor R2
Capacitor CL
(VinSrc.T): (Vin)
(VinSrc.B): (GND)
(Vin) -- R1 -- (M1.G)
(M1.D):(VDD)
(M1.B):(GND)
(Vout):(M1.S)
(Vout) -- [ R2 || CL ] -- (GND)
*/
#cetz.canvas({
let default_stroke = (stroke: (thickness:.6pt))
let x_vin = 0
let x_r1 = 1.
let x_m1 = 1.9
let x_out_comps = x_m1 + 0.9
let x_cl = x_out_comps + 2.0
let x_vout = x_cl + 1.0
let y_gate = 1.6
let y_m1_base = 1.0
let y_m1_s = y_m1_base
let y_m1_d = y_m1_base + 1.2
let y_m1_b = y_m1_base + 0.6
let y_vdd = y_m1_d + 1.5
let y_gnd = -1
let y_cl_center = y_m1_s - 0.95
comp.voltage_source((x_vin, y_gate -1), "Vin",
label: $V_"in"$,
voltage_arrow_pos: "left",
voltage_arrow_dir: "down",
annotation_label_pos: "left",
radius: 0.4,
lead_length: 0.2,
..default_stroke
)
comp.resistor((x_r1, y_gate ), "R1", label: $R_1$, label_pos: "north", label_offset: (0, 0.4), width: 1.0, ..default_stroke)
comp.nmos_transistor((x_m1, y_m1_base), "M1", label: $M_1$, label_pos: "east", label_anchor: "west", label_offset: (0.3, 0.3),..default_stroke)
comp.vdd_symbol((x_out_comps, y_vdd), "Vdd", label: $V_"DD"$, ..default_stroke)
comp.resistor((x_out_comps, y_m1_s -1), "R2", rotate: 90deg, label: $R_2$, label_pos: "west", label_offset: (0.5, 0.5), ..default_stroke)
comp.capacitor((x_cl, y_cl_center), "CL", rotate: 90deg, label: $C_L$, label_pos: "east", label_offset: (-0.2, -0.5), ..default_stroke)
comp.gnd_symbol((x_vin, y_gnd), "GND_Vin", ..default_stroke)
comp.gnd_symbol((x_m1 + 1.27, y_m1_b), "GND_M1B", ..default_stroke)
comp.gnd_symbol((x_out_comps, y_gnd), "GND_R2", ..default_stroke)
comp.gnd_symbol((x_cl, y_gnd), "GND_CL", ..default_stroke)
comp.node((x_vout, y_m1_b), "VoutNode", label:$V_"out"$, label_offset: (0.15, 0), label_anchor: "west", ..default_stroke)
comp.connect-orthogonal("Vin.T", "R1.L", style: "hv", ..default_stroke)
comp.connect-orthogonal("Vin.B", "GND_Vin.T", style: "hv", ..default_stroke)
comp.connect-orthogonal("R1.R", "M1.G", style: "hv", ..default_stroke)
comp.connect-orthogonal("M1.D", "Vdd.B", style: "hv", ..default_stroke)
comp.connect-orthogonal("M1.B", "GND_M1B.T", style: "hv", ..default_stroke)
comp.connect-orthogonal("M1.S", "R2.R", style: "hv", ..default_stroke)
comp.connect-orthogonal("GND_R2.T", "R2.L", style: "hv", ..default_stroke)
comp.connect-orthogonal("GND_CL.T", "CL.L", style: "hv", ..default_stroke)
comp.connect-orthogonal("CL.R", "M1.S", style: "hv", ..default_stroke)
comp.connect-orthogonal("VoutNode", "M1.S", style: "hv", ..default_stroke)
})
// /*
#figure(
cetz.canvas({
let default_stroke = (stroke: (thickness: 0.6pt))
let node_styling = (radius: 0.06, fill: white, ..default_stroke)
let label_styling(clr) = (text_fill: clr, label_size: 9pt)
let term_styling = label_styling(gray) + node_styling + (fill: none,)
let voltage_label_styling = label_styling(blue)
let xG = -2.5
let xS = 0
let xGm1 = 0
let xGmB1 = 1.8
let xRds = 3.6
let xD = xRds+1
let yTop = 1.0
let yBottom = 0.0
let yB = -1.0
let yGnd = yB - 0.5
let vgs_x = xG - 0.4
let vbs_x = xS - 0.5
comp.voltage_points(
(xG, yTop/2),
"Vgs",
label: $V_"GS"$,
point_separation: 1,
point_radius: 0.05,
point_fill: white,
point_stroke: black,
show_point_labels: true,
top_label: $G$,
bottom_label: none,
point_text_fill: gray,
point_label_size: 9pt,
top_label_offset: (0, 0.1),
top_label_anchor: "south",
bottom_label_offset: (0, -0.1),
bottom_label_anchor: "north",
show_voltage_annotation: true,
voltage_arrow_pos: "left",
voltage_arrow_dir: "down",
arrow_offset: 0.35,
arrow_length_factor: 0.9,
arrow_stroke_thickness: 0.8pt,
annotation_label_size: 10pt
)
comp.node((xD, yTop), "D", label:$D$, ..term_styling, label_anchor: "south", label_offset: (0, 0.15))
comp.voltage_points(
((xS, yBottom),50%, (xS, yB)),
"Vbs",
label: $V_"BS"$,
point_separation: 1,
point_radius: 0.05,
point_fill: white,
point_stroke: black,
show_point_labels: true,
top_label: $S$,
bottom_label: $B$,
point_text_fill: gray,
point_label_size: 9pt,
top_label_offset: (0.2, -0.25),
top_label_anchor: "south",
bottom_label_offset: (0.2, 0.25),
bottom_label_anchor: "north",
show_voltage_annotation: true,
voltage_arrow_pos: "left",
voltage_arrow_dir: "up",
arrow_offset: 0.35,
arrow_length_factor: 0.9,
arrow_stroke_thickness: 0.8pt,
annotation_label_size: 10pt
)
comp.current_source(
(xGm1, yTop/2), "gm1",
label: $g_"m1" V_"GS"$, arrow_dir: "down",
radius: 0.35, lead_length: (yTop/2 - 0.35),
label_offset: (0.75, 0), label_anchor: "west",
..default_stroke
)
comp.current_source(
(xGmB1, yTop/2), "gmB1",
label: $g_"mB1" V_"BS"$, arrow_dir: "down",
radius: 0.35, lead_length: (yTop/2 - 0.35),
label_offset: (0.75, 0), label_anchor: "west",
..default_stroke
)
comp.resistor(
(xRds, yTop/2), "rds1",
label: $r_"DS1"$, rotate: 90deg,
height: 0.4, width: 0.7,
lead_extension: (yTop - 0.7)/2,
label_offset: (0., -0.10), label_anchor: "west",
..default_stroke
)
comp.gnd_symbol((xS, yGnd), "GndB", ..default_stroke)
cetz.draw.line("gm1.T", "gmB1.T", ..default_stroke)
cetz.draw.line("gmB1.T", "rds1.R", ..default_stroke)
cetz.draw.line("rds1.R", "D", ..default_stroke)
cetz.draw.line("Vgs.B", (xS, yBottom), ..default_stroke)
cetz.draw.line("gm1.B", "gmB1.B", ..default_stroke)
cetz.draw.line("gmB1.B", "rds1.L", ..default_stroke)
cetz.draw.line("Vbs.B", "GndB.T", ..default_stroke)
}),
caption: [(b) Transistor small signal model]
)
//*
#cetz.canvas({
let default_stroke = (stroke: (thickness:.6pt))
let x_vin = 0
let x_r1 = 1.
let x_m1 = 1.9
let x_out_comps = x_m1 + 0.9
let x_cl = x_out_comps + 4.0
let x_vout = x_cl + 1.5
let y_gate = 1.6
let y_m1_base = 1.0
let y_m1_s = y_m1_base
let y_m1_d = y_m1_base + 1.2
let y_m1_b = y_m1_base + 0.6
let y_vdd = y_m1_d + 1.5
let y_gnd = -1
let y_cl_center = y_m1_s - 0.95
comp.voltage_source((x_vin, y_gate -1), "Vin",
label: $V_"in"$,
voltage_arrow_pos: "left",
voltage_arrow_dir: "down",
annotation_label_pos: "left",
radius: 0.4,
lead_length: 0.2,
..default_stroke
)
comp.resistor((x_r1, y_gate ), "R1", label: $R_1$, label_pos: "north", label_offset: (0, 0.4), width: 1.0, ..default_stroke)
comp.gnd_symbol((x_out_comps, y_vdd), "Vdd", label: $\"V_"DD"\"$, label_offset:(0, -0.6),rotate:180deg,..default_stroke)
comp.resistor((x_out_comps, y_m1_s -1), "R2", rotate: 90deg, label: $R_2$, label_pos: "west", label_offset: (0.5, 0.5), ..default_stroke)
comp.voltage_points(
("R1.R",50%,"R2.R"),
"Vgs",
label: $V_"GS"$,
point_separation: 1,
point_radius: 0.05,
point_fill: white,
point_stroke: black,
show_point_labels: true,
top_label: $G$,
bottom_label: none,
point_text_fill: gray,
point_label_size: 9pt,
top_label_offset: (0, 0.1),
top_label_anchor: "south",
bottom_label_offset: (0, -0.1),
bottom_label_anchor: "north",
show_voltage_annotation: true,
voltage_arrow_pos: "left",
voltage_arrow_dir: "down",
arrow_offset: 0.35,
arrow_length_factor: 0.9,
arrow_stroke_thickness: 0.8pt,
annotation_label_size: 10pt
)
comp.current_source(
(rel:(1,0),to:"Vgs.east"), "gm1",
label: $g_"m1" V_"GS"$, arrow_dir: "down",
radius: 0.35,
label_offset: (0.75, 0),
lead_length: 0.15,
label_anchor: "west",
..default_stroke
)
comp.current_source(
(rel:(1.7,0),to:"gm1.east"), "gmB1",
label: $g_"mB1" V_"BS"$, arrow_dir: "down",
radius: 0.35,
lead_length: 0.15,
label_offset: (0.75, 0), label_anchor: "west",
..default_stroke
)
comp.resistor(
(rel:(1.7,0),to:"gmB1.east"), "rds1",
label: $r_"DS1"$, rotate: 90deg,
height: 0.4, width: 0.7,
lead_extension: (0.1),
label_offset: (0., -0.10), label_anchor: "west",
..default_stroke
)
comp.capacitor((x_cl, y_cl_center), "CL", rotate: 90deg, label: $C_L$, label_pos: "east", label_offset: (-0.2, -0.5), ..default_stroke)
comp.voltage_points(
(to:"R2.center",rel:(2,0)),
"Vbs",
label: $V_"BS"$,
point_separation: 1.3,
point_radius: 0.05,
point_fill: white,
point_stroke: black,
show_point_labels: true,
top_label: $S$,
bottom_label: $B$,
point_text_fill: gray,
point_label_size: 9pt,
top_label_offset: (0.2, -0.25),
top_label_anchor: "south",
bottom_label_offset: (0.2, 0.25),
bottom_label_anchor: "north",
show_voltage_annotation: true,
voltage_arrow_pos: "left",
voltage_arrow_dir: "up",
arrow_offset: 0.35,
arrow_length_factor: 0.9,
arrow_stroke_thickness: 0.8pt,
annotation_label_size: 10pt
)
comp.gnd_symbol(("Vbs.B"), "GND_CL", ..default_stroke)
comp.gnd_symbol((x_vin, y_gnd), "GND_Vin", ..default_stroke)
comp.gnd_symbol((x_out_comps, y_gnd), "GND_R2", ..default_stroke)
comp.gnd_symbol((x_cl, y_gnd), "GND_CL", ..default_stroke)
comp.node((x_vout, y_m1_b), "VoutNode", label:$V_"out"$, label_offset: (0.15, 0), label_anchor: "west", ..default_stroke)
comp.connect-orthogonal("Vin.T", "R1.L", style: "hv", ..default_stroke)
comp.connect-orthogonal("Vin.B", "GND_Vin.T", style: "hv", ..default_stroke)
comp.connect-orthogonal("GND_R2.T", "R2.L", style: "hv", ..default_stroke)
comp.connect-orthogonal("GND_CL.T", "CL.L", style: "hv", ..default_stroke)
comp.connect-orthogonal("R1.R", "Vgs.T", style: "vh", ..default_stroke)
comp.connect-orthogonal("rds1.L", "Vgs.B", style: "hv", ..default_stroke)
comp.connect-orthogonal("rds1.R", "gm1.T", style: "hv", ..default_stroke)
comp.connect-orthogonal("Vdd.T", "gm1.T", style: "hv", ..default_stroke)
comp.connect-orthogonal("Vgs.B", "CL.R", style: "vh", ..default_stroke)
comp.connect-orthogonal("Vgs.B", "R2.R", style: "vh", ..default_stroke)
comp.connect-orthogonal("VoutNode", "rds1.L", style: "hv", ..default_stroke)
})
/*
Resistor R2
Capacitor CL
(M1_S):(Vout)
(M1_D):(GND)
(Vout) -- [ gm1*Vin (<-) || gmB1*Vout (<-) || rds1 || R2 || CL ] -- (GND)
*/
#cetz.canvas({
let default_stroke = (stroke: (thickness:.6pt))
comp.voltage_points(
(rel:(0,0)),
"Vgs",
label: $V_"out"$,
point_separation: 1.2,
point_radius: 0.05,
point_fill: white,
point_stroke: black,
show_point_labels: true,
top_label: $S$,
bottom_label: $D$,
point_text_fill: gray,
point_label_size: 9pt,
top_label_offset: (-0.2, -0.25),
top_label_anchor: "south",
bottom_label_offset: (-0.2, 0.25),
bottom_label_anchor: "north",
show_voltage_annotation: true,
voltage_arrow_pos: "left",
voltage_arrow_dir: "down",
arrow_offset: 0.65,
arrow_length_factor: 0.9,
arrow_stroke_thickness: 0.8pt,
annotation_label_size: 10pt
)
comp.current_source(
(rel:(1,0),to:"Vgs.center"), "gm1",
label: $g_"m1" (V_"in"-V_"out")$, arrow_dir: "up",
radius: 0.35,
label_offset: (0.75, 0),
label_anchor: "west",
..default_stroke
)
comp.current_source(
(rel:(2.4,0),to:("gm1.east")), "gmB1",
label: $-g_"mV1" V_"out"$, arrow_dir: "up",
radius: 0.35,
label_offset: (0.75, 0),
label_anchor: "west",
..default_stroke
)
comp.resistor(
(rel:(1.7,0),to:"gmB1.east"), "rds1",
label: $r_"DS1"$, rotate: 90deg,
height: 0.4, width: 0.7,
label_offset: (0., -0.10), label_anchor: "west",
..default_stroke
)
comp.resistor(
(rel:(1.7,0),to:"rds1.B"),
"R2",
label: $R_2$, rotate: 90deg,
height: 0.4, width: 0.7,
label_offset: (0., -0.10), label_anchor: "west",
..default_stroke
)
comp.capacitor((rel:(1.7,0),to:"R2.B"), "CL", rotate: 90deg, label: $C_L$, label_pos: "east", label_offset: (-0.2, -0.5), ..default_stroke)
comp.gnd_symbol(
(rel:(5, -0.2), to: "Vgs.B"),
"gnd",
..default_stroke
)
comp.connect-orthogonal("Vgs.T", "gm1.T", style: "hv", ..default_stroke)
cetz.draw.line("gm1.T", "gmB1.T", ..default_stroke)
cetz.draw.line("gmB1.T", "rds1.R", ..default_stroke)
cetz.draw.line("rds1.R", "R2.R", ..default_stroke)
cetz.draw.line("R2.R", "CL.R", ..default_stroke)
comp.connect-orthogonal("Vgs.B", "gm1.B", style: "hv", ..default_stroke)
cetz.draw.line("gm1.B", "gmB1.B", ..default_stroke)
cetz.draw.line("gmB1.B", "rds1.L", ..default_stroke)
cetz.draw.line("rds1.L", "R2.L", ..default_stroke)
cetz.draw.line("R2.L", "CL.L", ..default_stroke)
comp.connect-orthogonal("gm1.B", "gnd.T", style: "vh", ..default_stroke)
})
//*/
#cetz.canvas({
let default_stroke = (stroke: (thickness: 0.6pt))
let node_style = (fill: black, radius: 0.05pt)
let x_in_labels = -1
let x_transistors = 0
let x_out_label = 2
let y_vdd = 4.4
let y_m3_base = 3.0
let y_m2_base = 1.0
let y_m1_base = -0.5
let y_gnd = -1.5
let pt_vin_label = (x_in_labels, 0)
let pt_vbias_label = (x_in_labels, 1.6)
let pt_vout_label = (x_out_label+1, 2.2)
let pt_vdd_connect = (x_transistors, y_vdd)
let pt_gnd_connect = (x_transistors, y_gnd)
comp.vdd_symbol(pt_vdd_connect, "VddSym", label: $V_"DD"$, label_offset:(0, 0.3), ..default_stroke)
comp.gnd_symbol(pt_gnd_connect, "GndSym", ..default_stroke)
comp.nmos_transistor((x_transistors, y_m1_base), "M1", label: $M_1$, label_pos: "west", label_offset: (0.5, 0.3), ..default_stroke,bulk_lead_factor:-0.6)
comp.nmos_transistor((x_transistors, y_m2_base), "M2", label: $M_2$, label_pos: "west", label_offset: (0.5, 0.3), ..default_stroke,bulk_lead_factor:-0.6)
comp.pmos_transistor((x_transistors+1.8, y_m3_base), "M3", label: $M_3$, label_pos: "west", label_offset: (-1.0, -0.0), ..default_stroke,width:-0.9,bulk_lead_factor:-0.6)
comp.node(pt_vin_label, "VinNode", label: $V_"in"$, label_anchor: "east", label_offset: (-0.2, 0))
comp.node(pt_vbias_label, "VbiasNode", label: $V_"bias"$, label_anchor: "east", label_offset: (-0.2, 0))
comp.node(pt_vout_label, "VoutNode", label: $V_"out"$, label_anchor: "west", label_offset: (0.2, 0))
comp.connect-orthogonal("VinNode", "M1.G", style: "hv", ..default_stroke)
comp.connect-orthogonal("VbiasNode", "M2.G", style: "hv", ..default_stroke)
comp.connect-orthogonal("VddSym.B", "M3.S", style: "vh", ..default_stroke)
comp.connect-orthogonal("M1.S", "GndSym.T", style: "hv", ..default_stroke)
comp.connect-orthogonal("M1.D", "M2.S", style: "vh", ..default_stroke)
comp.connect-orthogonal("M2.D", "M3.D", style: "vh", ..default_stroke)
comp.wire_hop(
(rel:(0,0),to:"M3.G"), (rel:(1.16,0),to:"M1.D"),
"M2.D", "VoutNode",
hopping_wire: 1,
hop_radius: 0.1,
hop_direction: 1,
..default_stroke
)
comp.connect-orthogonal("M1.D", (rel:(1.16,0),to:"M1.D"), style: "vh", ..default_stroke)
})
#figure(
cetz.canvas({
let default_stroke = (stroke: (thickness: 0.6pt))
let x_in = -2.0
let x_m1 = 0.
let x_cl = 1.5
let x_vout = 0.7
let y_gnd = -1.0
let y_src = 0.0
let y_gate = 1.0
let y_drain = 2.5
let y_ib_mid = 3.25
let y_vdd = 4.0
let y_cl_mid = (y_drain + y_src) / 2
comp.nmos_transistor(
(x_m1, y_src), "M1",
anchor: "S",
label: $M_1$,
label_pos: "west",
label_offset: (-0.2, 0.5),
bulk_lead_factor:-0.6,
..default_stroke
)
comp.vdd_symbol(
(x_m1 -0.27, y_vdd),
"Vdd",
label: $V_"dd"$,
label_pos: "north",
label_anchor: "south",
label_offset: (0, 0.1),
..default_stroke
)
comp.current_source(
(x_m1 -0.27, y_ib_mid), "Ib",
label: $I_b$,
arrow_dir:"down",
label_offset: (0.6, 0),
..default_stroke
)
comp.capacitor(
(x_cl, y_cl_mid), "CL",
label: $C_L$,
label_pos: "center",
label_offset: (0.1, -0.6),
rotate: 90deg,
lead_extension: 0.9,
color: blue,
..default_stroke
)
comp.gnd_symbol((x_m1 -0.27, y_gnd), "GND_M1", ..default_stroke)
comp.gnd_symbol((x_cl, y_gnd), "GND_CL", ..default_stroke)
comp.node((rel:(0.9,0),to:"CL.R"),"Vout",label:$V_"out"$,label_offset:(0.4,0))
comp.node((rel:(-0.9,0),to:"M1.G"),"Vin")
cetz.draw.arc((rel:(0.,-0.2),to:"Vin"),start:180deg+10deg,stop:270deg,radius:2,stroke:black,mark: (end: ">"))
cetz.draw.content(
(rel:(0.3,-1.5),to:"Vin"),
[$V_"in"$]
)
comp.connect-orthogonal("Vin.east", "M1.G", ..default_stroke)
comp.connect-orthogonal("M1.S", "GND_M1.T", ..default_stroke)
comp.connect-orthogonal("CL.R", "Vout.west", ..default_stroke)
comp.connect-orthogonal("Vdd.B", "Ib.T", ..default_stroke)
comp.connect-orthogonal("M1.D", "Ib.B", ..default_stroke)
comp.connect-orthogonal("M1.D", "CL.R", ..default_stroke)
comp.connect-orthogonal("CL.L", "GND_CL.T", ..default_stroke)
cetz.draw.content(
(rel:(5,0),to:"Vout"),
[#rect([
desired amplifier specs:
#set list(indent: 1em)
- $|A_v| > 7$
- $omega_"3dB" >= 2pi dot "250 MHz at " C_L = "500 fF"$
- Assume for now: $V^\* = "200 mV"$
])]
)
}),
caption: [Common source amplifier and the desired specifications.]
)
/*
; Simple circuit to characterize NMOS (Figure 2)
; Components: M1 (NMOS), VGS (VSource), VDS (VSource)
; Current: ID1 (Drain Current)
(GND) -- VGS (-+) -- (M1.G) ; VGS source: negative terminal to GND, positive to M1.G
(GND) -- VDS (-+) -- ->ID1 -- (M1.D) ; VDS source: negative terminal to GND, positive to M1.D.
; Current ID1 flows from VDS(+) towards M1.D.
(M1.S):(GND) ; M1 Source terminal connected directly to GND
(M1.B):(GND) ; M1 Bulk terminal connected directly to GND (assumed standard connection)
*/
#figure(
cetz.canvas({
let default_stroke = (stroke: (thickness:0.6pt))
let node_styling = (radius: 0.06, fill: black, ..default_stroke)
let x_vgs = -2.0
let x_m1 = -0
let x_vds = 2.0
let y_gate = 1.0
let y_drain=3.0
let y_source_base = -0.5
let y_gnd = -1.0
let y_v_center = (y_gate + y_source_base) / 2
comp.nmos_transistor((x_m1 -0.9, 0.3), "M1",
label: $M_1$,
label_pos: "east",
label_offset: (0.2, 0.3),
..default_stroke
)
comp.voltage_source((x_vgs, y_v_center), "VGS",
label: $V_"GS"$,
voltage_arrow_pos: "left",
voltage_arrow_dir: "down",
annotation_label_pos: "left",
lead_length: (y_gate - y_source_base)/2 - 0.4,
..default_stroke
)
comp.voltage_source((x_vds, y_v_center), "VDS",
label: $V_"DS"$,
voltage_arrow_pos: "right",
voltage_arrow_dir: "down",
annotation_label_pos: "right",
lead_length: (y_gate - y_source_base)/2 -0.4,
..default_stroke
)
comp.gnd_symbol((x_m1, y_gnd), "GND", ..default_stroke)
let arrow_start = (x_m1, y_drain + 0.4)
let arrow_end = (x_m1, y_drain + 0.05)
comp.connect-orthogonal("VGS.T", "M1.G", ..default_stroke)
comp.connect-orthogonal("VGS.B", (x_vgs, y_source_base), ..default_stroke)
comp.connect-orthogonal((x_vgs, y_source_base), (x_m1, y_source_base), ..default_stroke)
comp.connect-orthogonal("VDS.T", (x_m1, y_drain), ..default_stroke)
comp.connect-orthogonal("VDS.B", (x_vds, y_source_base), ..default_stroke)
comp.connect-orthogonal((x_vds, y_source_base), (x_m1, y_source_base), ..default_stroke)
comp.connect-orthogonal("M1.S", (x_m1, y_source_base), ..default_stroke)
comp.connect-orthogonal("M1.B", (x_m1, y_source_base+0.5), ..default_stroke)
comp.connect-orthogonal((x_m1, y_source_base), "GND.T", ..default_stroke)
comp.current_arrow(
(0,y_drain),
"M1.D",
"I1",
label: $I_1$,
label_offset: (0.2, 0.15),
label_anchor: "north",
..default_stroke
)
}),
caption: [Simple circuit to characterize NMOS.]
)
feel free to take this, the code was also inspired out of circetz so not all credit is mine, and some part was modified by @janosh here https://github.com/janosh/diagrams/issues/44
Thanks a lot for the code you shared!
I think @fenjalien’s project is a really good starting point, but it’s not exactly the direction I want to follow — I totally agree with you.
For me, the best way to call components would be something like this:
#import "@preview/circuitor:0.1.0"
#canvas({
resistor("componentID", (x,y), label: $R_1$, label-position: "west", rotate: 0deg, scale: 1, ...parameters)
wire("firstComponentID", "secondComponentID", stroke: 1pt, ...parameters)
vsource("componentID", (x,y), label: $V1$, rotate: 0deg, scale: 1, color: blue, ...parameters)
//or wires using points
wire((x1,y1), "someComponentID", stroke: 1pt, ...parameters)
})
What do you think about this way of declaring components?
By the way, I want to implement something to allow users to customize stroke, color and scale globally for a project.
Hi all, I do apologise for the extended silence but I have been on a project hiatus, and still am until the end of May. It's coincidental that I've checked my notifications today to find this.
cirCeTZ is intended to be a port of circuitikz, which includes all the options and the settings, which requires reading whatever tex flavour its been written in because its not otherwise easily available. A lot of my previous active development time has also been used on improving CeTZ to make this even possible (and I still don't think its there yet). So yes even though nothing has happened in this repository for a long time, I would very much like to keep working on it, but there have been so many hurdles I've been stumbling over.
@l0uisgrange its fair if you want to make your own circuit drawing package, but you can't just take my code from here without attribution and use it as your own. See T&C 4 of the Apache 2.0 license. I'm also not sure you can change the license to MIT.
For the name of circetz, I don't think I have any particular claiming rights, its whoever gets there first for Typst Universe submission, However, I've been using the name for a while now and grown fond of it, what am I supposed to do when I continue my work on this? Its probably also going to confuse people.
Please reach out to me on discord DMs through the Typst server, I use the same username.
I will also add for the sake of revelvance to this issue, I am open to adding another maintainer but no one has yet volunteered themselves.
Thanks a lot for the code you shared!
I think @fenjalien’s project is a really good starting point, but it’s not exactly the direction I want to follow — I totally agree with you.
For me, the best way to call components would be something like this:
#import "@preview/circetz:0.1.0"
#canvas({ resistor("componentID", (x,y), label: $R_1$, label-position: "west", rotate: 0deg, scale: 1, ...parameters) wire("firstComponentID", "secondComponentID", stroke: 1pt, ...parameters) vsource("componentID", (x,y), label: $V1$, rotate: 0deg, scale: 1, color: blue, ...parameters)
//or wires using points wire((x1,y1), "someComponentID", stroke: 1pt, ...parameters)})
What do you think about this way of declaring components?
By the way, I want to implement something to allow users to customize
stroke,colorandscaleglobally for a project.
you know at the beginning I thought that was good tho, but after drawing circuits with myself I wish the api was different, it should've been like this
-> (starting_point,ending_point) this automagically implies rotation position (you don't have to think much). and how big the wires are.
this obviously will never apply to objects with more than 2 nodes, aka transistors gates, but for resistors,vdc,caps, I am pretty sure this is the correct thing to do even if a bit unconventional. I made a wrapper you see it's called capacitor_between I truly love it.
Hi @fenjalien, thanks for your quick reply.
I will remove your code from my repository and start fresh. I'm sorry for the confusion — I copied the code without thinking it through properly.
About the name: I had the idea for circetz before finding your repository. I’m open to changing it, but only if you want to continue working on this project (which seems inactive for now). I’m not interested in rushing to claim the name circetz on Typst Universe — I just want to release a solid package for version 0.1.0.
@Kreijstal I hadn't thought of that — thanks a lot!
Quick note: I think it would be better to continue this conversation in the dedicated discussion here: https://github.com/l0uisgrange/zap/discussions/14. I just posted a new idea there as well!
Hi @fenjalien, thanks for your quick reply.
I will remove your code from my repository and start fresh. I'm sorry for the confusion — I copied the code without thinking it through properly.
About the name: I had the idea for circetz before finding your repository. I’m open to changing it, but only if you want to continue working on this project (which seems inactive for now). I’m not interested in rushing to claim the name circetz on Typst Universe — I just want to release a solid package for version 0.1.0.
Thanks!
As said, I would like to continue at somepoint, I don't know when that is exactly.
@fenjalien, the code has been removed from my repository, and I apologize once again.
After some thought, I’ve decided to use a different name for the package instead of circetz.
Great news on your project — I’m looking forward to it! I’d gladly welcome your expertise, contributions and suggestions on my project!