@tscircuit/cli 0.1.2433 → 0.1.2435

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
@@ -74334,6 +74334,7 @@ import {
74334
74334
  } from "kicadts";
74335
74335
  import { applyToPoint as applyToPoint6 } from "transformation-matrix";
74336
74336
  import { symbols as symbols4 } from "schematic-symbols";
74337
+ import { NoConnect } from "kicadts";
74337
74338
  import { formatSiUnit } from "format-si-unit";
74338
74339
  import {
74339
74340
  EmbeddedFonts as EmbeddedFonts2,
@@ -74362,11 +74363,11 @@ import {
74362
74363
  import { Paper, Uuid as Uuid4 } from "kicadts";
74363
74364
  import { KicadPcb as KicadPcb2 } from "kicadts";
74364
74365
  import { cju as cju2 } from "@tscircuit/circuit-json-util";
74365
- import { compose as compose10, translate as translate10, scale as scale7 } from "transformation-matrix";
74366
+ import { compose as compose11, translate as translate11, scale as scale8 } from "transformation-matrix";
74366
74367
  import {
74367
74368
  Layer,
74368
- Pts as Pts4,
74369
- Xy as Xy4,
74369
+ Pts as Pts5,
74370
+ Xy as Xy5,
74370
74371
  Zone,
74371
74372
  ZoneConnectPads,
74372
74373
  ZoneFill,
@@ -74375,13 +74376,16 @@ import {
74375
74376
  ZonePolygon
74376
74377
  } from "kicadts";
74377
74378
  import {
74378
- applyToPoint as applyToPoint9,
74379
- compose as compose3,
74379
+ applyToPoint as applyToPoint10,
74380
+ compose as compose4,
74380
74381
  rotate,
74381
- translate as translate3
74382
+ translate as translate4
74382
74383
  } 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";
74384
+ import { PadPrimitiveGrPoly, PadPrimitives, Pts as Pts4, Xy as Xy4 } from "kicadts";
74385
+ import { applyToPoint as applyToPoint9, compose as compose3, scale as scale2, translate as translate3 } from "transformation-matrix";
74386
+ import { Pts as Pts6, Xy as Xy6, Zone as Zone2, ZoneKeepout, ZonePolygon as ZonePolygon2 } from "kicadts";
74387
+ import { applyToPoint as applyToPoint11 } from "transformation-matrix";
74388
+ var import_polygon_clipping = __toESM3(require_polygon_clipping_cjs(), 1);
74385
74389
  import {
74386
74390
  Paper as Paper2,
74387
74391
  PcbLayers,
@@ -74389,10 +74393,9 @@ import {
74389
74393
  PcbGeneral,
74390
74394
  Setup
74391
74395
  } from "kicadts";
74392
- import { getElementId } from "@tscircuit/circuit-json-util";
74396
+ import { createBoardOwnerMap, getElementId } from "@tscircuit/circuit-json-util";
74393
74397
  import { getFullConnectivityMapFromCircuitJson } from "circuit-json-to-connectivity-map";
74394
74398
  import { PcbNet } from "kicadts";
74395
- var import_polygon_clipping = __toESM3(require_polygon_clipping_cjs(), 1);
74396
74399
  import { Footprint as Footprint4, FootprintModel as FootprintModel4, FootprintAttr as FootprintAttr3 } from "kicadts";
74397
74400
  import {
74398
74401
  parseKicadSexpr,
@@ -74405,7 +74408,7 @@ import {
74405
74408
  TextEffectsFont as TextEffectsFont8
74406
74409
  } from "kicadts";
74407
74410
  import { FootprintAttr } from "kicadts";
74408
- import { applyToPoint as applyToPoint24 } from "transformation-matrix";
74411
+ import { applyToPoint as applyToPoint26 } from "transformation-matrix";
74409
74412
  import {
74410
74413
  Property as Property2,
74411
74414
  TextEffects as TextEffects9,
@@ -74415,80 +74418,74 @@ import {
74415
74418
  FootprintAttr as FootprintAttr2
74416
74419
  } from "kicadts";
74417
74420
  import { FpCircle, Stroke as Stroke5 } from "kicadts";
74418
- import { applyToPoint as applyToPoint11, identity, rotate as rotate2 } from "transformation-matrix";
74421
+ import { applyToPoint as applyToPoint12, identity, rotate as rotate2 } from "transformation-matrix";
74419
74422
  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";
74423
+ import { applyToPoint as applyToPoint13 } from "transformation-matrix";
74424
+ import { compose as compose5, rotateDEG, scale as scale3, translate as translate5 } from "transformation-matrix";
74422
74425
  import { FpRect as FpRect2 } from "kicadts";
74423
74426
  import { FpPoly, FpRect, Stroke as Stroke7 } from "kicadts";
74424
- import { applyToPoint as applyToPoint13, compose as compose5, translate as translate5 } from "transformation-matrix";
74427
+ import { applyToPoint as applyToPoint14, compose as compose6, translate as translate6 } from "transformation-matrix";
74425
74428
  import { FpRect as FpRect3 } from "kicadts";
74426
74429
  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
74430
  import {
74436
74431
  applyToPoint as applyToPoint15,
74437
74432
  compose as compose7,
74438
- rotate as rotate4,
74433
+ rotate as rotate3,
74439
74434
  scale as scale4,
74440
74435
  translate as translate7
74441
74436
  } from "transformation-matrix";
74437
+ import { FpPoly as FpPoly5, Pts as Pts7, Stroke as Stroke9, Xy as Xy7 } from "kicadts";
74438
+ import {
74439
+ applyToPoint as applyToPoint16,
74440
+ compose as compose8,
74441
+ rotate as rotate4,
74442
+ scale as scale5,
74443
+ translate as translate8
74444
+ } from "transformation-matrix";
74442
74445
  import"kicadts";
74443
74446
  import { FpText, TextEffects as TextEffects10, TextEffectsFont as TextEffectsFont10 } from "kicadts";
74444
- import { applyToPoint as applyToPoint16, rotate as rotate5, identity as identity2 } from "transformation-matrix";
74447
+ import { applyToPoint as applyToPoint17, rotate as rotate5, identity as identity2 } from "transformation-matrix";
74445
74448
  import { TextEffectsJustify as TextEffectsJustify6 } from "kicadts";
74446
74449
  import { FpLine, Stroke as Stroke10 } from "kicadts";
74447
- import { applyToPoint as applyToPoint17, identity as identity3, rotate as rotate6 } from "transformation-matrix";
74450
+ import { applyToPoint as applyToPoint18, identity as identity3, rotate as rotate6 } from "transformation-matrix";
74448
74451
  import { FpPoly as FpPoly6, Stroke as Stroke11 } from "kicadts";
74449
74452
  import {
74450
- applyToPoint as applyToPoint18,
74451
- compose as compose8,
74453
+ applyToPoint as applyToPoint19,
74454
+ compose as compose9,
74452
74455
  rotate as rotate7,
74453
- scale as scale5,
74454
- translate as translate8
74456
+ scale as scale6,
74457
+ translate as translate9
74455
74458
  } from "transformation-matrix";
74456
74459
  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";
74460
+ import { applyToPoint as applyToPoint20, rotate as rotate8, identity as identity4 } from "transformation-matrix";
74458
74461
  import { FootprintModel as FootprintModel3 } from "kicadts";
74462
+ import { FootprintPad, PadNet, PadOptions } from "kicadts";
74459
74463
  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
74464
+ applyToPoint as applyToPoint21,
74465
+ compose as compose10,
74466
+ rotate as rotate9,
74467
+ scale as scale7,
74468
+ translate as translate10
74474
74469
  } from "transformation-matrix";
74475
74470
  import { FootprintPad as FootprintPad2, PadDrill, PadNet as PadNet2 } from "kicadts";
74476
- import { applyToPoint as applyToPoint21, rotate as rotate10, identity as identity5 } from "transformation-matrix";
74471
+ import { applyToPoint as applyToPoint22, rotate as rotate10, identity as identity5 } from "transformation-matrix";
74477
74472
  import { FootprintPad as FootprintPad3, PadDrill as PadDrill2 } from "kicadts";
74478
- import { applyToPoint as applyToPoint22, rotate as rotate11, identity as identity6 } from "transformation-matrix";
74473
+ import { applyToPoint as applyToPoint23, rotate as rotate11, identity as identity6 } from "transformation-matrix";
74479
74474
  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";
74475
+ import { applyToPoint as applyToPoint24, identity as identity7, rotate as rotate12 } from "transformation-matrix";
74476
+ import { FootprintPad as FootprintPad4, PadOptions as PadOptions2 } from "kicadts";
74482
74477
  import { applyToPoint as applyToPoint25 } from "transformation-matrix";
74478
+ import { Segment, SegmentNet } from "kicadts";
74479
+ import { applyToPoint as applyToPoint27 } from "transformation-matrix";
74483
74480
  import {
74484
- createBoardOwnerMap,
74481
+ createBoardOwnerMap as createBoardOwnerMap2,
74485
74482
  isRouteViaCoveredByPhysicalVia
74486
74483
  } from "@tscircuit/circuit-json-util";
74487
74484
  import { Via, ViaNet } from "kicadts";
74488
- import { applyToPoint as applyToPoint26 } from "transformation-matrix";
74485
+ import { applyToPoint as applyToPoint28 } from "transformation-matrix";
74489
74486
  import { ViaTenting } from "kicadts";
74490
74487
  import { Footprint as Footprint5 } from "kicadts";
74491
- import { applyToPoint as applyToPoint27 } from "transformation-matrix";
74488
+ import { applyToPoint as applyToPoint29 } from "transformation-matrix";
74492
74489
  import { GrLine } from "kicadts";
74493
74490
  import {
74494
74491
  At as At3,
@@ -74497,11 +74494,11 @@ import {
74497
74494
  TextEffectsFont as TextEffectsFont12,
74498
74495
  TextEffectsJustify as TextEffectsJustify7
74499
74496
  } from "kicadts";
74500
- import { applyToPoint as applyToPoint28 } from "transformation-matrix";
74501
- import { applyToPoint as applyToPoint30, rotate as rotate13 } from "transformation-matrix";
74497
+ import { applyToPoint as applyToPoint30 } from "transformation-matrix";
74498
+ import { applyToPoint as applyToPoint32, rotate as rotate13 } from "transformation-matrix";
74502
74499
  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";
74500
+ import { applyToPoint as applyToPoint31 } from "transformation-matrix";
74501
+ import { cju as cju3, createBoardOwnerMap as createBoardOwnerMap3 } from "@tscircuit/circuit-json-util";
74505
74502
  import { cju as cju4 } from "@tscircuit/circuit-json-util";
74506
74503
  import { parseKicadSexpr as parseKicadSexpr2, KicadSch as KicadSch2 } from "kicadts";
74507
74504
  import { KicadSymbolLib } from "kicadts";
@@ -75270,7 +75267,7 @@ function transformSchematicPoint(transform, point) {
75270
75267
  function createArcFromPrimitive({
75271
75268
  primitive,
75272
75269
  transform,
75273
- scale: scale8
75270
+ scale: scale9
75274
75271
  }) {
75275
75272
  const start = transformSchematicPoint(transform, primitive.start);
75276
75273
  const mid = transformSchematicPoint(transform, primitive.mid);
@@ -75283,7 +75280,7 @@ function createArcFromPrimitive({
75283
75280
  "stroke",
75284
75281
  [
75285
75282
  "width",
75286
- roundSchematicCoordinate(primitive.strokeWidth == null ? 0.254 : primitive.strokeWidth * scale8)
75283
+ roundSchematicCoordinate(primitive.strokeWidth == null ? 0.254 : primitive.strokeWidth * scale9)
75287
75284
  ],
75288
75285
  ["type", primitive.isDashed ? "dash" : "default"]
75289
75286
  ],
@@ -75339,7 +75336,7 @@ function createSymbolFillSexprPrimitives({
75339
75336
  function createCircleFromPrimitive({
75340
75337
  primitive,
75341
75338
  transform,
75342
- scale: scale8
75339
+ scale: scale9
75343
75340
  }) {
75344
75341
  const scaledPos = transformSchematicPoint(transform, {
75345
75342
  x: primitive.x,
@@ -75347,12 +75344,12 @@ function createCircleFromPrimitive({
75347
75344
  });
75348
75345
  return SymbolCircle.fromSexprPrimitives([
75349
75346
  ["center", scaledPos.x, scaledPos.y],
75350
- ["radius", roundSchematicCoordinate(primitive.radius * scale8)],
75347
+ ["radius", roundSchematicCoordinate(primitive.radius * scale9)],
75351
75348
  [
75352
75349
  "stroke",
75353
75350
  [
75354
75351
  "width",
75355
- roundSchematicCoordinate(primitive.strokeWidth == null ? 0.254 : primitive.strokeWidth * scale8)
75352
+ roundSchematicCoordinate(primitive.strokeWidth == null ? 0.254 : primitive.strokeWidth * scale9)
75356
75353
  ],
75357
75354
  ["type", "default"]
75358
75355
  ],
@@ -75374,7 +75371,7 @@ function createPolylineFromPoints({
75374
75371
  fallbackFillType,
75375
75372
  strokeWidth,
75376
75373
  isDashed,
75377
- scale: scale8
75374
+ scale: scale9
75378
75375
  }) {
75379
75376
  const polyline = new SymbolPolyline;
75380
75377
  const xyPoints = points.map((p) => {
@@ -75384,7 +75381,7 @@ function createPolylineFromPoints({
75384
75381
  const pts = new Pts(xyPoints);
75385
75382
  polyline.points = pts;
75386
75383
  const stroke = new Stroke2;
75387
- stroke.width = roundSchematicCoordinate(strokeWidth == null ? 0.254 : strokeWidth * scale8);
75384
+ stroke.width = roundSchematicCoordinate(strokeWidth == null ? 0.254 : strokeWidth * scale9);
75388
75385
  stroke.type = isDashed ? "dash" : "default";
75389
75386
  polyline.stroke = stroke;
75390
75387
  const fill = SymbolPolylineFill.fromSexprPrimitives(createSymbolFillSexprPrimitives({
@@ -75398,7 +75395,7 @@ function createPolylineFromPoints({
75398
75395
  function createRectangleFromPrimitive({
75399
75396
  primitive,
75400
75397
  transform,
75401
- scale: scale8
75398
+ scale: scale9
75402
75399
  }) {
75403
75400
  const start = transformSchematicPoint(transform, primitive.start);
75404
75401
  const end = transformSchematicPoint(transform, primitive.end);
@@ -75409,7 +75406,7 @@ function createRectangleFromPrimitive({
75409
75406
  "stroke",
75410
75407
  [
75411
75408
  "width",
75412
- roundSchematicCoordinate(primitive.strokeWidth == null ? 0.254 : primitive.strokeWidth * scale8)
75409
+ roundSchematicCoordinate(primitive.strokeWidth == null ? 0.254 : primitive.strokeWidth * scale9)
75413
75410
  ],
75414
75411
  ["type", primitive.isDashed ? "dash" : "default"]
75415
75412
  ],
@@ -75425,7 +75422,7 @@ function createRectangleFromPrimitive({
75425
75422
  function createTextFromPrimitive({
75426
75423
  schText,
75427
75424
  transform,
75428
- scale: scale8
75425
+ scale: scale9
75429
75426
  }) {
75430
75427
  const symbolText = new SymbolText;
75431
75428
  const scaledPos = transformSchematicPoint(transform, {
@@ -75434,7 +75431,7 @@ function createTextFromPrimitive({
75434
75431
  });
75435
75432
  symbolText.value = schText.text;
75436
75433
  symbolText.at = [scaledPos.x, scaledPos.y, 0];
75437
- const scaledFontSize = roundSchematicCoordinate(schText.fontSize * scale8);
75434
+ const scaledFontSize = roundSchematicCoordinate(schText.fontSize * scale9);
75438
75435
  const font = new TextEffectsFont2;
75439
75436
  font.size = { height: scaledFontSize, width: scaledFontSize };
75440
75437
  symbolText.effects = new TextEffects2({ font });
@@ -75700,6 +75697,30 @@ function calculatePinPosition({
75700
75697
  }
75701
75698
  return { x, y, angle };
75702
75699
  }
75700
+ function getKicadPinElectricalType(sourcePort) {
75701
+ if (!sourcePort)
75702
+ return "passive";
75703
+ if (sourcePort.provides_power || sourcePort.provides_ground) {
75704
+ return "power_out";
75705
+ }
75706
+ if (sourcePort.requires_power || sourcePort.requires_ground) {
75707
+ return "power_in";
75708
+ }
75709
+ if (sourcePort.is_using_open_collector || sourcePort.is_using_open_drain) {
75710
+ return "open_collector";
75711
+ }
75712
+ if (sourcePort.is_using_open_emitter)
75713
+ return "open_emitter";
75714
+ if (sourcePort.is_using_tri_state)
75715
+ return "tri_state";
75716
+ if (sourcePort.is_bidirectional)
75717
+ return "bidirectional";
75718
+ if (sourcePort.is_output)
75719
+ return "output";
75720
+ if (sourcePort.is_input)
75721
+ return "input";
75722
+ return "passive";
75723
+ }
75703
75724
  function getPortSide(x, y) {
75704
75725
  if (Math.abs(x) > Math.abs(y))
75705
75726
  return x < 0 ? "left" : "right";
@@ -75716,12 +75737,11 @@ function getCircuitPinNumbers({
75716
75737
  symbolData,
75717
75738
  schematicComponent,
75718
75739
  schematicPorts,
75719
- sourcePorts
75740
+ sourcePortsById
75720
75741
  }) {
75721
75742
  const matches = /* @__PURE__ */ new Map;
75722
75743
  if (!schematicComponent)
75723
75744
  return matches;
75724
- const sourcePortsById = new Map(sourcePorts.map((port) => [port.source_port_id, port]));
75725
75745
  const symbolCenter = symbolData.center ?? { x: 0, y: 0 };
75726
75746
  const componentPorts = schematicPorts.filter((port) => port.schematic_component_id === schematicComponent.schematic_component_id);
75727
75747
  for (const side of ["left", "right", "up", "down"]) {
@@ -75782,17 +75802,17 @@ function createPinSubsymbol({
75782
75802
  });
75783
75803
  const CHIP_PIN_LENGTH = 6;
75784
75804
  const CUSTOM_SYMBOL_PIN_LENGTH = 0.01;
75805
+ const sourcePortsById = new Map(sourcePorts.map((port) => [port.source_port_id, port]));
75785
75806
  const inferredPinNumbers = isChip ? /* @__PURE__ */ new Map : getCircuitPinNumbers({
75786
75807
  symbolData,
75787
75808
  schematicComponent,
75788
75809
  schematicPorts,
75789
- sourcePorts
75810
+ sourcePortsById
75790
75811
  });
75791
75812
  const circuitPinNumbers = validateInferredPinNumbers(symbolData, inferredPinNumbers);
75792
75813
  const origin = schematicComponent ? applyToPoint3(c2kMatSch, schematicComponent.center) : { x: 0, y: 0 };
75793
75814
  for (const [i, port] of symbolData.ports.entries()) {
75794
75815
  const pin = new SymbolPin;
75795
- pin.pinElectricalType = "passive";
75796
75816
  pin.pinGraphicStyle = "line";
75797
75817
  const { x, y, angle } = calculatePinPosition({
75798
75818
  port,
@@ -75823,8 +75843,15 @@ function createPinSubsymbol({
75823
75843
  numFont.size = { height: 1.27, width: 1.27 };
75824
75844
  const numEffects = new TextEffects4({ font: numFont });
75825
75845
  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);
75846
+ const schematicPort = schematicPorts.find((candidate) => {
75847
+ if (candidate.schematic_component_id !== schematicComponent?.schematic_component_id) {
75848
+ return false;
75849
+ }
75850
+ const linkedSourcePort = sourcePortsById.get(candidate.source_port_id);
75851
+ return String(candidate.pin_number ?? linkedSourcePort?.pin_number) === pinNum;
75852
+ });
75853
+ const sourcePort = schematicPort ? sourcePortsById.get(schematicPort.source_port_id) : undefined;
75854
+ pin.pinElectricalType = getKicadPinElectricalType(sourcePort);
75828
75855
  pin._sxNumber = new SymbolPinNumber({
75829
75856
  value: getPhysicalPinName(sourcePort) ?? pinNum,
75830
75857
  effects: numEffects
@@ -76286,8 +76313,8 @@ var AddSchematicGraphicsStage = class extends ConverterStage {
76286
76313
  new Xy2(end.x, end.y)
76287
76314
  ]);
76288
76315
  const stroke = new Stroke3;
76289
- stroke.width = 0;
76290
- stroke.type = "default";
76316
+ stroke.width = (line.stroke_width ?? 0) * (this.ctx.kicadSchematicScaleFactor ?? 1);
76317
+ stroke.type = line.is_dashed ? "dash" : "default";
76291
76318
  stroke.color = preserveStandaloneGraphics ? parseColor(line.color) ?? DEFAULT_SECTION_LINE_COLOR : DEFAULT_SECTION_LINE_COLOR;
76292
76319
  polyline.stroke = stroke;
76293
76320
  polyline.uuid = new Uuid(crypto.randomUUID());
@@ -76598,6 +76625,30 @@ var AddSchematicNetLabelsStage = class extends ConverterStage {
76598
76625
  return this.ctx.kicadSch;
76599
76626
  }
76600
76627
  };
76628
+ var AddSchematicNoConnectsStage = class extends ConverterStage {
76629
+ _step() {
76630
+ const { db, kicadSch, c2kMatSch } = this.ctx;
76631
+ if (!kicadSch) {
76632
+ throw new Error("KicadSch instance not initialized in context");
76633
+ }
76634
+ if (!c2kMatSch) {
76635
+ throw new Error("Schematic transformation matrix not initialized in context");
76636
+ }
76637
+ const noConnectSourcePortIds = new Set(db.source_port.list().filter((sourcePort) => sourcePort.do_not_connect).map((sourcePort) => sourcePort.source_port_id));
76638
+ const noConnects = db.schematic_port.list().filter((schematicPort) => noConnectSourcePortIds.has(schematicPort.source_port_id) && !schematicPort.is_connected).map((schematicPort) => {
76639
+ const position = transformSchematicPoint(c2kMatSch, schematicPort.center);
76640
+ return new NoConnect({
76641
+ at: [position.x, position.y],
76642
+ uuid: crypto.randomUUID()
76643
+ });
76644
+ });
76645
+ kicadSch.noConnects = [...kicadSch.noConnects, ...noConnects];
76646
+ this.finished = true;
76647
+ }
76648
+ getOutput() {
76649
+ return this.ctx.kicadSch;
76650
+ }
76651
+ };
76601
76652
  var DEFAULT_PROPERTY_FONT_SIZE = 1.27;
76602
76653
  var AddSchematicSymbolsStage = class extends ConverterStage {
76603
76654
  _step() {
@@ -77226,6 +77277,7 @@ var CircuitJsonToKicadSchConverter = class {
77226
77277
  new AddLibrarySymbolsStage(circuitJson, this.ctx),
77227
77278
  new AddSchematicSymbolsStage(circuitJson, this.ctx),
77228
77279
  new AddSchematicNetLabelsStage(circuitJson, this.ctx),
77280
+ new AddSchematicNoConnectsStage(circuitJson, this.ctx),
77229
77281
  new AddSchematicTracesStage(circuitJson, this.ctx),
77230
77282
  new AddSchematicGraphicsStage(circuitJson, this.ctx),
77231
77283
  new AddSheetInstancesStage(circuitJson, this.ctx)
@@ -77384,6 +77436,7 @@ var CircuitJsonToKicadSchConverter = class {
77384
77436
  new AddLibrarySymbolsStage(circuitJson, ctx),
77385
77437
  new AddSchematicSymbolsStage(circuitJson, ctx),
77386
77438
  new AddSchematicNetLabelsStage(circuitJson, ctx),
77439
+ new AddSchematicNoConnectsStage(circuitJson, ctx),
77387
77440
  new AddSchematicTracesStage(circuitJson, ctx),
77388
77441
  new AddSchematicGraphicsStage(circuitJson, ctx)
77389
77442
  ];
@@ -77461,6 +77514,110 @@ function getViaLayerSpan(kicadLayers, numLayers) {
77461
77514
  }
77462
77515
  return top === bottom ? [top] : [top, bottom];
77463
77516
  }
77517
+ 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));
77518
+ var triangulatePolygonWithHoles = (outerContour, holes = []) => {
77519
+ const flattenedPoints = [];
77520
+ const holeIndices = [];
77521
+ let pointCount = 0;
77522
+ const addContour = (contour) => {
77523
+ for (const point of contour) {
77524
+ flattenedPoints.push(point.x, point.y);
77525
+ }
77526
+ pointCount += contour.length;
77527
+ };
77528
+ addContour(outerContour);
77529
+ for (const hole of holes) {
77530
+ holeIndices.push(pointCount);
77531
+ addContour(hole);
77532
+ }
77533
+ const triangleIndices = earcut(flattenedPoints, holeIndices, 2);
77534
+ const triangles = [];
77535
+ for (let index = 0;index < triangleIndices.length; index += 3) {
77536
+ const aIndex = triangleIndices[index];
77537
+ const bIndex = triangleIndices[index + 1];
77538
+ const cIndex = triangleIndices[index + 2];
77539
+ if (aIndex === undefined || bIndex === undefined || cIndex === undefined) {
77540
+ continue;
77541
+ }
77542
+ const getPoint = (pointIndex) => ({
77543
+ x: flattenedPoints[pointIndex * 2],
77544
+ y: flattenedPoints[pointIndex * 2 + 1]
77545
+ });
77546
+ const triangle = [
77547
+ getPoint(aIndex),
77548
+ getPoint(bIndex),
77549
+ getPoint(cIndex)
77550
+ ];
77551
+ if (getTriangleTwiceArea(...triangle) === 0)
77552
+ continue;
77553
+ triangles.push(triangle);
77554
+ }
77555
+ return triangles;
77556
+ };
77557
+ var getDistance = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
77558
+ var getSafePolygonAnchor = (outerContour, holes = []) => {
77559
+ const triangles = triangulatePolygonWithHoles(outerContour, holes);
77560
+ let bestTriangle;
77561
+ for (const [a, b, c] of triangles) {
77562
+ const sideA = getDistance(b, c);
77563
+ const sideB = getDistance(c, a);
77564
+ const sideC = getDistance(a, b);
77565
+ const perimeter = sideA + sideB + sideC;
77566
+ if (perimeter === 0)
77567
+ continue;
77568
+ const inradius = getTriangleTwiceArea(a, b, c) / perimeter;
77569
+ const center = {
77570
+ x: (sideA * a.x + sideB * b.x + sideC * c.x) / perimeter,
77571
+ y: (sideA * a.y + sideB * b.y + sideC * c.y) / perimeter
77572
+ };
77573
+ if (!bestTriangle || inradius > bestTriangle.inradius) {
77574
+ bestTriangle = { center, inradius };
77575
+ }
77576
+ }
77577
+ if (!bestTriangle) {
77578
+ throw new Error("Polygon must contain a valid filled area");
77579
+ }
77580
+ return {
77581
+ center: bestTriangle.center,
77582
+ diameter: Math.min(0.001, bestTriangle.inradius),
77583
+ triangles
77584
+ };
77585
+ };
77586
+ var createFilledPolygonPrimitives = ({
77587
+ contours,
77588
+ center
77589
+ }) => {
77590
+ const toLocalContour = (points) => new Pts4(points.map((point) => {
77591
+ const localPoint = applyToPoint9(compose3(scale2(1, -1), translate3(-center.x, -center.y)), point);
77592
+ return new Xy4(localPoint.x, localPoint.y);
77593
+ }));
77594
+ const primitives = new PadPrimitives;
77595
+ for (const contour of contours) {
77596
+ const polygon = new PadPrimitiveGrPoly;
77597
+ polygon.contours = [toLocalContour(contour)];
77598
+ polygon.width = 0;
77599
+ polygon.filled = true;
77600
+ primitives.addGraphic(polygon);
77601
+ }
77602
+ return primitives;
77603
+ };
77604
+ var createEllipseContour = ({
77605
+ center,
77606
+ width,
77607
+ height,
77608
+ segmentCount = 64
77609
+ }) => {
77610
+ if (width <= 0 || height <= 0) {
77611
+ throw new Error("Ellipse must have positive dimensions");
77612
+ }
77613
+ return Array.from({ length: segmentCount }, (_, index) => {
77614
+ const angle = index / segmentCount * Math.PI * 2;
77615
+ return {
77616
+ x: center.x + width / 2 * Math.cos(angle),
77617
+ y: center.y + height / 2 * Math.sin(angle)
77618
+ };
77619
+ });
77620
+ };
77464
77621
  function simpleHash(str) {
77465
77622
  let hash = 0;
77466
77623
  for (let i = 0;i < str.length; i++) {
@@ -77497,8 +77654,8 @@ var getCopperPourNetInfo = (pour, ctx) => {
77497
77654
  };
77498
77655
  var convertPointsToKicadXy = (points, c2kMatPcb) => {
77499
77656
  return (points ?? []).map((point) => {
77500
- const transformedPoint = applyToPoint9(c2kMatPcb, point);
77501
- return new Xy4(transformedPoint.x, transformedPoint.y);
77657
+ const transformedPoint = applyToPoint10(c2kMatPcb, point);
77658
+ return new Xy5(transformedPoint.x, transformedPoint.y);
77502
77659
  });
77503
77660
  };
77504
77661
  var rotateRingToStartAtTopRight = (points) => {
@@ -77538,7 +77695,7 @@ var removeRepeatedClosingPoint = (points) => {
77538
77695
  };
77539
77696
  var getRectRingPoints = (pour, c2kMatPcb) => {
77540
77697
  const ccwRotationDegrees = pour.rotation ?? 0;
77541
- const cornerTransform = compose3(translate3(pour.center.x, pour.center.y), rotate(ccwRotationDegrees * Math.PI / 180));
77698
+ const cornerTransform = compose4(translate4(pour.center.x, pour.center.y), rotate(ccwRotationDegrees * Math.PI / 180));
77542
77699
  const halfWidth = pour.width / 2;
77543
77700
  const halfHeight = pour.height / 2;
77544
77701
  const corners = [
@@ -77546,7 +77703,7 @@ var getRectRingPoints = (pour, c2kMatPcb) => {
77546
77703
  { x: halfWidth, y: -halfHeight },
77547
77704
  { x: halfWidth, y: halfHeight },
77548
77705
  { x: -halfWidth, y: halfHeight }
77549
- ].map((corner) => applyToPoint9(cornerTransform, corner));
77706
+ ].map((corner) => applyToPoint10(cornerTransform, corner));
77550
77707
  return convertPointsToKicadXy(corners, c2kMatPcb);
77551
77708
  };
77552
77709
  var getPolygonRingPoints = (pour, c2kMatPcb) => removeRepeatedClosingPoint(removeConsecutiveDuplicatePoints(rotateRingToStartAtTopRight(convertPointsToKicadXy(pour.points, c2kMatPcb))));
@@ -77570,16 +77727,15 @@ var getCopperPourZoneRings = (pour, c2kMatPcb) => {
77570
77727
  var createZonePolygons = (outerRing, innerRings) => {
77571
77728
  const polygons = [];
77572
77729
  if (outerRing.length >= 3) {
77573
- polygons.push(new ZonePolygon(new Pts4(outerRing)));
77730
+ polygons.push(new ZonePolygon(new Pts5(outerRing)));
77574
77731
  }
77575
77732
  for (const innerRing of innerRings) {
77576
77733
  if (innerRing.length < 3)
77577
77734
  continue;
77578
- polygons.push(new ZonePolygon(new Pts4(innerRing)));
77735
+ polygons.push(new ZonePolygon(new Pts5(innerRing)));
77579
77736
  }
77580
77737
  return polygons;
77581
77738
  };
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
77739
  var createZoneFilledPolygons = (outerRing, innerRings, kicadLayer) => {
77584
77740
  if (outerRing.length < 3) {
77585
77741
  return [];
@@ -77589,45 +77745,14 @@ var createZoneFilledPolygons = (outerRing, innerRings, kicadLayer) => {
77589
77745
  return [
77590
77746
  new ZoneFilledPolygon({
77591
77747
  layer,
77592
- pts: new Pts4(outerRing)
77748
+ pts: new Pts5(outerRing)
77593
77749
  })
77594
77750
  ];
77595
77751
  }
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;
77752
+ return triangulatePolygonWithHoles(outerRing, innerRings).map((triangle) => new ZoneFilledPolygon({
77753
+ layer,
77754
+ pts: new Pts5(triangle.map((point) => new Xy5(point.x, point.y)))
77755
+ }));
77631
77756
  };
77632
77757
  var AddCopperPoursStage = class extends ConverterStage {
77633
77758
  _step() {
@@ -77718,8 +77843,8 @@ var AddKeepoutsStage = class extends ConverterStage {
77718
77843
  rawPoints = keepout.outline;
77719
77844
  }
77720
77845
  const kicadPoints = rawPoints.map((p) => {
77721
- const t = applyToPoint10(c2kMatPcb, p);
77722
- return new Xy5(t.x, t.y);
77846
+ const t = applyToPoint11(c2kMatPcb, p);
77847
+ return new Xy6(t.x, t.y);
77723
77848
  });
77724
77849
  const zone = new Zone2({
77725
77850
  net: 0,
@@ -77733,7 +77858,7 @@ var AddKeepoutsStage = class extends ConverterStage {
77733
77858
  copperpour: "not_allowed",
77734
77859
  footprints: "not_allowed"
77735
77860
  }),
77736
- polygons: [new ZonePolygon2(new Pts5(kicadPoints))]
77861
+ polygons: [new ZonePolygon2(new Pts6(kicadPoints))]
77737
77862
  });
77738
77863
  const zones = kicadPcb.zones;
77739
77864
  zones.push(zone);
@@ -77781,6 +77906,14 @@ var InitializePcbStage = class extends ConverterStage {
77781
77906
  return this.ctx.kicadPcb;
77782
77907
  }
77783
77908
  };
77909
+ var resolveKicadPcbNetName = ({
77910
+ candidateName,
77911
+ connectivityKey,
77912
+ boardId
77913
+ }) => {
77914
+ const name = candidateName && candidateName.trim().length > 0 ? candidateName : connectivityKey;
77915
+ return boardId ? `${boardId}/${name}` : name;
77916
+ };
77784
77917
  var AddNetsStage = class extends ConverterStage {
77785
77918
  _step() {
77786
77919
  const { kicadPcb } = this.ctx;
@@ -77789,13 +77922,17 @@ var AddNetsStage = class extends ConverterStage {
77789
77922
  }
77790
77923
  this.ctx.pcbNetMap = /* @__PURE__ */ new Map;
77791
77924
  const netNameByKey = /* @__PURE__ */ new Map;
77925
+ const boardOwnerMap = this.ctx.db.pcb_board.list().length > 1 ? createBoardOwnerMap(this.input) : undefined;
77792
77926
  const sourceNets = this.ctx.db.source_net?.list() ?? [];
77793
77927
  for (const sourceNet of sourceNets) {
77794
77928
  const connectivityKey = sourceNet.subcircuit_connectivity_map_key || sourceNet.source_net_id;
77795
77929
  if (!connectivityKey)
77796
77930
  continue;
77797
- const candidateName = sourceNet.name || sourceNet.source_net_id || "";
77798
- const netName = candidateName && candidateName.trim().length > 0 ? candidateName : connectivityKey;
77931
+ const netName = resolveKicadPcbNetName({
77932
+ candidateName: sourceNet.name || sourceNet.source_net_id,
77933
+ connectivityKey,
77934
+ boardId: boardOwnerMap?.get(sourceNet.subcircuit_id ?? "")?.pcb_board_id
77935
+ });
77799
77936
  netNameByKey.set(connectivityKey, netName);
77800
77937
  }
77801
77938
  const sourceTraces = this.ctx.db.source_trace?.list() ?? [];
@@ -77813,8 +77950,11 @@ var AddNetsStage = class extends ConverterStage {
77813
77950
  if (!connectivityKey)
77814
77951
  continue;
77815
77952
  if (!netNameByKey.has(connectivityKey)) {
77816
- const candidateName = sourceTrace.display_name || sourceTrace.source_trace_id || "";
77817
- const netName = candidateName && candidateName.trim().length > 0 ? candidateName : connectivityKey;
77953
+ const netName = resolveKicadPcbNetName({
77954
+ candidateName: sourceTrace.display_name || sourceTrace.source_trace_id,
77955
+ connectivityKey,
77956
+ boardId: boardOwnerMap?.get(sourceTrace.subcircuit_id ?? "")?.pcb_board_id
77957
+ });
77818
77958
  netNameByKey.set(connectivityKey, netName);
77819
77959
  }
77820
77960
  }
@@ -78040,7 +78180,8 @@ var ExtractFootprintsStage = class extends ConverterStage {
78040
78180
  for (let i = 0;i < pads.length; i++) {
78041
78181
  const pad = pads[i];
78042
78182
  if (pad) {
78043
- pad.uuid = generateDeterministicUuid(`${footprintName}-pad-${pad.number ?? i}`);
78183
+ const padIdentity = pad.number === "" ? `unnumbered-${i}` : pad.number ?? i;
78184
+ pad.uuid = generateDeterministicUuid(`${footprintName}-pad-${padIdentity}`);
78044
78185
  pad.net = undefined;
78045
78186
  }
78046
78187
  }
@@ -78328,7 +78469,7 @@ function convertSilkscreenCircles(silkscreenCircles, { componentCenter, componen
78328
78469
  const fpCircles = [];
78329
78470
  const rotationMatrix = componentRotation !== 0 ? rotate2(componentRotation * Math.PI / 180) : identity();
78330
78471
  for (const circle of silkscreenCircles) {
78331
- const relativeCenter = applyToPoint11(rotationMatrix, {
78472
+ const relativeCenter = applyToPoint12(rotationMatrix, {
78332
78473
  x: circle.center.x - componentCenter.x,
78333
78474
  y: -(circle.center.y - componentCenter.y)
78334
78475
  });
@@ -78356,13 +78497,13 @@ function getBoardToFootprintTransform({
78356
78497
  footprintOrigin,
78357
78498
  ccwRotationDegrees = 0
78358
78499
  }) {
78359
- return compose4(rotateDEG(ccwRotationDegrees), scale2(1, -1), translate4(-footprintOrigin.x, -footprintOrigin.y));
78500
+ return compose5(rotateDEG(ccwRotationDegrees), scale3(1, -1), translate5(-footprintOrigin.x, -footprintOrigin.y));
78360
78501
  }
78361
78502
  function convertCourtyardCircles(courtyardCircles, placement) {
78362
78503
  const fpCircles = [];
78363
78504
  const boardToFootprintTransform = getBoardToFootprintTransform(placement);
78364
78505
  for (const circle of courtyardCircles) {
78365
- const center = applyToPoint12(boardToFootprintTransform, circle.center);
78506
+ const center = applyToPoint13(boardToFootprintTransform, circle.center);
78366
78507
  const layerMap = {
78367
78508
  top: "F.CrtYd",
78368
78509
  bottom: "B.CrtYd"
@@ -78389,7 +78530,7 @@ function convertNoteRectToFootprintGraphic({
78389
78530
  footprintOrigin,
78390
78531
  ccwRotationDegrees = 0
78391
78532
  }) {
78392
- const rectToFootprintTransform = compose5(getBoardToFootprintTransform({ footprintOrigin, ccwRotationDegrees }), translate5(rect.center.x, rect.center.y));
78533
+ const rectToFootprintTransform = compose6(getBoardToFootprintTransform({ footprintOrigin, ccwRotationDegrees }), translate6(rect.center.x, rect.center.y));
78393
78534
  const halfWidth = rect.width / 2;
78394
78535
  const halfHeight = rect.height / 2;
78395
78536
  const corners = [
@@ -78397,7 +78538,7 @@ function convertNoteRectToFootprintGraphic({
78397
78538
  { x: halfWidth, y: -halfHeight },
78398
78539
  { x: halfWidth, y: halfHeight },
78399
78540
  { x: -halfWidth, y: halfHeight }
78400
- ].map((point) => applyToPoint13(rectToFootprintTransform, point));
78541
+ ].map((point) => applyToPoint14(rectToFootprintTransform, point));
78401
78542
  const stroke = new Stroke7;
78402
78543
  stroke.width = rect.stroke_width || 0.1;
78403
78544
  stroke.type = "default";
@@ -78472,7 +78613,7 @@ function rotateCenter(point, degrees) {
78472
78613
  case 270:
78473
78614
  return { x: point.y, y: -point.x };
78474
78615
  default:
78475
- return applyToPoint14(rotate3(degrees * Math.PI / 180), point);
78616
+ return applyToPoint15(rotate3(degrees * Math.PI / 180), point);
78476
78617
  }
78477
78618
  }
78478
78619
  function convertCourtyardRects({
@@ -78482,7 +78623,7 @@ function convertCourtyardRects({
78482
78623
  }) {
78483
78624
  const fpRects = [];
78484
78625
  const fpPolys = [];
78485
- const toLocal = compose6(rotate3(componentRotation * Math.PI / 180), scale3(1, -1), translate6(-componentCenter.x, -componentCenter.y));
78626
+ const toLocal = compose7(rotate3(componentRotation * Math.PI / 180), scale4(1, -1), translate7(-componentCenter.x, -componentCenter.y));
78486
78627
  for (const rect of courtyardRects) {
78487
78628
  const halfW = rect.width / 2;
78488
78629
  const halfH = rect.height / 2;
@@ -78516,13 +78657,13 @@ function convertCourtyardRects({
78516
78657
  fill: false
78517
78658
  }));
78518
78659
  } else {
78519
- const fromRectCenter = compose6(translate6(rect.center.x, rect.center.y), rotate3(rotation * Math.PI / 180));
78660
+ const fromRectCenter = compose7(translate7(rect.center.x, rect.center.y), rotate3(rotation * Math.PI / 180));
78520
78661
  const points = [
78521
78662
  { x: -halfW, y: -halfH },
78522
78663
  { x: halfW, y: -halfH },
78523
78664
  { x: halfW, y: halfH },
78524
78665
  { x: -halfW, y: halfH }
78525
- ].map((point) => applyToPoint14(toLocal, applyToPoint14(fromRectCenter, point)));
78666
+ ].map((point) => applyToPoint15(toLocal, applyToPoint15(fromRectCenter, point)));
78526
78667
  fpPolys.push(new FpPoly4({
78527
78668
  points,
78528
78669
  layer,
@@ -78537,7 +78678,7 @@ function convertCourtyardRects({
78537
78678
  function convertCourtyardOutlines(params2) {
78538
78679
  const { courtyardOutlines, componentCenter, componentRotation = 0 } = params2;
78539
78680
  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));
78681
+ 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
78682
  for (const outline of courtyardOutlines) {
78542
78683
  if (!outline.outline || outline.outline.length < 2)
78543
78684
  continue;
@@ -78547,11 +78688,11 @@ function convertCourtyardOutlines(params2) {
78547
78688
  };
78548
78689
  const kicadLayer = layerMap[outline.layer] || "F.CrtYd";
78549
78690
  const xyPoints = outline.outline.map((point) => {
78550
- const transformedPoint = applyToPoint15(cj2kicadMatrix, point);
78551
- return new Xy6(transformedPoint.x, transformedPoint.y);
78691
+ const transformedPoint = applyToPoint16(cj2kicadMatrix, point);
78692
+ return new Xy7(transformedPoint.x, transformedPoint.y);
78552
78693
  });
78553
78694
  const fpPoly = new FpPoly5;
78554
- fpPoly.points = new Pts6(xyPoints);
78695
+ fpPoly.points = new Pts7(xyPoints);
78555
78696
  fpPoly.layer = kicadLayer;
78556
78697
  fpPoly.fill = false;
78557
78698
  fpPoly.uuid = generateDeterministicUuid(outline.pcb_courtyard_outline_id);
@@ -78621,7 +78762,7 @@ function createFpTextFromCircuitJson({
78621
78762
  const relativeX = textElement.anchor_position.x - componentCenter.x;
78622
78763
  const relativeY = -(textElement.anchor_position.y - componentCenter.y);
78623
78764
  const rotationMatrix = componentRotation !== 0 ? rotate5(componentRotation * Math.PI / 180) : identity2();
78624
- const rotatedPos = applyToPoint16(rotationMatrix, {
78765
+ const rotatedPos = applyToPoint17(rotationMatrix, {
78625
78766
  x: relativeX,
78626
78767
  y: relativeY
78627
78768
  });
@@ -78702,11 +78843,11 @@ function convertSilkscreenPaths(silkscreenPaths, { componentCenter, componentRot
78702
78843
  const endPoint = path.route[i + 1];
78703
78844
  if (!startPoint || !endPoint)
78704
78845
  continue;
78705
- const startRelative = applyToPoint17(rotationMatrix, {
78846
+ const startRelative = applyToPoint18(rotationMatrix, {
78706
78847
  x: startPoint.x - componentCenter.x,
78707
78848
  y: -(startPoint.y - componentCenter.y)
78708
78849
  });
78709
- const endRelative = applyToPoint17(rotationMatrix, {
78850
+ const endRelative = applyToPoint18(rotationMatrix, {
78710
78851
  x: endPoint.x - componentCenter.x,
78711
78852
  y: -(endPoint.y - componentCenter.y)
78712
78853
  });
@@ -78756,13 +78897,13 @@ function convertSilkscreenRects({
78756
78897
  componentCenter,
78757
78898
  componentRotation = 0
78758
78899
  }) {
78759
- const toLocal = compose8(rotate7(componentRotation * Math.PI / 180), scale5(1, -1), translate8(-componentCenter.x, -componentCenter.y));
78900
+ const toLocal = compose9(rotate7(componentRotation * Math.PI / 180), scale6(1, -1), translate9(-componentCenter.x, -componentCenter.y));
78760
78901
  return silkscreenRects.flatMap((rect) => {
78761
78902
  const hasStroke = rect.has_stroke ?? rect.stroke_width > 0;
78762
78903
  if (!hasStroke && !rect.is_filled)
78763
78904
  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)));
78905
+ const fromRectCenter = compose9(translate9(rect.center.x, rect.center.y), rotate7((rect.ccw_rotation ?? 0) * Math.PI / 180));
78906
+ const points = getRectanglePoints(rect).map((point) => applyToPoint19(toLocal, applyToPoint19(fromRectCenter, point)));
78766
78907
  const stroke = hasStroke ? new Stroke11 : undefined;
78767
78908
  if (stroke) {
78768
78909
  stroke.width = rect.stroke_width;
@@ -78786,7 +78927,7 @@ function convertNoteTexts(params2) {
78786
78927
  const relX = textElement.anchor_position.x - componentCenter.x;
78787
78928
  const relY = -(textElement.anchor_position.y - componentCenter.y);
78788
78929
  const rotationMatrix = componentRotation !== 0 ? rotate8(componentRotation * Math.PI / 180) : identity4();
78789
- const rotatedPos = applyToPoint19(rotationMatrix, { x: relX, y: relY });
78930
+ const rotatedPos = applyToPoint20(rotationMatrix, { x: relX, y: relY });
78790
78931
  const fontSize = textElement.font_size || 1;
78791
78932
  const font = new TextEffectsFont11;
78792
78933
  font.size = { width: fontSize, height: fontSize };
@@ -78900,15 +79041,47 @@ function create3DModelsFromCadComponent(cadComponent, componentCenter, options)
78900
79041
  model.offset = transform.offset;
78901
79042
  model.rotate = transform.rotation;
78902
79043
  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 };
79044
+ const scale9 = cadComponent.model_unit_to_mm_scale_factor;
79045
+ model.scale = { x: scale9, y: scale9, z: scale9 };
78905
79046
  }
78906
79047
  models.push(model);
78907
79048
  return models;
78908
79049
  }
79050
+ var getResolvedPillRadius = ({
79051
+ width,
79052
+ height,
79053
+ radius
79054
+ }) => typeof radius === "number" && Number.isFinite(radius) ? radius : Math.min(width, height) / 2;
79055
+ var getKicadNativePadGeometry = (pad) => {
79056
+ switch (pad.shape) {
79057
+ case "circle":
79058
+ return {
79059
+ shape: "circle",
79060
+ size: [pad.radius * 2, pad.radius * 2],
79061
+ rotation: 0
79062
+ };
79063
+ case "rect":
79064
+ case "rotated_rect": {
79065
+ const cornerRadius = pad.corner_radius ?? pad.rect_border_radius;
79066
+ return {
79067
+ shape: cornerRadius ? "roundrect" : "rect",
79068
+ size: [pad.width, pad.height],
79069
+ rotation: pad.shape === "rotated_rect" ? pad.ccw_rotation ?? 0 : 0,
79070
+ roundrectRatio: cornerRadius ? cornerRadius / Math.min(pad.width, pad.height) : undefined
79071
+ };
79072
+ }
79073
+ case "pill":
79074
+ case "rotated_pill":
79075
+ return {
79076
+ shape: "roundrect",
79077
+ size: [pad.width, pad.height],
79078
+ rotation: pad.shape === "rotated_pill" ? pad.ccw_rotation ?? 0 : 0,
79079
+ roundrectRatio: getResolvedPillRadius(pad) / Math.min(pad.width, pad.height)
79080
+ };
79081
+ }
79082
+ };
78909
79083
  function createSmdPadFromCircuitJson({
78910
79084
  pcbPad,
78911
- hasSolderPaste,
78912
79085
  componentCenter,
78913
79086
  padNumber,
78914
79087
  componentRotation = 0,
@@ -78927,8 +79100,8 @@ function createSmdPadFromCircuitJson({
78927
79100
  } else {
78928
79101
  throw new Error("Pad must have either x/y coordinates or points array");
78929
79102
  }
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, {
79103
+ 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));
79104
+ const rotatedPos = applyToPoint21(cj2kicadMatrix, {
78932
79105
  x: padX,
78933
79106
  y: padY
78934
79107
  });
@@ -78943,57 +79116,21 @@ function createSmdPadFromCircuitJson({
78943
79116
  let padPrimitives;
78944
79117
  let roundrect_rratio;
78945
79118
  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);
79119
+ if (pcbPad.shape === "polygon") {
79120
+ padPrimitives = createFilledPolygonPrimitives({
79121
+ contours: [pcbPad.points],
79122
+ center: { x: padX, y: padY }
78958
79123
  });
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
79124
  padShape = "custom";
78966
79125
  padOptions = new PadOptions;
78967
79126
  padOptions.anchor = "circle";
78968
79127
  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];
79128
+ } else {
79129
+ const geometry = getKicadNativePadGeometry(pcbPad);
79130
+ padShape = geometry.shape;
79131
+ padSize = geometry.size;
79132
+ rotation = geometry.rotation;
79133
+ roundrect_rratio = geometry.roundrectRatio;
78997
79134
  }
78998
79135
  const padData = `pad:${componentId}:${padNumber}:${rotatedPos.x},${rotatedPos.y}`;
78999
79136
  const pad = new FootprintPad({
@@ -79004,7 +79141,6 @@ function createSmdPadFromCircuitJson({
79004
79141
  size: padSize,
79005
79142
  layers: [
79006
79143
  `${padLayer}`,
79007
- ...hasSolderPaste ? [`${padLayer === "F.Cu" ? "F" : "B"}.Paste`] : [],
79008
79144
  ...pcbPad.is_covered_with_solder_mask ? [] : [`${padLayer === "F.Cu" ? "F" : "B"}.Mask`]
79009
79145
  ],
79010
79146
  solderMaskMargin: pcbPad.soldermask_margin,
@@ -79041,7 +79177,6 @@ function convertSmdPads({
79041
79177
  const resolvedPadNumber = getPhysicalPinName(sourcePort) ?? (sourcePort?.pin_number != null ? String(sourcePort.pin_number) : pinHint ? pinHint.replace(/^pin/i, "") : gridHint ?? String(padNumber));
79042
79178
  const pad = createSmdPadFromCircuitJson({
79043
79179
  pcbPad,
79044
- hasSolderPaste: ctx.db.pcb_solder_paste.list().some((paste) => paste.pcb_smtpad_id === pcbPad.pcb_smtpad_id),
79045
79180
  componentCenter,
79046
79181
  padNumber: resolvedPadNumber,
79047
79182
  componentRotation,
@@ -79067,7 +79202,7 @@ function createThruHolePadFromCircuitJson({
79067
79202
  const relativeX = platedHole.x - componentCenter.x;
79068
79203
  const relativeY = -(platedHole.y - componentCenter.y);
79069
79204
  const rotationMatrix = componentRotation !== 0 ? rotate10(componentRotation * Math.PI / 180) : identity5();
79070
- const rotatedPos = applyToPoint21(rotationMatrix, {
79205
+ const rotatedPos = applyToPoint22(rotationMatrix, {
79071
79206
  x: relativeX,
79072
79207
  y: relativeY
79073
79208
  });
@@ -79083,7 +79218,7 @@ function createThruHolePadFromCircuitJson({
79083
79218
  y: platedHole.hole_offset_y ?? 0
79084
79219
  };
79085
79220
  if (rawOffset.x !== 0 || rawOffset.y !== 0) {
79086
- const rotatedOffset = applyToPoint21(rotationMatrix, {
79221
+ const rotatedOffset = applyToPoint22(rotationMatrix, {
79087
79222
  x: -rawOffset.x,
79088
79223
  y: rawOffset.y
79089
79224
  });
@@ -79199,7 +79334,7 @@ function createNpthPadFromCircuitJson({
79199
79334
  const relativeX = pcbHole.x - componentCenter.x;
79200
79335
  const relativeY = -(pcbHole.y - componentCenter.y);
79201
79336
  const rotationMatrix = componentRotation !== 0 ? rotate11(componentRotation * Math.PI / 180) : identity6();
79202
- const rotatedPos = applyToPoint22(rotationMatrix, {
79337
+ const rotatedPos = applyToPoint23(rotationMatrix, {
79203
79338
  x: relativeX,
79204
79339
  y: relativeY
79205
79340
  });
@@ -79283,11 +79418,11 @@ function convertFabricationNotePaths(fabricationNotePaths, {
79283
79418
  const endPoint = fabricationNotePath.route[index + 1];
79284
79419
  if (!startPoint || !endPoint)
79285
79420
  continue;
79286
- const startRelative = applyToPoint23(rotationMatrix, {
79421
+ const startRelative = applyToPoint24(rotationMatrix, {
79287
79422
  x: startPoint.x - componentCenter.x,
79288
79423
  y: -(startPoint.y - componentCenter.y)
79289
79424
  });
79290
- const endRelative = applyToPoint23(rotationMatrix, {
79425
+ const endRelative = applyToPoint24(rotationMatrix, {
79291
79426
  x: endPoint.x - componentCenter.x,
79292
79427
  y: -(endPoint.y - componentCenter.y)
79293
79428
  });
@@ -79304,6 +79439,94 @@ function convertFabricationNotePaths(fabricationNotePaths, {
79304
79439
  }
79305
79440
  return fpLines;
79306
79441
  }
79442
+ var getSolderPasteCenter = (solderPaste) => {
79443
+ if ("x" in solderPaste && "y" in solderPaste) {
79444
+ return { x: solderPaste.x, y: solderPaste.y };
79445
+ }
79446
+ return {
79447
+ x: solderPaste.points.reduce((sum, point) => sum + point.x, 0) / solderPaste.points.length,
79448
+ y: solderPaste.points.reduce((sum, point) => sum + point.y, 0) / solderPaste.points.length
79449
+ };
79450
+ };
79451
+ var createSolderPastePadFromCircuitJson = ({
79452
+ solderPaste,
79453
+ componentCenter,
79454
+ componentRotation = 0
79455
+ }) => {
79456
+ const polygonAnchor = solderPaste.shape === "polygon" ? getSafePolygonAnchor(solderPaste.points, solderPaste.holes) : undefined;
79457
+ const center = polygonAnchor?.center ?? getSolderPasteCenter(solderPaste);
79458
+ const boardToFootprint = getBoardToFootprintTransform({
79459
+ footprintOrigin: componentCenter,
79460
+ ccwRotationDegrees: componentRotation
79461
+ });
79462
+ const position = applyToPoint25(boardToFootprint, center);
79463
+ let shape;
79464
+ let size;
79465
+ let rotation = 0;
79466
+ let roundrectRatio;
79467
+ let options;
79468
+ let customContours;
79469
+ if (solderPaste.shape === "oval") {
79470
+ shape = "custom";
79471
+ size = [
79472
+ Math.min(0.001, solderPaste.width, solderPaste.height),
79473
+ Math.min(0.001, solderPaste.width, solderPaste.height)
79474
+ ];
79475
+ options = new PadOptions2;
79476
+ options.anchor = "circle";
79477
+ customContours = [
79478
+ createEllipseContour({
79479
+ center,
79480
+ width: solderPaste.width,
79481
+ height: solderPaste.height
79482
+ })
79483
+ ];
79484
+ } else if (solderPaste.shape === "polygon") {
79485
+ if (!polygonAnchor) {
79486
+ throw new Error("Solder-paste polygon is missing its anchor geometry");
79487
+ }
79488
+ shape = "custom";
79489
+ size = [polygonAnchor.diameter, polygonAnchor.diameter];
79490
+ options = new PadOptions2;
79491
+ options.anchor = "circle";
79492
+ customContours = solderPaste.holes?.length ? polygonAnchor.triangles : [solderPaste.points];
79493
+ } else {
79494
+ const geometry = getKicadNativePadGeometry(solderPaste);
79495
+ shape = geometry.shape;
79496
+ size = geometry.size;
79497
+ rotation = geometry.rotation;
79498
+ roundrectRatio = geometry.roundrectRatio;
79499
+ }
79500
+ const layer = solderPaste.layer === "bottom" ? "B.Paste" : "F.Paste";
79501
+ const pad = new FootprintPad4({
79502
+ number: "",
79503
+ padType: "smd",
79504
+ shape,
79505
+ at: [position.x, position.y, rotation],
79506
+ size,
79507
+ layers: [layer],
79508
+ roundrectRatio,
79509
+ uuid: generateDeterministicUuid(`solder_paste:${solderPaste.pcb_solder_paste_id}`)
79510
+ });
79511
+ if (options)
79512
+ pad.options = options;
79513
+ if (customContours) {
79514
+ pad.primitives = createFilledPolygonPrimitives({
79515
+ contours: customContours,
79516
+ center
79517
+ });
79518
+ }
79519
+ return pad;
79520
+ };
79521
+ var convertSolderPaste = ({
79522
+ solderPaste,
79523
+ componentCenter,
79524
+ componentRotation = 0
79525
+ }) => solderPaste.map((aperture) => createSolderPastePadFromCircuitJson({
79526
+ solderPaste: aperture,
79527
+ componentCenter,
79528
+ componentRotation
79529
+ }));
79307
79530
  var AddFootprintsStage = class extends ConverterStage {
79308
79531
  componentsProcessed = 0;
79309
79532
  pcbComponents = [];
@@ -79379,7 +79602,7 @@ var AddFootprintsStage = class extends ConverterStage {
79379
79602
  const cadComponent = this.getCadComponentForPcbComponent(component.pcb_component_id);
79380
79603
  const footprintName = sourceComponent ? getKicadCompatibleComponentName(sourceComponent, cadComponent) : "Unknown";
79381
79604
  const footprintOrigin = component.footprint_origin ?? component.center;
79382
- const transformedPos = applyToPoint24(c2kMatPcb, {
79605
+ const transformedPos = applyToPoint26(c2kMatPcb, {
79383
79606
  x: footprintOrigin.x,
79384
79607
  y: footprintOrigin.y
79385
79608
  });
@@ -79429,6 +79652,18 @@ var AddFootprintsStage = class extends ConverterStage {
79429
79652
  getNetInfo
79430
79653
  }, this.ctx);
79431
79654
  fpPads.push(...smdPads);
79655
+ const pcbSolderPaste = this.ctx.db.pcb_solder_paste?.list().filter((paste) => {
79656
+ if (paste.pcb_component_id === component.pcb_component_id) {
79657
+ return true;
79658
+ }
79659
+ const linkedPad = paste.pcb_smtpad_id ? this.ctx.db.pcb_smtpad.get(paste.pcb_smtpad_id) : undefined;
79660
+ return !paste.pcb_component_id && linkedPad?.pcb_component_id === component.pcb_component_id;
79661
+ }) || [];
79662
+ fpPads.push(...convertSolderPaste({
79663
+ solderPaste: pcbSolderPaste,
79664
+ componentCenter: footprintOrigin,
79665
+ componentRotation: component.rotation || 0
79666
+ }));
79432
79667
  const pcbPlatedHoles = this.ctx.db.pcb_plated_hole?.list().filter((hole) => hole.pcb_component_id === component.pcb_component_id) || [];
79433
79668
  const { pads: thruHolePads } = convertPlatedHoles({
79434
79669
  platedHoles: pcbPlatedHoles,
@@ -79628,8 +79863,8 @@ var AddTracesStage = class extends ConverterStage {
79628
79863
  if (!startPosition || !endPosition) {
79629
79864
  throw new Error(`Unable to convert pcb_trace route segment ${trace.pcb_trace_id ?? this.tracesProcessed}:${i} to KiCad segment`);
79630
79865
  }
79631
- const transformedStart = applyToPoint25(c2kMatPcb, startPosition);
79632
- const transformedEnd = applyToPoint25(c2kMatPcb, endPosition);
79866
+ const transformedStart = applyToPoint27(c2kMatPcb, startPosition);
79867
+ const transformedEnd = applyToPoint27(c2kMatPcb, endPosition);
79633
79868
  if (transformedStart.x === transformedEnd.x && transformedStart.y === transformedEnd.y) {
79634
79869
  continue;
79635
79870
  }
@@ -79712,6 +79947,11 @@ var createViaTenting = (via, board) => {
79712
79947
  };
79713
79948
  var DEFAULT_VIA_OUTER_DIAMETER = 0.8;
79714
79949
  var DEFAULT_VIA_HOLE_DIAMETER = 0.4;
79950
+ var VIA_POSITION_TOLERANCE = 0.000001;
79951
+ var getViaPositionCell = (via) => ({
79952
+ x: Math.floor(via.x / VIA_POSITION_TOLERANCE),
79953
+ y: Math.floor(via.y / VIA_POSITION_TOLERANCE)
79954
+ });
79715
79955
  var AddViasStage = class extends ConverterStage {
79716
79956
  boardOwnerMap;
79717
79957
  viasProcessed = 0;
@@ -79725,7 +79965,8 @@ var AddViasStage = class extends ConverterStage {
79725
79965
  const seenRouteViaKeys = /* @__PURE__ */ new Set;
79726
79966
  const standaloneViasByPosition = /* @__PURE__ */ new Map;
79727
79967
  for (const via of standaloneVias) {
79728
- const positionKey = `${via.x}:${via.y}`;
79968
+ const { x, y } = getViaPositionCell(via);
79969
+ const positionKey = `${x}:${y}`;
79729
79970
  const viasAtPosition = standaloneViasByPosition.get(positionKey) ?? [];
79730
79971
  viasAtPosition.push(via);
79731
79972
  standaloneViasByPosition.set(positionKey, viasAtPosition);
@@ -79744,8 +79985,14 @@ var AddViasStage = class extends ConverterStage {
79744
79985
  tented_on_top: point.tented_on_top,
79745
79986
  tented_on_bottom: point.tented_on_bottom
79746
79987
  })).filter((via) => {
79747
- const physicalVias = standaloneViasByPosition.get(`${via.x}:${via.y}`);
79748
- if (physicalVias?.some((physicalVia) => {
79988
+ const { x, y } = getViaPositionCell(via);
79989
+ const physicalVias = [];
79990
+ for (let dx = -1;dx <= 1; dx++) {
79991
+ for (let dy = -1;dy <= 1; dy++) {
79992
+ physicalVias.push(...standaloneViasByPosition.get(`${x + dx}:${y + dy}`) ?? []);
79993
+ }
79994
+ }
79995
+ if (physicalVias.some((physicalVia) => {
79749
79996
  const sameTrace = physicalVia.pcb_trace_id !== undefined && physicalVia.pcb_trace_id === via.pcb_trace_id;
79750
79997
  const physicalConnection = physicalVia.net?.id ?? physicalVia.subcircuit_connectivity_map_key;
79751
79998
  const routeConnection = via.net?.id ?? via.subcircuit_connectivity_map_key;
@@ -79755,7 +80002,11 @@ var AddViasStage = class extends ConverterStage {
79755
80002
  } else if (!sameTrace) {
79756
80003
  return false;
79757
80004
  }
79758
- return isRouteViaCoveredByPhysicalVia(physicalVia, via);
80005
+ return isRouteViaCoveredByPhysicalVia({
80006
+ viaWithLayers: physicalVia,
80007
+ routeVia: via,
80008
+ positionTolerance: VIA_POSITION_TOLERANCE
80009
+ });
79759
80010
  })) {
79760
80011
  return false;
79761
80012
  }
@@ -79814,10 +80065,10 @@ var AddViasStage = class extends ConverterStage {
79814
80065
  this.finished = true;
79815
80066
  return;
79816
80067
  }
79817
- this.boardOwnerMap ??= createBoardOwnerMap(this.input);
80068
+ this.boardOwnerMap ??= createBoardOwnerMap2(this.input);
79818
80069
  const boardOwnerId = via.pcb_via_id ?? via.pcb_trace_id;
79819
80070
  const board = boardOwnerId ? this.boardOwnerMap.get(boardOwnerId) : undefined;
79820
- const transformedPos = applyToPoint26(c2kMatPcb, {
80071
+ const transformedPos = applyToPoint28(c2kMatPcb, {
79821
80072
  x: via.x,
79822
80073
  y: via.y
79823
80074
  });
@@ -79843,6 +80094,34 @@ var AddViasStage = class extends ConverterStage {
79843
80094
  return this.ctx.kicadPcb;
79844
80095
  }
79845
80096
  };
80097
+ var normalizePolygonPoints = (points, origin) => points.map((point) => ({
80098
+ x: point.x - origin.x,
80099
+ y: point.y - origin.y
80100
+ }));
80101
+ var getSolderPasteLibraryLink = (paste) => {
80102
+ const prefix = `tscircuit:solderpaste_${paste.layer}_${paste.shape}`;
80103
+ switch (paste.shape) {
80104
+ case "circle":
80105
+ return `${prefix}_diameter${paste.radius * 2}mm`;
80106
+ case "rect":
80107
+ case "oval":
80108
+ return `${prefix}_width${paste.width}mm_height${paste.height}mm`;
80109
+ case "rotated_rect":
80110
+ return `${prefix}_width${paste.width}mm_height${paste.height}mm_ccwRotation${paste.ccw_rotation}deg`;
80111
+ case "pill":
80112
+ return `${prefix}_width${paste.width}mm_height${paste.height}mm_radius${getResolvedPillRadius(paste)}mm`;
80113
+ case "rotated_pill":
80114
+ return `${prefix}_width${paste.width}mm_height${paste.height}mm_radius${getResolvedPillRadius(paste)}mm_ccwRotation${paste.ccw_rotation}deg`;
80115
+ case "polygon": {
80116
+ const origin = paste.points[0] ?? { x: 0, y: 0 };
80117
+ const geometry = JSON.stringify({
80118
+ points: normalizePolygonPoints(paste.points, origin),
80119
+ holes: paste.holes?.map((hole) => normalizePolygonPoints(hole, origin))
80120
+ });
80121
+ return `${prefix}_${generateDeterministicUuid(geometry)}`;
80122
+ }
80123
+ }
80124
+ };
79846
80125
  var AddStandalonePcbElements = class extends ConverterStage {
79847
80126
  unprocessedElements = [];
79848
80127
  constructor(input, ctx) {
@@ -79850,7 +80129,8 @@ var AddStandalonePcbElements = class extends ConverterStage {
79850
80129
  this.unprocessedElements = [
79851
80130
  ...this.ctx.db.pcb_hole.list().filter((hole) => !hole.pcb_component_id),
79852
80131
  ...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)
80132
+ ...this.ctx.db.pcb_smtpad.list().filter((pad) => !pad.pcb_component_id),
80133
+ ...this.ctx.db.pcb_solder_paste.list().filter((paste) => !paste.pcb_component_id && !paste.pcb_smtpad_id)
79854
80134
  ];
79855
80135
  }
79856
80136
  _step() {
@@ -79870,21 +80150,45 @@ var AddStandalonePcbElements = class extends ConverterStage {
79870
80150
  const pcbPad = elm;
79871
80151
  const padCenter = this.getPcbSmtPadCenter(pcbPad);
79872
80152
  const footprintSeed = `standalone_smtpad:${pcbPad.pcb_smtpad_id}:${padCenter.x},${padCenter.y}`;
79873
- const kicadPos = applyToPoint27(c2kMatPcb, padCenter);
80153
+ const kicadPos = applyToPoint29(c2kMatPcb, padCenter);
79874
80154
  const footprint = new Footprint5({
79875
80155
  libraryLink: this.getSmtPadLibraryLink(pcbPad),
79876
80156
  layer: "F.Cu",
79877
80157
  at: [kicadPos.x, kicadPos.y, 0],
79878
80158
  uuid: generateDeterministicUuid(footprintSeed)
79879
80159
  });
80160
+ const linkedSolderPaste = this.ctx.db.pcb_solder_paste.list().filter((paste) => paste.pcb_smtpad_id === pcbPad.pcb_smtpad_id);
79880
80161
  footprint.fpPads = [
79881
80162
  createSmdPadFromCircuitJson({
79882
80163
  pcbPad,
79883
- hasSolderPaste: this.ctx.db.pcb_solder_paste.list().some((paste) => paste.pcb_smtpad_id === pcbPad.pcb_smtpad_id),
79884
80164
  componentCenter: padCenter,
79885
80165
  padNumber: 1,
79886
80166
  componentRotation: 0,
79887
80167
  componentId: pcbPad.pcb_smtpad_id
80168
+ }),
80169
+ ...linkedSolderPaste.map((solderPaste) => createSolderPastePadFromCircuitJson({
80170
+ solderPaste,
80171
+ componentCenter: padCenter
80172
+ }))
80173
+ ];
80174
+ applyInferredFootprintType(footprint);
80175
+ const footprints = kicadPcb.footprints;
80176
+ footprints.push(footprint);
80177
+ kicadPcb.footprints = footprints;
80178
+ } else if (elm.type === "pcb_solder_paste") {
80179
+ const center = getSolderPasteCenter(elm);
80180
+ const footprintSeed = `standalone_solder_paste:${elm.pcb_solder_paste_id}`;
80181
+ const kicadPos = applyToPoint29(c2kMatPcb, center);
80182
+ const footprint = new Footprint5({
80183
+ libraryLink: getSolderPasteLibraryLink(elm),
80184
+ layer: elm.layer === "bottom" ? "B.Cu" : "F.Cu",
80185
+ at: [kicadPos.x, kicadPos.y, 0],
80186
+ uuid: generateDeterministicUuid(footprintSeed)
80187
+ });
80188
+ footprint.fpPads = [
80189
+ createSolderPastePadFromCircuitJson({
80190
+ solderPaste: elm,
80191
+ componentCenter: center
79888
80192
  })
79889
80193
  ];
79890
80194
  applyInferredFootprintType(footprint);
@@ -79894,7 +80198,7 @@ var AddStandalonePcbElements = class extends ConverterStage {
79894
80198
  } else if (elm.type === "pcb_hole") {
79895
80199
  const hole = elm;
79896
80200
  const footprintSeed = `standalone_hole:${hole.pcb_hole_id}:${hole.x},${hole.y}`;
79897
- const kicadPos = applyToPoint27(c2kMatPcb, { x: hole.x, y: hole.y });
80201
+ const kicadPos = applyToPoint29(c2kMatPcb, { x: hole.x, y: hole.y });
79898
80202
  const libraryLink = this.getHoleLibraryLink(hole);
79899
80203
  const footprint = new Footprint5({
79900
80204
  libraryLink,
@@ -79917,7 +80221,7 @@ var AddStandalonePcbElements = class extends ConverterStage {
79917
80221
  } else if (elm.type === "pcb_plated_hole") {
79918
80222
  const hole = elm;
79919
80223
  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 });
80224
+ const kicadPos = applyToPoint29(c2kMatPcb, { x: hole.x, y: hole.y });
79921
80225
  const libraryLink = this.getPlatedHoleLibraryLink(hole);
79922
80226
  const footprint = new Footprint5({
79923
80227
  libraryLink,
@@ -80015,7 +80319,7 @@ function createFabricationNoteTextFromCircuitJson({
80015
80319
  if (!textElement.text || !textElement.anchor_position) {
80016
80320
  return null;
80017
80321
  }
80018
- const transformedPos = applyToPoint28(c2kMatPcb, {
80322
+ const transformedPos = applyToPoint30(c2kMatPcb, {
80019
80323
  x: textElement.anchor_position.x,
80020
80324
  y: textElement.anchor_position.y
80021
80325
  });
@@ -80056,7 +80360,8 @@ function createFabricationNoteTextFromCircuitJson({
80056
80360
  if (anchorAlignment !== "center") {
80057
80361
  textEffects.justify = justify;
80058
80362
  }
80059
- const position = new At3([transformedPos.x, transformedPos.y, 0]);
80363
+ const rotation = textElement.type === "pcb_fabrication_note_text" ? textElement.ccw_rotation ?? 0 : 0;
80364
+ const position = new At3([transformedPos.x, transformedPos.y, rotation]);
80060
80365
  const grText = new GrText({
80061
80366
  text: textElement.text,
80062
80367
  layer: kicadLayer,
@@ -80073,7 +80378,7 @@ function createGrTextFromCircuitJson({
80073
80378
  if (!textElement.text || !textElement.anchor_position) {
80074
80379
  return null;
80075
80380
  }
80076
- const transformedPos = applyToPoint29(c2kMatPcb, {
80381
+ const transformedPos = applyToPoint31(c2kMatPcb, {
80077
80382
  x: textElement.anchor_position.x,
80078
80383
  y: textElement.anchor_position.y
80079
80384
  });
@@ -80110,6 +80415,9 @@ function createGrTextFromCircuitJson({
80110
80415
  grText.position = position;
80111
80416
  return grText;
80112
80417
  }
80418
+ var KICAD_PCB_UNITS_PER_MM = 1e6;
80419
+ var roundBoardCoordinate = (coordinateMm) => Math.round(coordinateMm * KICAD_PCB_UNITS_PER_MM) / KICAD_PCB_UNITS_PER_MM;
80420
+ var roundPolygonToKicadPrecision = (geometry) => geometry.map((polygon) => polygon.map((ring) => ring.map(([x, y]) => [roundBoardCoordinate(x), roundBoardCoordinate(y)])));
80113
80421
  var pointsAreEqual = (a, b) => !!a && !!b && a.x === b.x && a.y === b.y;
80114
80422
  var normalizeOutlineCorners = (corners) => {
80115
80423
  const dedupedCorners = [];
@@ -80125,6 +80433,13 @@ var normalizeOutlineCorners = (corners) => {
80125
80433
  return dedupedCorners;
80126
80434
  };
80127
80435
  var DEFAULT_EDGE_CUTS_WIDTH = 0.1;
80436
+ var toKicadPoint = (point, c2kMatPcb) => {
80437
+ const transformed = applyToPoint32(c2kMatPcb, point);
80438
+ return [
80439
+ roundBoardCoordinate(transformed.x),
80440
+ roundBoardCoordinate(transformed.y)
80441
+ ];
80442
+ };
80128
80443
  var appendGraphicLine = (kicadPcb, grLine) => {
80129
80444
  const graphicLines = kicadPcb.graphicLines;
80130
80445
  graphicLines.push(grLine);
@@ -80133,18 +80448,10 @@ var appendGraphicLine = (kicadPcb, grLine) => {
80133
80448
  var rotatePointAroundOrigin = (point, rotationDegrees = 0) => {
80134
80449
  if (!rotationDegrees)
80135
80450
  return point;
80136
- return applyToPoint30(rotate13(rotationDegrees * Math.PI / 180), point);
80451
+ return applyToPoint32(rotate13(rotationDegrees * Math.PI / 180), point);
80137
80452
  };
80138
80453
  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) => {
80454
+ return getRectanglePoints(cutout).map((point) => {
80148
80455
  const rotatedPoint = rotatePointAroundOrigin(point, cutout.rotation);
80149
80456
  return {
80150
80457
  x: rotatedPoint.x + cutout.center.x,
@@ -80171,11 +80478,11 @@ var AddGraphicsStage = class extends ConverterStage {
80171
80478
  const endPoint = path.route[i + 1];
80172
80479
  if (!startPoint || !endPoint)
80173
80480
  continue;
80174
- const transformedStart = applyToPoint30(c2kMatPcb, {
80481
+ const transformedStart = applyToPoint32(c2kMatPcb, {
80175
80482
  x: startPoint.x,
80176
80483
  y: startPoint.y
80177
80484
  });
80178
- const transformedEnd = applyToPoint30(c2kMatPcb, {
80485
+ const transformedEnd = applyToPoint32(c2kMatPcb, {
80179
80486
  x: endPoint.x,
80180
80487
  y: endPoint.y
80181
80488
  });
@@ -80221,15 +80528,14 @@ var AddGraphicsStage = class extends ConverterStage {
80221
80528
  kicadPcb.graphicTexts = graphicTexts;
80222
80529
  }
80223
80530
  }
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
- }
80531
+ const outlineElements = [
80532
+ ...this.ctx.db.pcb_panel.list(),
80533
+ ...this.ctx.db.pcb_board.list()
80534
+ ];
80535
+ const outlinePolys = [];
80536
+ for (const board of outlineElements) {
80231
80537
  let corners;
80232
- if (board.outline && board.outline.length > 0) {
80538
+ if (board.type === "pcb_board" && board.outline && board.outline.length > 0) {
80233
80539
  corners = normalizeOutlineCorners(board.outline);
80234
80540
  } else {
80235
80541
  const halfWidth = board.width ? board.width / 2 : 0;
@@ -80241,25 +80547,43 @@ var AddGraphicsStage = class extends ConverterStage {
80241
80547
  { x: board.center.x - halfWidth, y: board.center.y + halfHeight }
80242
80548
  ];
80243
80549
  }
80550
+ outlinePolys.push([
80551
+ [corners.map((point) => toKicadPoint(point, c2kMatPcb))]
80552
+ ]);
80553
+ }
80554
+ const [firstOutline, ...otherOutlines] = outlinePolys;
80555
+ if (firstOutline) {
80244
80556
  const pcbCutouts2 = this.ctx.db.pcb_cutout?.list() || [];
80245
80557
  const cutoutPolys = [];
80246
80558
  for (const cutout of pcbCutouts2) {
80247
80559
  if (cutout.shape === "rect") {
80248
80560
  const cutoutCorners = getRectCutoutCorners(cutout);
80249
- cutoutPolys.push([[cutoutCorners.map((c) => [c.x, c.y])]]);
80561
+ cutoutPolys.push([
80562
+ [cutoutCorners.map((point) => toKicadPoint(point, c2kMatPcb))]
80563
+ ]);
80250
80564
  } else if (cutout.shape === "circle") {
80251
- cutoutPolys.push([[circleToPolygon(cutout.center, cutout.radius)]]);
80565
+ cutoutPolys.push([
80566
+ [
80567
+ circleToPolygon(cutout.center, cutout.radius).map(([x, y]) => toKicadPoint({ x, y }, c2kMatPcb))
80568
+ ]
80569
+ ]);
80252
80570
  } else if (cutout.shape === "polygon") {
80253
- cutoutPolys.push([[cutout.points.map((c) => [c.x, c.y])]]);
80571
+ cutoutPolys.push([
80572
+ [cutout.points.map((point) => toKicadPoint(point, c2kMatPcb))]
80573
+ ]);
80254
80574
  }
80255
80575
  }
80256
- let boardGeom = [[corners.map((c) => [c.x, c.y])]];
80576
+ let boardGeom = otherOutlines.length > 0 ? import_polygon_clipping.default.union(firstOutline, ...otherOutlines) : firstOutline;
80257
80577
  if (cutoutPolys.length > 0) {
80258
- boardGeom = import_polygon_clipping.default.difference(boardGeom, ...cutoutPolys);
80578
+ boardGeom = import_polygon_clipping.default.difference(roundPolygonToKicadPrecision(boardGeom), ...cutoutPolys);
80259
80579
  }
80580
+ boardGeom = roundPolygonToKicadPrecision(boardGeom);
80260
80581
  for (const poly of boardGeom) {
80261
80582
  for (const ring of poly) {
80262
- const transformedRing = ring.map((point) => applyToPoint30(c2kMatPcb, { x: point[0], y: point[1] }));
80583
+ const transformedRing = ring.map(([x, y]) => ({
80584
+ x,
80585
+ y
80586
+ }));
80263
80587
  for (let i = 0;i < transformedRing.length; i++) {
80264
80588
  const start = transformedRing[i];
80265
80589
  const end = transformedRing[(i + 1) % transformedRing.length];
@@ -80288,8 +80612,8 @@ var AddGraphicsStage = class extends ConverterStage {
80288
80612
  const endPoint = cutout.route[i + 1];
80289
80613
  if (!startPoint || !endPoint)
80290
80614
  continue;
80291
- const transformedStart = applyToPoint30(c2kMatPcb, startPoint);
80292
- const transformedEnd = applyToPoint30(c2kMatPcb, endPoint);
80615
+ const transformedStart = applyToPoint32(c2kMatPcb, startPoint);
80616
+ const transformedEnd = applyToPoint32(c2kMatPcb, endPoint);
80293
80617
  appendGraphicLine(kicadPcb, new GrLine({
80294
80618
  start: transformedStart,
80295
80619
  end: transformedEnd,
@@ -80324,7 +80648,7 @@ var CircuitJsonToKicadPcbConverter = class {
80324
80648
  generator: "circuit-json-to-kicad",
80325
80649
  generatorVersion: "0.0.1"
80326
80650
  }),
80327
- c2kMatPcb: compose10(translate10(KICAD_PCB_CENTER_X, KICAD_PCB_CENTER_Y), scale7(CIRCUIT_JSON_TO_MM_SCALE, -CIRCUIT_JSON_TO_MM_SCALE)),
80651
+ c2kMatPcb: compose11(translate11(KICAD_PCB_CENTER_X, KICAD_PCB_CENTER_Y), scale8(CIRCUIT_JSON_TO_MM_SCALE, -CIRCUIT_JSON_TO_MM_SCALE)),
80328
80652
  projectName: options?.projectName,
80329
80653
  edgeCutsWidth: options?.edgeCutsWidth,
80330
80654
  pcbModel3dSourcePaths: []
@@ -80410,6 +80734,46 @@ var CircuitJsonToKicadProConverter = class {
80410
80734
  trackWidth: minTrackWidth
80411
80735
  })
80412
80736
  ];
80737
+ const sourceNets = this.ctx.db.source_net?.list() ?? [];
80738
+ const netClassNameByTraceWidth = /* @__PURE__ */ new Map([
80739
+ [minTrackWidth, "Default"]
80740
+ ]);
80741
+ const explicitTraceWidths = Array.from(new Set(sourceNets.flatMap((sourceNet) => sourceNet.trace_width === undefined ? [] : [sourceNet.trace_width]))).sort((a, b) => a - b);
80742
+ for (const traceWidth of explicitTraceWidths) {
80743
+ if (netClassNameByTraceWidth.has(traceWidth))
80744
+ continue;
80745
+ const netClassName = `Trace Width ${traceWidth} mm`;
80746
+ netClasses.push(this.createBaseNetClass({
80747
+ name: netClassName,
80748
+ viaDiameter: minViaDiameter,
80749
+ viaDrill: minViaDrill,
80750
+ clearance: minTraceToPadClearance,
80751
+ trackWidth: traceWidth
80752
+ }));
80753
+ netClassNameByTraceWidth.set(traceWidth, netClassName);
80754
+ }
80755
+ const netClassByNetName = /* @__PURE__ */ new Map;
80756
+ const boardOwnerMap = this.ctx.db.pcb_board.list().length > 1 ? createBoardOwnerMap3(this.ctx.circuitJson) : undefined;
80757
+ const sourceNetsWithTraceWidths = sourceNets.filter((sourceNet) => sourceNet.trace_width !== undefined).map((sourceNet) => {
80758
+ const connectivityKey = sourceNet.subcircuit_connectivity_map_key || sourceNet.source_net_id;
80759
+ return {
80760
+ sourceNet,
80761
+ netName: resolveKicadPcbNetName({
80762
+ candidateName: sourceNet.name || sourceNet.source_net_id,
80763
+ connectivityKey,
80764
+ boardId: boardOwnerMap?.get(sourceNet.subcircuit_id ?? "")?.pcb_board_id
80765
+ })
80766
+ };
80767
+ }).sort((a, b) => a.netName.localeCompare(b.netName) || a.sourceNet.source_net_id.localeCompare(b.sourceNet.source_net_id));
80768
+ for (const { sourceNet, netName } of sourceNetsWithTraceWidths) {
80769
+ const netClassName = netClassNameByTraceWidth.get(sourceNet.trace_width);
80770
+ const existingNetClassName = netClassByNetName.get(netName);
80771
+ if (existingNetClassName !== undefined && existingNetClassName !== netClassName) {
80772
+ throw new Error(`Cannot preserve conflicting trace widths for KiCad net "${netName}": ${existingNetClassName} and ${netClassName}`);
80773
+ }
80774
+ netClassByNetName.set(netName, netClassName);
80775
+ }
80776
+ const netclassPatterns = Array.from(netClassByNetName, ([pattern, netclass]) => ({ pattern, netclass }));
80413
80777
  this.project = {
80414
80778
  version: 1,
80415
80779
  head: {
@@ -80441,10 +80805,11 @@ var CircuitJsonToKicadProConverter = class {
80441
80805
  },
80442
80806
  net_settings: {
80443
80807
  meta: {
80444
- version: 1
80808
+ version: 3
80445
80809
  },
80446
- last_net_id: netClasses.length - 1,
80447
- classes: netClasses
80810
+ last_net_id: 0,
80811
+ classes: netClasses,
80812
+ netclass_patterns: netclassPatterns
80448
80813
  },
80449
80814
  pcbnew: {
80450
80815
  page_layout_descr_file: "",