Boards grew a third dimension
This release makes the copper layer count a first-class citizen of a typeCAD board, and fills in the copper features around it: planes, controlled impedance, polygon zones, via stitching, teardrops, per-text fonts, and a zone-aware typecad import.
The layer set is the source of truth
let pcb = new PCB('board', { layers: 4 });
pcb.copperLayers; // ['F.Cu', 'In1.Cu', 'In2.Cu', 'B.Cu']One number drives everything: routing defaults (the autorouter uses every declared copper layer), through-hole pad clearance (pads exist on every layer they should), via spans, and write-time validation — referencing a layer that isn’t declared fails with an actionable error instead of producing a board KiCad can’t open. Physical construction comes from pcb.stackup(), now with per-layer material overrides (thickness, material, epsilon_r, loss_tangent) that redistribute the remaining dielectric budget to keep the board at its total thickness.
Planes
pcb.outline(0, 0, 60, 45);
pcb.plane('GND', 'In1.Cu');
pcb.plane('+3V3', 'In2.Cu');A plane dedicates a whole layer to a net: at create() it becomes a board-covering filled zone (through-hole pads connect solid, SMDs reach it through vias), and the autorouter keeps signals off the layer. Net classes can claim preferred layers (pcb.netClass('mem', { layers: ['In2.Cu'] })) — through-hole nets anchor directly on the class’s layer.
Controlled impedance, now with tolerance
pcb.impedanceWidth('In1.Cu', 50); // exact solved width
pcb.impedanceWidth('F.Cu', 50, 5); // snapped to a 0.01mm fab grid within 50±5Ω
pcb.route(clk, { impedance: { target: 100, tolerance: 10 } });Microstrip on outer layers, symmetric stripline on inner ones, computed from the board’s resolved stackup. With a tolerance, the width snaps to a fabrication grid inside the band; if the design-rule floor forces the trace outside it, routing warns with the achieved impedance.
Via policy
pcb.viaPolicy({ type: 'through' }); // default: budget-fab-friendly
pcb.viaPolicy({ type: 'blind-buried', maxSpan: 2 }); // exact layer pairs, span-limitedRouter vias follow a manufacturing policy instead of silently emitting buried vias for inner-layer transitions.
Zones grew up
pcb.zone({
net: 'GND', layers: ['F.Cu'],
points: [{ x: 10, y: 10 }, { x: 20, y: 10 }, { x: 20, y: 14 }, { x: 14, y: 14 }, { x: 14, y: 20 }, { x: 10, y: 20 }],
fill: { mode: 'hatched', hatchWidth: 0.2, islandRemovalMode: 2 },
});Arbitrary polygons (points), grouped fill settings mirroring KiCad’s (fill ...) block, filledAreasThickness finally serialized, islandRemovalMode actually defaulting to 2, and rule areas with placement. Unconnected pours (no net) are legal. Keepouts take the same geometry.
Via stitching
pcb.stitch('GND', { pitch: 1.5 }); // → number of vias placedA clearance-aware grid of vias tying pours to planes. Candidates are skipped against pads, tracks, vias, zones, and keepouts on every layer the stitch spans — component footprints block regardless of net (even components that aren’t placed yet), same-net pours never block, and a partial layers span emits blind vias over exactly that span.
Teardrops
pcb.teardrops(); // writes KiCad's tool settings AND generates wedge geometry at router viasReinforced track-to-via junctions: the .kicad_pro opens preconfigured, and vias placed by the autorouter get real tapered wedge segments on each layer they connect.
Fonts and arcs
r1.referenceLayout = { x: 0, y: -1.5, font: 'OCR A Std', bold: true };
pcb.arc({ start: { x: 10, y: 10 }, mid: { x: 13.5, y: 13.5 }, end: { x: 15, y: 10 }, layer: 'F.SilkS' });TrueType faces on component text and pcb.text() (via KiCad’s (face ...)), with bold/italic now actually emitted, plus three-point arcs completing the graphics set.
typecad import keeps zones
Imported boards no longer silently lose their copper: pours emit as pcb.zone() with polygon geometry and fill settings, rule areas as pcb.keepout() with their restrictions, and stackups as pcb.stackup(N).
Verified against the parser
Every serializer token introduced here — (face ...), (mode hatch), (filled_areas_thickness ...), (placement ...), the .kicad_pro teardrop arrays — was probed against KiCad 10’s parser before shipping, and the full demo board (planes, stitching, teardrops, hatched pours) loads cleanly. A few tokens that didn’t survive verification (custom_rule, zone-level smoothing, (mode hatched)) were removed or fixed along the way.