@tscircuit/core 0.0.1894 → 0.0.1895

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.
Files changed (2) hide show
  1. package/dist/index.js +160 -52
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -18234,6 +18234,21 @@ function inflateSourceFiducial(sourceElm, inflatorContext) {
18234
18234
  }
18235
18235
  }
18236
18236
 
18237
+ // lib/components/primitive-components/Group/Group.ts
18238
+ import {
18239
+ convertSrjToGraphicsObject,
18240
+ getRerouteSimpleRouteJson,
18241
+ reconnectReroutedSimpleRouteJsonRegion
18242
+ } from "@tscircuit/capacity-autorouter";
18243
+ import { getBoundsFromPoints as getBoundsFromPoints9 } from "@tscircuit/math-utils";
18244
+ import {
18245
+ groupProps
18246
+ } from "@tscircuit/props";
18247
+ import {
18248
+ distance as distance15
18249
+ } from "circuit-json";
18250
+ import Debug15 from "debug";
18251
+
18237
18252
  // lib/utils/autorouting/with-fixed-fanout-traces.ts
18238
18253
  function withFixedFanoutTraces(input, fixedTraceIds) {
18239
18254
  if (fixedTraceIds.size === 0) return input;
@@ -18351,21 +18366,6 @@ function assignSchematicNetLabelSuperscripts(db) {
18351
18366
  }
18352
18367
  }
18353
18368
 
18354
- // lib/components/primitive-components/Group/Group.ts
18355
- import {
18356
- convertSrjToGraphicsObject,
18357
- getRerouteSimpleRouteJson,
18358
- reconnectReroutedSimpleRouteJsonRegion
18359
- } from "@tscircuit/capacity-autorouter";
18360
- import { getBoundsFromPoints as getBoundsFromPoints9 } from "@tscircuit/math-utils";
18361
- import {
18362
- groupProps
18363
- } from "@tscircuit/props";
18364
- import {
18365
- distance as distance15
18366
- } from "circuit-json";
18367
- import Debug15 from "debug";
18368
-
18369
18369
  // lib/components/base-components/is-assembly-device-container.ts
18370
18370
  var isAssemblyDeviceContainer = (component) => typeof component === "object" && component !== null && "isAssemblyDeviceContainer" in component && component.isAssemblyDeviceContainer === true;
18371
18371
 
@@ -21825,8 +21825,6 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
21825
21825
  const gridStep = params.gridStep ?? (alignGridToPitch ? pitch / (2 * Math.ceil(pitch / (2 * baseGridStep))) : baseGridStep);
21826
21826
  if (!Number.isFinite(gridStep) || gridStep <= 0) return [];
21827
21827
  const { minX, maxX, minY, maxY } = bus.sharedBoundary;
21828
- const exitAxis = bus.exitEdge === "left" || bus.exitEdge === "right" ? "x" : "y";
21829
- const exitCoordinate = bus.exitEdge === "left" ? minX : bus.exitEdge === "right" ? maxX : bus.exitEdge === "top" ? maxY : minY;
21830
21828
  const originX = params.gridOrigin?.x ?? bus.xCoordinates[0] ?? minX;
21831
21829
  const originY = params.gridOrigin?.y ?? bus.yCoordinates[0] ?? minY;
21832
21830
  const gridMinX = alignGridToPitch ? originX + Math.ceil((minX - originX) / gridStep) * gridStep : minX;
@@ -21966,8 +21964,9 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
21966
21964
  const segmentIsClear = (params2) => {
21967
21965
  const { segment: segment2, terminal, acceptedAttemptSegmentIndex } = params2;
21968
21966
  if (forbidEarlyExitBoundaryContact) {
21967
+ const boundary = bus.sharedBoundary;
21969
21968
  for (const point6 of [segment2.start, segment2.end])
21970
- if (Math.abs(point6[exitAxis] - exitCoordinate) < EPSILON6 && distance9(point6, terminal.exitPoint) > EPSILON6)
21969
+ if ((Math.abs(point6.x - boundary.minX) < EPSILON6 || Math.abs(point6.x - boundary.maxX) < EPSILON6 || Math.abs(point6.y - boundary.minY) < EPSILON6 || Math.abs(point6.y - boundary.maxY) < EPSILON6) && distance9(point6, terminal.exitPoint) > EPSILON6)
21971
21970
  return false;
21972
21971
  }
21973
21972
  const connectionName = terminal.connection.connection.name;
@@ -24627,7 +24626,8 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
24627
24626
  fixedViaFallbackRouteOrderAttempts = 24,
24628
24627
  allowFixedViaReservedExitFallback = false,
24629
24628
  cornerBandTargetTrackOffset,
24630
- cornerExitLaneOffset
24629
+ cornerExitLaneOffset,
24630
+ forbidEarlyExitBoundaryContact = false
24631
24631
  } = params;
24632
24632
  if (!Number.isInteger(maxAlternatives) || maxAlternatives < 1) {
24633
24633
  throw new Error(
@@ -24778,7 +24778,8 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
24778
24778
  gridStep: params.windingGridStep,
24779
24779
  alignGridToPads: alignWindingGridToPads,
24780
24780
  sourceEscapePaths: params.sourceEscapePaths,
24781
- heuristicWeight: params.windingHeuristicWeight
24781
+ heuristicWeight: params.windingHeuristicWeight,
24782
+ forbidEarlyExitBoundaryContact
24782
24783
  },
24783
24784
  maxAlternatives,
24784
24785
  includeVisualization
@@ -25187,7 +25188,8 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
25187
25188
  gridStepDivisor,
25188
25189
  gridStep: params.windingGridStep,
25189
25190
  sourceEscapePaths: params.sourceEscapePaths,
25190
- preferTargetDirectedLaneBias: terminalPattern.preferTargetDirectedLaneBias
25191
+ preferTargetDirectedLaneBias: terminalPattern.preferTargetDirectedLaneBias,
25192
+ forbidEarlyExitBoundaryContact
25191
25193
  },
25192
25194
  fixedViaPointsByConnectionIndex && viaMinimalOnly ? Math.min(2, Math.max(1, maxAlternatives - alternatives.length)) : terminalPattern.maximumRouteOrderAttempts === void 0 ? Math.min(2, maxAlternatives - alternatives.length) : 2,
25193
25195
  includeVisualization
@@ -25452,7 +25454,8 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
25452
25454
  reservedVias: bus.connections.length > 1 ? [...reservedVias, ...boundaryVias] : reservedVias,
25453
25455
  gridStepDivisor: 2,
25454
25456
  allowSourceLayerRouting: true,
25455
- alignGridToPads: true
25457
+ alignGridToPads: true,
25458
+ forbidEarlyExitBoundaryContact
25456
25459
  },
25457
25460
  1,
25458
25461
  includeVisualization
@@ -25495,7 +25498,8 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
25495
25498
  reservedVias,
25496
25499
  softReservedVias,
25497
25500
  gridStepDivisor: 2,
25498
- alignGridToPads: true
25501
+ alignGridToPads: true,
25502
+ forbidEarlyExitBoundaryContact
25499
25503
  },
25500
25504
  1,
25501
25505
  includeVisualization
@@ -27682,6 +27686,46 @@ function matchBusPlanLengths(params) {
27682
27686
  throw error;
27683
27687
  }
27684
27688
  }
27689
+ function getDeclaredPairPlan({
27690
+ plans,
27691
+ connectionIndex,
27692
+ connectionName
27693
+ }) {
27694
+ const matchingPlans = plans.filter(
27695
+ (plan2) => plan2.connectionIndex === connectionIndex
27696
+ );
27697
+ const plan = matchingPlans[0];
27698
+ if (matchingPlans.length !== 1 || !plan || plan.connectionName !== connectionName || !Number.isFinite(plan.length))
27699
+ throw new Error(
27700
+ `Differential-pair matching cannot resolve exactly one original plan for ${connectionName}`
27701
+ );
27702
+ const measuredLength = [
27703
+ ...plan.segments,
27704
+ ...plan.planeEndpointSegments ?? []
27705
+ ].reduce((sum, segment2) => sum + distance9(segment2.start, segment2.end), 0);
27706
+ if (!Number.isFinite(measuredLength) || Math.abs(measuredLength - plan.length) > EPSILON9)
27707
+ throw new Error(
27708
+ `Differential-pair matching found inconsistent copper length for ${plan.connectionName}`
27709
+ );
27710
+ return plan;
27711
+ }
27712
+ function getDeclaredPairPlans({
27713
+ plans,
27714
+ pair
27715
+ }) {
27716
+ return [
27717
+ getDeclaredPairPlan({
27718
+ plans,
27719
+ connectionIndex: pair.connectionIndices[0],
27720
+ connectionName: pair.connectionNames[0]
27721
+ }),
27722
+ getDeclaredPairPlan({
27723
+ plans,
27724
+ connectionIndex: pair.connectionIndices[1],
27725
+ connectionName: pair.connectionNames[1]
27726
+ })
27727
+ ];
27728
+ }
27685
27729
  function matchDeclaredPairLengths(params, originalPlans, pairs3, validatePlans, workBudget) {
27686
27730
  const byIndex = new Map(
27687
27731
  params.preparedBuses.flatMap(
@@ -27701,23 +27745,6 @@ function matchDeclaredPairLengths(params, originalPlans, pairs3, validatePlans,
27701
27745
  )
27702
27746
  }));
27703
27747
  let plans = originalPlans;
27704
- const pairPlans = (candidate, pair) => pair.connectionIndices.map((index, i) => {
27705
- const matches = candidate.filter((plan2) => plan2.connectionIndex === index);
27706
- const plan = matches[0];
27707
- if (matches.length !== 1 || plan?.connectionName !== pair.connectionNames[i] || !Number.isFinite(plan.length))
27708
- throw new Error(
27709
- `Differential-pair matching cannot resolve exactly one original plan for ${pair.connectionNames[i]}`
27710
- );
27711
- const measured = [
27712
- ...plan.segments,
27713
- ...plan.planeEndpointSegments ?? []
27714
- ].reduce((sum, segment2) => sum + distance9(segment2.start, segment2.end), 0);
27715
- if (!Number.isFinite(measured) || Math.abs(measured - plan.length) > EPSILON9)
27716
- throw new Error(
27717
- `Differential-pair matching found inconsistent copper length for ${plan.connectionName}`
27718
- );
27719
- return plan;
27720
- });
27721
27748
  const originalBusLimitsHold = (candidate) => scopedBuses.every(({ bus, indices }) => {
27722
27749
  if (bus.maxLengthSkew === void 0) return true;
27723
27750
  const own = getPlansForIndices(candidate, indices);
@@ -27725,7 +27752,7 @@ function matchDeclaredPairLengths(params, originalPlans, pairs3, validatePlans,
27725
27752
  });
27726
27753
  for (let pass = 0; pass <= activePairs.length; pass++) {
27727
27754
  for (const pair of activePairs) {
27728
- const own = pairPlans(plans, pair);
27755
+ const own = getDeclaredPairPlans({ plans, pair });
27729
27756
  if (getBusSkew(own) <= pair.lengthTolerance + EPSILON9) continue;
27730
27757
  const entries = pair.connectionIndices.map((index) => byIndex.get(index));
27731
27758
  const owner = entries.find((entry) => entry).bus;
@@ -27749,7 +27776,7 @@ function matchDeclaredPairLengths(params, originalPlans, pairs3, validatePlans,
27749
27776
  unroutedSourceBuses: void 0,
27750
27777
  allowUnconstrainedLaneRerouting: false,
27751
27778
  allowPairLaneSpreading: entries[0].bus === entries[1].bus && params.allowPairLaneSpreading,
27752
- candidatePlansAreFeasible: (candidate) => originalBusLimitsHold(candidate) && pairPlans(candidate, pair).every(
27779
+ candidatePlansAreFeasible: (candidate) => originalBusLimitsHold(candidate) && getDeclaredPairPlans({ plans: candidate, pair }).every(
27753
27780
  (plan) => plan.length <= maximum + EPSILON9
27754
27781
  ) && (!params.candidatePlansAreFeasible || params.candidatePlansAreFeasible(candidate))
27755
27782
  },
@@ -27760,7 +27787,7 @@ function matchDeclaredPairLengths(params, originalPlans, pairs3, validatePlans,
27760
27787
  plans = result.plans;
27761
27788
  }
27762
27789
  if (activePairs.every(
27763
- (pair) => getBusSkew(pairPlans(plans, pair)) <= pair.lengthTolerance + EPSILON9
27790
+ (pair) => getBusSkew(getDeclaredPairPlans({ plans, pair })) <= pair.lengthTolerance + EPSILON9
27764
27791
  )) {
27765
27792
  if (!originalBusLimitsHold(plans))
27766
27793
  return {
@@ -27773,7 +27800,7 @@ function matchDeclaredPairLengths(params, originalPlans, pairs3, validatePlans,
27773
27800
  }
27774
27801
  }
27775
27802
  const failed = activePairs.find(
27776
- (pair) => getBusSkew(pairPlans(plans, pair)) > pair.lengthTolerance + EPSILON9
27803
+ (pair) => getBusSkew(getDeclaredPairPlans({ plans, pair })) > pair.lengthTolerance + EPSILON9
27777
27804
  );
27778
27805
  return {
27779
27806
  plans: null,
@@ -44228,6 +44255,10 @@ var FanoutSolver = class extends BaseSolver {
44228
44255
  params.generator,
44229
44256
  params.getProgress
44230
44257
  );
44258
+ this.MAX_ITERATIONS = Math.max(
44259
+ this.MAX_ITERATIONS,
44260
+ this.iterations + solver.MAX_ITERATIONS + 1
44261
+ );
44231
44262
  this.activeOperation = {
44232
44263
  solver,
44233
44264
  onSolved: params.onSolved
@@ -45109,6 +45140,34 @@ var FanoutSolver = class extends BaseSolver {
45109
45140
  bus
45110
45141
  ])
45111
45142
  ];
45143
+ const allWideBusesExitDirectly = wideBoundaryBuses.every(
45144
+ (bus) => bus.exitEdge && bus.direction === getDirectionForExitEdge(bus.exitEdge)
45145
+ );
45146
+ const recoveryLeadingBuses = useSameNetPlaneCopperStaging && allWideBusesExitDirectly ? singletonBoundaryBuses.filter((bus) => {
45147
+ const containingWideBus = getContainingWideSourceField(bus);
45148
+ return Boolean(
45149
+ containingWideBus && bus.direction === containingWideBus.direction
45150
+ );
45151
+ }) : [];
45152
+ if (params.boundaryRecovery && recoveryLeadingBuses.length > 0) {
45153
+ const recoveredBusIndex = denseBoundaryBusesInRoutingOrder.findIndex(
45154
+ (bus) => bus.busId === params.boundaryRecovery.busId
45155
+ );
45156
+ if (recoveredBusIndex >= 0) {
45157
+ const recoveredBus = denseBoundaryBusesInRoutingOrder[recoveredBusIndex];
45158
+ const leadingBusSet = new Set(recoveryLeadingBuses);
45159
+ const remainingBuses = denseBoundaryBusesInRoutingOrder.filter(
45160
+ (bus) => bus !== recoveredBus && !leadingBusSet.has(bus)
45161
+ );
45162
+ denseBoundaryBusesInRoutingOrder.splice(
45163
+ 0,
45164
+ denseBoundaryBusesInRoutingOrder.length,
45165
+ recoveredBus,
45166
+ ...recoveryLeadingBuses,
45167
+ ...remainingBuses
45168
+ );
45169
+ }
45170
+ }
45112
45171
  for (const [bus] of adjacentCenteredFieldByTurningBus) {
45113
45172
  const axis = bus.direction === "up" || bus.direction === "down" ? "y" : "x";
45114
45173
  const sign = bus.direction === "up" || bus.direction === "right" ? 1 : -1;
@@ -45278,6 +45337,12 @@ var FanoutSolver = class extends BaseSolver {
45278
45337
  const adaptiveWindingRouteOrder = !useConfiguredDensePlaneRouting && !getCornerBandSide(bus.exitEdge, bus.preferredExit) && bus.connections.length > 2 && wideBoundaryBuses.some(
45279
45338
  (candidate) => getCornerBandSide(candidate.exitEdge, candidate.preferredExit)
45280
45339
  );
45340
+ const adjacentBoundaryMargin = this.config.traceWidth + this.config.clearance;
45341
+ const exitsNearAdjacentBoundary = bus.connections.some((connection) => {
45342
+ const target = connection.exitTargetPoint ?? connection.targetPoint;
45343
+ const boundary = bus.sharedBoundary;
45344
+ return bus.exitEdge === "left" || bus.exitEdge === "right" ? target.y - boundary.minY <= adjacentBoundaryMargin + 1e-9 || boundary.maxY - target.y <= adjacentBoundaryMargin + 1e-9 : target.x - boundary.minX <= adjacentBoundaryMargin + 1e-9 || boundary.maxX - target.x <= adjacentBoundaryMargin + 1e-9;
45345
+ });
45281
45346
  const routeParams = {
45282
45347
  srj: this.routingSrj,
45283
45348
  bus,
@@ -45299,6 +45364,7 @@ var FanoutSolver = class extends BaseSolver {
45299
45364
  preferCornerBoundaryVia: useConfiguredDensePlaneRouting,
45300
45365
  adaptiveWindingRouteOrder,
45301
45366
  allowFixedViaReservedExitFallback: useBoundaryRecovery,
45367
+ forbidEarlyExitBoundaryContact: exitsNearAdjacentBoundary,
45302
45368
  // Retain pad-aligned channels even when plane sites are reserved
45303
45369
  // adaptively; the boundary grid can fence off a turning wide bus.
45304
45370
  alignWindingGridToPads: usePadAlignedDenseRouting && !configuredDensePlaneRouting,
@@ -46875,7 +46941,25 @@ var FanoutSolver = class extends BaseSolver {
46875
46941
  densePlansAreClear
46876
46942
  );
46877
46943
  if (densePlansAreClear) {
46878
- return { plans: matchedPlans, failedBusIds: [] };
46944
+ const plans = this.normalizeCompletePlanCorners(matchedPlans);
46945
+ if (plans) {
46946
+ const outputSrj = buildOutputSimpleRouteJson({
46947
+ inputSrj: this.inputSrj,
46948
+ plans,
46949
+ layerNames: this.config.layerNames
46950
+ });
46951
+ const completeValidation = this.validateCompletePlans(
46952
+ plans,
46953
+ outputSrj
46954
+ );
46955
+ if (completeValidation.valid) {
46956
+ return {
46957
+ plans,
46958
+ failedBusIds: [],
46959
+ stopAfterCompleteValidation: true
46960
+ };
46961
+ }
46962
+ }
46879
46963
  }
46880
46964
  }
46881
46965
  if ((useAdaptiveDensePlaneRouting || useSameNetPlaneCopperStaging) && unroutablePlaneBusIds.size > 0 && (params.planeReservationRetryCount ?? 0) < 5) {
@@ -47409,7 +47493,8 @@ var FanoutSolver = class extends BaseSolver {
47409
47493
  yield;
47410
47494
  }
47411
47495
  let validationIssues;
47412
- if (plans.length === this.inputSrj.connections.length) {
47496
+ const stopAfterCompleteValidation = mixedTerminationState?.stopAfterCompleteValidation === true && plans.length === this.inputSrj.connections.length;
47497
+ if (!stopAfterCompleteValidation && plans.length === this.inputSrj.connections.length) {
47413
47498
  const lengthMatching = this.matchCompletePlanLengths(plans);
47414
47499
  if (lengthMatching.plans) {
47415
47500
  plans = lengthMatching.plans;
@@ -47430,7 +47515,7 @@ var FanoutSolver = class extends BaseSolver {
47430
47515
  blockingBusCounts.clear();
47431
47516
  }
47432
47517
  }
47433
- if (plans.length === this.inputSrj.connections.length) {
47518
+ if (!stopAfterCompleteValidation && plans.length === this.inputSrj.connections.length) {
47434
47519
  const normalized = this.normalizeCompletePlanCorners(plans);
47435
47520
  if (normalized) {
47436
47521
  plans = normalized;
@@ -48729,6 +48814,19 @@ var FanoutAutorouter = class _FanoutAutorouter {
48729
48814
  }
48730
48815
  };
48731
48816
 
48817
+ // lib/utils/autorouting/compare-pcb-vias.ts
48818
+ var PCB_VIA_PHYSICAL_MATCH_TOLERANCE_MM = 1e-6;
48819
+ function pcbViasSharePhysicalHole(firstPcbVia, secondPcbVia) {
48820
+ if (!firstPcbVia.subcircuit_connectivity_map_key || firstPcbVia.subcircuit_connectivity_map_key !== secondPcbVia.subcircuit_connectivity_map_key || firstPcbVia.subcircuit_id !== secondPcbVia.subcircuit_id) {
48821
+ return false;
48822
+ }
48823
+ const firstPcbViaLayers = firstPcbVia.layers.toSorted();
48824
+ const secondPcbViaLayers = secondPcbVia.layers.toSorted();
48825
+ return Math.abs(firstPcbVia.x - secondPcbVia.x) <= PCB_VIA_PHYSICAL_MATCH_TOLERANCE_MM && Math.abs(firstPcbVia.y - secondPcbVia.y) <= PCB_VIA_PHYSICAL_MATCH_TOLERANCE_MM && Math.abs(firstPcbVia.hole_diameter - secondPcbVia.hole_diameter) <= PCB_VIA_PHYSICAL_MATCH_TOLERANCE_MM && Math.abs(firstPcbVia.outer_diameter - secondPcbVia.outer_diameter) <= PCB_VIA_PHYSICAL_MATCH_TOLERANCE_MM && firstPcbViaLayers.length === secondPcbViaLayers.length && firstPcbViaLayers.every(
48826
+ (firstPcbViaLayer, pcbViaLayerIndex) => firstPcbViaLayer === secondPcbViaLayers[pcbViaLayerIndex]
48827
+ );
48828
+ }
48829
+
48732
48830
  // lib/utils/autorouting/createSourceTracesFromOffboardConnections.ts
48733
48831
  var createSourceTracesFromOffboardConnections = ({
48734
48832
  db,
@@ -61603,7 +61701,7 @@ function Group_getRoutingPhasePlans(group) {
61603
61701
  var package_default = {
61604
61702
  name: "@tscircuit/core",
61605
61703
  type: "module",
61606
- version: "0.0.1893",
61704
+ version: "0.0.1894",
61607
61705
  types: "dist/index.d.ts",
61608
61706
  main: "dist/index.js",
61609
61707
  module: "dist/index.js",
@@ -61643,7 +61741,7 @@ var package_default = {
61643
61741
  "@tscircuit/common": "^0.0.20",
61644
61742
  "@tscircuit/copper-pour-solver": "^0.0.54",
61645
61743
  "@tscircuit/create-fdm-enclosure": "0.0.4",
61646
- "@tscircuit/fanout-solver": "0.0.72",
61744
+ "@tscircuit/fanout-solver": "0.0.73",
61647
61745
  "@tscircuit/footprinter": "^0.0.426",
61648
61746
  "@tscircuit/image-utils": "^0.0.8",
61649
61747
  "@tscircuit/implicit-copper-pour-solver": "github:tscircuit/implicit-copper-pour-solver#a5cbbda0cd65fd4707c5c1af25779837c25aba92",
@@ -63719,7 +63817,10 @@ var Group5 = class extends NormalComponent3 {
63719
63817
  }
63720
63818
  simpleRouteJson = {
63721
63819
  ...phaseInput,
63722
- traces: [...phaseInput.traces ?? [], ...outputTraces]
63820
+ traces: getAccumulatedPcbTracesWithStageOutputReplacements({
63821
+ accumulatedPcbTraces: phaseInput.traces ?? [],
63822
+ stageOutputPcbTraces: outputTraces
63823
+ })
63723
63824
  };
63724
63825
  }
63725
63826
  simpleRouteJson = withFixedFanoutTraces(
@@ -64225,6 +64326,7 @@ var Group5 = class extends NormalComponent3 {
64225
64326
  outputPcbTraces: output_pcb_traces,
64226
64327
  pcbTraceIdsToReplace: pcb_trace_ids_to_be_replaced
64227
64328
  });
64329
+ const materializedPcbVias = db.pcb_via.list();
64228
64330
  for (const pcb_trace of output_pcb_traces) {
64229
64331
  if (pcb_trace.type !== "pcb_trace") continue;
64230
64332
  pcb_trace.subcircuit_id ??= this.subcircuit_id;
@@ -64312,7 +64414,7 @@ var Group5 = class extends NormalComponent3 {
64312
64414
  const routedViaPoint = point6;
64313
64415
  const fromLayer = point6.from_layer;
64314
64416
  const toLayer = point6.to_layer;
64315
- db.pcb_via.insert({
64417
+ const pcbVia = {
64316
64418
  pcb_trace_id: pcb_trace.pcb_trace_id,
64317
64419
  x: point6.x,
64318
64420
  y: point6.y,
@@ -64330,7 +64432,13 @@ var Group5 = class extends NormalComponent3 {
64330
64432
  subcircuit_id: this.subcircuit_id,
64331
64433
  pcb_group_id: this.pcb_group_id ?? void 0,
64332
64434
  subcircuit_connectivity_map_key: subcircuitConnectivityMapKey
64333
- });
64435
+ };
64436
+ if (materializedPcbVias.some(
64437
+ (materializedPcbVia) => pcbViasSharePhysicalHole(materializedPcbVia, pcbVia)
64438
+ )) {
64439
+ continue;
64440
+ }
64441
+ materializedPcbVias.push(db.pcb_via.insert(pcbVia));
64334
64442
  }
64335
64443
  }
64336
64444
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/core",
3
3
  "type": "module",
4
- "version": "0.0.1894",
4
+ "version": "0.0.1895",
5
5
  "types": "dist/index.d.ts",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -41,7 +41,7 @@
41
41
  "@tscircuit/common": "^0.0.20",
42
42
  "@tscircuit/copper-pour-solver": "^0.0.54",
43
43
  "@tscircuit/create-fdm-enclosure": "0.0.4",
44
- "@tscircuit/fanout-solver": "0.0.72",
44
+ "@tscircuit/fanout-solver": "0.0.73",
45
45
  "@tscircuit/footprinter": "^0.0.426",
46
46
  "@tscircuit/image-utils": "^0.0.8",
47
47
  "@tscircuit/implicit-copper-pour-solver": "github:tscircuit/implicit-copper-pour-solver#a5cbbda0cd65fd4707c5c1af25779837c25aba92",