@tscircuit/core 0.0.1894 → 0.0.1896

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 +165 -54
  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,
@@ -42350,8 +42377,11 @@ function validatePlanStructure(params) {
42350
42377
  plan,
42351
42378
  issues
42352
42379
  });
42353
- if (traceSegments.length !== plan.segments.length || traceSegments.some((segment2, index) => {
42354
- const declared = plan.segments[index];
42380
+ const declaredTraceSegments = plan.segments.filter(
42381
+ (segment2) => distance9(segment2.start, segment2.end) > EPSILON23
42382
+ );
42383
+ if (traceSegments.length !== declaredTraceSegments.length || traceSegments.some((segment2, index) => {
42384
+ const declared = declaredTraceSegments[index];
42355
42385
  return !declared || segment2.layer !== declared.layer || Math.abs(segment2.width - declared.width) > EPSILON23 || !pointsMatch3(segment2.start, declared.start) || !pointsMatch3(segment2.end, declared.end);
42356
42386
  })) {
42357
42387
  addIssue2(
@@ -44228,6 +44258,10 @@ var FanoutSolver = class extends BaseSolver {
44228
44258
  params.generator,
44229
44259
  params.getProgress
44230
44260
  );
44261
+ this.MAX_ITERATIONS = Math.max(
44262
+ this.MAX_ITERATIONS,
44263
+ this.iterations + solver.MAX_ITERATIONS + 1
44264
+ );
44231
44265
  this.activeOperation = {
44232
44266
  solver,
44233
44267
  onSolved: params.onSolved
@@ -45109,6 +45143,34 @@ var FanoutSolver = class extends BaseSolver {
45109
45143
  bus
45110
45144
  ])
45111
45145
  ];
45146
+ const allWideBusesExitDirectly = wideBoundaryBuses.every(
45147
+ (bus) => bus.exitEdge && bus.direction === getDirectionForExitEdge(bus.exitEdge)
45148
+ );
45149
+ const recoveryLeadingBuses = useSameNetPlaneCopperStaging && allWideBusesExitDirectly ? singletonBoundaryBuses.filter((bus) => {
45150
+ const containingWideBus = getContainingWideSourceField(bus);
45151
+ return Boolean(
45152
+ containingWideBus && bus.direction === containingWideBus.direction
45153
+ );
45154
+ }) : [];
45155
+ if (params.boundaryRecovery && recoveryLeadingBuses.length > 0) {
45156
+ const recoveredBusIndex = denseBoundaryBusesInRoutingOrder.findIndex(
45157
+ (bus) => bus.busId === params.boundaryRecovery.busId
45158
+ );
45159
+ if (recoveredBusIndex >= 0) {
45160
+ const recoveredBus = denseBoundaryBusesInRoutingOrder[recoveredBusIndex];
45161
+ const leadingBusSet = new Set(recoveryLeadingBuses);
45162
+ const remainingBuses = denseBoundaryBusesInRoutingOrder.filter(
45163
+ (bus) => bus !== recoveredBus && !leadingBusSet.has(bus)
45164
+ );
45165
+ denseBoundaryBusesInRoutingOrder.splice(
45166
+ 0,
45167
+ denseBoundaryBusesInRoutingOrder.length,
45168
+ recoveredBus,
45169
+ ...recoveryLeadingBuses,
45170
+ ...remainingBuses
45171
+ );
45172
+ }
45173
+ }
45112
45174
  for (const [bus] of adjacentCenteredFieldByTurningBus) {
45113
45175
  const axis = bus.direction === "up" || bus.direction === "down" ? "y" : "x";
45114
45176
  const sign = bus.direction === "up" || bus.direction === "right" ? 1 : -1;
@@ -45278,6 +45340,12 @@ var FanoutSolver = class extends BaseSolver {
45278
45340
  const adaptiveWindingRouteOrder = !useConfiguredDensePlaneRouting && !getCornerBandSide(bus.exitEdge, bus.preferredExit) && bus.connections.length > 2 && wideBoundaryBuses.some(
45279
45341
  (candidate) => getCornerBandSide(candidate.exitEdge, candidate.preferredExit)
45280
45342
  );
45343
+ const adjacentBoundaryMargin = this.config.traceWidth + this.config.clearance;
45344
+ const exitsNearAdjacentBoundary = bus.connections.some((connection) => {
45345
+ const target = connection.exitTargetPoint ?? connection.targetPoint;
45346
+ const boundary = bus.sharedBoundary;
45347
+ 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;
45348
+ });
45281
45349
  const routeParams = {
45282
45350
  srj: this.routingSrj,
45283
45351
  bus,
@@ -45299,6 +45367,7 @@ var FanoutSolver = class extends BaseSolver {
45299
45367
  preferCornerBoundaryVia: useConfiguredDensePlaneRouting,
45300
45368
  adaptiveWindingRouteOrder,
45301
45369
  allowFixedViaReservedExitFallback: useBoundaryRecovery,
45370
+ forbidEarlyExitBoundaryContact: exitsNearAdjacentBoundary,
45302
45371
  // Retain pad-aligned channels even when plane sites are reserved
45303
45372
  // adaptively; the boundary grid can fence off a turning wide bus.
45304
45373
  alignWindingGridToPads: usePadAlignedDenseRouting && !configuredDensePlaneRouting,
@@ -46875,7 +46944,25 @@ var FanoutSolver = class extends BaseSolver {
46875
46944
  densePlansAreClear
46876
46945
  );
46877
46946
  if (densePlansAreClear) {
46878
- return { plans: matchedPlans, failedBusIds: [] };
46947
+ const plans = this.normalizeCompletePlanCorners(matchedPlans);
46948
+ if (plans) {
46949
+ const outputSrj = buildOutputSimpleRouteJson({
46950
+ inputSrj: this.inputSrj,
46951
+ plans,
46952
+ layerNames: this.config.layerNames
46953
+ });
46954
+ const completeValidation = this.validateCompletePlans(
46955
+ plans,
46956
+ outputSrj
46957
+ );
46958
+ if (completeValidation.valid) {
46959
+ return {
46960
+ plans,
46961
+ failedBusIds: [],
46962
+ stopAfterCompleteValidation: true
46963
+ };
46964
+ }
46965
+ }
46879
46966
  }
46880
46967
  }
46881
46968
  if ((useAdaptiveDensePlaneRouting || useSameNetPlaneCopperStaging) && unroutablePlaneBusIds.size > 0 && (params.planeReservationRetryCount ?? 0) < 5) {
@@ -47409,7 +47496,8 @@ var FanoutSolver = class extends BaseSolver {
47409
47496
  yield;
47410
47497
  }
47411
47498
  let validationIssues;
47412
- if (plans.length === this.inputSrj.connections.length) {
47499
+ const stopAfterCompleteValidation = mixedTerminationState?.stopAfterCompleteValidation === true && plans.length === this.inputSrj.connections.length;
47500
+ if (!stopAfterCompleteValidation && plans.length === this.inputSrj.connections.length) {
47413
47501
  const lengthMatching = this.matchCompletePlanLengths(plans);
47414
47502
  if (lengthMatching.plans) {
47415
47503
  plans = lengthMatching.plans;
@@ -47430,7 +47518,7 @@ var FanoutSolver = class extends BaseSolver {
47430
47518
  blockingBusCounts.clear();
47431
47519
  }
47432
47520
  }
47433
- if (plans.length === this.inputSrj.connections.length) {
47521
+ if (!stopAfterCompleteValidation && plans.length === this.inputSrj.connections.length) {
47434
47522
  const normalized = this.normalizeCompletePlanCorners(plans);
47435
47523
  if (normalized) {
47436
47524
  plans = normalized;
@@ -48729,6 +48817,19 @@ var FanoutAutorouter = class _FanoutAutorouter {
48729
48817
  }
48730
48818
  };
48731
48819
 
48820
+ // lib/utils/autorouting/compare-pcb-vias.ts
48821
+ var PCB_VIA_PHYSICAL_MATCH_TOLERANCE_MM = 1e-6;
48822
+ function pcbViasSharePhysicalHole(firstPcbVia, secondPcbVia) {
48823
+ if (!firstPcbVia.subcircuit_connectivity_map_key || firstPcbVia.subcircuit_connectivity_map_key !== secondPcbVia.subcircuit_connectivity_map_key || firstPcbVia.subcircuit_id !== secondPcbVia.subcircuit_id) {
48824
+ return false;
48825
+ }
48826
+ const firstPcbViaLayers = firstPcbVia.layers.toSorted();
48827
+ const secondPcbViaLayers = secondPcbVia.layers.toSorted();
48828
+ 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(
48829
+ (firstPcbViaLayer, pcbViaLayerIndex) => firstPcbViaLayer === secondPcbViaLayers[pcbViaLayerIndex]
48830
+ );
48831
+ }
48832
+
48732
48833
  // lib/utils/autorouting/createSourceTracesFromOffboardConnections.ts
48733
48834
  var createSourceTracesFromOffboardConnections = ({
48734
48835
  db,
@@ -61603,7 +61704,7 @@ function Group_getRoutingPhasePlans(group) {
61603
61704
  var package_default = {
61604
61705
  name: "@tscircuit/core",
61605
61706
  type: "module",
61606
- version: "0.0.1893",
61707
+ version: "0.0.1895",
61607
61708
  types: "dist/index.d.ts",
61608
61709
  main: "dist/index.js",
61609
61710
  module: "dist/index.js",
@@ -61643,7 +61744,7 @@ var package_default = {
61643
61744
  "@tscircuit/common": "^0.0.20",
61644
61745
  "@tscircuit/copper-pour-solver": "^0.0.54",
61645
61746
  "@tscircuit/create-fdm-enclosure": "0.0.4",
61646
- "@tscircuit/fanout-solver": "0.0.72",
61747
+ "@tscircuit/fanout-solver": "0.0.74",
61647
61748
  "@tscircuit/footprinter": "^0.0.426",
61648
61749
  "@tscircuit/image-utils": "^0.0.8",
61649
61750
  "@tscircuit/implicit-copper-pour-solver": "github:tscircuit/implicit-copper-pour-solver#a5cbbda0cd65fd4707c5c1af25779837c25aba92",
@@ -63719,7 +63820,10 @@ var Group5 = class extends NormalComponent3 {
63719
63820
  }
63720
63821
  simpleRouteJson = {
63721
63822
  ...phaseInput,
63722
- traces: [...phaseInput.traces ?? [], ...outputTraces]
63823
+ traces: getAccumulatedPcbTracesWithStageOutputReplacements({
63824
+ accumulatedPcbTraces: phaseInput.traces ?? [],
63825
+ stageOutputPcbTraces: outputTraces
63826
+ })
63723
63827
  };
63724
63828
  }
63725
63829
  simpleRouteJson = withFixedFanoutTraces(
@@ -64225,6 +64329,7 @@ var Group5 = class extends NormalComponent3 {
64225
64329
  outputPcbTraces: output_pcb_traces,
64226
64330
  pcbTraceIdsToReplace: pcb_trace_ids_to_be_replaced
64227
64331
  });
64332
+ const materializedPcbVias = db.pcb_via.list();
64228
64333
  for (const pcb_trace of output_pcb_traces) {
64229
64334
  if (pcb_trace.type !== "pcb_trace") continue;
64230
64335
  pcb_trace.subcircuit_id ??= this.subcircuit_id;
@@ -64312,7 +64417,7 @@ var Group5 = class extends NormalComponent3 {
64312
64417
  const routedViaPoint = point6;
64313
64418
  const fromLayer = point6.from_layer;
64314
64419
  const toLayer = point6.to_layer;
64315
- db.pcb_via.insert({
64420
+ const pcbVia = {
64316
64421
  pcb_trace_id: pcb_trace.pcb_trace_id,
64317
64422
  x: point6.x,
64318
64423
  y: point6.y,
@@ -64330,7 +64435,13 @@ var Group5 = class extends NormalComponent3 {
64330
64435
  subcircuit_id: this.subcircuit_id,
64331
64436
  pcb_group_id: this.pcb_group_id ?? void 0,
64332
64437
  subcircuit_connectivity_map_key: subcircuitConnectivityMapKey
64333
- });
64438
+ };
64439
+ if (materializedPcbVias.some(
64440
+ (materializedPcbVia) => pcbViasSharePhysicalHole(materializedPcbVia, pcbVia)
64441
+ )) {
64442
+ continue;
64443
+ }
64444
+ materializedPcbVias.push(db.pcb_via.insert(pcbVia));
64334
64445
  }
64335
64446
  }
64336
64447
  }
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.1896",
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.74",
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",