@tscircuit/cli 0.1.2434 → 0.1.2436

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lib/index.js CHANGED
@@ -3,6 +3,7 @@ var __create3 = Object.create;
3
3
  var __getProtoOf3 = Object.getPrototypeOf;
4
4
  var __defProp3 = Object.defineProperty;
5
5
  var __getOwnPropNames3 = Object.getOwnPropertyNames;
6
+ var __getOwnPropDesc3 = Object.getOwnPropertyDescriptor;
6
7
  var __hasOwnProp3 = Object.prototype.hasOwnProperty;
7
8
  function __accessProp(key) {
8
9
  return this[key];
@@ -31,6 +32,23 @@ var __toESM3 = (mod, isNodeMode, target) => {
31
32
  cache.set(mod, to);
32
33
  return to;
33
34
  };
35
+ var __toCommonJS3 = (from) => {
36
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
37
+ if (entry)
38
+ return entry;
39
+ entry = __defProp3({}, "__esModule", { value: true });
40
+ if (from && typeof from === "object" || typeof from === "function") {
41
+ for (var key of __getOwnPropNames3(from))
42
+ if (!__hasOwnProp3.call(entry, key))
43
+ __defProp3(entry, key, {
44
+ get: __accessProp.bind(from, key),
45
+ enumerable: !(desc = __getOwnPropDesc3(from, key)) || desc.enumerable
46
+ });
47
+ }
48
+ __moduleCache.set(from, entry);
49
+ return entry;
50
+ };
51
+ var __moduleCache;
34
52
  var __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
35
53
  var __returnValue = (v) => v;
36
54
  function __exportSetter(name, newValue) {
@@ -74334,6 +74352,7 @@ import {
74334
74352
  } from "kicadts";
74335
74353
  import { applyToPoint as applyToPoint6 } from "transformation-matrix";
74336
74354
  import { symbols as symbols4 } from "schematic-symbols";
74355
+ import { NoConnect } from "kicadts";
74337
74356
  import { formatSiUnit } from "format-si-unit";
74338
74357
  import {
74339
74358
  EmbeddedFonts as EmbeddedFonts2,
@@ -74362,11 +74381,11 @@ import {
74362
74381
  import { Paper, Uuid as Uuid4 } from "kicadts";
74363
74382
  import { KicadPcb as KicadPcb2 } from "kicadts";
74364
74383
  import { cju as cju2 } from "@tscircuit/circuit-json-util";
74365
- import { compose as compose10, translate as translate10, scale as scale7 } from "transformation-matrix";
74384
+ import { compose as compose11, translate as translate11, scale as scale8 } from "transformation-matrix";
74366
74385
  import {
74367
74386
  Layer,
74368
- Pts as Pts4,
74369
- Xy as Xy4,
74387
+ Pts as Pts5,
74388
+ Xy as Xy5,
74370
74389
  Zone,
74371
74390
  ZoneConnectPads,
74372
74391
  ZoneFill,
@@ -74375,13 +74394,16 @@ import {
74375
74394
  ZonePolygon
74376
74395
  } from "kicadts";
74377
74396
  import {
74378
- applyToPoint as applyToPoint9,
74379
- compose as compose3,
74397
+ applyToPoint as applyToPoint10,
74398
+ compose as compose4,
74380
74399
  rotate,
74381
- translate as translate3
74400
+ translate as translate4
74382
74401
  } from "transformation-matrix";
74383
- import { Pts as Pts5, Xy as Xy5, Zone as Zone2, ZoneKeepout, ZonePolygon as ZonePolygon2 } from "kicadts";
74384
- import { applyToPoint as applyToPoint10 } from "transformation-matrix";
74402
+ import { PadPrimitiveGrPoly, PadPrimitives, Pts as Pts4, Xy as Xy4 } from "kicadts";
74403
+ import { applyToPoint as applyToPoint9, compose as compose3, scale as scale2, translate as translate3 } from "transformation-matrix";
74404
+ import { Pts as Pts6, Xy as Xy6, Zone as Zone2, ZoneKeepout, ZonePolygon as ZonePolygon2 } from "kicadts";
74405
+ import { applyToPoint as applyToPoint11 } from "transformation-matrix";
74406
+ var import_polygon_clipping = __toESM3(require_polygon_clipping_cjs(), 1);
74385
74407
  import {
74386
74408
  Paper as Paper2,
74387
74409
  PcbLayers,
@@ -74389,10 +74411,9 @@ import {
74389
74411
  PcbGeneral,
74390
74412
  Setup
74391
74413
  } from "kicadts";
74392
- import { getElementId } from "@tscircuit/circuit-json-util";
74414
+ import { createBoardOwnerMap, getElementId } from "@tscircuit/circuit-json-util";
74393
74415
  import { getFullConnectivityMapFromCircuitJson } from "circuit-json-to-connectivity-map";
74394
74416
  import { PcbNet } from "kicadts";
74395
- var import_polygon_clipping = __toESM3(require_polygon_clipping_cjs(), 1);
74396
74417
  import { Footprint as Footprint4, FootprintModel as FootprintModel4, FootprintAttr as FootprintAttr3 } from "kicadts";
74397
74418
  import {
74398
74419
  parseKicadSexpr,
@@ -74405,7 +74426,7 @@ import {
74405
74426
  TextEffectsFont as TextEffectsFont8
74406
74427
  } from "kicadts";
74407
74428
  import { FootprintAttr } from "kicadts";
74408
- import { applyToPoint as applyToPoint24 } from "transformation-matrix";
74429
+ import { applyToPoint as applyToPoint26 } from "transformation-matrix";
74409
74430
  import {
74410
74431
  Property as Property2,
74411
74432
  TextEffects as TextEffects9,
@@ -74415,80 +74436,74 @@ import {
74415
74436
  FootprintAttr as FootprintAttr2
74416
74437
  } from "kicadts";
74417
74438
  import { FpCircle, Stroke as Stroke5 } from "kicadts";
74418
- import { applyToPoint as applyToPoint11, identity, rotate as rotate2 } from "transformation-matrix";
74439
+ import { applyToPoint as applyToPoint12, identity, rotate as rotate2 } from "transformation-matrix";
74419
74440
  import { FpCircle as FpCircle2, Stroke as Stroke6 } from "kicadts";
74420
- import { applyToPoint as applyToPoint12 } from "transformation-matrix";
74421
- import { compose as compose4, rotateDEG, scale as scale2, translate as translate4 } from "transformation-matrix";
74441
+ import { applyToPoint as applyToPoint13 } from "transformation-matrix";
74442
+ import { compose as compose5, rotateDEG, scale as scale3, translate as translate5 } from "transformation-matrix";
74422
74443
  import { FpRect as FpRect2 } from "kicadts";
74423
74444
  import { FpPoly, FpRect, Stroke as Stroke7 } from "kicadts";
74424
- import { applyToPoint as applyToPoint13, compose as compose5, translate as translate5 } from "transformation-matrix";
74445
+ import { applyToPoint as applyToPoint14, compose as compose6, translate as translate6 } from "transformation-matrix";
74425
74446
  import { FpRect as FpRect3 } from "kicadts";
74426
74447
  import { FpPoly as FpPoly4, FpRect as FpRect4, Stroke as Stroke8 } from "kicadts";
74427
- import {
74428
- applyToPoint as applyToPoint14,
74429
- compose as compose6,
74430
- rotate as rotate3,
74431
- scale as scale3,
74432
- translate as translate6
74433
- } from "transformation-matrix";
74434
- import { FpPoly as FpPoly5, Pts as Pts6, Stroke as Stroke9, Xy as Xy6 } from "kicadts";
74435
74448
  import {
74436
74449
  applyToPoint as applyToPoint15,
74437
74450
  compose as compose7,
74438
- rotate as rotate4,
74451
+ rotate as rotate3,
74439
74452
  scale as scale4,
74440
74453
  translate as translate7
74441
74454
  } from "transformation-matrix";
74455
+ import { FpPoly as FpPoly5, Pts as Pts7, Stroke as Stroke9, Xy as Xy7 } from "kicadts";
74456
+ import {
74457
+ applyToPoint as applyToPoint16,
74458
+ compose as compose8,
74459
+ rotate as rotate4,
74460
+ scale as scale5,
74461
+ translate as translate8
74462
+ } from "transformation-matrix";
74442
74463
  import"kicadts";
74443
74464
  import { FpText, TextEffects as TextEffects10, TextEffectsFont as TextEffectsFont10 } from "kicadts";
74444
- import { applyToPoint as applyToPoint16, rotate as rotate5, identity as identity2 } from "transformation-matrix";
74465
+ import { applyToPoint as applyToPoint17, rotate as rotate5, identity as identity2 } from "transformation-matrix";
74445
74466
  import { TextEffectsJustify as TextEffectsJustify6 } from "kicadts";
74446
74467
  import { FpLine, Stroke as Stroke10 } from "kicadts";
74447
- import { applyToPoint as applyToPoint17, identity as identity3, rotate as rotate6 } from "transformation-matrix";
74468
+ import { applyToPoint as applyToPoint18, identity as identity3, rotate as rotate6 } from "transformation-matrix";
74448
74469
  import { FpPoly as FpPoly6, Stroke as Stroke11 } from "kicadts";
74449
74470
  import {
74450
- applyToPoint as applyToPoint18,
74451
- compose as compose8,
74471
+ applyToPoint as applyToPoint19,
74472
+ compose as compose9,
74452
74473
  rotate as rotate7,
74453
- scale as scale5,
74454
- translate as translate8
74474
+ scale as scale6,
74475
+ translate as translate9
74455
74476
  } from "transformation-matrix";
74456
74477
  import { FpText as FpText3, TextEffects as TextEffects11, TextEffectsFont as TextEffectsFont11 } from "kicadts";
74457
- import { applyToPoint as applyToPoint19, rotate as rotate8, identity as identity4 } from "transformation-matrix";
74478
+ import { applyToPoint as applyToPoint20, rotate as rotate8, identity as identity4 } from "transformation-matrix";
74458
74479
  import { FootprintModel as FootprintModel3 } from "kicadts";
74480
+ import { FootprintPad, PadNet, PadOptions } from "kicadts";
74459
74481
  import {
74460
- FootprintPad,
74461
- PadPrimitives,
74462
- PadPrimitiveGrPoly,
74463
- Pts as Pts7,
74464
- Xy as Xy7,
74465
- PadOptions,
74466
- PadNet
74467
- } from "kicadts";
74468
- import {
74469
- applyToPoint as applyToPoint20,
74470
- compose as compose9,
74471
- translate as translate9,
74472
- scale as scale6,
74473
- rotate as rotate9
74482
+ applyToPoint as applyToPoint21,
74483
+ compose as compose10,
74484
+ rotate as rotate9,
74485
+ scale as scale7,
74486
+ translate as translate10
74474
74487
  } from "transformation-matrix";
74475
74488
  import { FootprintPad as FootprintPad2, PadDrill, PadNet as PadNet2 } from "kicadts";
74476
- import { applyToPoint as applyToPoint21, rotate as rotate10, identity as identity5 } from "transformation-matrix";
74489
+ import { applyToPoint as applyToPoint22, rotate as rotate10, identity as identity5 } from "transformation-matrix";
74477
74490
  import { FootprintPad as FootprintPad3, PadDrill as PadDrill2 } from "kicadts";
74478
- import { applyToPoint as applyToPoint22, rotate as rotate11, identity as identity6 } from "transformation-matrix";
74491
+ import { applyToPoint as applyToPoint23, rotate as rotate11, identity as identity6 } from "transformation-matrix";
74479
74492
  import { FpLine as FpLine2, Stroke as Stroke12 } from "kicadts";
74480
- import { applyToPoint as applyToPoint23, identity as identity7, rotate as rotate12 } from "transformation-matrix";
74481
- import { Segment, SegmentNet } from "kicadts";
74493
+ import { applyToPoint as applyToPoint24, identity as identity7, rotate as rotate12 } from "transformation-matrix";
74494
+ import { FootprintPad as FootprintPad4, PadOptions as PadOptions2 } from "kicadts";
74482
74495
  import { applyToPoint as applyToPoint25 } from "transformation-matrix";
74496
+ import { Segment, SegmentNet } from "kicadts";
74497
+ import { applyToPoint as applyToPoint27 } from "transformation-matrix";
74483
74498
  import {
74484
- createBoardOwnerMap,
74499
+ createBoardOwnerMap as createBoardOwnerMap2,
74485
74500
  isRouteViaCoveredByPhysicalVia
74486
74501
  } from "@tscircuit/circuit-json-util";
74487
74502
  import { Via, ViaNet } from "kicadts";
74488
- import { applyToPoint as applyToPoint26 } from "transformation-matrix";
74503
+ import { applyToPoint as applyToPoint28 } from "transformation-matrix";
74489
74504
  import { ViaTenting } from "kicadts";
74490
74505
  import { Footprint as Footprint5 } from "kicadts";
74491
- import { applyToPoint as applyToPoint27 } from "transformation-matrix";
74506
+ import { applyToPoint as applyToPoint29 } from "transformation-matrix";
74492
74507
  import { GrLine } from "kicadts";
74493
74508
  import {
74494
74509
  At as At3,
@@ -74497,11 +74512,11 @@ import {
74497
74512
  TextEffectsFont as TextEffectsFont12,
74498
74513
  TextEffectsJustify as TextEffectsJustify7
74499
74514
  } from "kicadts";
74500
- import { applyToPoint as applyToPoint28 } from "transformation-matrix";
74501
- import { applyToPoint as applyToPoint30, rotate as rotate13 } from "transformation-matrix";
74515
+ import { applyToPoint as applyToPoint30 } from "transformation-matrix";
74516
+ import { applyToPoint as applyToPoint32, rotate as rotate13 } from "transformation-matrix";
74502
74517
  import { GrText as GrText2, TextEffects as TextEffects13, TextEffectsFont as TextEffectsFont13, At as At4 } from "kicadts";
74503
- import { applyToPoint as applyToPoint29 } from "transformation-matrix";
74504
- import { cju as cju3 } from "@tscircuit/circuit-json-util";
74518
+ import { applyToPoint as applyToPoint31 } from "transformation-matrix";
74519
+ import { cju as cju3, createBoardOwnerMap as createBoardOwnerMap3 } from "@tscircuit/circuit-json-util";
74505
74520
  import { cju as cju4 } from "@tscircuit/circuit-json-util";
74506
74521
  import { parseKicadSexpr as parseKicadSexpr2, KicadSch as KicadSch2 } from "kicadts";
74507
74522
  import { KicadSymbolLib } from "kicadts";
@@ -75270,7 +75285,7 @@ function transformSchematicPoint(transform, point) {
75270
75285
  function createArcFromPrimitive({
75271
75286
  primitive,
75272
75287
  transform,
75273
- scale: scale8
75288
+ scale: scale9
75274
75289
  }) {
75275
75290
  const start = transformSchematicPoint(transform, primitive.start);
75276
75291
  const mid = transformSchematicPoint(transform, primitive.mid);
@@ -75283,7 +75298,7 @@ function createArcFromPrimitive({
75283
75298
  "stroke",
75284
75299
  [
75285
75300
  "width",
75286
- roundSchematicCoordinate(primitive.strokeWidth == null ? 0.254 : primitive.strokeWidth * scale8)
75301
+ roundSchematicCoordinate(primitive.strokeWidth == null ? 0.254 : primitive.strokeWidth * scale9)
75287
75302
  ],
75288
75303
  ["type", primitive.isDashed ? "dash" : "default"]
75289
75304
  ],
@@ -75339,7 +75354,7 @@ function createSymbolFillSexprPrimitives({
75339
75354
  function createCircleFromPrimitive({
75340
75355
  primitive,
75341
75356
  transform,
75342
- scale: scale8
75357
+ scale: scale9
75343
75358
  }) {
75344
75359
  const scaledPos = transformSchematicPoint(transform, {
75345
75360
  x: primitive.x,
@@ -75347,12 +75362,12 @@ function createCircleFromPrimitive({
75347
75362
  });
75348
75363
  return SymbolCircle.fromSexprPrimitives([
75349
75364
  ["center", scaledPos.x, scaledPos.y],
75350
- ["radius", roundSchematicCoordinate(primitive.radius * scale8)],
75365
+ ["radius", roundSchematicCoordinate(primitive.radius * scale9)],
75351
75366
  [
75352
75367
  "stroke",
75353
75368
  [
75354
75369
  "width",
75355
- roundSchematicCoordinate(primitive.strokeWidth == null ? 0.254 : primitive.strokeWidth * scale8)
75370
+ roundSchematicCoordinate(primitive.strokeWidth == null ? 0.254 : primitive.strokeWidth * scale9)
75356
75371
  ],
75357
75372
  ["type", "default"]
75358
75373
  ],
@@ -75374,7 +75389,7 @@ function createPolylineFromPoints({
75374
75389
  fallbackFillType,
75375
75390
  strokeWidth,
75376
75391
  isDashed,
75377
- scale: scale8
75392
+ scale: scale9
75378
75393
  }) {
75379
75394
  const polyline = new SymbolPolyline;
75380
75395
  const xyPoints = points.map((p) => {
@@ -75384,7 +75399,7 @@ function createPolylineFromPoints({
75384
75399
  const pts = new Pts(xyPoints);
75385
75400
  polyline.points = pts;
75386
75401
  const stroke = new Stroke2;
75387
- stroke.width = roundSchematicCoordinate(strokeWidth == null ? 0.254 : strokeWidth * scale8);
75402
+ stroke.width = roundSchematicCoordinate(strokeWidth == null ? 0.254 : strokeWidth * scale9);
75388
75403
  stroke.type = isDashed ? "dash" : "default";
75389
75404
  polyline.stroke = stroke;
75390
75405
  const fill = SymbolPolylineFill.fromSexprPrimitives(createSymbolFillSexprPrimitives({
@@ -75398,7 +75413,7 @@ function createPolylineFromPoints({
75398
75413
  function createRectangleFromPrimitive({
75399
75414
  primitive,
75400
75415
  transform,
75401
- scale: scale8
75416
+ scale: scale9
75402
75417
  }) {
75403
75418
  const start = transformSchematicPoint(transform, primitive.start);
75404
75419
  const end = transformSchematicPoint(transform, primitive.end);
@@ -75409,7 +75424,7 @@ function createRectangleFromPrimitive({
75409
75424
  "stroke",
75410
75425
  [
75411
75426
  "width",
75412
- roundSchematicCoordinate(primitive.strokeWidth == null ? 0.254 : primitive.strokeWidth * scale8)
75427
+ roundSchematicCoordinate(primitive.strokeWidth == null ? 0.254 : primitive.strokeWidth * scale9)
75413
75428
  ],
75414
75429
  ["type", primitive.isDashed ? "dash" : "default"]
75415
75430
  ],
@@ -75425,7 +75440,7 @@ function createRectangleFromPrimitive({
75425
75440
  function createTextFromPrimitive({
75426
75441
  schText,
75427
75442
  transform,
75428
- scale: scale8
75443
+ scale: scale9
75429
75444
  }) {
75430
75445
  const symbolText = new SymbolText;
75431
75446
  const scaledPos = transformSchematicPoint(transform, {
@@ -75434,7 +75449,7 @@ function createTextFromPrimitive({
75434
75449
  });
75435
75450
  symbolText.value = schText.text;
75436
75451
  symbolText.at = [scaledPos.x, scaledPos.y, 0];
75437
- const scaledFontSize = roundSchematicCoordinate(schText.fontSize * scale8);
75452
+ const scaledFontSize = roundSchematicCoordinate(schText.fontSize * scale9);
75438
75453
  const font = new TextEffectsFont2;
75439
75454
  font.size = { height: scaledFontSize, width: scaledFontSize };
75440
75455
  symbolText.effects = new TextEffects2({ font });
@@ -75700,6 +75715,30 @@ function calculatePinPosition({
75700
75715
  }
75701
75716
  return { x, y, angle };
75702
75717
  }
75718
+ function getKicadPinElectricalType(sourcePort) {
75719
+ if (!sourcePort)
75720
+ return "passive";
75721
+ if (sourcePort.provides_power || sourcePort.provides_ground) {
75722
+ return "power_out";
75723
+ }
75724
+ if (sourcePort.requires_power || sourcePort.requires_ground) {
75725
+ return "power_in";
75726
+ }
75727
+ if (sourcePort.is_using_open_collector || sourcePort.is_using_open_drain) {
75728
+ return "open_collector";
75729
+ }
75730
+ if (sourcePort.is_using_open_emitter)
75731
+ return "open_emitter";
75732
+ if (sourcePort.is_using_tri_state)
75733
+ return "tri_state";
75734
+ if (sourcePort.is_bidirectional)
75735
+ return "bidirectional";
75736
+ if (sourcePort.is_output)
75737
+ return "output";
75738
+ if (sourcePort.is_input)
75739
+ return "input";
75740
+ return "passive";
75741
+ }
75703
75742
  function getPortSide(x, y) {
75704
75743
  if (Math.abs(x) > Math.abs(y))
75705
75744
  return x < 0 ? "left" : "right";
@@ -75716,12 +75755,11 @@ function getCircuitPinNumbers({
75716
75755
  symbolData,
75717
75756
  schematicComponent,
75718
75757
  schematicPorts,
75719
- sourcePorts
75758
+ sourcePortsById
75720
75759
  }) {
75721
75760
  const matches = /* @__PURE__ */ new Map;
75722
75761
  if (!schematicComponent)
75723
75762
  return matches;
75724
- const sourcePortsById = new Map(sourcePorts.map((port) => [port.source_port_id, port]));
75725
75763
  const symbolCenter = symbolData.center ?? { x: 0, y: 0 };
75726
75764
  const componentPorts = schematicPorts.filter((port) => port.schematic_component_id === schematicComponent.schematic_component_id);
75727
75765
  for (const side of ["left", "right", "up", "down"]) {
@@ -75782,17 +75820,17 @@ function createPinSubsymbol({
75782
75820
  });
75783
75821
  const CHIP_PIN_LENGTH = 6;
75784
75822
  const CUSTOM_SYMBOL_PIN_LENGTH = 0.01;
75823
+ const sourcePortsById = new Map(sourcePorts.map((port) => [port.source_port_id, port]));
75785
75824
  const inferredPinNumbers = isChip ? /* @__PURE__ */ new Map : getCircuitPinNumbers({
75786
75825
  symbolData,
75787
75826
  schematicComponent,
75788
75827
  schematicPorts,
75789
- sourcePorts
75828
+ sourcePortsById
75790
75829
  });
75791
75830
  const circuitPinNumbers = validateInferredPinNumbers(symbolData, inferredPinNumbers);
75792
75831
  const origin = schematicComponent ? applyToPoint3(c2kMatSch, schematicComponent.center) : { x: 0, y: 0 };
75793
75832
  for (const [i, port] of symbolData.ports.entries()) {
75794
75833
  const pin = new SymbolPin;
75795
- pin.pinElectricalType = "passive";
75796
75834
  pin.pinGraphicStyle = "line";
75797
75835
  const { x, y, angle } = calculatePinPosition({
75798
75836
  port,
@@ -75823,8 +75861,15 @@ function createPinSubsymbol({
75823
75861
  numFont.size = { height: 1.27, width: 1.27 };
75824
75862
  const numEffects = new TextEffects4({ font: numFont });
75825
75863
  const pinNum = port.pinNumber?.toString() ?? circuitPinNumbers.get(i) ?? `${i + 1}`;
75826
- const schematicPort = schematicPorts.find((candidate) => candidate.schematic_component_id === schematicComponent?.schematic_component_id && String(candidate.pin_number) === pinNum);
75827
- const sourcePort = sourcePorts.find((candidate) => candidate.source_port_id === schematicPort?.source_port_id);
75864
+ const schematicPort = schematicPorts.find((candidate) => {
75865
+ if (candidate.schematic_component_id !== schematicComponent?.schematic_component_id) {
75866
+ return false;
75867
+ }
75868
+ const linkedSourcePort = sourcePortsById.get(candidate.source_port_id);
75869
+ return String(candidate.pin_number ?? linkedSourcePort?.pin_number) === pinNum;
75870
+ });
75871
+ const sourcePort = schematicPort ? sourcePortsById.get(schematicPort.source_port_id) : undefined;
75872
+ pin.pinElectricalType = getKicadPinElectricalType(sourcePort);
75828
75873
  pin._sxNumber = new SymbolPinNumber({
75829
75874
  value: getPhysicalPinName(sourcePort) ?? pinNum,
75830
75875
  effects: numEffects
@@ -76286,8 +76331,8 @@ var AddSchematicGraphicsStage = class extends ConverterStage {
76286
76331
  new Xy2(end.x, end.y)
76287
76332
  ]);
76288
76333
  const stroke = new Stroke3;
76289
- stroke.width = 0;
76290
- stroke.type = "default";
76334
+ stroke.width = (line.stroke_width ?? 0) * (this.ctx.kicadSchematicScaleFactor ?? 1);
76335
+ stroke.type = line.is_dashed ? "dash" : "default";
76291
76336
  stroke.color = preserveStandaloneGraphics ? parseColor(line.color) ?? DEFAULT_SECTION_LINE_COLOR : DEFAULT_SECTION_LINE_COLOR;
76292
76337
  polyline.stroke = stroke;
76293
76338
  polyline.uuid = new Uuid(crypto.randomUUID());
@@ -76598,6 +76643,30 @@ var AddSchematicNetLabelsStage = class extends ConverterStage {
76598
76643
  return this.ctx.kicadSch;
76599
76644
  }
76600
76645
  };
76646
+ var AddSchematicNoConnectsStage = class extends ConverterStage {
76647
+ _step() {
76648
+ const { db, kicadSch, c2kMatSch } = this.ctx;
76649
+ if (!kicadSch) {
76650
+ throw new Error("KicadSch instance not initialized in context");
76651
+ }
76652
+ if (!c2kMatSch) {
76653
+ throw new Error("Schematic transformation matrix not initialized in context");
76654
+ }
76655
+ const noConnectSourcePortIds = new Set(db.source_port.list().filter((sourcePort) => sourcePort.do_not_connect).map((sourcePort) => sourcePort.source_port_id));
76656
+ const noConnects = db.schematic_port.list().filter((schematicPort) => noConnectSourcePortIds.has(schematicPort.source_port_id) && !schematicPort.is_connected).map((schematicPort) => {
76657
+ const position = transformSchematicPoint(c2kMatSch, schematicPort.center);
76658
+ return new NoConnect({
76659
+ at: [position.x, position.y],
76660
+ uuid: crypto.randomUUID()
76661
+ });
76662
+ });
76663
+ kicadSch.noConnects = [...kicadSch.noConnects, ...noConnects];
76664
+ this.finished = true;
76665
+ }
76666
+ getOutput() {
76667
+ return this.ctx.kicadSch;
76668
+ }
76669
+ };
76601
76670
  var DEFAULT_PROPERTY_FONT_SIZE = 1.27;
76602
76671
  var AddSchematicSymbolsStage = class extends ConverterStage {
76603
76672
  _step() {
@@ -77226,6 +77295,7 @@ var CircuitJsonToKicadSchConverter = class {
77226
77295
  new AddLibrarySymbolsStage(circuitJson, this.ctx),
77227
77296
  new AddSchematicSymbolsStage(circuitJson, this.ctx),
77228
77297
  new AddSchematicNetLabelsStage(circuitJson, this.ctx),
77298
+ new AddSchematicNoConnectsStage(circuitJson, this.ctx),
77229
77299
  new AddSchematicTracesStage(circuitJson, this.ctx),
77230
77300
  new AddSchematicGraphicsStage(circuitJson, this.ctx),
77231
77301
  new AddSheetInstancesStage(circuitJson, this.ctx)
@@ -77384,6 +77454,7 @@ var CircuitJsonToKicadSchConverter = class {
77384
77454
  new AddLibrarySymbolsStage(circuitJson, ctx),
77385
77455
  new AddSchematicSymbolsStage(circuitJson, ctx),
77386
77456
  new AddSchematicNetLabelsStage(circuitJson, ctx),
77457
+ new AddSchematicNoConnectsStage(circuitJson, ctx),
77387
77458
  new AddSchematicTracesStage(circuitJson, ctx),
77388
77459
  new AddSchematicGraphicsStage(circuitJson, ctx)
77389
77460
  ];
@@ -77461,6 +77532,110 @@ function getViaLayerSpan(kicadLayers, numLayers) {
77461
77532
  }
77462
77533
  return top === bottom ? [top] : [top, bottom];
77463
77534
  }
77535
+ var getTriangleTwiceArea = (a, b, c) => Math.abs(a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y));
77536
+ var triangulatePolygonWithHoles = (outerContour, holes = []) => {
77537
+ const flattenedPoints = [];
77538
+ const holeIndices = [];
77539
+ let pointCount = 0;
77540
+ const addContour = (contour) => {
77541
+ for (const point of contour) {
77542
+ flattenedPoints.push(point.x, point.y);
77543
+ }
77544
+ pointCount += contour.length;
77545
+ };
77546
+ addContour(outerContour);
77547
+ for (const hole of holes) {
77548
+ holeIndices.push(pointCount);
77549
+ addContour(hole);
77550
+ }
77551
+ const triangleIndices = earcut(flattenedPoints, holeIndices, 2);
77552
+ const triangles = [];
77553
+ for (let index = 0;index < triangleIndices.length; index += 3) {
77554
+ const aIndex = triangleIndices[index];
77555
+ const bIndex = triangleIndices[index + 1];
77556
+ const cIndex = triangleIndices[index + 2];
77557
+ if (aIndex === undefined || bIndex === undefined || cIndex === undefined) {
77558
+ continue;
77559
+ }
77560
+ const getPoint = (pointIndex) => ({
77561
+ x: flattenedPoints[pointIndex * 2],
77562
+ y: flattenedPoints[pointIndex * 2 + 1]
77563
+ });
77564
+ const triangle = [
77565
+ getPoint(aIndex),
77566
+ getPoint(bIndex),
77567
+ getPoint(cIndex)
77568
+ ];
77569
+ if (getTriangleTwiceArea(...triangle) === 0)
77570
+ continue;
77571
+ triangles.push(triangle);
77572
+ }
77573
+ return triangles;
77574
+ };
77575
+ var getDistance = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
77576
+ var getSafePolygonAnchor = (outerContour, holes = []) => {
77577
+ const triangles = triangulatePolygonWithHoles(outerContour, holes);
77578
+ let bestTriangle;
77579
+ for (const [a, b, c] of triangles) {
77580
+ const sideA = getDistance(b, c);
77581
+ const sideB = getDistance(c, a);
77582
+ const sideC = getDistance(a, b);
77583
+ const perimeter = sideA + sideB + sideC;
77584
+ if (perimeter === 0)
77585
+ continue;
77586
+ const inradius = getTriangleTwiceArea(a, b, c) / perimeter;
77587
+ const center = {
77588
+ x: (sideA * a.x + sideB * b.x + sideC * c.x) / perimeter,
77589
+ y: (sideA * a.y + sideB * b.y + sideC * c.y) / perimeter
77590
+ };
77591
+ if (!bestTriangle || inradius > bestTriangle.inradius) {
77592
+ bestTriangle = { center, inradius };
77593
+ }
77594
+ }
77595
+ if (!bestTriangle) {
77596
+ throw new Error("Polygon must contain a valid filled area");
77597
+ }
77598
+ return {
77599
+ center: bestTriangle.center,
77600
+ diameter: Math.min(0.001, bestTriangle.inradius),
77601
+ triangles
77602
+ };
77603
+ };
77604
+ var createFilledPolygonPrimitives = ({
77605
+ contours,
77606
+ center
77607
+ }) => {
77608
+ const toLocalContour = (points) => new Pts4(points.map((point) => {
77609
+ const localPoint = applyToPoint9(compose3(scale2(1, -1), translate3(-center.x, -center.y)), point);
77610
+ return new Xy4(localPoint.x, localPoint.y);
77611
+ }));
77612
+ const primitives = new PadPrimitives;
77613
+ for (const contour of contours) {
77614
+ const polygon = new PadPrimitiveGrPoly;
77615
+ polygon.contours = [toLocalContour(contour)];
77616
+ polygon.width = 0;
77617
+ polygon.filled = true;
77618
+ primitives.addGraphic(polygon);
77619
+ }
77620
+ return primitives;
77621
+ };
77622
+ var createEllipseContour = ({
77623
+ center,
77624
+ width,
77625
+ height,
77626
+ segmentCount = 64
77627
+ }) => {
77628
+ if (width <= 0 || height <= 0) {
77629
+ throw new Error("Ellipse must have positive dimensions");
77630
+ }
77631
+ return Array.from({ length: segmentCount }, (_, index) => {
77632
+ const angle = index / segmentCount * Math.PI * 2;
77633
+ return {
77634
+ x: center.x + width / 2 * Math.cos(angle),
77635
+ y: center.y + height / 2 * Math.sin(angle)
77636
+ };
77637
+ });
77638
+ };
77464
77639
  function simpleHash(str) {
77465
77640
  let hash = 0;
77466
77641
  for (let i = 0;i < str.length; i++) {
@@ -77497,8 +77672,8 @@ var getCopperPourNetInfo = (pour, ctx) => {
77497
77672
  };
77498
77673
  var convertPointsToKicadXy = (points, c2kMatPcb) => {
77499
77674
  return (points ?? []).map((point) => {
77500
- const transformedPoint = applyToPoint9(c2kMatPcb, point);
77501
- return new Xy4(transformedPoint.x, transformedPoint.y);
77675
+ const transformedPoint = applyToPoint10(c2kMatPcb, point);
77676
+ return new Xy5(transformedPoint.x, transformedPoint.y);
77502
77677
  });
77503
77678
  };
77504
77679
  var rotateRingToStartAtTopRight = (points) => {
@@ -77538,7 +77713,7 @@ var removeRepeatedClosingPoint = (points) => {
77538
77713
  };
77539
77714
  var getRectRingPoints = (pour, c2kMatPcb) => {
77540
77715
  const ccwRotationDegrees = pour.rotation ?? 0;
77541
- const cornerTransform = compose3(translate3(pour.center.x, pour.center.y), rotate(ccwRotationDegrees * Math.PI / 180));
77716
+ const cornerTransform = compose4(translate4(pour.center.x, pour.center.y), rotate(ccwRotationDegrees * Math.PI / 180));
77542
77717
  const halfWidth = pour.width / 2;
77543
77718
  const halfHeight = pour.height / 2;
77544
77719
  const corners = [
@@ -77546,7 +77721,7 @@ var getRectRingPoints = (pour, c2kMatPcb) => {
77546
77721
  { x: halfWidth, y: -halfHeight },
77547
77722
  { x: halfWidth, y: halfHeight },
77548
77723
  { x: -halfWidth, y: halfHeight }
77549
- ].map((corner) => applyToPoint9(cornerTransform, corner));
77724
+ ].map((corner) => applyToPoint10(cornerTransform, corner));
77550
77725
  return convertPointsToKicadXy(corners, c2kMatPcb);
77551
77726
  };
77552
77727
  var getPolygonRingPoints = (pour, c2kMatPcb) => removeRepeatedClosingPoint(removeConsecutiveDuplicatePoints(rotateRingToStartAtTopRight(convertPointsToKicadXy(pour.points, c2kMatPcb))));
@@ -77570,16 +77745,15 @@ var getCopperPourZoneRings = (pour, c2kMatPcb) => {
77570
77745
  var createZonePolygons = (outerRing, innerRings) => {
77571
77746
  const polygons = [];
77572
77747
  if (outerRing.length >= 3) {
77573
- polygons.push(new ZonePolygon(new Pts4(outerRing)));
77748
+ polygons.push(new ZonePolygon(new Pts5(outerRing)));
77574
77749
  }
77575
77750
  for (const innerRing of innerRings) {
77576
77751
  if (innerRing.length < 3)
77577
77752
  continue;
77578
- polygons.push(new ZonePolygon(new Pts4(innerRing)));
77753
+ polygons.push(new ZonePolygon(new Pts5(innerRing)));
77579
77754
  }
77580
77755
  return polygons;
77581
77756
  };
77582
- var getTriangleArea = (a, b, c) => Math.abs((a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2);
77583
77757
  var createZoneFilledPolygons = (outerRing, innerRings, kicadLayer) => {
77584
77758
  if (outerRing.length < 3) {
77585
77759
  return [];
@@ -77589,45 +77763,14 @@ var createZoneFilledPolygons = (outerRing, innerRings, kicadLayer) => {
77589
77763
  return [
77590
77764
  new ZoneFilledPolygon({
77591
77765
  layer,
77592
- pts: new Pts4(outerRing)
77766
+ pts: new Pts5(outerRing)
77593
77767
  })
77594
77768
  ];
77595
77769
  }
77596
- const flattenedPoints = [];
77597
- const holeIndices = [];
77598
- let pointIndex = 0;
77599
- const addRing = (ring) => {
77600
- for (const point of ring) {
77601
- flattenedPoints.push(point.x, point.y);
77602
- }
77603
- pointIndex += ring.length;
77604
- };
77605
- addRing(outerRing);
77606
- for (const innerRing of innerRings) {
77607
- holeIndices.push(pointIndex);
77608
- addRing(innerRing);
77609
- }
77610
- const triangleIndices = earcut(flattenedPoints, holeIndices, 2);
77611
- const filledPolygons = [];
77612
- for (let i = 0;i < triangleIndices.length; i += 3) {
77613
- const trianglePoints = [];
77614
- for (let offset = 0;offset < 3; offset++) {
77615
- const pointOffset = triangleIndices[i + offset] * 2;
77616
- const x = flattenedPoints[pointOffset];
77617
- const y = flattenedPoints[pointOffset + 1];
77618
- if (x === undefined || y === undefined)
77619
- continue;
77620
- trianglePoints.push(new Xy4(x, y));
77621
- }
77622
- if (trianglePoints.length !== 3 || getTriangleArea(trianglePoints[0], trianglePoints[1], trianglePoints[2]) === 0) {
77623
- continue;
77624
- }
77625
- filledPolygons.push(new ZoneFilledPolygon({
77626
- layer,
77627
- pts: new Pts4(trianglePoints)
77628
- }));
77629
- }
77630
- return filledPolygons;
77770
+ return triangulatePolygonWithHoles(outerRing, innerRings).map((triangle) => new ZoneFilledPolygon({
77771
+ layer,
77772
+ pts: new Pts5(triangle.map((point) => new Xy5(point.x, point.y)))
77773
+ }));
77631
77774
  };
77632
77775
  var AddCopperPoursStage = class extends ConverterStage {
77633
77776
  _step() {
@@ -77718,8 +77861,8 @@ var AddKeepoutsStage = class extends ConverterStage {
77718
77861
  rawPoints = keepout.outline;
77719
77862
  }
77720
77863
  const kicadPoints = rawPoints.map((p) => {
77721
- const t = applyToPoint10(c2kMatPcb, p);
77722
- return new Xy5(t.x, t.y);
77864
+ const t = applyToPoint11(c2kMatPcb, p);
77865
+ return new Xy6(t.x, t.y);
77723
77866
  });
77724
77867
  const zone = new Zone2({
77725
77868
  net: 0,
@@ -77733,7 +77876,7 @@ var AddKeepoutsStage = class extends ConverterStage {
77733
77876
  copperpour: "not_allowed",
77734
77877
  footprints: "not_allowed"
77735
77878
  }),
77736
- polygons: [new ZonePolygon2(new Pts5(kicadPoints))]
77879
+ polygons: [new ZonePolygon2(new Pts6(kicadPoints))]
77737
77880
  });
77738
77881
  const zones = kicadPcb.zones;
77739
77882
  zones.push(zone);
@@ -77781,6 +77924,14 @@ var InitializePcbStage = class extends ConverterStage {
77781
77924
  return this.ctx.kicadPcb;
77782
77925
  }
77783
77926
  };
77927
+ var resolveKicadPcbNetName = ({
77928
+ candidateName,
77929
+ connectivityKey,
77930
+ boardId
77931
+ }) => {
77932
+ const name = candidateName && candidateName.trim().length > 0 ? candidateName : connectivityKey;
77933
+ return boardId ? `${boardId}/${name}` : name;
77934
+ };
77784
77935
  var AddNetsStage = class extends ConverterStage {
77785
77936
  _step() {
77786
77937
  const { kicadPcb } = this.ctx;
@@ -77789,13 +77940,17 @@ var AddNetsStage = class extends ConverterStage {
77789
77940
  }
77790
77941
  this.ctx.pcbNetMap = /* @__PURE__ */ new Map;
77791
77942
  const netNameByKey = /* @__PURE__ */ new Map;
77943
+ const boardOwnerMap = this.ctx.db.pcb_board.list().length > 1 ? createBoardOwnerMap(this.input) : undefined;
77792
77944
  const sourceNets = this.ctx.db.source_net?.list() ?? [];
77793
77945
  for (const sourceNet of sourceNets) {
77794
77946
  const connectivityKey = sourceNet.subcircuit_connectivity_map_key || sourceNet.source_net_id;
77795
77947
  if (!connectivityKey)
77796
77948
  continue;
77797
- const candidateName = sourceNet.name || sourceNet.source_net_id || "";
77798
- const netName = candidateName && candidateName.trim().length > 0 ? candidateName : connectivityKey;
77949
+ const netName = resolveKicadPcbNetName({
77950
+ candidateName: sourceNet.name || sourceNet.source_net_id,
77951
+ connectivityKey,
77952
+ boardId: boardOwnerMap?.get(sourceNet.subcircuit_id ?? "")?.pcb_board_id
77953
+ });
77799
77954
  netNameByKey.set(connectivityKey, netName);
77800
77955
  }
77801
77956
  const sourceTraces = this.ctx.db.source_trace?.list() ?? [];
@@ -77813,8 +77968,11 @@ var AddNetsStage = class extends ConverterStage {
77813
77968
  if (!connectivityKey)
77814
77969
  continue;
77815
77970
  if (!netNameByKey.has(connectivityKey)) {
77816
- const candidateName = sourceTrace.display_name || sourceTrace.source_trace_id || "";
77817
- const netName = candidateName && candidateName.trim().length > 0 ? candidateName : connectivityKey;
77971
+ const netName = resolveKicadPcbNetName({
77972
+ candidateName: sourceTrace.display_name || sourceTrace.source_trace_id,
77973
+ connectivityKey,
77974
+ boardId: boardOwnerMap?.get(sourceTrace.subcircuit_id ?? "")?.pcb_board_id
77975
+ });
77818
77976
  netNameByKey.set(connectivityKey, netName);
77819
77977
  }
77820
77978
  }
@@ -78040,7 +78198,8 @@ var ExtractFootprintsStage = class extends ConverterStage {
78040
78198
  for (let i = 0;i < pads.length; i++) {
78041
78199
  const pad = pads[i];
78042
78200
  if (pad) {
78043
- pad.uuid = generateDeterministicUuid(`${footprintName}-pad-${pad.number ?? i}`);
78201
+ const padIdentity = pad.number === "" ? `unnumbered-${i}` : pad.number ?? i;
78202
+ pad.uuid = generateDeterministicUuid(`${footprintName}-pad-${padIdentity}`);
78044
78203
  pad.net = undefined;
78045
78204
  }
78046
78205
  }
@@ -78328,7 +78487,7 @@ function convertSilkscreenCircles(silkscreenCircles, { componentCenter, componen
78328
78487
  const fpCircles = [];
78329
78488
  const rotationMatrix = componentRotation !== 0 ? rotate2(componentRotation * Math.PI / 180) : identity();
78330
78489
  for (const circle of silkscreenCircles) {
78331
- const relativeCenter = applyToPoint11(rotationMatrix, {
78490
+ const relativeCenter = applyToPoint12(rotationMatrix, {
78332
78491
  x: circle.center.x - componentCenter.x,
78333
78492
  y: -(circle.center.y - componentCenter.y)
78334
78493
  });
@@ -78356,13 +78515,13 @@ function getBoardToFootprintTransform({
78356
78515
  footprintOrigin,
78357
78516
  ccwRotationDegrees = 0
78358
78517
  }) {
78359
- return compose4(rotateDEG(ccwRotationDegrees), scale2(1, -1), translate4(-footprintOrigin.x, -footprintOrigin.y));
78518
+ return compose5(rotateDEG(ccwRotationDegrees), scale3(1, -1), translate5(-footprintOrigin.x, -footprintOrigin.y));
78360
78519
  }
78361
78520
  function convertCourtyardCircles(courtyardCircles, placement) {
78362
78521
  const fpCircles = [];
78363
78522
  const boardToFootprintTransform = getBoardToFootprintTransform(placement);
78364
78523
  for (const circle of courtyardCircles) {
78365
- const center = applyToPoint12(boardToFootprintTransform, circle.center);
78524
+ const center = applyToPoint13(boardToFootprintTransform, circle.center);
78366
78525
  const layerMap = {
78367
78526
  top: "F.CrtYd",
78368
78527
  bottom: "B.CrtYd"
@@ -78389,7 +78548,7 @@ function convertNoteRectToFootprintGraphic({
78389
78548
  footprintOrigin,
78390
78549
  ccwRotationDegrees = 0
78391
78550
  }) {
78392
- const rectToFootprintTransform = compose5(getBoardToFootprintTransform({ footprintOrigin, ccwRotationDegrees }), translate5(rect.center.x, rect.center.y));
78551
+ const rectToFootprintTransform = compose6(getBoardToFootprintTransform({ footprintOrigin, ccwRotationDegrees }), translate6(rect.center.x, rect.center.y));
78393
78552
  const halfWidth = rect.width / 2;
78394
78553
  const halfHeight = rect.height / 2;
78395
78554
  const corners = [
@@ -78397,7 +78556,7 @@ function convertNoteRectToFootprintGraphic({
78397
78556
  { x: halfWidth, y: -halfHeight },
78398
78557
  { x: halfWidth, y: halfHeight },
78399
78558
  { x: -halfWidth, y: halfHeight }
78400
- ].map((point) => applyToPoint13(rectToFootprintTransform, point));
78559
+ ].map((point) => applyToPoint14(rectToFootprintTransform, point));
78401
78560
  const stroke = new Stroke7;
78402
78561
  stroke.width = rect.stroke_width || 0.1;
78403
78562
  stroke.type = "default";
@@ -78472,7 +78631,7 @@ function rotateCenter(point, degrees) {
78472
78631
  case 270:
78473
78632
  return { x: point.y, y: -point.x };
78474
78633
  default:
78475
- return applyToPoint14(rotate3(degrees * Math.PI / 180), point);
78634
+ return applyToPoint15(rotate3(degrees * Math.PI / 180), point);
78476
78635
  }
78477
78636
  }
78478
78637
  function convertCourtyardRects({
@@ -78482,7 +78641,7 @@ function convertCourtyardRects({
78482
78641
  }) {
78483
78642
  const fpRects = [];
78484
78643
  const fpPolys = [];
78485
- const toLocal = compose6(rotate3(componentRotation * Math.PI / 180), scale3(1, -1), translate6(-componentCenter.x, -componentCenter.y));
78644
+ const toLocal = compose7(rotate3(componentRotation * Math.PI / 180), scale4(1, -1), translate7(-componentCenter.x, -componentCenter.y));
78486
78645
  for (const rect of courtyardRects) {
78487
78646
  const halfW = rect.width / 2;
78488
78647
  const halfH = rect.height / 2;
@@ -78516,13 +78675,13 @@ function convertCourtyardRects({
78516
78675
  fill: false
78517
78676
  }));
78518
78677
  } else {
78519
- const fromRectCenter = compose6(translate6(rect.center.x, rect.center.y), rotate3(rotation * Math.PI / 180));
78678
+ const fromRectCenter = compose7(translate7(rect.center.x, rect.center.y), rotate3(rotation * Math.PI / 180));
78520
78679
  const points = [
78521
78680
  { x: -halfW, y: -halfH },
78522
78681
  { x: halfW, y: -halfH },
78523
78682
  { x: halfW, y: halfH },
78524
78683
  { x: -halfW, y: halfH }
78525
- ].map((point) => applyToPoint14(toLocal, applyToPoint14(fromRectCenter, point)));
78684
+ ].map((point) => applyToPoint15(toLocal, applyToPoint15(fromRectCenter, point)));
78526
78685
  fpPolys.push(new FpPoly4({
78527
78686
  points,
78528
78687
  layer,
@@ -78537,7 +78696,7 @@ function convertCourtyardRects({
78537
78696
  function convertCourtyardOutlines(params2) {
78538
78697
  const { courtyardOutlines, componentCenter, componentRotation = 0 } = params2;
78539
78698
  const fpPolys = [];
78540
- const cj2kicadMatrix = compose7(componentRotation !== 0 ? rotate4(componentRotation * Math.PI / 180) : { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, scale4(1, -1), translate7(-componentCenter.x, -componentCenter.y));
78699
+ const cj2kicadMatrix = compose8(componentRotation !== 0 ? rotate4(componentRotation * Math.PI / 180) : { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, scale5(1, -1), translate8(-componentCenter.x, -componentCenter.y));
78541
78700
  for (const outline of courtyardOutlines) {
78542
78701
  if (!outline.outline || outline.outline.length < 2)
78543
78702
  continue;
@@ -78547,11 +78706,11 @@ function convertCourtyardOutlines(params2) {
78547
78706
  };
78548
78707
  const kicadLayer = layerMap[outline.layer] || "F.CrtYd";
78549
78708
  const xyPoints = outline.outline.map((point) => {
78550
- const transformedPoint = applyToPoint15(cj2kicadMatrix, point);
78551
- return new Xy6(transformedPoint.x, transformedPoint.y);
78709
+ const transformedPoint = applyToPoint16(cj2kicadMatrix, point);
78710
+ return new Xy7(transformedPoint.x, transformedPoint.y);
78552
78711
  });
78553
78712
  const fpPoly = new FpPoly5;
78554
- fpPoly.points = new Pts6(xyPoints);
78713
+ fpPoly.points = new Pts7(xyPoints);
78555
78714
  fpPoly.layer = kicadLayer;
78556
78715
  fpPoly.fill = false;
78557
78716
  fpPoly.uuid = generateDeterministicUuid(outline.pcb_courtyard_outline_id);
@@ -78621,7 +78780,7 @@ function createFpTextFromCircuitJson({
78621
78780
  const relativeX = textElement.anchor_position.x - componentCenter.x;
78622
78781
  const relativeY = -(textElement.anchor_position.y - componentCenter.y);
78623
78782
  const rotationMatrix = componentRotation !== 0 ? rotate5(componentRotation * Math.PI / 180) : identity2();
78624
- const rotatedPos = applyToPoint16(rotationMatrix, {
78783
+ const rotatedPos = applyToPoint17(rotationMatrix, {
78625
78784
  x: relativeX,
78626
78785
  y: relativeY
78627
78786
  });
@@ -78702,11 +78861,11 @@ function convertSilkscreenPaths(silkscreenPaths, { componentCenter, componentRot
78702
78861
  const endPoint = path.route[i + 1];
78703
78862
  if (!startPoint || !endPoint)
78704
78863
  continue;
78705
- const startRelative = applyToPoint17(rotationMatrix, {
78864
+ const startRelative = applyToPoint18(rotationMatrix, {
78706
78865
  x: startPoint.x - componentCenter.x,
78707
78866
  y: -(startPoint.y - componentCenter.y)
78708
78867
  });
78709
- const endRelative = applyToPoint17(rotationMatrix, {
78868
+ const endRelative = applyToPoint18(rotationMatrix, {
78710
78869
  x: endPoint.x - componentCenter.x,
78711
78870
  y: -(endPoint.y - componentCenter.y)
78712
78871
  });
@@ -78756,13 +78915,13 @@ function convertSilkscreenRects({
78756
78915
  componentCenter,
78757
78916
  componentRotation = 0
78758
78917
  }) {
78759
- const toLocal = compose8(rotate7(componentRotation * Math.PI / 180), scale5(1, -1), translate8(-componentCenter.x, -componentCenter.y));
78918
+ const toLocal = compose9(rotate7(componentRotation * Math.PI / 180), scale6(1, -1), translate9(-componentCenter.x, -componentCenter.y));
78760
78919
  return silkscreenRects.flatMap((rect) => {
78761
78920
  const hasStroke = rect.has_stroke ?? rect.stroke_width > 0;
78762
78921
  if (!hasStroke && !rect.is_filled)
78763
78922
  return [];
78764
- const fromRectCenter = compose8(translate8(rect.center.x, rect.center.y), rotate7((rect.ccw_rotation ?? 0) * Math.PI / 180));
78765
- const points = getRectanglePoints(rect).map((point) => applyToPoint18(toLocal, applyToPoint18(fromRectCenter, point)));
78923
+ const fromRectCenter = compose9(translate9(rect.center.x, rect.center.y), rotate7((rect.ccw_rotation ?? 0) * Math.PI / 180));
78924
+ const points = getRectanglePoints(rect).map((point) => applyToPoint19(toLocal, applyToPoint19(fromRectCenter, point)));
78766
78925
  const stroke = hasStroke ? new Stroke11 : undefined;
78767
78926
  if (stroke) {
78768
78927
  stroke.width = rect.stroke_width;
@@ -78786,7 +78945,7 @@ function convertNoteTexts(params2) {
78786
78945
  const relX = textElement.anchor_position.x - componentCenter.x;
78787
78946
  const relY = -(textElement.anchor_position.y - componentCenter.y);
78788
78947
  const rotationMatrix = componentRotation !== 0 ? rotate8(componentRotation * Math.PI / 180) : identity4();
78789
- const rotatedPos = applyToPoint19(rotationMatrix, { x: relX, y: relY });
78948
+ const rotatedPos = applyToPoint20(rotationMatrix, { x: relX, y: relY });
78790
78949
  const fontSize = textElement.font_size || 1;
78791
78950
  const font = new TextEffectsFont11;
78792
78951
  font.size = { width: fontSize, height: fontSize };
@@ -78900,15 +79059,47 @@ function create3DModelsFromCadComponent(cadComponent, componentCenter, options)
78900
79059
  model.offset = transform.offset;
78901
79060
  model.rotate = transform.rotation;
78902
79061
  if (cadComponent.model_unit_to_mm_scale_factor) {
78903
- const scale8 = cadComponent.model_unit_to_mm_scale_factor;
78904
- model.scale = { x: scale8, y: scale8, z: scale8 };
79062
+ const scale9 = cadComponent.model_unit_to_mm_scale_factor;
79063
+ model.scale = { x: scale9, y: scale9, z: scale9 };
78905
79064
  }
78906
79065
  models.push(model);
78907
79066
  return models;
78908
79067
  }
79068
+ var getResolvedPillRadius = ({
79069
+ width,
79070
+ height,
79071
+ radius
79072
+ }) => typeof radius === "number" && Number.isFinite(radius) ? radius : Math.min(width, height) / 2;
79073
+ var getKicadNativePadGeometry = (pad) => {
79074
+ switch (pad.shape) {
79075
+ case "circle":
79076
+ return {
79077
+ shape: "circle",
79078
+ size: [pad.radius * 2, pad.radius * 2],
79079
+ rotation: 0
79080
+ };
79081
+ case "rect":
79082
+ case "rotated_rect": {
79083
+ const cornerRadius = pad.corner_radius ?? pad.rect_border_radius;
79084
+ return {
79085
+ shape: cornerRadius ? "roundrect" : "rect",
79086
+ size: [pad.width, pad.height],
79087
+ rotation: pad.shape === "rotated_rect" ? pad.ccw_rotation ?? 0 : 0,
79088
+ roundrectRatio: cornerRadius ? cornerRadius / Math.min(pad.width, pad.height) : undefined
79089
+ };
79090
+ }
79091
+ case "pill":
79092
+ case "rotated_pill":
79093
+ return {
79094
+ shape: "roundrect",
79095
+ size: [pad.width, pad.height],
79096
+ rotation: pad.shape === "rotated_pill" ? pad.ccw_rotation ?? 0 : 0,
79097
+ roundrectRatio: getResolvedPillRadius(pad) / Math.min(pad.width, pad.height)
79098
+ };
79099
+ }
79100
+ };
78909
79101
  function createSmdPadFromCircuitJson({
78910
79102
  pcbPad,
78911
- hasSolderPaste,
78912
79103
  componentCenter,
78913
79104
  padNumber,
78914
79105
  componentRotation = 0,
@@ -78927,8 +79118,8 @@ function createSmdPadFromCircuitJson({
78927
79118
  } else {
78928
79119
  throw new Error("Pad must have either x/y coordinates or points array");
78929
79120
  }
78930
- const cj2kicadMatrix = compose9(componentRotation !== 0 ? rotate9(componentRotation * Math.PI / 180) : { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, scale6(1, -1), translate9(-componentCenter.x, -componentCenter.y));
78931
- const rotatedPos = applyToPoint20(cj2kicadMatrix, {
79121
+ const cj2kicadMatrix = compose10(componentRotation !== 0 ? rotate9(componentRotation * Math.PI / 180) : { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, scale7(1, -1), translate10(-componentCenter.x, -componentCenter.y));
79122
+ const rotatedPos = applyToPoint21(cj2kicadMatrix, {
78932
79123
  x: padX,
78933
79124
  y: padY
78934
79125
  });
@@ -78943,57 +79134,21 @@ function createSmdPadFromCircuitJson({
78943
79134
  let padPrimitives;
78944
79135
  let roundrect_rratio;
78945
79136
  let rotation = 0;
78946
- if (pcbPad.shape === "circle") {
78947
- padShape = "circle";
78948
- padSize = [
78949
- "radius" in pcbPad ? pcbPad.radius * 2 : 0.5,
78950
- "radius" in pcbPad ? pcbPad.radius * 2 : 0.5
78951
- ];
78952
- } else if (pcbPad.shape === "polygon" && "points" in pcbPad) {
78953
- const points = pcbPad.points;
78954
- const pointTransformMatrix = compose9(scale6(1, -1), translate9(-padX, -padY));
78955
- const relativePoints = points.map((p) => {
78956
- const transformed = applyToPoint20(pointTransformMatrix, { x: p.x, y: p.y });
78957
- return new Xy7(transformed.x, transformed.y);
79137
+ if (pcbPad.shape === "polygon") {
79138
+ padPrimitives = createFilledPolygonPrimitives({
79139
+ contours: [pcbPad.points],
79140
+ center: { x: padX, y: padY }
78958
79141
  });
78959
- const grPoly = new PadPrimitiveGrPoly;
78960
- grPoly.contours = [new Pts7(relativePoints)];
78961
- grPoly.width = 0;
78962
- grPoly.filled = true;
78963
- padPrimitives = new PadPrimitives;
78964
- padPrimitives.addGraphic(grPoly);
78965
79142
  padShape = "custom";
78966
79143
  padOptions = new PadOptions;
78967
79144
  padOptions.anchor = "circle";
78968
79145
  padSize = [0.2, 0.2];
78969
- } else if (pcbPad.shape === "rotated_rect") {
78970
- const cornerRadius = pcbPad.corner_radius ?? pcbPad.rect_border_radius;
78971
- if (cornerRadius) {
78972
- padShape = "roundrect";
78973
- roundrect_rratio = cornerRadius / Math.min(pcbPad.width, pcbPad.height);
78974
- } else {
78975
- padShape = "rect";
78976
- }
78977
- padSize = [pcbPad.width, pcbPad.height];
78978
- rotation = pcbPad.ccw_rotation;
78979
- } else if (pcbPad.shape === "pill") {
78980
- padShape = "roundrect";
78981
- padSize = [pcbPad.width, pcbPad.height];
78982
- roundrect_rratio = pcbPad.radius / Math.min(pcbPad.width, pcbPad.height);
78983
- } else if (pcbPad.shape === "rotated_pill") {
78984
- padShape = "roundrect";
78985
- padSize = [pcbPad.width, pcbPad.height];
78986
- roundrect_rratio = pcbPad.radius / Math.min(pcbPad.width, pcbPad.height);
78987
- rotation = pcbPad.ccw_rotation;
78988
- } else if (pcbPad.shape === "rect") {
78989
- const cornerRadius = pcbPad.corner_radius ?? pcbPad.rect_border_radius;
78990
- if (cornerRadius) {
78991
- padShape = "roundrect";
78992
- roundrect_rratio = cornerRadius / Math.min(pcbPad.width, pcbPad.height);
78993
- } else {
78994
- padShape = "rect";
78995
- }
78996
- padSize = [pcbPad.width, pcbPad.height];
79146
+ } else {
79147
+ const geometry = getKicadNativePadGeometry(pcbPad);
79148
+ padShape = geometry.shape;
79149
+ padSize = geometry.size;
79150
+ rotation = geometry.rotation;
79151
+ roundrect_rratio = geometry.roundrectRatio;
78997
79152
  }
78998
79153
  const padData = `pad:${componentId}:${padNumber}:${rotatedPos.x},${rotatedPos.y}`;
78999
79154
  const pad = new FootprintPad({
@@ -79004,7 +79159,6 @@ function createSmdPadFromCircuitJson({
79004
79159
  size: padSize,
79005
79160
  layers: [
79006
79161
  `${padLayer}`,
79007
- ...hasSolderPaste ? [`${padLayer === "F.Cu" ? "F" : "B"}.Paste`] : [],
79008
79162
  ...pcbPad.is_covered_with_solder_mask ? [] : [`${padLayer === "F.Cu" ? "F" : "B"}.Mask`]
79009
79163
  ],
79010
79164
  solderMaskMargin: pcbPad.soldermask_margin,
@@ -79041,7 +79195,6 @@ function convertSmdPads({
79041
79195
  const resolvedPadNumber = getPhysicalPinName(sourcePort) ?? (sourcePort?.pin_number != null ? String(sourcePort.pin_number) : pinHint ? pinHint.replace(/^pin/i, "") : gridHint ?? String(padNumber));
79042
79196
  const pad = createSmdPadFromCircuitJson({
79043
79197
  pcbPad,
79044
- hasSolderPaste: ctx.db.pcb_solder_paste.list().some((paste) => paste.pcb_smtpad_id === pcbPad.pcb_smtpad_id),
79045
79198
  componentCenter,
79046
79199
  padNumber: resolvedPadNumber,
79047
79200
  componentRotation,
@@ -79067,7 +79220,7 @@ function createThruHolePadFromCircuitJson({
79067
79220
  const relativeX = platedHole.x - componentCenter.x;
79068
79221
  const relativeY = -(platedHole.y - componentCenter.y);
79069
79222
  const rotationMatrix = componentRotation !== 0 ? rotate10(componentRotation * Math.PI / 180) : identity5();
79070
- const rotatedPos = applyToPoint21(rotationMatrix, {
79223
+ const rotatedPos = applyToPoint22(rotationMatrix, {
79071
79224
  x: relativeX,
79072
79225
  y: relativeY
79073
79226
  });
@@ -79083,7 +79236,7 @@ function createThruHolePadFromCircuitJson({
79083
79236
  y: platedHole.hole_offset_y ?? 0
79084
79237
  };
79085
79238
  if (rawOffset.x !== 0 || rawOffset.y !== 0) {
79086
- const rotatedOffset = applyToPoint21(rotationMatrix, {
79239
+ const rotatedOffset = applyToPoint22(rotationMatrix, {
79087
79240
  x: -rawOffset.x,
79088
79241
  y: rawOffset.y
79089
79242
  });
@@ -79199,7 +79352,7 @@ function createNpthPadFromCircuitJson({
79199
79352
  const relativeX = pcbHole.x - componentCenter.x;
79200
79353
  const relativeY = -(pcbHole.y - componentCenter.y);
79201
79354
  const rotationMatrix = componentRotation !== 0 ? rotate11(componentRotation * Math.PI / 180) : identity6();
79202
- const rotatedPos = applyToPoint22(rotationMatrix, {
79355
+ const rotatedPos = applyToPoint23(rotationMatrix, {
79203
79356
  x: relativeX,
79204
79357
  y: relativeY
79205
79358
  });
@@ -79283,11 +79436,11 @@ function convertFabricationNotePaths(fabricationNotePaths, {
79283
79436
  const endPoint = fabricationNotePath.route[index + 1];
79284
79437
  if (!startPoint || !endPoint)
79285
79438
  continue;
79286
- const startRelative = applyToPoint23(rotationMatrix, {
79439
+ const startRelative = applyToPoint24(rotationMatrix, {
79287
79440
  x: startPoint.x - componentCenter.x,
79288
79441
  y: -(startPoint.y - componentCenter.y)
79289
79442
  });
79290
- const endRelative = applyToPoint23(rotationMatrix, {
79443
+ const endRelative = applyToPoint24(rotationMatrix, {
79291
79444
  x: endPoint.x - componentCenter.x,
79292
79445
  y: -(endPoint.y - componentCenter.y)
79293
79446
  });
@@ -79304,6 +79457,94 @@ function convertFabricationNotePaths(fabricationNotePaths, {
79304
79457
  }
79305
79458
  return fpLines;
79306
79459
  }
79460
+ var getSolderPasteCenter = (solderPaste) => {
79461
+ if ("x" in solderPaste && "y" in solderPaste) {
79462
+ return { x: solderPaste.x, y: solderPaste.y };
79463
+ }
79464
+ return {
79465
+ x: solderPaste.points.reduce((sum, point) => sum + point.x, 0) / solderPaste.points.length,
79466
+ y: solderPaste.points.reduce((sum, point) => sum + point.y, 0) / solderPaste.points.length
79467
+ };
79468
+ };
79469
+ var createSolderPastePadFromCircuitJson = ({
79470
+ solderPaste,
79471
+ componentCenter,
79472
+ componentRotation = 0
79473
+ }) => {
79474
+ const polygonAnchor = solderPaste.shape === "polygon" ? getSafePolygonAnchor(solderPaste.points, solderPaste.holes) : undefined;
79475
+ const center = polygonAnchor?.center ?? getSolderPasteCenter(solderPaste);
79476
+ const boardToFootprint = getBoardToFootprintTransform({
79477
+ footprintOrigin: componentCenter,
79478
+ ccwRotationDegrees: componentRotation
79479
+ });
79480
+ const position = applyToPoint25(boardToFootprint, center);
79481
+ let shape;
79482
+ let size;
79483
+ let rotation = 0;
79484
+ let roundrectRatio;
79485
+ let options;
79486
+ let customContours;
79487
+ if (solderPaste.shape === "oval") {
79488
+ shape = "custom";
79489
+ size = [
79490
+ Math.min(0.001, solderPaste.width, solderPaste.height),
79491
+ Math.min(0.001, solderPaste.width, solderPaste.height)
79492
+ ];
79493
+ options = new PadOptions2;
79494
+ options.anchor = "circle";
79495
+ customContours = [
79496
+ createEllipseContour({
79497
+ center,
79498
+ width: solderPaste.width,
79499
+ height: solderPaste.height
79500
+ })
79501
+ ];
79502
+ } else if (solderPaste.shape === "polygon") {
79503
+ if (!polygonAnchor) {
79504
+ throw new Error("Solder-paste polygon is missing its anchor geometry");
79505
+ }
79506
+ shape = "custom";
79507
+ size = [polygonAnchor.diameter, polygonAnchor.diameter];
79508
+ options = new PadOptions2;
79509
+ options.anchor = "circle";
79510
+ customContours = solderPaste.holes?.length ? polygonAnchor.triangles : [solderPaste.points];
79511
+ } else {
79512
+ const geometry = getKicadNativePadGeometry(solderPaste);
79513
+ shape = geometry.shape;
79514
+ size = geometry.size;
79515
+ rotation = geometry.rotation;
79516
+ roundrectRatio = geometry.roundrectRatio;
79517
+ }
79518
+ const layer = solderPaste.layer === "bottom" ? "B.Paste" : "F.Paste";
79519
+ const pad = new FootprintPad4({
79520
+ number: "",
79521
+ padType: "smd",
79522
+ shape,
79523
+ at: [position.x, position.y, rotation],
79524
+ size,
79525
+ layers: [layer],
79526
+ roundrectRatio,
79527
+ uuid: generateDeterministicUuid(`solder_paste:${solderPaste.pcb_solder_paste_id}`)
79528
+ });
79529
+ if (options)
79530
+ pad.options = options;
79531
+ if (customContours) {
79532
+ pad.primitives = createFilledPolygonPrimitives({
79533
+ contours: customContours,
79534
+ center
79535
+ });
79536
+ }
79537
+ return pad;
79538
+ };
79539
+ var convertSolderPaste = ({
79540
+ solderPaste,
79541
+ componentCenter,
79542
+ componentRotation = 0
79543
+ }) => solderPaste.map((aperture) => createSolderPastePadFromCircuitJson({
79544
+ solderPaste: aperture,
79545
+ componentCenter,
79546
+ componentRotation
79547
+ }));
79307
79548
  var AddFootprintsStage = class extends ConverterStage {
79308
79549
  componentsProcessed = 0;
79309
79550
  pcbComponents = [];
@@ -79379,7 +79620,7 @@ var AddFootprintsStage = class extends ConverterStage {
79379
79620
  const cadComponent = this.getCadComponentForPcbComponent(component.pcb_component_id);
79380
79621
  const footprintName = sourceComponent ? getKicadCompatibleComponentName(sourceComponent, cadComponent) : "Unknown";
79381
79622
  const footprintOrigin = component.footprint_origin ?? component.center;
79382
- const transformedPos = applyToPoint24(c2kMatPcb, {
79623
+ const transformedPos = applyToPoint26(c2kMatPcb, {
79383
79624
  x: footprintOrigin.x,
79384
79625
  y: footprintOrigin.y
79385
79626
  });
@@ -79429,6 +79670,18 @@ var AddFootprintsStage = class extends ConverterStage {
79429
79670
  getNetInfo
79430
79671
  }, this.ctx);
79431
79672
  fpPads.push(...smdPads);
79673
+ const pcbSolderPaste = this.ctx.db.pcb_solder_paste?.list().filter((paste) => {
79674
+ if (paste.pcb_component_id === component.pcb_component_id) {
79675
+ return true;
79676
+ }
79677
+ const linkedPad = paste.pcb_smtpad_id ? this.ctx.db.pcb_smtpad.get(paste.pcb_smtpad_id) : undefined;
79678
+ return !paste.pcb_component_id && linkedPad?.pcb_component_id === component.pcb_component_id;
79679
+ }) || [];
79680
+ fpPads.push(...convertSolderPaste({
79681
+ solderPaste: pcbSolderPaste,
79682
+ componentCenter: footprintOrigin,
79683
+ componentRotation: component.rotation || 0
79684
+ }));
79432
79685
  const pcbPlatedHoles = this.ctx.db.pcb_plated_hole?.list().filter((hole) => hole.pcb_component_id === component.pcb_component_id) || [];
79433
79686
  const { pads: thruHolePads } = convertPlatedHoles({
79434
79687
  platedHoles: pcbPlatedHoles,
@@ -79628,8 +79881,8 @@ var AddTracesStage = class extends ConverterStage {
79628
79881
  if (!startPosition || !endPosition) {
79629
79882
  throw new Error(`Unable to convert pcb_trace route segment ${trace.pcb_trace_id ?? this.tracesProcessed}:${i} to KiCad segment`);
79630
79883
  }
79631
- const transformedStart = applyToPoint25(c2kMatPcb, startPosition);
79632
- const transformedEnd = applyToPoint25(c2kMatPcb, endPosition);
79884
+ const transformedStart = applyToPoint27(c2kMatPcb, startPosition);
79885
+ const transformedEnd = applyToPoint27(c2kMatPcb, endPosition);
79633
79886
  if (transformedStart.x === transformedEnd.x && transformedStart.y === transformedEnd.y) {
79634
79887
  continue;
79635
79888
  }
@@ -79712,6 +79965,11 @@ var createViaTenting = (via, board) => {
79712
79965
  };
79713
79966
  var DEFAULT_VIA_OUTER_DIAMETER = 0.8;
79714
79967
  var DEFAULT_VIA_HOLE_DIAMETER = 0.4;
79968
+ var VIA_POSITION_TOLERANCE = 0.000001;
79969
+ var getViaPositionCell = (via) => ({
79970
+ x: Math.floor(via.x / VIA_POSITION_TOLERANCE),
79971
+ y: Math.floor(via.y / VIA_POSITION_TOLERANCE)
79972
+ });
79715
79973
  var AddViasStage = class extends ConverterStage {
79716
79974
  boardOwnerMap;
79717
79975
  viasProcessed = 0;
@@ -79725,7 +79983,8 @@ var AddViasStage = class extends ConverterStage {
79725
79983
  const seenRouteViaKeys = /* @__PURE__ */ new Set;
79726
79984
  const standaloneViasByPosition = /* @__PURE__ */ new Map;
79727
79985
  for (const via of standaloneVias) {
79728
- const positionKey = `${via.x}:${via.y}`;
79986
+ const { x, y } = getViaPositionCell(via);
79987
+ const positionKey = `${x}:${y}`;
79729
79988
  const viasAtPosition = standaloneViasByPosition.get(positionKey) ?? [];
79730
79989
  viasAtPosition.push(via);
79731
79990
  standaloneViasByPosition.set(positionKey, viasAtPosition);
@@ -79744,8 +80003,14 @@ var AddViasStage = class extends ConverterStage {
79744
80003
  tented_on_top: point.tented_on_top,
79745
80004
  tented_on_bottom: point.tented_on_bottom
79746
80005
  })).filter((via) => {
79747
- const physicalVias = standaloneViasByPosition.get(`${via.x}:${via.y}`);
79748
- if (physicalVias?.some((physicalVia) => {
80006
+ const { x, y } = getViaPositionCell(via);
80007
+ const physicalVias = [];
80008
+ for (let dx = -1;dx <= 1; dx++) {
80009
+ for (let dy = -1;dy <= 1; dy++) {
80010
+ physicalVias.push(...standaloneViasByPosition.get(`${x + dx}:${y + dy}`) ?? []);
80011
+ }
80012
+ }
80013
+ if (physicalVias.some((physicalVia) => {
79749
80014
  const sameTrace = physicalVia.pcb_trace_id !== undefined && physicalVia.pcb_trace_id === via.pcb_trace_id;
79750
80015
  const physicalConnection = physicalVia.net?.id ?? physicalVia.subcircuit_connectivity_map_key;
79751
80016
  const routeConnection = via.net?.id ?? via.subcircuit_connectivity_map_key;
@@ -79755,7 +80020,11 @@ var AddViasStage = class extends ConverterStage {
79755
80020
  } else if (!sameTrace) {
79756
80021
  return false;
79757
80022
  }
79758
- return isRouteViaCoveredByPhysicalVia(physicalVia, via);
80023
+ return isRouteViaCoveredByPhysicalVia({
80024
+ viaWithLayers: physicalVia,
80025
+ routeVia: via,
80026
+ positionTolerance: VIA_POSITION_TOLERANCE
80027
+ });
79759
80028
  })) {
79760
80029
  return false;
79761
80030
  }
@@ -79814,10 +80083,10 @@ var AddViasStage = class extends ConverterStage {
79814
80083
  this.finished = true;
79815
80084
  return;
79816
80085
  }
79817
- this.boardOwnerMap ??= createBoardOwnerMap(this.input);
80086
+ this.boardOwnerMap ??= createBoardOwnerMap2(this.input);
79818
80087
  const boardOwnerId = via.pcb_via_id ?? via.pcb_trace_id;
79819
80088
  const board = boardOwnerId ? this.boardOwnerMap.get(boardOwnerId) : undefined;
79820
- const transformedPos = applyToPoint26(c2kMatPcb, {
80089
+ const transformedPos = applyToPoint28(c2kMatPcb, {
79821
80090
  x: via.x,
79822
80091
  y: via.y
79823
80092
  });
@@ -79843,6 +80112,34 @@ var AddViasStage = class extends ConverterStage {
79843
80112
  return this.ctx.kicadPcb;
79844
80113
  }
79845
80114
  };
80115
+ var normalizePolygonPoints = (points, origin) => points.map((point) => ({
80116
+ x: point.x - origin.x,
80117
+ y: point.y - origin.y
80118
+ }));
80119
+ var getSolderPasteLibraryLink = (paste) => {
80120
+ const prefix = `tscircuit:solderpaste_${paste.layer}_${paste.shape}`;
80121
+ switch (paste.shape) {
80122
+ case "circle":
80123
+ return `${prefix}_diameter${paste.radius * 2}mm`;
80124
+ case "rect":
80125
+ case "oval":
80126
+ return `${prefix}_width${paste.width}mm_height${paste.height}mm`;
80127
+ case "rotated_rect":
80128
+ return `${prefix}_width${paste.width}mm_height${paste.height}mm_ccwRotation${paste.ccw_rotation}deg`;
80129
+ case "pill":
80130
+ return `${prefix}_width${paste.width}mm_height${paste.height}mm_radius${getResolvedPillRadius(paste)}mm`;
80131
+ case "rotated_pill":
80132
+ return `${prefix}_width${paste.width}mm_height${paste.height}mm_radius${getResolvedPillRadius(paste)}mm_ccwRotation${paste.ccw_rotation}deg`;
80133
+ case "polygon": {
80134
+ const origin = paste.points[0] ?? { x: 0, y: 0 };
80135
+ const geometry = JSON.stringify({
80136
+ points: normalizePolygonPoints(paste.points, origin),
80137
+ holes: paste.holes?.map((hole) => normalizePolygonPoints(hole, origin))
80138
+ });
80139
+ return `${prefix}_${generateDeterministicUuid(geometry)}`;
80140
+ }
80141
+ }
80142
+ };
79846
80143
  var AddStandalonePcbElements = class extends ConverterStage {
79847
80144
  unprocessedElements = [];
79848
80145
  constructor(input, ctx) {
@@ -79850,7 +80147,8 @@ var AddStandalonePcbElements = class extends ConverterStage {
79850
80147
  this.unprocessedElements = [
79851
80148
  ...this.ctx.db.pcb_hole.list().filter((hole) => !hole.pcb_component_id),
79852
80149
  ...this.ctx.db.pcb_plated_hole.list().filter((hole) => !hole.pcb_component_id),
79853
- ...this.ctx.db.pcb_smtpad.list().filter((pad) => !pad.pcb_component_id)
80150
+ ...this.ctx.db.pcb_smtpad.list().filter((pad) => !pad.pcb_component_id),
80151
+ ...this.ctx.db.pcb_solder_paste.list().filter((paste) => !paste.pcb_component_id && !paste.pcb_smtpad_id)
79854
80152
  ];
79855
80153
  }
79856
80154
  _step() {
@@ -79870,21 +80168,45 @@ var AddStandalonePcbElements = class extends ConverterStage {
79870
80168
  const pcbPad = elm;
79871
80169
  const padCenter = this.getPcbSmtPadCenter(pcbPad);
79872
80170
  const footprintSeed = `standalone_smtpad:${pcbPad.pcb_smtpad_id}:${padCenter.x},${padCenter.y}`;
79873
- const kicadPos = applyToPoint27(c2kMatPcb, padCenter);
80171
+ const kicadPos = applyToPoint29(c2kMatPcb, padCenter);
79874
80172
  const footprint = new Footprint5({
79875
80173
  libraryLink: this.getSmtPadLibraryLink(pcbPad),
79876
80174
  layer: "F.Cu",
79877
80175
  at: [kicadPos.x, kicadPos.y, 0],
79878
80176
  uuid: generateDeterministicUuid(footprintSeed)
79879
80177
  });
80178
+ const linkedSolderPaste = this.ctx.db.pcb_solder_paste.list().filter((paste) => paste.pcb_smtpad_id === pcbPad.pcb_smtpad_id);
79880
80179
  footprint.fpPads = [
79881
80180
  createSmdPadFromCircuitJson({
79882
80181
  pcbPad,
79883
- hasSolderPaste: this.ctx.db.pcb_solder_paste.list().some((paste) => paste.pcb_smtpad_id === pcbPad.pcb_smtpad_id),
79884
80182
  componentCenter: padCenter,
79885
80183
  padNumber: 1,
79886
80184
  componentRotation: 0,
79887
80185
  componentId: pcbPad.pcb_smtpad_id
80186
+ }),
80187
+ ...linkedSolderPaste.map((solderPaste) => createSolderPastePadFromCircuitJson({
80188
+ solderPaste,
80189
+ componentCenter: padCenter
80190
+ }))
80191
+ ];
80192
+ applyInferredFootprintType(footprint);
80193
+ const footprints = kicadPcb.footprints;
80194
+ footprints.push(footprint);
80195
+ kicadPcb.footprints = footprints;
80196
+ } else if (elm.type === "pcb_solder_paste") {
80197
+ const center = getSolderPasteCenter(elm);
80198
+ const footprintSeed = `standalone_solder_paste:${elm.pcb_solder_paste_id}`;
80199
+ const kicadPos = applyToPoint29(c2kMatPcb, center);
80200
+ const footprint = new Footprint5({
80201
+ libraryLink: getSolderPasteLibraryLink(elm),
80202
+ layer: elm.layer === "bottom" ? "B.Cu" : "F.Cu",
80203
+ at: [kicadPos.x, kicadPos.y, 0],
80204
+ uuid: generateDeterministicUuid(footprintSeed)
80205
+ });
80206
+ footprint.fpPads = [
80207
+ createSolderPastePadFromCircuitJson({
80208
+ solderPaste: elm,
80209
+ componentCenter: center
79888
80210
  })
79889
80211
  ];
79890
80212
  applyInferredFootprintType(footprint);
@@ -79894,7 +80216,7 @@ var AddStandalonePcbElements = class extends ConverterStage {
79894
80216
  } else if (elm.type === "pcb_hole") {
79895
80217
  const hole = elm;
79896
80218
  const footprintSeed = `standalone_hole:${hole.pcb_hole_id}:${hole.x},${hole.y}`;
79897
- const kicadPos = applyToPoint27(c2kMatPcb, { x: hole.x, y: hole.y });
80219
+ const kicadPos = applyToPoint29(c2kMatPcb, { x: hole.x, y: hole.y });
79898
80220
  const libraryLink = this.getHoleLibraryLink(hole);
79899
80221
  const footprint = new Footprint5({
79900
80222
  libraryLink,
@@ -79917,7 +80239,7 @@ var AddStandalonePcbElements = class extends ConverterStage {
79917
80239
  } else if (elm.type === "pcb_plated_hole") {
79918
80240
  const hole = elm;
79919
80241
  const footprintSeed = `standalone_plated_hole:${hole.pcb_plated_hole_id}:${hole.x},${hole.y}`;
79920
- const kicadPos = applyToPoint27(c2kMatPcb, { x: hole.x, y: hole.y });
80242
+ const kicadPos = applyToPoint29(c2kMatPcb, { x: hole.x, y: hole.y });
79921
80243
  const libraryLink = this.getPlatedHoleLibraryLink(hole);
79922
80244
  const footprint = new Footprint5({
79923
80245
  libraryLink,
@@ -80015,7 +80337,7 @@ function createFabricationNoteTextFromCircuitJson({
80015
80337
  if (!textElement.text || !textElement.anchor_position) {
80016
80338
  return null;
80017
80339
  }
80018
- const transformedPos = applyToPoint28(c2kMatPcb, {
80340
+ const transformedPos = applyToPoint30(c2kMatPcb, {
80019
80341
  x: textElement.anchor_position.x,
80020
80342
  y: textElement.anchor_position.y
80021
80343
  });
@@ -80056,7 +80378,8 @@ function createFabricationNoteTextFromCircuitJson({
80056
80378
  if (anchorAlignment !== "center") {
80057
80379
  textEffects.justify = justify;
80058
80380
  }
80059
- const position = new At3([transformedPos.x, transformedPos.y, 0]);
80381
+ const rotation = textElement.type === "pcb_fabrication_note_text" ? textElement.ccw_rotation ?? 0 : 0;
80382
+ const position = new At3([transformedPos.x, transformedPos.y, rotation]);
80060
80383
  const grText = new GrText({
80061
80384
  text: textElement.text,
80062
80385
  layer: kicadLayer,
@@ -80073,7 +80396,7 @@ function createGrTextFromCircuitJson({
80073
80396
  if (!textElement.text || !textElement.anchor_position) {
80074
80397
  return null;
80075
80398
  }
80076
- const transformedPos = applyToPoint29(c2kMatPcb, {
80399
+ const transformedPos = applyToPoint31(c2kMatPcb, {
80077
80400
  x: textElement.anchor_position.x,
80078
80401
  y: textElement.anchor_position.y
80079
80402
  });
@@ -80110,6 +80433,9 @@ function createGrTextFromCircuitJson({
80110
80433
  grText.position = position;
80111
80434
  return grText;
80112
80435
  }
80436
+ var KICAD_PCB_UNITS_PER_MM = 1e6;
80437
+ var roundBoardCoordinate = (coordinateMm) => Math.round(coordinateMm * KICAD_PCB_UNITS_PER_MM) / KICAD_PCB_UNITS_PER_MM;
80438
+ var roundPolygonToKicadPrecision = (geometry) => geometry.map((polygon) => polygon.map((ring) => ring.map(([x, y]) => [roundBoardCoordinate(x), roundBoardCoordinate(y)])));
80113
80439
  var pointsAreEqual = (a, b) => !!a && !!b && a.x === b.x && a.y === b.y;
80114
80440
  var normalizeOutlineCorners = (corners) => {
80115
80441
  const dedupedCorners = [];
@@ -80125,6 +80451,13 @@ var normalizeOutlineCorners = (corners) => {
80125
80451
  return dedupedCorners;
80126
80452
  };
80127
80453
  var DEFAULT_EDGE_CUTS_WIDTH = 0.1;
80454
+ var toKicadPoint = (point, c2kMatPcb) => {
80455
+ const transformed = applyToPoint32(c2kMatPcb, point);
80456
+ return [
80457
+ roundBoardCoordinate(transformed.x),
80458
+ roundBoardCoordinate(transformed.y)
80459
+ ];
80460
+ };
80128
80461
  var appendGraphicLine = (kicadPcb, grLine) => {
80129
80462
  const graphicLines = kicadPcb.graphicLines;
80130
80463
  graphicLines.push(grLine);
@@ -80133,18 +80466,10 @@ var appendGraphicLine = (kicadPcb, grLine) => {
80133
80466
  var rotatePointAroundOrigin = (point, rotationDegrees = 0) => {
80134
80467
  if (!rotationDegrees)
80135
80468
  return point;
80136
- return applyToPoint30(rotate13(rotationDegrees * Math.PI / 180), point);
80469
+ return applyToPoint32(rotate13(rotationDegrees * Math.PI / 180), point);
80137
80470
  };
80138
80471
  var getRectCutoutCorners = (cutout) => {
80139
- const halfWidth = cutout.width / 2;
80140
- const halfHeight = cutout.height / 2;
80141
- const localCorners = [
80142
- { x: -halfWidth, y: -halfHeight },
80143
- { x: halfWidth, y: -halfHeight },
80144
- { x: halfWidth, y: halfHeight },
80145
- { x: -halfWidth, y: halfHeight }
80146
- ];
80147
- return localCorners.map((point) => {
80472
+ return getRectanglePoints(cutout).map((point) => {
80148
80473
  const rotatedPoint = rotatePointAroundOrigin(point, cutout.rotation);
80149
80474
  return {
80150
80475
  x: rotatedPoint.x + cutout.center.x,
@@ -80171,11 +80496,11 @@ var AddGraphicsStage = class extends ConverterStage {
80171
80496
  const endPoint = path.route[i + 1];
80172
80497
  if (!startPoint || !endPoint)
80173
80498
  continue;
80174
- const transformedStart = applyToPoint30(c2kMatPcb, {
80499
+ const transformedStart = applyToPoint32(c2kMatPcb, {
80175
80500
  x: startPoint.x,
80176
80501
  y: startPoint.y
80177
80502
  });
80178
- const transformedEnd = applyToPoint30(c2kMatPcb, {
80503
+ const transformedEnd = applyToPoint32(c2kMatPcb, {
80179
80504
  x: endPoint.x,
80180
80505
  y: endPoint.y
80181
80506
  });
@@ -80221,15 +80546,14 @@ var AddGraphicsStage = class extends ConverterStage {
80221
80546
  kicadPcb.graphicTexts = graphicTexts;
80222
80547
  }
80223
80548
  }
80224
- const pcbBoards = this.ctx.db.pcb_board?.list() || [];
80225
- if (pcbBoards.length > 0) {
80226
- const board = pcbBoards[0];
80227
- if (!board) {
80228
- this.finished = true;
80229
- return;
80230
- }
80549
+ const outlineElements = [
80550
+ ...this.ctx.db.pcb_panel.list(),
80551
+ ...this.ctx.db.pcb_board.list()
80552
+ ];
80553
+ const outlinePolys = [];
80554
+ for (const board of outlineElements) {
80231
80555
  let corners;
80232
- if (board.outline && board.outline.length > 0) {
80556
+ if (board.type === "pcb_board" && board.outline && board.outline.length > 0) {
80233
80557
  corners = normalizeOutlineCorners(board.outline);
80234
80558
  } else {
80235
80559
  const halfWidth = board.width ? board.width / 2 : 0;
@@ -80241,25 +80565,43 @@ var AddGraphicsStage = class extends ConverterStage {
80241
80565
  { x: board.center.x - halfWidth, y: board.center.y + halfHeight }
80242
80566
  ];
80243
80567
  }
80568
+ outlinePolys.push([
80569
+ [corners.map((point) => toKicadPoint(point, c2kMatPcb))]
80570
+ ]);
80571
+ }
80572
+ const [firstOutline, ...otherOutlines] = outlinePolys;
80573
+ if (firstOutline) {
80244
80574
  const pcbCutouts2 = this.ctx.db.pcb_cutout?.list() || [];
80245
80575
  const cutoutPolys = [];
80246
80576
  for (const cutout of pcbCutouts2) {
80247
80577
  if (cutout.shape === "rect") {
80248
80578
  const cutoutCorners = getRectCutoutCorners(cutout);
80249
- cutoutPolys.push([[cutoutCorners.map((c) => [c.x, c.y])]]);
80579
+ cutoutPolys.push([
80580
+ [cutoutCorners.map((point) => toKicadPoint(point, c2kMatPcb))]
80581
+ ]);
80250
80582
  } else if (cutout.shape === "circle") {
80251
- cutoutPolys.push([[circleToPolygon(cutout.center, cutout.radius)]]);
80583
+ cutoutPolys.push([
80584
+ [
80585
+ circleToPolygon(cutout.center, cutout.radius).map(([x, y]) => toKicadPoint({ x, y }, c2kMatPcb))
80586
+ ]
80587
+ ]);
80252
80588
  } else if (cutout.shape === "polygon") {
80253
- cutoutPolys.push([[cutout.points.map((c) => [c.x, c.y])]]);
80589
+ cutoutPolys.push([
80590
+ [cutout.points.map((point) => toKicadPoint(point, c2kMatPcb))]
80591
+ ]);
80254
80592
  }
80255
80593
  }
80256
- let boardGeom = [[corners.map((c) => [c.x, c.y])]];
80594
+ let boardGeom = otherOutlines.length > 0 ? import_polygon_clipping.default.union(firstOutline, ...otherOutlines) : firstOutline;
80257
80595
  if (cutoutPolys.length > 0) {
80258
- boardGeom = import_polygon_clipping.default.difference(boardGeom, ...cutoutPolys);
80596
+ boardGeom = import_polygon_clipping.default.difference(roundPolygonToKicadPrecision(boardGeom), ...cutoutPolys);
80259
80597
  }
80598
+ boardGeom = roundPolygonToKicadPrecision(boardGeom);
80260
80599
  for (const poly of boardGeom) {
80261
80600
  for (const ring of poly) {
80262
- const transformedRing = ring.map((point) => applyToPoint30(c2kMatPcb, { x: point[0], y: point[1] }));
80601
+ const transformedRing = ring.map(([x, y]) => ({
80602
+ x,
80603
+ y
80604
+ }));
80263
80605
  for (let i = 0;i < transformedRing.length; i++) {
80264
80606
  const start = transformedRing[i];
80265
80607
  const end = transformedRing[(i + 1) % transformedRing.length];
@@ -80288,8 +80630,8 @@ var AddGraphicsStage = class extends ConverterStage {
80288
80630
  const endPoint = cutout.route[i + 1];
80289
80631
  if (!startPoint || !endPoint)
80290
80632
  continue;
80291
- const transformedStart = applyToPoint30(c2kMatPcb, startPoint);
80292
- const transformedEnd = applyToPoint30(c2kMatPcb, endPoint);
80633
+ const transformedStart = applyToPoint32(c2kMatPcb, startPoint);
80634
+ const transformedEnd = applyToPoint32(c2kMatPcb, endPoint);
80293
80635
  appendGraphicLine(kicadPcb, new GrLine({
80294
80636
  start: transformedStart,
80295
80637
  end: transformedEnd,
@@ -80324,7 +80666,7 @@ var CircuitJsonToKicadPcbConverter = class {
80324
80666
  generator: "circuit-json-to-kicad",
80325
80667
  generatorVersion: "0.0.1"
80326
80668
  }),
80327
- c2kMatPcb: compose10(translate10(KICAD_PCB_CENTER_X, KICAD_PCB_CENTER_Y), scale7(CIRCUIT_JSON_TO_MM_SCALE, -CIRCUIT_JSON_TO_MM_SCALE)),
80669
+ c2kMatPcb: compose11(translate11(KICAD_PCB_CENTER_X, KICAD_PCB_CENTER_Y), scale8(CIRCUIT_JSON_TO_MM_SCALE, -CIRCUIT_JSON_TO_MM_SCALE)),
80328
80670
  projectName: options?.projectName,
80329
80671
  edgeCutsWidth: options?.edgeCutsWidth,
80330
80672
  pcbModel3dSourcePaths: []
@@ -80410,6 +80752,46 @@ var CircuitJsonToKicadProConverter = class {
80410
80752
  trackWidth: minTrackWidth
80411
80753
  })
80412
80754
  ];
80755
+ const sourceNets = this.ctx.db.source_net?.list() ?? [];
80756
+ const netClassNameByTraceWidth = /* @__PURE__ */ new Map([
80757
+ [minTrackWidth, "Default"]
80758
+ ]);
80759
+ const explicitTraceWidths = Array.from(new Set(sourceNets.flatMap((sourceNet) => sourceNet.trace_width === undefined ? [] : [sourceNet.trace_width]))).sort((a, b) => a - b);
80760
+ for (const traceWidth of explicitTraceWidths) {
80761
+ if (netClassNameByTraceWidth.has(traceWidth))
80762
+ continue;
80763
+ const netClassName = `Trace Width ${traceWidth} mm`;
80764
+ netClasses.push(this.createBaseNetClass({
80765
+ name: netClassName,
80766
+ viaDiameter: minViaDiameter,
80767
+ viaDrill: minViaDrill,
80768
+ clearance: minTraceToPadClearance,
80769
+ trackWidth: traceWidth
80770
+ }));
80771
+ netClassNameByTraceWidth.set(traceWidth, netClassName);
80772
+ }
80773
+ const netClassByNetName = /* @__PURE__ */ new Map;
80774
+ const boardOwnerMap = this.ctx.db.pcb_board.list().length > 1 ? createBoardOwnerMap3(this.ctx.circuitJson) : undefined;
80775
+ const sourceNetsWithTraceWidths = sourceNets.filter((sourceNet) => sourceNet.trace_width !== undefined).map((sourceNet) => {
80776
+ const connectivityKey = sourceNet.subcircuit_connectivity_map_key || sourceNet.source_net_id;
80777
+ return {
80778
+ sourceNet,
80779
+ netName: resolveKicadPcbNetName({
80780
+ candidateName: sourceNet.name || sourceNet.source_net_id,
80781
+ connectivityKey,
80782
+ boardId: boardOwnerMap?.get(sourceNet.subcircuit_id ?? "")?.pcb_board_id
80783
+ })
80784
+ };
80785
+ }).sort((a, b) => a.netName.localeCompare(b.netName) || a.sourceNet.source_net_id.localeCompare(b.sourceNet.source_net_id));
80786
+ for (const { sourceNet, netName } of sourceNetsWithTraceWidths) {
80787
+ const netClassName = netClassNameByTraceWidth.get(sourceNet.trace_width);
80788
+ const existingNetClassName = netClassByNetName.get(netName);
80789
+ if (existingNetClassName !== undefined && existingNetClassName !== netClassName) {
80790
+ throw new Error(`Cannot preserve conflicting trace widths for KiCad net "${netName}": ${existingNetClassName} and ${netClassName}`);
80791
+ }
80792
+ netClassByNetName.set(netName, netClassName);
80793
+ }
80794
+ const netclassPatterns = Array.from(netClassByNetName, ([pattern, netclass]) => ({ pattern, netclass }));
80413
80795
  this.project = {
80414
80796
  version: 1,
80415
80797
  head: {
@@ -80441,10 +80823,11 @@ var CircuitJsonToKicadProConverter = class {
80441
80823
  },
80442
80824
  net_settings: {
80443
80825
  meta: {
80444
- version: 1
80826
+ version: 3
80445
80827
  },
80446
- last_net_id: netClasses.length - 1,
80447
- classes: netClasses
80828
+ last_net_id: 0,
80829
+ classes: netClasses,
80830
+ netclass_patterns: netclassPatterns
80448
80831
  },
80449
80832
  pcbnew: {
80450
80833
  page_layout_descr_file: "",