@tscircuit/core 0.0.1914 → 0.0.1916

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/index.js CHANGED
@@ -6119,11 +6119,11 @@ function Trace_doInitialPcbTraceRender(trace) {
6119
6119
  const closestNetBPort = getClosest(netAPort, netBPorts);
6120
6120
  const netAPortPosition = netAPort._getGlobalPcbPositionBeforeLayout();
6121
6121
  const netBPortPosition = closestNetBPort._getGlobalPcbPositionBeforeLayout();
6122
- const distance22 = Math.sqrt(
6122
+ const distance23 = Math.sqrt(
6123
6123
  (netAPortPosition.x - netBPortPosition.x) ** 2 + (netAPortPosition.y - netBPortPosition.y) ** 2
6124
6124
  );
6125
- if (distance22 < closestDistance) {
6126
- closestDistance = distance22;
6125
+ if (distance23 < closestDistance) {
6126
+ closestDistance = distance23;
6127
6127
  closestPortPair = [netAPort, closestNetBPort];
6128
6128
  }
6129
6129
  }
@@ -7283,7 +7283,7 @@ var getMaxLengthFromConnectedComponents = (ports, { db }) => {
7283
7283
  return sourceComponent.max_decoupling_trace_length;
7284
7284
  }
7285
7285
  return null;
7286
- }).filter((length7) => typeof length7 === "number");
7286
+ }).filter((length8) => typeof length8 === "number");
7287
7287
  if (componentMaxLengths.length === 0) return void 0;
7288
7288
  return Math.min(...componentMaxLengths);
7289
7289
  };
@@ -10130,11 +10130,11 @@ import { schematicPathProps } from "@tscircuit/props";
10130
10130
  import SVGPathCommander from "svg-path-commander";
10131
10131
  function sampleCurveSegment(segmentPath, samplesPerUnit) {
10132
10132
  const commander = new SVGPathCommander(segmentPath);
10133
- const length7 = commander.getTotalLength();
10134
- const numSamples = Math.max(2, Math.ceil(length7 * samplesPerUnit));
10133
+ const length8 = commander.getTotalLength();
10134
+ const numSamples = Math.max(2, Math.ceil(length8 * samplesPerUnit));
10135
10135
  const points = [];
10136
10136
  for (let i = 1; i <= numSamples; i++) {
10137
- const t = i / numSamples * length7;
10137
+ const t = i / numSamples * length8;
10138
10138
  const point6 = commander.getPointAtLength(t);
10139
10139
  points.push({ x: point6.x, y: point6.y });
10140
10140
  }
@@ -12303,7 +12303,7 @@ var createComponentsFromCircuitJson = ({
12303
12303
  const schematicSymbolId = schematicComponent?.schematic_symbol_id;
12304
12304
  const parentSymbol = typeof schematicSymbolId === "string" ? schematicSymbolsByImportedId.get(schematicSymbolId) : void 0;
12305
12305
  if (parentSymbol && schematicComponent?.is_box_with_pins === true && elm.center && elm.side_of_component) {
12306
- const distance22 = elm.distance_from_component_edge;
12306
+ const distance23 = elm.distance_from_component_edge;
12307
12307
  const facingDirection = getFacingDirectionFromSide(
12308
12308
  elm.side_of_component
12309
12309
  );
@@ -12311,8 +12311,8 @@ var createComponentsFromCircuitJson = ({
12311
12311
  const directionVector = getUnitVectorFromDirection2(facingDirection);
12312
12312
  const stemDirection = elm.facing_direction === facingDirection ? 1 : -1;
12313
12313
  const portCenter = {
12314
- x: elm.center.x + directionVector.x * distance22 * stemDirection,
12315
- y: elm.center.y + directionVector.y * distance22 * stemDirection
12314
+ x: elm.center.x + directionVector.x * distance23 * stemDirection,
12315
+ y: elm.center.y + directionVector.y * distance23 * stemDirection
12316
12316
  };
12317
12317
  const sourcePort = sourcePortsByImportedId.get(elm.source_port_id);
12318
12318
  const aliases = Array.from(
@@ -13543,9 +13543,9 @@ var getLabelPlacementAlongRoute = (route) => {
13543
13543
  for (let pointIndex = 1; pointIndex < route.length; pointIndex++) {
13544
13544
  const from = route[pointIndex - 1];
13545
13545
  const to = route[pointIndex];
13546
- const length7 = Math.hypot(to.x - from.x, to.y - from.y);
13547
- if (length7 > longestSegment.length) {
13548
- longestSegment = { from, to, length: length7 };
13546
+ const length8 = Math.hypot(to.x - from.x, to.y - from.y);
13547
+ if (length8 > longestSegment.length) {
13548
+ longestSegment = { from, to, length: length8 };
13549
13549
  }
13550
13550
  }
13551
13551
  const deltaX = longestSegment.to.x - longestSegment.from.x;
@@ -16770,15 +16770,15 @@ var Capacitor_addPolarityFabricationSymbols = (capacitor) => {
16770
16770
  source_port_id: negatives[0].source_port_id
16771
16771
  });
16772
16772
  if (!positive || !negative) return;
16773
- const distance22 = Math.hypot(negative.x - positive.x, negative.y - positive.y);
16774
- if (distance22 < 1e-6) return;
16773
+ const distance23 = Math.hypot(negative.x - positive.x, negative.y - positive.y);
16774
+ if (distance23 < 1e-6) return;
16775
16775
  const frame = compose6(
16776
16776
  translate4((positive.x + negative.x) / 2, (positive.y + negative.y) / 2),
16777
16777
  rotate3(Math.atan2(negative.y - positive.y, negative.x - positive.x))
16778
16778
  );
16779
16779
  const point6 = (x, y) => applyToPoint25(frame, { x, y });
16780
- const offset = distance22 / 4;
16781
- const halfSize = Math.min(distance22 * 0.12, 0.3);
16780
+ const offset = distance23 / 4;
16781
+ const halfSize = Math.min(distance23 * 0.12, 0.3);
16782
16782
  const routes = [
16783
16783
  [point6(-offset - halfSize, 0), point6(-offset + halfSize, 0)],
16784
16784
  [point6(-offset, -halfSize), point6(-offset, halfSize)],
@@ -18270,7 +18270,7 @@ import {
18270
18270
  groupProps
18271
18271
  } from "@tscircuit/props";
18272
18272
  import {
18273
- distance as distance15
18273
+ distance as distance16
18274
18274
  } from "circuit-json";
18275
18275
  import Debug15 from "debug";
18276
18276
 
@@ -18977,7 +18977,7 @@ function validateOriginalEndpointConnectivity(params) {
18977
18977
  representativeConnection.name
18978
18978
  )
18979
18979
  ).map((obstacle) => ({ type: "obstacle", obstacle }));
18980
- const routeCopper = (routedSrj.traces ?? []).filter(
18980
+ const routeCopper2 = (routedSrj.traces ?? []).filter(
18981
18981
  (trace) => traceBelongsToNet(inputSrj, trace, representativeConnection)
18982
18982
  ).flatMap((trace) => extractTraceCopper(trace, layerNames));
18983
18983
  const planeCopper = [
@@ -18994,7 +18994,7 @@ function validateOriginalEndpointConnectivity(params) {
18994
18994
  const copper = [
18995
18995
  ...endpointCopper,
18996
18996
  ...obstacleCopper,
18997
- ...routeCopper,
18997
+ ...routeCopper2,
18998
18998
  ...planeCopper
18999
18999
  ];
19000
19000
  const connectedCopper = new DisjointSet(copper.length);
@@ -19469,8 +19469,8 @@ function getTerminalDirections(params) {
19469
19469
  x: plan.targetPoint.x - plan.sourcePoint.x,
19470
19470
  y: plan.targetPoint.y - plan.sourcePoint.y
19471
19471
  };
19472
- const length7 = Math.hypot(rawOutward.x, rawOutward.y);
19473
- const outward = length7 > EPSILON4 ? { x: rawOutward.x / length7, y: rawOutward.y / length7 } : { x: 1, y: 0 };
19472
+ const length8 = Math.hypot(rawOutward.x, rawOutward.y);
19473
+ const outward = length8 > EPSILON4 ? { x: rawOutward.x / length8, y: rawOutward.y / length8 } : { x: 1, y: 0 };
19474
19474
  return {
19475
19475
  outward,
19476
19476
  perpendicular: { x: -outward.y, y: outward.x }
@@ -19759,7 +19759,7 @@ function runLocalCompletionPass(params) {
19759
19759
  };
19760
19760
  }
19761
19761
  function traceLength(trace) {
19762
- let length7 = 0;
19762
+ let length8 = 0;
19763
19763
  let previousWire;
19764
19764
  for (const point6 of trace.route) {
19765
19765
  if (point6.route_type !== "wire") {
@@ -19767,10 +19767,10 @@ function traceLength(trace) {
19767
19767
  continue;
19768
19768
  }
19769
19769
  if (previousWire?.layer === point6.layer)
19770
- length7 += distance9(previousWire, point6);
19770
+ length8 += distance9(previousWire, point6);
19771
19771
  previousWire = point6;
19772
19772
  }
19773
- return length7;
19773
+ return length8;
19774
19774
  }
19775
19775
  function trimCompletedFanoutTails(params) {
19776
19776
  const { fanoutSrj, completionTraces } = params;
@@ -20480,8 +20480,8 @@ function* getMultiSegmentTuningWindows(params) {
20480
20480
  for (const [index, segment2] of plan.segments.entries()) {
20481
20481
  if (segment2.layer !== plan.targetLayer || index < (plan.sourceEscapeSegmentCount ?? 1))
20482
20482
  continue;
20483
- const length7 = distance9(segment2.start, segment2.end);
20484
- const steps = Math.max(1, Math.ceil(length7 / (segment2.width / 2)));
20483
+ const length8 = distance9(segment2.start, segment2.end);
20484
+ const steps = Math.max(1, Math.ceil(length8 / (segment2.width / 2)));
20485
20485
  for (let step = 0; step <= steps; step++) {
20486
20486
  consumeWork();
20487
20487
  const fraction = step / steps;
@@ -26416,7 +26416,7 @@ function hasValidTurns(segments) {
26416
26416
  const segment2 = segments[i];
26417
26417
  const dx = segment2.end.x - segment2.start.x;
26418
26418
  const dy = segment2.end.y - segment2.start.y;
26419
- const length7 = Math.hypot(dx, dy);
26419
+ const length8 = Math.hypot(dx, dy);
26420
26420
  if (Math.min(
26421
26421
  Math.abs(dx),
26422
26422
  Math.abs(dy),
@@ -26425,12 +26425,12 @@ function hasValidTurns(segments) {
26425
26425
  return false;
26426
26426
  const next = segments[i + 1];
26427
26427
  if (next && distance9(segment2.end, next.start) > EPSILON8) return false;
26428
- if (!next || next.layer !== segment2.layer || length7 < EPSILON8) continue;
26428
+ if (!next || next.layer !== segment2.layer || length8 < EPSILON8) continue;
26429
26429
  const nx = next.end.x - next.start.x;
26430
26430
  const ny = next.end.y - next.start.y;
26431
26431
  const nextLength = Math.hypot(nx, ny);
26432
26432
  if (nextLength < EPSILON8) continue;
26433
- if ((dx * nx + dy * ny) / (length7 * nextLength) < Math.SQRT1_2 - EPSILON8)
26433
+ if ((dx * nx + dy * ny) / (length8 * nextLength) < Math.SQRT1_2 - EPSILON8)
26434
26434
  return false;
26435
26435
  }
26436
26436
  return true;
@@ -26751,7 +26751,7 @@ function normalizeFanoutPlanCorners(params) {
26751
26751
  0
26752
26752
  )
26753
26753
  );
26754
- if (lengths.some((length7) => !Number.isFinite(length7)) || Math.abs(lengths[0] - lengths[1]) > pair.lengthTolerance + EPSILON8)
26754
+ if (lengths.some((length8) => !Number.isFinite(length8)) || Math.abs(lengths[0] - lengths[1]) > pair.lengthTolerance + EPSILON8)
26755
26755
  return null;
26756
26756
  }
26757
26757
  if (params.inputSrj.traces?.length && plans.length) {
@@ -26990,7 +26990,7 @@ function rebuildTraceRoute(plan, segments) {
26990
26990
  function createPlanWithSegments(plan, segments, updateSourceEscapeCount = false) {
26991
26991
  const route = rebuildTraceRoute(plan, segments);
26992
26992
  if (!route) return null;
26993
- const length7 = [...segments, ...plan.planeEndpointSegments ?? []].reduce(
26993
+ const length8 = [...segments, ...plan.planeEndpointSegments ?? []].reduce(
26994
26994
  (total, segment2) => total + distance9(segment2.start, segment2.end),
26995
26995
  0
26996
26996
  );
@@ -27002,7 +27002,7 @@ function createPlanWithSegments(plan, segments, updateSourceEscapeCount = false)
27002
27002
  ...updateSourceEscapeCount && plan.sourceEscapeSegmentCount !== void 0 && sourceEscapeSegmentCount >= 0 ? { sourceEscapeSegmentCount } : {},
27003
27003
  trace: { ...plan.trace, route },
27004
27004
  segments,
27005
- length: length7
27005
+ length: length8
27006
27006
  };
27007
27007
  }
27008
27008
  function addedTuningViasAreSelfClear(plan, addedVias, clearance) {
@@ -27532,11 +27532,11 @@ function* createExtendedFoldCandidates(params) {
27532
27532
  continue;
27533
27533
  } else if (start.layer !== plan.targetLayer && (!params.allowTransitLayerMatching || !(bus.allowedLayers ?? [plan.targetLayer]).includes(start.layer) || !(bus.routableEscapeLayers ?? bus.allowedLayers ?? [plan.targetLayer]).includes(start.layer)))
27534
27534
  continue;
27535
- const length7 = distance9(start.start, start.end);
27536
- if (length7 <= EPSILON9) continue;
27535
+ const length8 = distance9(start.start, start.end);
27536
+ if (length8 <= EPSILON9) continue;
27537
27537
  const direction = {
27538
- x: (start.end.x - start.start.x) / length7,
27539
- y: (start.end.y - start.start.y) / length7
27538
+ x: (start.end.x - start.start.x) / length8,
27539
+ y: (start.end.y - start.start.y) / length8
27540
27540
  };
27541
27541
  for (let last = first + 2; last < Math.min(plan.segments.length, first + 16); last++) {
27542
27542
  const span = plan.segments.slice(first, last + 1);
@@ -27613,19 +27613,19 @@ function* createSpreadLaneCandidates(plan, clearance, workBudget) {
27613
27613
  for (const { segment: segment2, index } of plan.segments.map((segment3, index2) => ({ segment: segment3, index: index2 })).filter(({ segment: segment3 }) => segment3.layer === plan.targetLayer).toSorted(
27614
27614
  (a, b) => distance9(b.segment.start, b.segment.end) - distance9(a.segment.start, a.segment.end)
27615
27615
  )) {
27616
- const length7 = distance9(segment2.start, segment2.end);
27617
- if (length7 <= EPSILON9) continue;
27616
+ const length8 = distance9(segment2.start, segment2.end);
27617
+ if (length8 <= EPSILON9) continue;
27618
27618
  const dx = Math.abs(segment2.end.x - segment2.start.x);
27619
27619
  const dy = Math.abs(segment2.end.y - segment2.start.y);
27620
27620
  if (dx > EPSILON9 && dy > EPSILON9 && Math.abs(dx - dy) > EPSILON9) continue;
27621
27621
  const tangent = {
27622
- x: (segment2.end.x - segment2.start.x) / length7,
27623
- y: (segment2.end.y - segment2.start.y) / length7
27622
+ x: (segment2.end.x - segment2.start.x) / length8,
27623
+ y: (segment2.end.y - segment2.start.y) / length8
27624
27624
  };
27625
27625
  const pitch = segment2.width + clearance;
27626
27626
  for (const multiple of [2, 3, 4, 6]) {
27627
27627
  const offset = pitch * multiple;
27628
- if (length7 < 2 * offset + pitch) continue;
27628
+ if (length8 < 2 * offset + pitch) continue;
27629
27629
  for (const sign of [1, -1]) {
27630
27630
  consumeMatchingWork(workBudget);
27631
27631
  const normal = { x: -tangent.y * sign, y: tangent.x * sign };
@@ -28395,15 +28395,15 @@ function sourceOriginRouteIsSelfClear(params) {
28395
28395
  const radius = (viaDiameter + traceWidth) / 2 + clearance;
28396
28396
  const incident = /* @__PURE__ */ new Set();
28397
28397
  for (const direction of [-1, 1]) {
28398
- let point6 = via.center, length7 = 0;
28398
+ let point6 = via.center, length8 = 0;
28399
28399
  const z18 = direction === -1 ? via.fromZ : via.toZ;
28400
28400
  for (let edge = via.edge + direction; edge >= 0 && edge + 1 < points.length; edge += direction) {
28401
28401
  const a = points[edge], b = points[edge + 1];
28402
28402
  const near = direction === -1 ? b : a, far = direction === -1 ? a : b;
28403
- if (a.z !== z18 || b.z !== z18 || distance9(point6, near) > 1e-7 || length7 > radius + 1e-7)
28403
+ if (a.z !== z18 || b.z !== z18 || distance9(point6, near) > 1e-7 || length8 > radius + 1e-7)
28404
28404
  break;
28405
28405
  incident.add(edge);
28406
- length7 += distance9(near, far);
28406
+ length8 += distance9(near, far);
28407
28407
  point6 = far;
28408
28408
  }
28409
28409
  }
@@ -28466,10 +28466,10 @@ function shortcutSection(points, width, clear) {
28466
28466
  const lowerBound = Math.max(dx, dy) + (Math.SQRT2 - 1) * Math.min(dx, dy);
28467
28467
  if (cost[i] + lowerBound >= cost[j] - EPSILON10) continue;
28468
28468
  for (const path of connectorVariants(a, b)) {
28469
- const length7 = path.slice(1).reduce((sum, p, index) => sum + distance9(path[index], p), 0);
28470
- if (cost[i] + length7 >= cost[j] - EPSILON10 || !path.slice(1).every((p, index) => clear(path[index], p)))
28469
+ const length8 = path.slice(1).reduce((sum, p, index) => sum + distance9(path[index], p), 0);
28470
+ if (cost[i] + length8 >= cost[j] - EPSILON10 || !path.slice(1).every((p, index) => clear(path[index], p)))
28471
28471
  continue;
28472
- cost[j] = cost[i] + length7;
28472
+ cost[j] = cost[i] + length8;
28473
28473
  edges[j] = { previous: i, points: path };
28474
28474
  }
28475
28475
  }
@@ -28814,14 +28814,14 @@ var intersectsRegion = (regions, a, b = a) => regions.some(
28814
28814
  );
28815
28815
  function terminalSuffixAt(segments, point6, layer) {
28816
28816
  const positions = [];
28817
- let length7 = 0;
28817
+ let length8 = 0;
28818
28818
  for (const [index2, segment2] of segments.entries()) {
28819
28819
  if (segment2.layer === layer && distancePointToSegment(point6, segment2.start, segment2.end) < 1e-8) {
28820
- const offset = length7 + distance9(segment2.start, point6);
28820
+ const offset = length8 + distance9(segment2.start, point6);
28821
28821
  if (!positions.some((p) => Math.abs(p.offset - offset) < 1e-8))
28822
28822
  positions.push({ index: index2, offset });
28823
28823
  }
28824
- length7 += distance9(segment2.start, segment2.end);
28824
+ length8 += distance9(segment2.start, segment2.end);
28825
28825
  }
28826
28826
  if (positions.length !== 1) return null;
28827
28827
  let index = positions[0].index;
@@ -28884,12 +28884,12 @@ function boundaryTerminalPlanIsSelfClear(params) {
28884
28884
  if (Math.abs(ux * vy - uy * vx) >= 1e-10 || ux * vx + uy * vy < 0) break;
28885
28885
  runStart = previous.start;
28886
28886
  }
28887
- const length7 = distance9(runStart, cut);
28888
- if (length7 < 1e-10) return false;
28889
- const trim = Math.min(maximumJoinLength, length7 / 3);
28887
+ const length8 = distance9(runStart, cut);
28888
+ if (length8 < 1e-10) return false;
28889
+ const trim = Math.min(maximumJoinLength, length8 / 3);
28890
28890
  cut = {
28891
- x: cut.x + (runStart.x - cut.x) * trim / length7,
28892
- y: cut.y + (runStart.y - cut.y) * trim / length7
28891
+ x: cut.x + (runStart.x - cut.x) * trim / length8,
28892
+ y: cut.y + (runStart.y - cut.y) * trim / length8
28893
28893
  };
28894
28894
  }
28895
28895
  const suffix = terminalSuffixAt(plan.segments, cut, start.layer);
@@ -33562,13 +33562,13 @@ function* sourcePrefixFoldShortcuts(plan, clearance) {
33562
33562
  if (first < 0) return;
33563
33563
  const pitch = prefix[first].width + clearance;
33564
33564
  for (let start = first; start >= Math.max(0, first - 3); start--) {
33565
- let length7 = 0;
33565
+ let length8 = 0;
33566
33566
  for (let end = start; end < Math.min(prefix.length, first + 8); end++) {
33567
33567
  const segment2 = prefix[end];
33568
33568
  if (segment2.layer !== plan.sourceLayer || end > start && distance9(prefix[end - 1].end, segment2.start) > 1e-7)
33569
33569
  break;
33570
- length7 += distance9(segment2.start, segment2.end);
33571
- if (length7 > 4 * pitch + 1e-7) break;
33570
+ length8 += distance9(segment2.start, segment2.end);
33571
+ if (length8 > 4 * pitch + 1e-7) break;
33572
33572
  if (end <= first) continue;
33573
33573
  const a = prefix[start].start, b = segment2.end, dx = b.x - a.x, dy = b.y - a.y, diagonal = Math.min(Math.abs(dx), Math.abs(dy));
33574
33574
  for (const bend of [
@@ -33581,7 +33581,7 @@ function* sourcePrefixFoldShortcuts(plan, clearance) {
33581
33581
  y: b.y - Math.sign(dy) * diagonal
33582
33582
  }
33583
33583
  ]) {
33584
- if (distance9(a, bend) + distance9(bend, b) >= length7 - 1e-7) continue;
33584
+ if (distance9(a, bend) + distance9(bend, b) >= length8 - 1e-7) continue;
33585
33585
  yield [
33586
33586
  prefix[0].start,
33587
33587
  ...prefix.slice(0, start).map((s) => s.end),
@@ -33711,8 +33711,8 @@ function repairSourcePadReentries(params) {
33711
33711
  const key = JSON.stringify([anchor, points]);
33712
33712
  if (seen.has(key)) continue;
33713
33713
  seen.add(key);
33714
- const length7 = points.slice(1).reduce((sum, p, i) => sum + distance9(points[i], p), 0) + prefix.slice(anchor).reduce((sum, s) => sum + distance9(s.start, s.end), 0);
33715
- proposals.push({ anchor, points, length: length7 });
33714
+ const length8 = points.slice(1).reduce((sum, p, i) => sum + distance9(points[i], p), 0) + prefix.slice(anchor).reduce((sum, s) => sum + distance9(s.start, s.end), 0);
33715
+ proposals.push({ anchor, points, length: length8 });
33716
33716
  }
33717
33717
  }
33718
33718
  }
@@ -37514,9 +37514,9 @@ function getFreeBoundaryTracks(params) {
37514
37514
  for (const via of vias) {
37515
37515
  if (!via.spanLayers.includes(targetLayer2)) continue;
37516
37516
  const radius = via.diameter / 2 + traceWidth / 2 + clearance;
37517
- const distance22 = Math.abs(via.center[across] - edge);
37518
- if (distance22 >= radius) continue;
37519
- const extent = Math.sqrt(radius * radius - distance22 * distance22);
37517
+ const distance23 = Math.abs(via.center[across] - edge);
37518
+ if (distance23 >= radius) continue;
37519
+ const extent = Math.sqrt(radius * radius - distance23 * distance23);
37520
37520
  blocked.push([via.center[along] - extent, via.center[along] + extent]);
37521
37521
  }
37522
37522
  const gaps = [];
@@ -38731,8 +38731,8 @@ var LayerRoutingAttempts = class {
38731
38731
 
38732
38732
  // node_modules/@tscircuit/fanout-solver/lib/get-boundary-approach-reservations.ts
38733
38733
  function getBoundaryApproachReservations(params) {
38734
- const length7 = params.traceWidth + params.clearance;
38735
- if (!Number.isFinite(length7) || !(params.traceWidth > 0) || params.clearance < 0)
38734
+ const length8 = params.traceWidth + params.clearance;
38735
+ if (!Number.isFinite(length8) || !(params.traceWidth > 0) || params.clearance < 0)
38736
38736
  throw new Error("Boundary approach reservations require valid trace rules");
38737
38737
  return params.buses.filter(
38738
38738
  (bus) => bus.termination.type === "boundary" && !params.excludedBusIds.has(bus.busId)
@@ -38749,10 +38749,10 @@ function getBoundaryApproachReservations(params) {
38749
38749
  `Missing boundary approach target for ${connection.connectionIndex}`
38750
38750
  );
38751
38751
  const start = { ...end };
38752
- if (bus.exitEdge === "bottom") start.y += length7;
38753
- else if (bus.exitEdge === "top") start.y -= length7;
38754
- else if (bus.exitEdge === "left") start.x += length7;
38755
- else start.x -= length7;
38752
+ if (bus.exitEdge === "bottom") start.y += length8;
38753
+ else if (bus.exitEdge === "top") start.y -= length8;
38754
+ else if (bus.exitEdge === "left") start.x += length8;
38755
+ else start.x -= length8;
38756
38756
  return {
38757
38757
  type: "pcb_trace",
38758
38758
  pcb_trace_id: `fanout-approach-reservation:${connection.connectionIndex}`,
@@ -43010,7 +43010,7 @@ function validateDifferentialPairs(inputSrj, plansByConnection, issues) {
43010
43010
  0
43011
43011
  )
43012
43012
  );
43013
- if (lengths.some((length7) => !Number.isFinite(length7))) {
43013
+ if (lengths.some((length8) => !Number.isFinite(length8))) {
43014
43014
  addIssue2(
43015
43015
  issues,
43016
43016
  "invalid-differential-pair",
@@ -49205,6 +49205,8 @@ function getPresetAutoroutingConfig(autorouterConfig, platformConfig) {
49205
49205
  groupMode: "subcircuit",
49206
49206
  preset: "single_layer_fanout"
49207
49207
  };
49208
+ case "bus-lanes":
49209
+ return { local: true, groupMode: "subcircuit", preset: "bus_lanes" };
49208
49210
  case "fanout":
49209
49211
  return {
49210
49212
  local: true,
@@ -49267,6 +49269,1153 @@ function getPresetAutoroutingConfig(autorouterConfig, platformConfig) {
49267
49269
  }
49268
49270
  }
49269
49271
 
49272
+ // node_modules/@tscircuit/bus-lanes-solver/lib/bus-lanes-solver.ts
49273
+ import { BaseSolver as BaseSolver4 } from "@tscircuit/solver-utils";
49274
+
49275
+ // node_modules/@tscircuit/bus-lanes-solver/lib/geometry.ts
49276
+ var distance12 = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
49277
+ function pointSegmentDistance(p, edge) {
49278
+ const [a, b] = edge, dx = b.x - a.x, dy = b.y - a.y, t = Math.max(
49279
+ 0,
49280
+ Math.min(
49281
+ 1,
49282
+ ((p.x - a.x) * dx + (p.y - a.y) * dy) / (dx * dx + dy * dy || 1)
49283
+ )
49284
+ );
49285
+ return Math.hypot(p.x - a.x - t * dx, p.y - a.y - t * dy);
49286
+ }
49287
+ var cross2 = (a, b, c) => (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
49288
+ function segmentDistance(ab, cd) {
49289
+ const [a, b] = ab, [c, d] = cd;
49290
+ if (Math.max(a.x, b.x) >= Math.min(c.x, d.x) && Math.max(c.x, d.x) >= Math.min(a.x, b.x) && Math.max(a.y, b.y) >= Math.min(c.y, d.y) && Math.max(c.y, d.y) >= Math.min(a.y, b.y) && cross2(a, b, c) * cross2(a, b, d) <= 0 && cross2(c, d, a) * cross2(c, d, b) <= 0)
49291
+ return 0;
49292
+ return Math.min(
49293
+ pointSegmentDistance(a, cd),
49294
+ pointSegmentDistance(b, cd),
49295
+ pointSegmentDistance(c, ab),
49296
+ pointSegmentDistance(d, ab)
49297
+ );
49298
+ }
49299
+ var length = (path) => path.slice(1).reduce((sum, p, i) => sum + distance12(path[i], p), 0);
49300
+ function simplify(path) {
49301
+ return path.filter(
49302
+ (p, i) => !i || i === path.length - 1 || Math.abs(cross2(path[i - 1], p, path[i + 1])) > 1e-9
49303
+ );
49304
+ }
49305
+
49306
+ // node_modules/@tscircuit/bus-lanes-solver/lib/vector-scene.ts
49307
+ function fixedCopper(input) {
49308
+ const result = [];
49309
+ for (const o of input.obstacles)
49310
+ for (const layer of o.layers)
49311
+ result.push({
49312
+ a: o.center,
49313
+ b: o.center,
49314
+ radius: 0,
49315
+ layer,
49316
+ owners: o.connectedTo,
49317
+ rect: {
49318
+ minX: o.center.x - o.width / 2,
49319
+ maxX: o.center.x + o.width / 2,
49320
+ minY: o.center.y - o.height / 2,
49321
+ maxY: o.center.y + o.height / 2
49322
+ }
49323
+ });
49324
+ const layers = Array.from(
49325
+ { length: input.layerCount },
49326
+ (_, i) => i === 0 ? "top" : i === input.layerCount - 1 ? "bottom" : `inner${i}`
49327
+ );
49328
+ for (const t of input.traces ?? [])
49329
+ for (let i = 0; i < t.route.length; i++) {
49330
+ const p = t.route[i], q = t.route[i + 1], owners = [t.connection_name ?? "", t.source_trace_id ?? ""];
49331
+ if (p.route_type === "via") {
49332
+ const a = layers.indexOf(p.from_layer), b = layers.indexOf(p.to_layer);
49333
+ for (const layer of p.layers ?? layers.slice(Math.min(a, b), Math.max(a, b) + 1))
49334
+ result.push({
49335
+ a: p,
49336
+ b: p,
49337
+ radius: (p.via_diameter ?? 0.3) / 2,
49338
+ layer,
49339
+ owners
49340
+ });
49341
+ } else if (q?.route_type === "wire") {
49342
+ if (p.layer !== q.layer)
49343
+ throw Error("Fixed trace changes layer without a via");
49344
+ result.push({ a: p, b: q, radius: p.width / 2, layer: p.layer, owners });
49345
+ }
49346
+ if (q && p.route_type !== q.route_type && distance12(p, q) > 1e-10) {
49347
+ const wire = p.route_type === "wire" ? p : q;
49348
+ result.push({
49349
+ a: p,
49350
+ b: q,
49351
+ radius: wire.width / 2,
49352
+ layer: wire.layer,
49353
+ owners
49354
+ });
49355
+ }
49356
+ }
49357
+ return result;
49358
+ }
49359
+ function routeCopper(t) {
49360
+ return t.route.slice(1).map((p, i) => ({
49361
+ a: t.route[i],
49362
+ b: p,
49363
+ radius: p.width / 2,
49364
+ layer: p.layer,
49365
+ owners: [t.connection_name ?? ""]
49366
+ }));
49367
+ }
49368
+ var intersectsRect = (a, b, r) => {
49369
+ let t0 = 0, t1 = 1;
49370
+ for (const [origin, delta, min, max] of [
49371
+ [a.x, b.x - a.x, r.minX, r.maxX],
49372
+ [a.y, b.y - a.y, r.minY, r.maxY]
49373
+ ]) {
49374
+ if (Math.abs(delta) < 1e-12) {
49375
+ if (origin < min || origin > max) return false;
49376
+ continue;
49377
+ }
49378
+ let lo = (min - origin) / delta, hi = (max - origin) / delta;
49379
+ if (lo > hi) [lo, hi] = [hi, lo];
49380
+ t0 = Math.max(t0, lo);
49381
+ t1 = Math.min(t1, hi);
49382
+ if (t0 > t1) return false;
49383
+ }
49384
+ return true;
49385
+ };
49386
+ function clearanceToCopper(a, b, c) {
49387
+ if (!c.rect) return segmentDistance([a, b], [c.a, c.b]) - c.radius;
49388
+ const r = c.rect;
49389
+ if (intersectsRect(a, b, r)) return 0;
49390
+ const corners = [
49391
+ { x: r.minX, y: r.minY },
49392
+ { x: r.maxX, y: r.minY },
49393
+ { x: r.maxX, y: r.maxY },
49394
+ { x: r.minX, y: r.maxY }
49395
+ ];
49396
+ return Math.min(
49397
+ ...corners.map(
49398
+ (p, i) => segmentDistance([a, b], [p, corners[(i + 1) % 4]])
49399
+ )
49400
+ );
49401
+ }
49402
+ var VectorScene = class {
49403
+ constructor(input, connection, width, all) {
49404
+ this.input = input;
49405
+ this.connection = connection;
49406
+ this.width = width;
49407
+ this.owners = new Set(
49408
+ [
49409
+ connection.name,
49410
+ connection.source_trace_id,
49411
+ ...connection.pointsToConnect.flatMap((p) => [
49412
+ p.pointId,
49413
+ p.pcb_port_id
49414
+ ])
49415
+ ].filter((s) => !!s)
49416
+ );
49417
+ this.copper = all.filter(
49418
+ (c) => c.layer === connection.pointsToConnect[0].layer && !c.owners.some((n) => this.owners.has(n))
49419
+ );
49420
+ this.margin = width / 2 + (input.minTraceToPadEdgeClearance ?? input.defaultObstacleMargin ?? 0.075);
49421
+ }
49422
+ input;
49423
+ connection;
49424
+ width;
49425
+ copper;
49426
+ owners;
49427
+ margin;
49428
+ visible(a, b) {
49429
+ const m = this.width / 2 + (this.input.minBoardEdgeClearance ?? 0), r = this.input.bounds;
49430
+ if ([a, b].some(
49431
+ (p) => p.x < r.minX + m - 1e-9 || p.x > r.maxX - m + 1e-9 || p.y < r.minY + m - 1e-9 || p.y > r.maxY - m + 1e-9
49432
+ ))
49433
+ return false;
49434
+ const minX = Math.min(a.x, b.x) - this.margin, maxX = Math.max(a.x, b.x) + this.margin, minY = Math.min(a.y, b.y) - this.margin, maxY = Math.max(a.y, b.y) + this.margin;
49435
+ for (const c of this.copper) {
49436
+ const r2 = c.rect ?? {
49437
+ minX: Math.min(c.a.x, c.b.x) - c.radius,
49438
+ maxX: Math.max(c.a.x, c.b.x) + c.radius,
49439
+ minY: Math.min(c.a.y, c.b.y) - c.radius,
49440
+ maxY: Math.max(c.a.y, c.b.y) + c.radius
49441
+ };
49442
+ if (r2.minX > maxX || r2.maxX < minX || r2.minY > maxY || r2.maxY < minY)
49443
+ continue;
49444
+ if (clearanceToCopper(a, b, c) < this.margin - 1e-8) return false;
49445
+ }
49446
+ return true;
49447
+ }
49448
+ pathVisible(path) {
49449
+ return path.slice(1).every((b, i) => this.visible(path[i], b));
49450
+ }
49451
+ /** Vertices of circumscribed octagonal Minkowski offsets. Their edges are
49452
+ * horizontal, vertical or 45 degrees, including circular vias and trace caps. */
49453
+ vertices() {
49454
+ const result = [], k = Math.SQRT2 - 1, epsilon = 1e-5;
49455
+ for (const c of this.copper) {
49456
+ if (c.rect) {
49457
+ const r = c.rect, m = this.margin + epsilon;
49458
+ result.push(
49459
+ { x: r.minX - m, y: r.minY - m },
49460
+ { x: r.minX - m, y: r.maxY + m },
49461
+ { x: r.maxX + m, y: r.minY - m },
49462
+ { x: r.maxX + m, y: r.maxY + m }
49463
+ );
49464
+ } else {
49465
+ const r = c.radius + this.margin + epsilon;
49466
+ for (const p of distance12(c.a, c.b) < 1e-9 ? [c.a] : [c.a, c.b])
49467
+ for (const [x, y] of [
49468
+ [1, k],
49469
+ [k, 1],
49470
+ [-k, 1],
49471
+ [-1, k],
49472
+ [-1, -k],
49473
+ [-k, -1],
49474
+ [k, -1],
49475
+ [1, -k]
49476
+ ])
49477
+ result.push({ x: p.x + x * r, y: p.y + y * r });
49478
+ }
49479
+ }
49480
+ const seen = /* @__PURE__ */ new Set();
49481
+ return result.filter((p) => {
49482
+ const key = `${p.x.toFixed(9)},${p.y.toFixed(9)}`;
49483
+ if (seen.has(key)) return false;
49484
+ seen.add(key);
49485
+ return this.visible(p, p);
49486
+ });
49487
+ }
49488
+ };
49489
+
49490
+ // node_modules/@tscircuit/bus-lanes-solver/lib/min-heap.ts
49491
+ var MinHeap2 = class {
49492
+ entries = [];
49493
+ sequence = 0;
49494
+ get length() {
49495
+ return this.entries.length;
49496
+ }
49497
+ values() {
49498
+ return this.entries.map((e) => e.value);
49499
+ }
49500
+ before(a, b) {
49501
+ return a.value.f < b.value.f || a.value.f === b.value.f && a.sequence < b.sequence;
49502
+ }
49503
+ push(value) {
49504
+ const entry = { value, sequence: this.sequence++ };
49505
+ let i = this.entries.length;
49506
+ this.entries.push(entry);
49507
+ while (i > 0) {
49508
+ const parent = i - 1 >> 1;
49509
+ if (!this.before(entry, this.entries[parent])) break;
49510
+ this.entries[i] = this.entries[parent];
49511
+ i = parent;
49512
+ }
49513
+ this.entries[i] = entry;
49514
+ }
49515
+ pop() {
49516
+ const first = this.entries[0], last = this.entries.pop();
49517
+ if (this.entries.length) {
49518
+ let i = 0;
49519
+ while (i * 2 + 1 < this.entries.length) {
49520
+ let child = i * 2 + 1;
49521
+ if (child + 1 < this.entries.length && this.before(this.entries[child + 1], this.entries[child]))
49522
+ child++;
49523
+ if (!this.before(this.entries[child], last)) break;
49524
+ this.entries[i] = this.entries[child];
49525
+ i = child;
49526
+ }
49527
+ this.entries[i] = last;
49528
+ }
49529
+ return first.value;
49530
+ }
49531
+ };
49532
+
49533
+ // node_modules/@tscircuit/bus-lanes-solver/lib/vector-visibility.ts
49534
+ function connectors(a, b) {
49535
+ const dx = b.x - a.x, dy = b.y - a.y, sx = Math.sign(dx), sy = Math.sign(dy), d = Math.min(Math.abs(dx), Math.abs(dy));
49536
+ if (Math.min(Math.abs(dx), Math.abs(dy)) < 1e-10 || Math.abs(Math.abs(dx) - Math.abs(dy)) < 1e-10)
49537
+ return [[a, b]];
49538
+ const lead = (Math.max(Math.abs(dx), Math.abs(dy)) - d) / 2;
49539
+ const centered = Math.abs(dx) > Math.abs(dy) ? [a, { x: a.x + sx * lead, y: a.y }, { x: b.x - sx * lead, y: b.y }, b] : [a, { x: a.x, y: a.y + sy * lead }, { x: b.x, y: b.y - sy * lead }, b];
49540
+ return [
49541
+ centered,
49542
+ [a, { x: a.x + sx * d, y: a.y + sy * d }, b],
49543
+ [a, { x: b.x - sx * d, y: b.y - sy * d }, b],
49544
+ [a, { x: a.x, y: b.y }, b],
49545
+ [a, { x: b.x, y: a.y }, b]
49546
+ ];
49547
+ }
49548
+ var VectorVisibilitySearch = class {
49549
+ constructor(scene, start, end) {
49550
+ this.scene = scene;
49551
+ this.start = start;
49552
+ this.end = end;
49553
+ this.vertices = [start, end];
49554
+ this.open.push({ id: 0, g: 0, f: distance12(start, end) });
49555
+ this.best.set(0, 0);
49556
+ }
49557
+ scene;
49558
+ start;
49559
+ end;
49560
+ vertices;
49561
+ open = new MinHeap2();
49562
+ best = /* @__PURE__ */ new Map();
49563
+ expanded = 0;
49564
+ failed = false;
49565
+ solved = false;
49566
+ result = [];
49567
+ geometryLoaded = false;
49568
+ current;
49569
+ visibleEdges = [];
49570
+ step() {
49571
+ if (!this.geometryLoaded) {
49572
+ this.geometryLoaded = true;
49573
+ const lowerBound = Math.max(
49574
+ Math.abs(this.end.x - this.start.x),
49575
+ Math.abs(this.end.y - this.start.y)
49576
+ ) + (Math.SQRT2 - 1) * Math.min(
49577
+ Math.abs(this.end.x - this.start.x),
49578
+ Math.abs(this.end.y - this.start.y)
49579
+ );
49580
+ for (const path of connectors(this.start, this.end)) {
49581
+ if (length(path) > lowerBound + 1e-9 || !this.scene.pathVisible(path))
49582
+ continue;
49583
+ this.visibleEdges = [path];
49584
+ this.result = simplify(path);
49585
+ this.solved = true;
49586
+ this.expanded = 1;
49587
+ return;
49588
+ }
49589
+ this.vertices.push(...this.scene.vertices());
49590
+ }
49591
+ if (!this.open.length) {
49592
+ this.failed = true;
49593
+ return;
49594
+ }
49595
+ const cur = this.open.pop();
49596
+ if (cur.g > (this.best.get(cur.id) ?? Infinity) + 1e-9) return;
49597
+ this.current = cur;
49598
+ this.expanded++;
49599
+ this.visibleEdges = [];
49600
+ if (cur.id === 1) {
49601
+ const edges = [];
49602
+ for (let c = cur; c?.parent; c = c.parent)
49603
+ edges.push(c.edge);
49604
+ this.result = simplify([
49605
+ this.start,
49606
+ ...edges.reverse().flatMap((p) => p.slice(1))
49607
+ ]);
49608
+ this.solved = true;
49609
+ return;
49610
+ }
49611
+ const from = this.vertices[cur.id];
49612
+ for (let id = 1; id < this.vertices.length; id++) {
49613
+ if (id === cur.id) continue;
49614
+ const to = this.vertices[id], lower = cur.g + distance12(from, to);
49615
+ if (lower >= (this.best.get(id) ?? Infinity) - 1e-9) continue;
49616
+ const candidates = connectors(from, to).sort(
49617
+ (a, b) => length(a) - length(b)
49618
+ );
49619
+ for (const path of candidates) {
49620
+ const g = cur.g + length(path);
49621
+ if (g >= (this.best.get(id) ?? Infinity) - 1e-9) continue;
49622
+ if (!this.scene.pathVisible(path)) continue;
49623
+ this.best.set(id, g);
49624
+ this.open.push({
49625
+ id,
49626
+ g,
49627
+ f: g + distance12(to, this.end),
49628
+ parent: cur,
49629
+ edge: path
49630
+ });
49631
+ if (this.visibleEdges.length < 100) this.visibleEdges.push(path);
49632
+ break;
49633
+ }
49634
+ }
49635
+ }
49636
+ currentPath() {
49637
+ const edges = [];
49638
+ for (let c = this.current; c?.parent; c = c.parent) edges.push(c.edge);
49639
+ return [this.start, ...edges.reverse().flatMap((e) => e.slice(1))];
49640
+ }
49641
+ };
49642
+
49643
+ // node_modules/@tscircuit/bus-lanes-solver/lib/winding-orders.ts
49644
+ function windingOrders(connections) {
49645
+ const orders = [];
49646
+ const seen = /* @__PURE__ */ new Set();
49647
+ const add = (order) => {
49648
+ const key = order.map((c) => c.name).join("\0");
49649
+ if (!seen.has(key)) {
49650
+ seen.add(key);
49651
+ orders.push(order);
49652
+ }
49653
+ };
49654
+ const direction = connections.reduce(
49655
+ (v, c) => ({
49656
+ x: v.x + c.pointsToConnect[1].x - c.pointsToConnect[0].x,
49657
+ y: v.y + c.pointsToConnect[1].y - c.pointsToConnect[0].y
49658
+ }),
49659
+ { x: 0, y: 0 }
49660
+ );
49661
+ const transverse = (c) => -direction.y * c.pointsToConnect[0].x + direction.x * c.pointsToConnect[0].y;
49662
+ const sweep = [...connections].sort(
49663
+ (a, b) => a.pointsToConnect[0].layer.localeCompare(b.pointsToConnect[0].layer) || transverse(a) - transverse(b) || a.name.localeCompare(b.name)
49664
+ );
49665
+ const span = (endpoint, axis) => Math.max(...connections.map((c) => c.pointsToConnect[endpoint][axis])) - Math.min(...connections.map((c) => c.pointsToConnect[endpoint][axis]));
49666
+ const transverseAxis = span(0, "x") < 1e-7 && span(1, "x") < 1e-7 ? "y" : span(0, "y") < 1e-7 && span(1, "y") < 1e-7 ? "x" : void 0;
49667
+ if (transverseAxis) {
49668
+ const drift = connections.reduce(
49669
+ (s, c) => s + c.pointsToConnect[1][transverseAxis] - c.pointsToConnect[0][transverseAxis],
49670
+ 0
49671
+ );
49672
+ if (Math.abs(drift) > 1e-7)
49673
+ add(
49674
+ [...connections].sort(
49675
+ (a, b) => a.pointsToConnect[0].layer.localeCompare(
49676
+ b.pointsToConnect[0].layer
49677
+ ) || -Math.sign(drift) * (a.pointsToConnect[0][transverseAxis] - b.pointsToConnect[0][transverseAxis]) || a.name.localeCompare(b.name)
49678
+ )
49679
+ );
49680
+ }
49681
+ add(sweep);
49682
+ add([...sweep].reverse());
49683
+ add(connections);
49684
+ for (const endpoint of [1, 0]) {
49685
+ const center = connections.reduce(
49686
+ (p, c) => ({
49687
+ x: p.x + c.pointsToConnect[endpoint].x / connections.length,
49688
+ y: p.y + c.pointsToConnect[endpoint].y / connections.length
49689
+ }),
49690
+ { x: 0, y: 0 }
49691
+ );
49692
+ const angle = (c) => Math.atan2(
49693
+ c.pointsToConnect[endpoint].y - center.y,
49694
+ c.pointsToConnect[endpoint].x - center.x
49695
+ );
49696
+ const angular = [...connections].sort(
49697
+ (a, b) => angle(a) - angle(b) || a.name.localeCompare(b.name)
49698
+ );
49699
+ add(angular);
49700
+ add([...angular].reverse());
49701
+ for (const axis of ["x", "y"]) {
49702
+ const other = axis === "x" ? "y" : "x";
49703
+ const sorted = [...connections].sort(
49704
+ (a, b) => a.pointsToConnect[endpoint][axis] - b.pointsToConnect[endpoint][axis] || a.pointsToConnect[endpoint][other] - b.pointsToConnect[endpoint][other]
49705
+ );
49706
+ add(sorted);
49707
+ add([...sorted].reverse());
49708
+ }
49709
+ for (let i = 1; i < angular.length; i++) {
49710
+ const rotated = [...angular.slice(i), ...angular.slice(0, i)];
49711
+ add(rotated);
49712
+ add([...rotated].reverse());
49713
+ }
49714
+ }
49715
+ return orders;
49716
+ }
49717
+
49718
+ // node_modules/@tscircuit/bus-lanes-solver/lib/route-lengths.ts
49719
+ function fixedRouteLength(input, name) {
49720
+ const connection = input.connections.find((c) => c.name === name);
49721
+ return (input.traces ?? []).filter(
49722
+ (t) => t.connection_name === name || t.source_trace_id === (connection?.source_trace_id ?? name)
49723
+ ).reduce((sum, t) => sum + length(t.route), 0);
49724
+ }
49725
+ function busLengthReports(input, traces) {
49726
+ return (input.buses ?? []).map((bus) => {
49727
+ const lengths = bus.connectionNames.map((name) => {
49728
+ const route = traces.find((t) => t.connection_name === name);
49729
+ return {
49730
+ name,
49731
+ carrierLengthMm: route ? length(route.route) : null,
49732
+ fixedLengthMm: fixedRouteLength(input, name),
49733
+ totalLengthMm: route ? length(route.route) + fixedRouteLength(input, name) : null
49734
+ };
49735
+ });
49736
+ const complete = lengths.every((l) => l.totalLengthMm !== null);
49737
+ const values = lengths.map((l) => l.totalLengthMm ?? 0);
49738
+ const skewMm = complete && values.length ? Math.max(...values) - Math.min(...values) : null;
49739
+ return {
49740
+ busId: bus.busId,
49741
+ toleranceMm: bus.maxLengthSkew ?? null,
49742
+ skewMm,
49743
+ matched: bus.maxLengthSkew !== void 0 && skewMm !== null && skewMm <= bus.maxLengthSkew + 1e-7,
49744
+ lengths
49745
+ };
49746
+ });
49747
+ }
49748
+ function lengthConstraints(input) {
49749
+ return [
49750
+ ...(input.buses ?? []).filter((b) => b.maxLengthSkew !== void 0).map((b) => ({
49751
+ names: b.connectionNames,
49752
+ tolerance: b.maxLengthSkew
49753
+ })),
49754
+ ...(input.differentialPairs ?? []).map((p) => ({
49755
+ names: p.connectionNames,
49756
+ tolerance: p.lengthTolerance
49757
+ }))
49758
+ ];
49759
+ }
49760
+ function minimumLengthTargets(input, traces) {
49761
+ const targets = new Map(
49762
+ traces.map((t) => [
49763
+ t.connection_name,
49764
+ length(t.route) + fixedRouteLength(input, t.connection_name)
49765
+ ])
49766
+ );
49767
+ const constraints = lengthConstraints(input);
49768
+ for (let pass = 0; pass < targets.size; pass++) {
49769
+ let changed = false;
49770
+ for (const { names, tolerance } of constraints) {
49771
+ const floor = Math.max(...names.map((n) => targets.get(n))) - tolerance;
49772
+ for (const name of names)
49773
+ if (targets.get(name) < floor - 1e-9) {
49774
+ targets.set(name, floor);
49775
+ changed = true;
49776
+ }
49777
+ }
49778
+ if (!changed) break;
49779
+ }
49780
+ return targets;
49781
+ }
49782
+ function pairLengthReports(input, traces) {
49783
+ return busLengthReports(
49784
+ {
49785
+ ...input,
49786
+ buses: (input.differentialPairs ?? []).map((p, i) => ({
49787
+ busId: `pair_${i}`,
49788
+ connectionNames: p.connectionNames,
49789
+ maxLengthSkew: p.lengthTolerance
49790
+ }))
49791
+ },
49792
+ traces
49793
+ );
49794
+ }
49795
+
49796
+ // node_modules/@tscircuit/bus-lanes-solver/lib/spread-tuning-lanes.ts
49797
+ function spreadTuningLanes(input, traces, pitch) {
49798
+ const dx = traces.reduce((s, t) => s + t.route.at(-1).x - t.route[0].x, 0);
49799
+ const dy = traces.reduce((s, t) => s + t.route.at(-1).y - t.route[0].y, 0);
49800
+ const angle = Math.round(Math.atan2(dy, dx) / (Math.PI / 4)) * Math.PI / 4, ux = Math.cos(angle), uy = Math.sin(angle);
49801
+ const uv = (p) => ({ u: p.x * ux + p.y * uy, v: -p.x * uy + p.y * ux });
49802
+ const xy = (u, v) => ({
49803
+ x: u * ux - v * uy,
49804
+ y: u * uy + v * ux
49805
+ });
49806
+ const result = [];
49807
+ for (const layer of new Set(traces.map((t) => t.route[0].layer))) {
49808
+ const group = traces.filter((t) => t.route[0].layer === layer).sort((a, b) => uv(a.route[0]).v - uv(b.route[0]).v);
49809
+ const center = group.reduce(
49810
+ (s, t) => s + (uv(t.route[0]).v + uv(t.route.at(-1)).v) / 2,
49811
+ 0
49812
+ ) / group.length;
49813
+ for (const [index, t] of group.entries()) {
49814
+ const a = uv(t.route[0]), b = uv(t.route.at(-1)), v = center + (index - (group.length - 1) / 2) * pitch;
49815
+ const da = Math.abs(v - a.v), db = Math.abs(v - b.v), space = b.u - a.u - da - db;
49816
+ if (space < 1) return null;
49817
+ const lead = space * 0.15;
49818
+ const points = simplify([
49819
+ xy(a.u, a.v),
49820
+ xy(a.u + lead, a.v),
49821
+ xy(a.u + lead + da, v),
49822
+ xy(b.u - lead - db, v),
49823
+ xy(b.u - lead, b.v),
49824
+ xy(b.u, b.v)
49825
+ ]);
49826
+ result.push({
49827
+ ...t,
49828
+ route: points.map((p) => ({
49829
+ ...p,
49830
+ route_type: "wire",
49831
+ layer,
49832
+ width: t.route[0].width
49833
+ }))
49834
+ });
49835
+ }
49836
+ }
49837
+ const copper = [...fixedCopper(input), ...result.flatMap(routeCopper)];
49838
+ for (const t of result) {
49839
+ const c = input.connections.find((c2) => c2.name === t.connection_name);
49840
+ if (!new VectorScene(
49841
+ input,
49842
+ c,
49843
+ t.route[0].width,
49844
+ copper
49845
+ ).pathVisible(t.route))
49846
+ return null;
49847
+ }
49848
+ return traces.map(
49849
+ (t) => result.find((r) => r.connection_name === t.connection_name)
49850
+ );
49851
+ }
49852
+
49853
+ // node_modules/@tscircuit/bus-lanes-solver/lib/length-tuning.ts
49854
+ function tuneLengths(input, traces, targets) {
49855
+ const fixed = fixedCopper(input);
49856
+ function* candidates(t, scene) {
49857
+ const connection = input.connections.find(
49858
+ (c) => c.name === t.connection_name
49859
+ );
49860
+ const width = t.route[0].width;
49861
+ const delta = targets.get(connection.name) - length(t.route) - fixedRouteLength(input, connection.name);
49862
+ if (delta < 1e-8) {
49863
+ yield t;
49864
+ return;
49865
+ }
49866
+ const clearance = input.minTraceToPadEdgeClearance ?? input.defaultObstacleMargin ?? 0.075;
49867
+ const returnSpacing = Math.max(width + clearance, 3 * width);
49868
+ const pitch = 2 * (returnSpacing + 2 * width);
49869
+ const segments = t.route.slice(1).map((p, i) => ({ i, span: distance12(t.route[i], p) })).sort((a, b) => b.span - a.span);
49870
+ for (const { i } of segments) {
49871
+ const a = t.route[i], b = t.route[i + 1], span = distance12(a, b);
49872
+ if (span < 0.01) continue;
49873
+ const ux = (b.x - a.x) / span, uy = (b.y - a.y) / span;
49874
+ const maximumTeeth = Math.floor(span * 0.9 / pitch);
49875
+ const preferredTeeth = Math.min(
49876
+ maximumTeeth,
49877
+ Math.max(2, Math.ceil(delta / (12 * width)))
49878
+ );
49879
+ const counts = Array.from({ length: maximumTeeth }, (_, i2) => i2 + 1).sort(
49880
+ (a2, b2) => Math.abs(a2 - preferredTeeth) - Math.abs(b2 - preferredTeeth) || b2 - a2
49881
+ );
49882
+ for (const teeth of counts) {
49883
+ for (const fraction of [0.9, 0.65, 0.4]) {
49884
+ const w = span * fraction / teeth;
49885
+ if (w < pitch) continue;
49886
+ const c = Math.min(
49887
+ delta / (2 * teeth),
49888
+ w / 10,
49889
+ (w / 2 - returnSpacing) / 2
49890
+ );
49891
+ const h = delta / (2 * teeth) + 2 * c * (2 - Math.SQRT2);
49892
+ for (const phase of [0.5, 0, 1])
49893
+ for (const side of [1, -1]) {
49894
+ const at = (x, y) => ({
49895
+ x: a.x + ux * x - uy * y * side,
49896
+ y: a.y + uy * x + ux * y * side
49897
+ });
49898
+ const bump = [at(0, 0)];
49899
+ for (let tooth = 0; tooth < teeth; tooth++) {
49900
+ const offset = span * (1 - fraction) * (0.05 + 0.9 * phase) + tooth * w, gap = w * 0.5;
49901
+ bump.push(
49902
+ ...[
49903
+ [offset, 0],
49904
+ [offset + c, c],
49905
+ [offset + c, h - c],
49906
+ [offset + 2 * c, h],
49907
+ [offset + gap - 2 * c, h],
49908
+ [offset + gap - c, h - c],
49909
+ [offset + gap - c, c],
49910
+ [offset + gap, 0]
49911
+ ].map(([x, y]) => at(x, y))
49912
+ );
49913
+ }
49914
+ bump.push(at(span, 0));
49915
+ if (!scene.pathVisible(bump)) continue;
49916
+ const next = simplify([
49917
+ ...t.route.slice(0, i),
49918
+ ...bump,
49919
+ ...t.route.slice(i + 2)
49920
+ ]);
49921
+ if (Math.abs(
49922
+ length(next) + fixedRouteLength(input, connection.name) - targets.get(connection.name)
49923
+ ) > 1e-6)
49924
+ continue;
49925
+ if (!tuningPathIsSelfClear(next, returnSpacing)) continue;
49926
+ yield {
49927
+ ...t,
49928
+ route: next.map((p) => ({
49929
+ ...p,
49930
+ route_type: "wire",
49931
+ layer: connection.pointsToConnect[0].layer,
49932
+ width
49933
+ }))
49934
+ };
49935
+ }
49936
+ }
49937
+ }
49938
+ }
49939
+ }
49940
+ const result = [...traces];
49941
+ const pending = new Set(traces.map((_, i) => i));
49942
+ let changed = true;
49943
+ while (pending.size && changed) {
49944
+ changed = false;
49945
+ for (const index of pending) {
49946
+ const connection = input.connections.find(
49947
+ (c) => c.name === traces[index].connection_name
49948
+ );
49949
+ const scene = new VectorScene(
49950
+ input,
49951
+ connection,
49952
+ traces[index].route[0].width,
49953
+ [...fixed, ...result.flatMap(routeCopper)]
49954
+ );
49955
+ const next = candidates(traces[index], scene).next().value;
49956
+ if (!next) continue;
49957
+ result[index] = next;
49958
+ pending.delete(index);
49959
+ changed = true;
49960
+ }
49961
+ }
49962
+ if (pending.size)
49963
+ throw Error(
49964
+ `Insufficient tuning clearance for ${[...pending].map((i) => traces[i].connection_name).join(", ")}`
49965
+ );
49966
+ return result;
49967
+ }
49968
+ function tuningPathIsSelfClear(path, required) {
49969
+ const cumulative = [0];
49970
+ for (let i = 1; i < path.length; i++)
49971
+ cumulative.push(cumulative[i - 1] + distance12(path[i - 1], path[i]));
49972
+ for (let i = 0; i < path.length - 1; i++)
49973
+ for (let j = i + 2; j < path.length - 1; j++) {
49974
+ if (cumulative[j] - cumulative[i + 1] <= required + 1e-8) continue;
49975
+ const a = path[i], b = path[i + 1], c = path[j], d = path[j + 1];
49976
+ if (Math.max(a.x, b.x) + required < Math.min(c.x, d.x) || Math.max(c.x, d.x) + required < Math.min(a.x, b.x) || Math.max(a.y, b.y) + required < Math.min(c.y, d.y) || Math.max(c.y, d.y) + required < Math.min(a.y, b.y))
49977
+ continue;
49978
+ if (segmentDistance([a, b], [c, d]) >= required - 1e-8) continue;
49979
+ let sx = 0, sy = 0;
49980
+ for (let k = i; k <= j; k++) {
49981
+ const dx = path[k + 1].x - path[k].x, dy = path[k + 1].y - path[k].y;
49982
+ if (Math.abs(dx) > 1e-8) {
49983
+ if (sx && sx !== Math.sign(dx)) return false;
49984
+ sx = Math.sign(dx);
49985
+ }
49986
+ if (Math.abs(dy) > 1e-8) {
49987
+ if (sy && sy !== Math.sign(dy)) return false;
49988
+ sy = Math.sign(dy);
49989
+ }
49990
+ }
49991
+ }
49992
+ return true;
49993
+ }
49994
+
49995
+ // node_modules/@tscircuit/bus-lanes-solver/lib/layer-colors.ts
49996
+ var layerColors = {
49997
+ top: "#dc2626",
49998
+ inner1: "#ca8a04",
49999
+ inner2: "#16a34a",
50000
+ inner3: "#0891b2",
50001
+ inner4: "#9333ea",
50002
+ inner5: "#ea580c",
50003
+ inner6: "#db2777",
50004
+ bottom: "#2563eb"
50005
+ };
50006
+ var layerColor = (layer) => layerColors[layer] ?? "#64748b";
50007
+
50008
+ // node_modules/@tscircuit/bus-lanes-solver/lib/bus-lanes-solver.ts
50009
+ var BusLanesSolver = class extends BaseSolver4 {
50010
+ input;
50011
+ options;
50012
+ phase = "validate";
50013
+ failureCode = null;
50014
+ traces = [];
50015
+ widths = /* @__PURE__ */ new Map();
50016
+ fixed = [];
50017
+ orders = [];
50018
+ attempt = 0;
50019
+ lane = 0;
50020
+ search;
50021
+ bestPartial = [];
50022
+ constructor(input, options = {}) {
50023
+ super();
50024
+ this.input = structuredClone(input);
50025
+ this.options = options;
50026
+ this.MAX_ITERATIONS = options.maxSearchIterations ?? 2e5;
50027
+ }
50028
+ getConstructorParams() {
50029
+ return [this.input, this.options];
50030
+ }
50031
+ getOutput() {
50032
+ if (!this.solved) throw Error(this.error ?? "Bus lanes are not solved");
50033
+ return {
50034
+ ...this.input,
50035
+ traces: [...this.input.traces ?? [], ...this.traces]
50036
+ };
50037
+ }
50038
+ fail(code, message) {
50039
+ this.failureCode = code;
50040
+ this.error = message;
50041
+ this.failed = true;
50042
+ this.phase = "failed";
50043
+ }
50044
+ tryFinalAcceptance() {
50045
+ if (!this.solved)
50046
+ this.fail(
50047
+ "search_budget_exhausted",
50048
+ "Vector visibility search budget exhausted"
50049
+ );
50050
+ }
50051
+ initialize() {
50052
+ const input = this.input;
50053
+ if (input.outline?.length) {
50054
+ this.fail("unsupported_outline", "Custom board outlines are unsupported");
50055
+ return;
50056
+ }
50057
+ if (input.differentialPairs?.some(
50058
+ (p) => p.traceGap !== void 0 || p.maxUncoupledLength !== void 0
50059
+ )) {
50060
+ this.fail(
50061
+ "unsupported_coupling",
50062
+ "Coupled differential geometry is not supported"
50063
+ );
50064
+ return;
50065
+ }
50066
+ const names = /* @__PURE__ */ new Set();
50067
+ for (const c of input.connections) {
50068
+ if (names.has(c.name)) throw Error(`Duplicate connection ${c.name}`);
50069
+ names.add(c.name);
50070
+ if (c.pointsToConnect.length !== 2) {
50071
+ this.fail(
50072
+ "invalid_terminals",
50073
+ `${c.name}: exactly two terminals required`
50074
+ );
50075
+ return;
50076
+ }
50077
+ const [a, b] = c.pointsToConnect;
50078
+ if (a.layers || b.layers || a.layer !== b.layer) {
50079
+ this.fail(
50080
+ "layer_change_required",
50081
+ `${c.name}: ${a.layer} \u2192 ${b.layer}; a common fixed layer is required; fanout handoff layers must agree`
50082
+ );
50083
+ return;
50084
+ }
50085
+ if (![a.x, a.y, b.x, b.y].every(Number.isFinite))
50086
+ throw Error("Nonfinite terminal");
50087
+ this.widths.set(
50088
+ c.name,
50089
+ c.nominalTraceWidth ?? c.width ?? input.minTraceWidth
50090
+ );
50091
+ }
50092
+ for (const b of input.buses ?? [])
50093
+ if (b.maxLengthSkew !== void 0 && (!Number.isFinite(b.maxLengthSkew) || b.maxLengthSkew < 0))
50094
+ throw Error("Invalid maximum length skew");
50095
+ for (const { names: members, tolerance } of lengthConstraints(input)) {
50096
+ if (!Number.isFinite(tolerance) || tolerance < 0)
50097
+ throw Error("Invalid maximum length skew");
50098
+ if (members.length < 1 || new Set(members).size !== members.length || members.some((n) => !names.has(n)))
50099
+ throw Error("Invalid length matching members");
50100
+ }
50101
+ const claimed = /* @__PURE__ */ new Set();
50102
+ for (const b of input.buses ?? [])
50103
+ for (const name of b.connectionNames) {
50104
+ const c = input.connections.find((c2) => c2.name === name);
50105
+ if (!c || claimed.has(name))
50106
+ throw Error(`Missing or duplicated bus member ${name}`);
50107
+ claimed.add(name);
50108
+ const layer = c.pointsToConnect[0].layer;
50109
+ if (b.allowedLayers && !b.allowedLayers.includes(layer))
50110
+ throw Error(`${b.busId}: forbidden layer ${layer}`);
50111
+ const width = b.traceWidth;
50112
+ if (width !== void 0) this.widths.set(name, width);
50113
+ }
50114
+ for (const width of this.widths.values())
50115
+ if (!Number.isFinite(width) || width <= 0 || width < input.minTraceWidth)
50116
+ throw Error("Invalid trace width");
50117
+ this.fixed = fixedCopper(input);
50118
+ for (const c of input.connections)
50119
+ for (const p of c.pointsToConnect)
50120
+ this.fixed.push({
50121
+ a: p,
50122
+ b: p,
50123
+ layer: p.layer,
50124
+ radius: this.widths.get(c.name) / 2,
50125
+ owners: [c.name]
50126
+ });
50127
+ this.orders = windingOrders(input.connections);
50128
+ if (!input.connections.length) {
50129
+ this.solved = true;
50130
+ this.phase = "solved";
50131
+ return;
50132
+ }
50133
+ this.phase = "route";
50134
+ this.startLane();
50135
+ }
50136
+ scene(c) {
50137
+ return new VectorScene(this.input, c, this.widths.get(c.name), [
50138
+ ...this.fixed,
50139
+ ...this.traces.flatMap(routeCopper)
50140
+ ]);
50141
+ }
50142
+ startLane() {
50143
+ if (this.lane === this.input.connections.length) {
50144
+ this.phase = "match";
50145
+ return;
50146
+ }
50147
+ const c = this.orders[this.attempt][this.lane], [a, b] = c.pointsToConnect;
50148
+ this.search = new VectorVisibilitySearch(this.scene(c), a, b);
50149
+ }
50150
+ retry() {
50151
+ if (this.traces.length > this.bestPartial.length)
50152
+ this.bestPartial = structuredClone(this.traces);
50153
+ this.attempt++;
50154
+ if (this.attempt >= this.orders.length) {
50155
+ this.traces = this.bestPartial;
50156
+ this.fail(
50157
+ "no_planar_route",
50158
+ "No collision-free routing found in the vector visibility graph"
50159
+ );
50160
+ return;
50161
+ }
50162
+ this.traces = [];
50163
+ this.lane = 0;
50164
+ this.startLane();
50165
+ }
50166
+ route() {
50167
+ const s = this.search;
50168
+ s.step();
50169
+ if (s.solved) {
50170
+ const c = this.orders[this.attempt][this.lane];
50171
+ this.traces.push({
50172
+ type: "pcb_trace",
50173
+ pcb_trace_id: `bus_lane_${c.name}`,
50174
+ connection_name: c.name,
50175
+ source_trace_id: c.source_trace_id ?? c.name,
50176
+ route: s.result.map((p) => ({
50177
+ route_type: "wire",
50178
+ x: p.x,
50179
+ y: p.y,
50180
+ layer: c.pointsToConnect[0].layer,
50181
+ width: this.widths.get(c.name)
50182
+ }))
50183
+ });
50184
+ this.lane++;
50185
+ this.startLane();
50186
+ } else if (s.failed || s.expanded >= (this.options.maxLaneIterations ?? 4e3))
50187
+ this.retry();
50188
+ }
50189
+ match() {
50190
+ const original = this.traces;
50191
+ const input = this.input;
50192
+ function* candidates() {
50193
+ yield original;
50194
+ const width = Math.max(...original.map((t) => t.route[0].width));
50195
+ for (const multiplier of [16, 24, 28, 32]) {
50196
+ const spread = spreadTuningLanes(input, original, width * multiplier);
50197
+ if (spread) yield spread;
50198
+ }
50199
+ }
50200
+ let error;
50201
+ for (const candidate of candidates()) {
50202
+ const targets = minimumLengthTargets(input, candidate);
50203
+ try {
50204
+ this.traces = tuneLengths(input, candidate, targets);
50205
+ this.phase = "validate_output";
50206
+ return;
50207
+ } catch (e) {
50208
+ error = e;
50209
+ }
50210
+ }
50211
+ this.fail("length_matching_failed", String(error));
50212
+ return;
50213
+ }
50214
+ validateOutput() {
50215
+ for (const c of this.input.connections) {
50216
+ const t = this.traces.find((t2) => t2.connection_name === c.name);
50217
+ if (!t) throw Error("Missing lane");
50218
+ const scene = this.scene(c);
50219
+ if (distance12(t.route[0], c.pointsToConnect[0]) > 1e-8 || distance12(t.route.at(-1), c.pointsToConnect[1]) > 1e-8)
50220
+ throw Error("Broken lane endpoints");
50221
+ for (let i = 1; i < t.route.length; i++) {
50222
+ const a = t.route[i - 1], b = t.route[i], dx = Math.abs(a.x - b.x), dy = Math.abs(a.y - b.y);
50223
+ if (a.route_type !== "wire" || b.route_type !== "wire" || a.layer !== b.layer)
50224
+ throw Error("Forbidden layer transition");
50225
+ if (Math.min(dx, dy) > 1e-8 && Math.abs(dx - dy) > 1e-8)
50226
+ throw Error("Non-octilinear segment");
50227
+ if (!scene.visible(a, b))
50228
+ throw Error("Final copper clearance violation");
50229
+ }
50230
+ }
50231
+ if ([
50232
+ ...busLengthReports(this.input, this.traces),
50233
+ ...pairLengthReports(this.input, this.traces)
50234
+ ].some((b) => b.toleranceMm !== null && !b.matched))
50235
+ throw Error("Final bus length skew violation");
50236
+ this.phase = "solved";
50237
+ this.solved = true;
50238
+ }
50239
+ _step() {
50240
+ try {
50241
+ if (this.phase === "validate") this.initialize();
50242
+ else if (this.phase === "route") this.route();
50243
+ else if (this.phase === "match") this.match();
50244
+ else if (this.phase === "validate_output") this.validateOutput();
50245
+ } catch (e) {
50246
+ this.fail("constraint_error", String(e));
50247
+ }
50248
+ this.progress = this.lane / Math.max(1, this.input.connections.length);
50249
+ this.stats = {
50250
+ busLengths: busLengthReports(this.input, this.traces),
50251
+ pairLengths: pairLengthReports(this.input, this.traces),
50252
+ phase: this.phase,
50253
+ algorithm: "octilinear_visibility",
50254
+ attempt: this.attempt,
50255
+ lane: this.lane,
50256
+ totalLanes: this.input.connections.length,
50257
+ vertices: this.search?.vertices.length ?? 0,
50258
+ expandedVertices: this.search?.expanded ?? 0,
50259
+ frontier: this.search?.open.length ?? 0,
50260
+ failureCode: this.failureCode,
50261
+ traceLengthsMm: this.traces.map((t) => ({
50262
+ name: t.connection_name,
50263
+ length: length(t.route)
50264
+ }))
50265
+ };
50266
+ }
50267
+ visualize() {
50268
+ const lines = [], points = [], rects = [], circles = [];
50269
+ for (const c of fixedCopper(this.input)) {
50270
+ if (c.rect) {
50271
+ rects.push({
50272
+ center: {
50273
+ x: (c.rect.minX + c.rect.maxX) / 2,
50274
+ y: (c.rect.minY + c.rect.maxY) / 2
50275
+ },
50276
+ width: c.rect.maxX - c.rect.minX,
50277
+ height: c.rect.maxY - c.rect.minY,
50278
+ layer: c.layer,
50279
+ fill: `${layerColor(c.layer)}30`,
50280
+ stroke: layerColor(c.layer)
50281
+ });
50282
+ continue;
50283
+ }
50284
+ if (distance12(c.a, c.b) < 1e-9)
50285
+ circles.push({
50286
+ center: c.a,
50287
+ radius: c.radius,
50288
+ layer: c.layer,
50289
+ fill: "transparent",
50290
+ stroke: layerColor(c.layer)
50291
+ });
50292
+ else
50293
+ lines.push({
50294
+ points: [
50295
+ { x: c.a.x, y: c.a.y },
50296
+ { x: c.b.x, y: c.b.y }
50297
+ ],
50298
+ strokeWidth: c.radius * 2,
50299
+ strokeColor: layerColor(c.layer),
50300
+ layer: c.layer,
50301
+ label: `Fixed fanout [${c.layer}]`
50302
+ });
50303
+ }
50304
+ for (const t of this.traces)
50305
+ lines.push({
50306
+ points: t.route.map(({ x, y }) => ({ x, y })),
50307
+ strokeWidth: t.route[0].width,
50308
+ strokeColor: layerColor(t.route[0].layer),
50309
+ layer: t.route[0].layer,
50310
+ label: t.connection_name
50311
+ });
50312
+ for (const c of this.input.connections)
50313
+ for (const [i, p] of c.pointsToConnect.entries())
50314
+ points.push({
50315
+ ...p,
50316
+ color: i ? "#d97706" : "#2563eb",
50317
+ label: `${c.name} ${i ? "target" : "source"} [${p.layer}]`
50318
+ });
50319
+ if (this.search && this.phase === "route") {
50320
+ const layer = this.orders[this.attempt][this.lane]?.pointsToConnect[0].layer;
50321
+ for (const edge of this.search.visibleEdges)
50322
+ lines.push({
50323
+ points: edge,
50324
+ strokeWidth: 0.02,
50325
+ strokeColor: "#67e8f980",
50326
+ layer
50327
+ });
50328
+ for (const n of this.search.open.values().slice(0, 80))
50329
+ points.push({ ...this.search.vertices[n.id], color: "#06b6d4", layer });
50330
+ lines.push({
50331
+ points: this.search.currentPath(),
50332
+ strokeColor: "#f43f5e",
50333
+ strokeWidth: 0.09,
50334
+ layer
50335
+ });
50336
+ }
50337
+ return {
50338
+ title: `Vector bus lanes \xB7 ${this.phase}`,
50339
+ lines,
50340
+ points,
50341
+ rects,
50342
+ circles
50343
+ };
50344
+ }
50345
+ };
50346
+
50347
+ // lib/utils/autorouting/BusLanesAutorouter.ts
50348
+ var BusLanesAutorouter = class {
50349
+ constructor(input) {
50350
+ this.input = input;
50351
+ this.solver = new BusLanesSolver(input);
50352
+ }
50353
+ input;
50354
+ isRouting = false;
50355
+ solver;
50356
+ timer;
50357
+ listeners = [];
50358
+ on(event, callback) {
50359
+ this.listeners.push({
50360
+ event,
50361
+ callback
50362
+ });
50363
+ }
50364
+ emit(event) {
50365
+ for (const listener of this.listeners)
50366
+ if (listener.event === event.type) listener.callback(event);
50367
+ }
50368
+ start() {
50369
+ if (this.isRouting) return;
50370
+ this.isRouting = true;
50371
+ const tick = () => {
50372
+ if (!this.isRouting) return;
50373
+ try {
50374
+ for (let i = 0; i < 200 && !this.solver.solved && !this.solver.failed; i++)
50375
+ this.solver.step();
50376
+ if (this.solver.failed) {
50377
+ this.isRouting = false;
50378
+ this.emit({
50379
+ type: "error",
50380
+ error: new Error(this.solver.error ?? "Bus lanes routing failed")
50381
+ });
50382
+ return;
50383
+ }
50384
+ if (this.solver.solved) {
50385
+ this.isRouting = false;
50386
+ this.emit({ type: "complete", traces: this.solver.traces });
50387
+ return;
50388
+ }
50389
+ this.emit({
50390
+ type: "progress",
50391
+ steps: this.solver.iterations,
50392
+ progress: this.solver.progress,
50393
+ phase: this.solver.phase,
50394
+ debugGraphics: this.solver.visualize()
50395
+ });
50396
+ this.timer = setTimeout(tick, 0);
50397
+ } catch (error) {
50398
+ this.isRouting = false;
50399
+ this.emit({
50400
+ type: "error",
50401
+ error: error instanceof Error ? error : new Error(String(error))
50402
+ });
50403
+ }
50404
+ };
50405
+ this.timer = setTimeout(tick, 0);
50406
+ }
50407
+ stop() {
50408
+ this.isRouting = false;
50409
+ if (this.timer) clearTimeout(this.timer);
50410
+ }
50411
+ solveSync() {
50412
+ this.solver.solve();
50413
+ if (this.solver.failed)
50414
+ throw new Error(this.solver.error ?? "Bus lanes routing failed");
50415
+ return this.solver.traces;
50416
+ }
50417
+ };
50418
+
49270
50419
  // lib/utils/autorouting/CapacityMeshAutorouter.ts
49271
50420
  import {
49272
50421
  AutoroutingPipelineSolver9_Networked as AutoroutingPipelineSolver9_Networked2
@@ -49293,7 +50442,7 @@ import { CreateFdmEnclosureSolver } from "@tscircuit/create-fdm-enclosure";
49293
50442
  // node_modules/@tscircuit/implicit-copper-pour-solver/lib/implicit-copper-pour-pipeline-solver.ts
49294
50443
  import {
49295
50444
  BasePipelineSolver,
49296
- BaseSolver as BaseSolver4,
50445
+ BaseSolver as BaseSolver5,
49297
50446
  definePipelineStep
49298
50447
  } from "@tscircuit/solver-utils";
49299
50448
 
@@ -49311,7 +50460,7 @@ import { applyToPoint as applyToPoint27, compose as compose9, scale as scale2, t
49311
50460
  // node_modules/@tscircuit/implicit-copper-pour-solver/lib/geometry.ts
49312
50461
  import {
49313
50462
  clamp,
49314
- distance as distance12,
50463
+ distance as distance13,
49315
50464
  doBoundsOverlap as doBoundsOverlap2,
49316
50465
  getBoundsFromPoints as getBoundsFromPoints3,
49317
50466
  getUnitVectorFromPointAToB as getUnitVectorFromPointAToB2,
@@ -49360,7 +50509,7 @@ var distanceToPolygon = (point6, polygon) => {
49360
50509
  var distanceToPrimitive = (primitive, px, py) => {
49361
50510
  const point6 = { x: px, y: py };
49362
50511
  if (primitive.kind === "circle") {
49363
- return Math.max(distance12(point6, primitive) - primitive.radius, 0);
50512
+ return Math.max(distance13(point6, primitive) - primitive.radius, 0);
49364
50513
  }
49365
50514
  if (primitive.kind === "segment") {
49366
50515
  return Math.max(
@@ -49411,7 +50560,7 @@ var getClosestPointOnPrimitive = (primitive, point6) => {
49411
50560
  const start = primitive.points[index];
49412
50561
  const end = primitive.points[(index + 1) % primitive.points.length];
49413
50562
  const candidate = pointToSegmentClosestPoint(point6, start, end);
49414
- const candidateDistance = distance12(point6, candidate);
50563
+ const candidateDistance = distance13(point6, candidate);
49415
50564
  if (candidateDistance < bestDistance) {
49416
50565
  bestDistance = candidateDistance;
49417
50566
  closestPoint = candidate;
@@ -50467,7 +51616,7 @@ var prepareCircuitJson = (input) => {
50467
51616
 
50468
51617
  // node_modules/@tscircuit/implicit-copper-pour-solver/lib/simplify-polygon-edges.ts
50469
51618
  import {
50470
- distance as distance13,
51619
+ distance as distance14,
50471
51620
  doSegmentsIntersect,
50472
51621
  onSegment,
50473
51622
  pointToSegmentDistance as pointToSegmentDistance2
@@ -50486,13 +51635,13 @@ var simplifyRdpPath = (points, tolerance) => {
50486
51635
  let furthestIndex = -1;
50487
51636
  let furthestDistance = tolerance;
50488
51637
  for (let index = firstIndex + 1; index < lastIndex; index++) {
50489
- const segmentDistance = pointToSegmentDistance2(
51638
+ const segmentDistance2 = pointToSegmentDistance2(
50490
51639
  points[index],
50491
51640
  first,
50492
51641
  last
50493
51642
  );
50494
- if (segmentDistance > furthestDistance) {
50495
- furthestDistance = segmentDistance;
51643
+ if (segmentDistance2 > furthestDistance) {
51644
+ furthestDistance = segmentDistance2;
50496
51645
  furthestIndex = index;
50497
51646
  }
50498
51647
  }
@@ -50530,8 +51679,8 @@ var simplifyOpenPath = (points, tolerance) => {
50530
51679
  const point6 = points[index];
50531
51680
  const next = points[index + 1];
50532
51681
  const shortestLeg = Math.min(
50533
- distance13(point6, previous),
50534
- distance13(next, point6)
51682
+ distance14(point6, previous),
51683
+ distance14(next, point6)
50535
51684
  );
50536
51685
  if (repeatsTurnDirection || shortestLeg > tolerance) {
50537
51686
  anchorIndices.push(index);
@@ -50580,8 +51729,8 @@ var normalizePolygon = (polygon) => {
50580
51729
  };
50581
51730
  var isPointStrictlyInsideSegment = (point6, start, end) => {
50582
51731
  if (pointsEqual(point6, start) || pointsEqual(point6, end)) return false;
50583
- const cross3 = crossProduct(start, end, point6);
50584
- if (Math.abs(cross3) > 1e-9) return false;
51732
+ const cross4 = crossProduct(start, end, point6);
51733
+ if (Math.abs(cross4) > 1e-9) return false;
50585
51734
  return onSegment(start, point6, end);
50586
51735
  };
50587
51736
  var splitEdgesAtVertices = (polygon, allVertices) => {
@@ -50933,7 +52082,7 @@ var mergeSolverGraphics = (source, overlay) => {
50933
52082
  };
50934
52083
 
50935
52084
  // node_modules/@tscircuit/implicit-copper-pour-solver/lib/implicit-copper-pour-pipeline-solver.ts
50936
- var PrepareCircuitJsonSolver = class extends BaseSolver4 {
52085
+ var PrepareCircuitJsonSolver = class extends BaseSolver5 {
50937
52086
  constructor(input) {
50938
52087
  super();
50939
52088
  this.input = input;
@@ -50949,7 +52098,7 @@ var PrepareCircuitJsonSolver = class extends BaseSolver4 {
50949
52098
  return this.output;
50950
52099
  }
50951
52100
  };
50952
- var AssignGridCellsSolver = class extends BaseSolver4 {
52101
+ var AssignGridCellsSolver = class extends BaseSolver5 {
50953
52102
  constructor(input) {
50954
52103
  super();
50955
52104
  this.input = input;
@@ -50972,7 +52121,7 @@ var AssignGridCellsSolver = class extends BaseSolver4 {
50972
52121
  return this.output;
50973
52122
  }
50974
52123
  };
50975
- var TracePowerPolygonsSolver = class extends BaseSolver4 {
52124
+ var TracePowerPolygonsSolver = class extends BaseSolver5 {
50976
52125
  constructor(input) {
50977
52126
  super();
50978
52127
  this.input = input;
@@ -51008,7 +52157,7 @@ var TracePowerPolygonsSolver = class extends BaseSolver4 {
51008
52157
  };
51009
52158
  }
51010
52159
  };
51011
- var SimplifyPolygonEdgesSolver = class extends BaseSolver4 {
52160
+ var SimplifyPolygonEdgesSolver = class extends BaseSolver5 {
51012
52161
  constructor(input) {
51013
52162
  super();
51014
52163
  this.input = input;
@@ -51146,13 +52295,13 @@ import { LayoutPipelineSolver } from "@tscircuit/matchpack";
51146
52295
  import { SchematicTracePipelineSolver } from "@tscircuit/schematic-trace-solver";
51147
52296
 
51148
52297
  // node_modules/@tscircuit/via-stitch-solver/lib/solvers/ViaStitchSolver.ts
51149
- import { BaseSolver as BaseSolver5 } from "@tscircuit/solver-utils";
52298
+ import { BaseSolver as BaseSolver6 } from "@tscircuit/solver-utils";
51150
52299
 
51151
52300
  // node_modules/@tscircuit/via-stitch-solver/lib/geometry/brep-point-containment.ts
51152
52301
  var POINT_EPSILON = 1e-8;
51153
52302
  var isPointOnSegment = (point6, start, end) => {
51154
- const cross3 = (point6.y - start.y) * (end.x - start.x) - (point6.x - start.x) * (end.y - start.y);
51155
- if (Math.abs(cross3) > POINT_EPSILON) return false;
52303
+ const cross4 = (point6.y - start.y) * (end.x - start.x) - (point6.x - start.x) * (end.y - start.y);
52304
+ if (Math.abs(cross4) > POINT_EPSILON) return false;
51156
52305
  const dot = (point6.x - start.x) * (end.x - start.x) + (point6.y - start.y) * (end.y - start.y);
51157
52306
  if (dot < -POINT_EPSILON) return false;
51158
52307
  const squaredLength = (end.x - start.x) ** 2 + (end.y - start.y) ** 2;
@@ -51473,7 +52622,7 @@ var getGridCoordinates = ({
51473
52622
  }
51474
52623
  return coordinates;
51475
52624
  };
51476
- var ViaStitchSolver = class extends BaseSolver5 {
52625
+ var ViaStitchSolver = class extends BaseSolver6 {
51477
52626
  constructor(input) {
51478
52627
  super();
51479
52628
  this.input = input;
@@ -51620,6 +52769,7 @@ var ViaStitchSolver = class extends BaseSolver5 {
51620
52769
  // lib/solvers.ts
51621
52770
  import { PackSolver2 } from "calculate-packing";
51622
52771
  var SOLVERS = {
52772
+ BusLanesSolver,
51623
52773
  PackSolver2,
51624
52774
  LayoutPipelineSolver,
51625
52775
  AutoroutingPipelineSolver,
@@ -52082,6 +53232,22 @@ var localAutorouterStrategies = /* @__PURE__ */ new Map([
52082
53232
  createFanoutAutorouterStrategy("single_layer_fanout")
52083
53233
  ],
52084
53234
  ["fanout", createFanoutAutorouterStrategy("fanout")],
53235
+ [
53236
+ "bus_lanes",
53237
+ {
53238
+ name: "bus_lanes",
53239
+ cacheable: false,
53240
+ getSolverName: () => "BusLanesSolver",
53241
+ create: ({ simpleRouteJson, onSolverStarted }) => {
53242
+ onSolverStarted?.({
53243
+ solverName: "BusLanesSolver",
53244
+ solverParams: simpleRouteJson,
53245
+ solverConstructorArgs: [simpleRouteJson]
53246
+ });
53247
+ return new BusLanesAutorouter(simpleRouteJson);
53248
+ }
53249
+ }
53250
+ ],
52085
53251
  ["simplify", simplificationLocalAutorouterStrategy]
52086
53252
  ]);
52087
53253
  var getLocalAutorouterStrategy = (preset) => localAutorouterStrategies.get(preset ?? "") ?? defaultLocalAutorouterStrategy;
@@ -54640,7 +55806,7 @@ function Group_doInitialPcbComponentAnchorAlignment(group) {
54640
55806
  // lib/components/primitive-components/Group/Group_doInitialPcbLayoutFlex.ts
54641
55807
  import { getMinimumFlexContainer } from "@tscircuit/circuit-json-util";
54642
55808
  import { RootFlexBox } from "@tscircuit/miniflex";
54643
- import { length } from "circuit-json";
55809
+ import { length as length2 } from "circuit-json";
54644
55810
  var Group_doInitialPcbLayoutFlex = (group) => {
54645
55811
  const { db } = group.root;
54646
55812
  const { _parsedProps: props } = group;
@@ -54692,8 +55858,8 @@ var Group_doInitialPcbLayoutFlex = (group) => {
54692
55858
  rowGap = rawGap;
54693
55859
  columnGap = rawGap;
54694
55860
  } else if (typeof rawGap === "string") {
54695
- rowGap = length.parse(rawGap);
54696
- columnGap = length.parse(rawGap);
55861
+ rowGap = length2.parse(rawGap);
55862
+ columnGap = length2.parse(rawGap);
54697
55863
  }
54698
55864
  let minFlexContainer;
54699
55865
  let width = props.width ?? props.pcbWidth ?? void 0;
@@ -54773,7 +55939,7 @@ import {
54773
55939
  repositionPcbComponentTo as repositionPcbComponentTo2,
54774
55940
  repositionPcbGroupTo as repositionPcbGroupTo2
54775
55941
  } from "@tscircuit/circuit-json-util";
54776
- import { length as length2 } from "circuit-json";
55942
+ import { length as length3 } from "circuit-json";
54777
55943
  import { CssGrid } from "minicssgrid";
54778
55944
  var MIN_GAP = 1;
54779
55945
  function Group_doInitialPcbLayoutGrid(group) {
@@ -54835,7 +56001,7 @@ function parseGridConfiguration(props) {
54835
56001
  const templateRows = props.pcbGridTemplateRows;
54836
56002
  const parseGap = (gapValue) => {
54837
56003
  if (gapValue === void 0) return MIN_GAP;
54838
- return typeof gapValue === "number" ? gapValue : length2.parse(gapValue);
56004
+ return typeof gapValue === "number" ? gapValue : length3.parse(gapValue);
54839
56005
  };
54840
56006
  const gridGapOption = props.pcbGridGap ?? props.gridGap ?? props.pcbLayout?.gridGap;
54841
56007
  const rowGapOption = props.pcbGridRowGap ?? props.gridRowGap ?? props.pcbLayout?.gridRowGap;
@@ -54986,7 +56152,7 @@ import {
54986
56152
  convertPackOutputToPackInput,
54987
56153
  getGraphicsFromPackOutput
54988
56154
  } from "calculate-packing";
54989
- import { length as length3 } from "circuit-json";
56155
+ import { length as length4 } from "circuit-json";
54990
56156
  import Debug6 from "debug";
54991
56157
 
54992
56158
  // lib/utils/packing/solvePackSolverWithTimeout.ts
@@ -55495,16 +56661,16 @@ var Group_doInitialPcbLayoutPack = (group) => {
55495
56661
  pcbPackGap
55496
56662
  } = props;
55497
56663
  const gap = pcbPackGap ?? pcbGap ?? gapProp;
55498
- const gapMm = length3.parse(gap ?? DEFAULT_MIN_GAP);
56664
+ const gapMm = length4.parse(gap ?? DEFAULT_MIN_GAP);
55499
56665
  const chipMarginsMap = {};
55500
56666
  const staticPcbComponentIds = /* @__PURE__ */ new Set();
55501
56667
  const collectMargins = (comp) => {
55502
56668
  if (comp?.pcb_component_id && comp?._parsedProps) {
55503
56669
  const props2 = comp._parsedProps;
55504
- const left = length3.parse(props2.pcbMarginLeft ?? props2.pcbMarginX ?? 0);
55505
- const right = length3.parse(props2.pcbMarginRight ?? props2.pcbMarginX ?? 0);
55506
- const top = length3.parse(props2.pcbMarginTop ?? props2.pcbMarginY ?? 0);
55507
- const bottom = length3.parse(
56670
+ const left = length4.parse(props2.pcbMarginLeft ?? props2.pcbMarginX ?? 0);
56671
+ const right = length4.parse(props2.pcbMarginRight ?? props2.pcbMarginX ?? 0);
56672
+ const top = length4.parse(props2.pcbMarginTop ?? props2.pcbMarginY ?? 0);
56673
+ const bottom = length4.parse(
55508
56674
  props2.pcbMarginBottom ?? props2.pcbMarginY ?? 0
55509
56675
  );
55510
56676
  if (left || right || top || bottom) {
@@ -55553,8 +56719,8 @@ var Group_doInitialPcbLayoutPack = (group) => {
55553
56719
  const filteredCircuitJson = db.toArray();
55554
56720
  let bounds;
55555
56721
  if (props.width !== void 0 && props.height !== void 0) {
55556
- const widthMm = length3.parse(props.width);
55557
- const heightMm = length3.parse(props.height);
56722
+ const widthMm = length4.parse(props.width);
56723
+ const heightMm = length4.parse(props.height);
55558
56724
  bounds = {
55559
56725
  minX: -widthMm / 2,
55560
56726
  maxX: widthMm / 2,
@@ -55859,7 +57025,7 @@ import {
55859
57025
  repositionSchematicGroupTo
55860
57026
  } from "@tscircuit/circuit-json-util";
55861
57027
  import { RootFlexBox as RootFlexBox2 } from "@tscircuit/miniflex";
55862
- import { length as length4 } from "circuit-json";
57028
+ import { length as length5 } from "circuit-json";
55863
57029
  var getSizeOfTreeNodeChild = (db, child) => {
55864
57030
  const { sourceComponent, sourceGroup } = child;
55865
57031
  if (child.nodeType === "component") {
@@ -55952,8 +57118,8 @@ var Group_doInitialSchematicLayoutFlex = (group) => {
55952
57118
  rowGap = rawGap;
55953
57119
  columnGap = rawGap;
55954
57120
  } else if (typeof rawGap === "string") {
55955
- rowGap = length4.parse(rawGap);
55956
- columnGap = length4.parse(rawGap);
57121
+ rowGap = length5.parse(rawGap);
57122
+ columnGap = length5.parse(rawGap);
55957
57123
  }
55958
57124
  let minFlexContainer;
55959
57125
  let width = props.width ?? props.schWidth ?? void 0;
@@ -56047,7 +57213,7 @@ var Group_doInitialSchematicLayoutFlex = (group) => {
56047
57213
  };
56048
57214
 
56049
57215
  // lib/components/primitive-components/Group/Group_doInitialSchematicLayoutGrid.ts
56050
- import { length as length5 } from "circuit-json";
57216
+ import { length as length6 } from "circuit-json";
56051
57217
 
56052
57218
  // lib/components/primitive-components/Group/utils/updateSchematicPrimitivesForLayoutShift.ts
56053
57219
  function updateSchematicPrimitivesForLayoutShift({
@@ -56151,7 +57317,7 @@ function Group_doInitialSchematicLayoutGrid(group) {
56151
57317
  let gridGapY;
56152
57318
  const parseGap = (val) => {
56153
57319
  if (val === void 0) return void 0;
56154
- return typeof val === "number" ? val : length5.parse(val);
57320
+ return typeof val === "number" ? val : length6.parse(val);
56155
57321
  };
56156
57322
  if (gridRowGapOption !== void 0 || gridColumnGapOption !== void 0) {
56157
57323
  const fallbackX = typeof gridGapOption === "object" && gridGapOption !== null ? gridGapOption.x : gridGapOption;
@@ -56162,14 +57328,14 @@ function Group_doInitialSchematicLayoutGrid(group) {
56162
57328
  gridGapX = gridGapOption;
56163
57329
  gridGapY = gridGapOption;
56164
57330
  } else if (typeof gridGapOption === "string") {
56165
- const parsed = length5.parse(gridGapOption);
57331
+ const parsed = length6.parse(gridGapOption);
56166
57332
  gridGapX = parsed;
56167
57333
  gridGapY = parsed;
56168
57334
  } else if (typeof gridGapOption === "object" && gridGapOption !== null) {
56169
57335
  const xRaw = gridGapOption.x;
56170
57336
  const yRaw = gridGapOption.y;
56171
- gridGapX = typeof xRaw === "number" ? xRaw : length5.parse(xRaw ?? "0mm");
56172
- gridGapY = typeof yRaw === "number" ? yRaw : length5.parse(yRaw ?? "0mm");
57337
+ gridGapX = typeof xRaw === "number" ? xRaw : length6.parse(xRaw ?? "0mm");
57338
+ gridGapY = typeof yRaw === "number" ? yRaw : length6.parse(yRaw ?? "0mm");
56173
57339
  } else {
56174
57340
  gridGapX = 1;
56175
57341
  gridGapY = 1;
@@ -58402,19 +59568,19 @@ import Debug12 from "debug";
58402
59568
  import { isValidElement as isValidElement2 } from "react";
58403
59569
 
58404
59570
  // lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/segment-intersection.ts
58405
- var cross2 = (ax, ay, bx, by) => {
59571
+ var cross3 = (ax, ay, bx, by) => {
58406
59572
  return ax * by - ay * bx;
58407
59573
  };
58408
59574
  function segmentIntersection(p1, p2, q1, q2, tol) {
58409
59575
  const r = { x: p2.x - p1.x, y: p2.y - p1.y };
58410
59576
  const s = { x: q2.x - q1.x, y: q2.y - q1.y };
58411
- const rxs = cross2(r.x, r.y, s.x, s.y);
59577
+ const rxs = cross3(r.x, r.y, s.x, s.y);
58412
59578
  const q_p = { x: q1.x - p1.x, y: q1.y - p1.y };
58413
- const q_pxr = cross2(q_p.x, q_p.y, r.x, r.y);
59579
+ const q_pxr = cross3(q_p.x, q_p.y, r.x, r.y);
58414
59580
  if (Math.abs(rxs) < tol && Math.abs(q_pxr) < tol) return null;
58415
59581
  if (Math.abs(rxs) < tol && Math.abs(q_pxr) >= tol) return null;
58416
- const t = cross2(q_p.x, q_p.y, s.x, s.y) / rxs;
58417
- const u = cross2(q_p.x, q_p.y, r.x, r.y) / rxs;
59582
+ const t = cross3(q_p.x, q_p.y, s.x, s.y) / rxs;
59583
+ const u = cross3(q_p.x, q_p.y, r.x, r.y) / rxs;
58418
59584
  if (t < -tol || t > 1 + tol || u < -tol || u > 1 + tol) return null;
58419
59585
  return { x: p1.x + t * r.x, y: p1.y + t * r.y };
58420
59586
  }
@@ -58426,14 +59592,14 @@ function isHorizontalEdge(edge) {
58426
59592
  const dy = Math.abs(edge.to.y - edge.from.y);
58427
59593
  return dx >= dy;
58428
59594
  }
58429
- function length6(a, b) {
59595
+ function length7(a, b) {
58430
59596
  return Math.hypot(b.x - a.x, b.y - a.y);
58431
59597
  }
58432
59598
  function pointAt(a, b, t) {
58433
59599
  return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };
58434
59600
  }
58435
59601
  function paramAlong(a, b, p) {
58436
- const L = length6(a, b);
59602
+ const L = length7(a, b);
58437
59603
  if (L < TOL) return 0;
58438
59604
  const t = ((p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y)) / ((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y));
58439
59605
  return Math.max(0, Math.min(1, t)) * L;
@@ -58456,7 +59622,7 @@ function mergeIntervals(intervals, tol = TOL) {
58456
59622
  return merged;
58457
59623
  }
58458
59624
  function splitEdgeByCrossings(edge, crossingDistances, crossLen) {
58459
- const L = length6(edge.from, edge.to);
59625
+ const L = length7(edge.from, edge.to);
58460
59626
  if (L < TOL || crossingDistances.length === 0) return [edge];
58461
59627
  const half = crossLen / 2;
58462
59628
  const rawIntervals = crossingDistances.map((d) => ({
@@ -58508,8 +59674,8 @@ function computeCrossings(traces, opts = {}) {
58508
59674
  }
58509
59675
  const P = segmentIntersection(eA.from, eA.to, eB.from, eB.to, tol);
58510
59676
  if (!P) continue;
58511
- const LA = length6(eA.from, eA.to);
58512
- const LB = length6(eB.from, eB.to);
59677
+ const LA = length7(eA.from, eA.to);
59678
+ const LB = length7(eB.from, eB.to);
58513
59679
  if (LA < tol || LB < tol) continue;
58514
59680
  const dA = paramAlong(eA.from, eA.to, P);
58515
59681
  const dB = paramAlong(eB.from, eB.to, P);
@@ -58607,8 +59773,8 @@ function isParallel(e1, e2, tol = TOL2) {
58607
59773
  const L1 = Math.hypot(v1.x, v1.y);
58608
59774
  const L2 = Math.hypot(v2.x, v2.y);
58609
59775
  if (L1 < tol || L2 < tol) return true;
58610
- const cross3 = v1.x * v2.y - v1.y * v2.x;
58611
- return Math.abs(cross3) <= tol * L1 * L2;
59776
+ const cross4 = v1.x * v2.y - v1.y * v2.x;
59777
+ return Math.abs(cross4) <= tol * L1 * L2;
58612
59778
  }
58613
59779
  function incidentEdgesAtPoint(trace, p, tol = TOL2) {
58614
59780
  return trace.edges.filter(
@@ -58758,7 +59924,7 @@ function isCollinear(a, b, tol = TOL3) {
58758
59924
  function splitEdgeAroundIntervals(edge, intervals) {
58759
59925
  const merged = mergeIntervals(intervals);
58760
59926
  if (merged.length === 0) return [edge];
58761
- const edgeLength = length6(edge.from, edge.to);
59927
+ const edgeLength = length7(edge.from, edge.to);
58762
59928
  if (edgeLength <= TOL3) return [edge];
58763
59929
  const result = [];
58764
59930
  let cursor = 0;
@@ -58792,7 +59958,7 @@ function removeOverlappingSameNetCrossingSegments(traces) {
58792
59958
  const crossingTrace = traces[crossingTraceIndex];
58793
59959
  if (!crossingTrace.connectivity_key) continue;
58794
59960
  for (const crossingEdge of crossingTrace.edges) {
58795
- if (!crossingEdge.is_crossing || length6(crossingEdge.from, crossingEdge.to) <= TOL3)
59961
+ if (!crossingEdge.is_crossing || length7(crossingEdge.from, crossingEdge.to) <= TOL3)
58796
59962
  continue;
58797
59963
  for (let traceIndex = 0; traceIndex < traces.length; traceIndex++) {
58798
59964
  const candidateTrace = traces[traceIndex];
@@ -58801,7 +59967,7 @@ function removeOverlappingSameNetCrossingSegments(traces) {
58801
59967
  }
58802
59968
  for (let edgeIndex = 0; edgeIndex < candidateTrace.edges.length; edgeIndex++) {
58803
59969
  const candidateEdge = candidateTrace.edges[edgeIndex];
58804
- const candidateLength = length6(candidateEdge.from, candidateEdge.to);
59970
+ const candidateLength = length7(candidateEdge.from, candidateEdge.to);
58805
59971
  if (candidateEdge.is_crossing || candidateLength <= TOL3) continue;
58806
59972
  if (!isCollinear(candidateEdge, crossingEdge)) continue;
58807
59973
  const start = Math.max(
@@ -61976,6 +63142,7 @@ function Group_getRoutingPhasePlans(group) {
61976
63142
  for (const plan of plans) {
61977
63143
  plan.autorouter = autoroutersByPhaseIndex.get(plan.routingPhaseIndex);
61978
63144
  const phaseProps = phasePropsByPhaseIndex.get(plan.routingPhaseIndex);
63145
+ plan.isImplicitRemainingPhase = plan.routingPhaseIndex === null && phaseProps === void 0;
61979
63146
  plan.phaseName = phaseProps?.name;
61980
63147
  plan.reroute = phaseProps?.reroute;
61981
63148
  plan.region = phaseProps?.region;
@@ -62101,6 +63268,7 @@ function Group_getRoutingPhasePlans(group) {
62101
63268
  ...breakoutPlans,
62102
63269
  {
62103
63270
  routingPhaseIndex: null,
63271
+ isImplicitRemainingPhase: true,
62104
63272
  nets: [...reroutePlan.nets],
62105
63273
  traces: [...reroutePlan.traces]
62106
63274
  },
@@ -62114,7 +63282,7 @@ function Group_getRoutingPhasePlans(group) {
62114
63282
  var package_default = {
62115
63283
  name: "@tscircuit/core",
62116
63284
  type: "module",
62117
- version: "0.0.1913",
63285
+ version: "0.0.1915",
62118
63286
  types: "dist/index.d.ts",
62119
63287
  main: "dist/index.js",
62120
63288
  module: "dist/index.js",
@@ -62148,6 +63316,7 @@ var package_default = {
62148
63316
  "@tsci/tscircuit.ti": "github:tscircuit/ti#57e314be4b2b06bc546d4def7453e619604d1157",
62149
63317
  "@tscircuit/alphabet": "0.0.25",
62150
63318
  "@tscircuit/bga-fanout-solver": "github:tscircuit/bga-fanout-solver#247c835cb8d69b65e53891dcf869731079415b39",
63319
+ "@tscircuit/bus-lanes-solver": "github:tscircuit/bus-lanes-solver#0bc8d071241b18a3e7465a878881abe4998f6384",
62151
63320
  "@tscircuit/capacity-autorouter": "^0.0.900",
62152
63321
  "@tscircuit/checks": "^0.0.192",
62153
63322
  "@tscircuit/circuit-json-util": "^0.0.113",
@@ -62846,7 +64015,7 @@ function getAccumulatedPcbTracesWithStageOutputReplacements({
62846
64015
  }
62847
64016
 
62848
64017
  // lib/components/primitive-components/Group/get-source-trace-id-for-routed-trace.ts
62849
- import { distance as distance14, pointToSegmentDistance as pointToSegmentDistance3 } from "@tscircuit/math-utils";
64018
+ import { distance as distance15, pointToSegmentDistance as pointToSegmentDistance3 } from "@tscircuit/math-utils";
62850
64019
  import { ConnectivityMap as ConnectivityMap5 } from "circuit-json-to-connectivity-map";
62851
64020
  var POINT_EPSILON2 = 1e-6;
62852
64021
  function isPointOnWireSegment({
@@ -62868,7 +64037,7 @@ function isPointInSmtPad({
62868
64037
  return Math.abs(point6.x - pad.x) <= pad.width / 2 + traceWidth / 2 && Math.abs(point6.y - pad.y) <= pad.height / 2 + traceWidth / 2;
62869
64038
  }
62870
64039
  if (pad.shape === "circle") {
62871
- return distance14(point6, pad) <= pad.radius + traceWidth / 2;
64040
+ return distance15(point6, pad) <= pad.radius + traceWidth / 2;
62872
64041
  }
62873
64042
  return false;
62874
64043
  }
@@ -62877,7 +64046,7 @@ function isPointInPlatedHole({
62877
64046
  hole,
62878
64047
  traceWidth = 0
62879
64048
  }) {
62880
- return distance14(point6, hole) <= (hole.outer_diameter ?? hole.hole_diameter ?? 0) / 2 + traceWidth / 2;
64049
+ return distance15(point6, hole) <= (hole.outer_diameter ?? hole.hole_diameter ?? 0) / 2 + traceWidth / 2;
62881
64050
  }
62882
64051
  function getEndpointSourcePortIdsFromGeometry(db, trace) {
62883
64052
  const sourcePortIds = /* @__PURE__ */ new Set();
@@ -62890,7 +64059,7 @@ function getEndpointSourcePortIdsFromGeometry(db, trace) {
62890
64059
  if (!pcbPort.source_port_id) continue;
62891
64060
  if (endpoint.layer && pcbPort.layers && !pcbPort.layers.includes(endpoint.layer))
62892
64061
  continue;
62893
- if (distance14(endpoint, pcbPort) <= 0.01) {
64062
+ if (distance15(endpoint, pcbPort) <= 0.01) {
62894
64063
  sourcePortIds.add(pcbPort.source_port_id);
62895
64064
  }
62896
64065
  }
@@ -63574,8 +64743,8 @@ var Group5 = class extends NormalComponent3 {
63574
64743
  const groupProps2 = props;
63575
64744
  const hasOutline = groupProps2.outline && groupProps2.outline.length > 0;
63576
64745
  const numericOutline = hasOutline ? groupProps2.outline.map((point6) => ({
63577
- x: distance15.parse(point6.x),
63578
- y: distance15.parse(point6.y)
64746
+ x: distance16.parse(point6.x),
64747
+ y: distance16.parse(point6.y)
63579
64748
  })) : void 0;
63580
64749
  const ctx = this.props;
63581
64750
  const anchorPosition = this._getGlobalPcbPositionBeforeLayout();
@@ -63613,8 +64782,8 @@ var Group5 = class extends NormalComponent3 {
63613
64782
  const hasExplicitPositioning = this._parsedProps.pcbX !== void 0 || this._parsedProps.pcbY !== void 0;
63614
64783
  if (hasOutline) {
63615
64784
  const numericOutline = props.outline.map((point6) => ({
63616
- x: distance15.parse(point6.x),
63617
- y: distance15.parse(point6.y)
64785
+ x: distance16.parse(point6.x),
64786
+ y: distance16.parse(point6.y)
63618
64787
  }));
63619
64788
  const outlineBounds = getBoundsFromPoints9(numericOutline);
63620
64789
  if (!outlineBounds) return;
@@ -64136,6 +65305,7 @@ var Group5 = class extends NormalComponent3 {
64136
65305
  };
64137
65306
  const fixedFanoutTraceIds = /* @__PURE__ */ new Set();
64138
65307
  let previousStageOutputSimpleRouteJson;
65308
+ const skippedRemainingPhases = /* @__PURE__ */ new Set();
64139
65309
  for (const [
64140
65310
  routingStageIndex,
64141
65311
  {
@@ -64147,6 +65317,7 @@ var Group5 = class extends NormalComponent3 {
64147
65317
  phaseStageCount
64148
65318
  }
64149
65319
  ] of routingStages.entries()) {
65320
+ if (skippedRemainingPhases.has(routingPhasePlan)) continue;
64150
65321
  if (!usesPreviousStageOutput) {
64151
65322
  previousStageOutputSimpleRouteJson = void 0;
64152
65323
  }
@@ -64238,16 +65409,31 @@ var Group5 = class extends NormalComponent3 {
64238
65409
  })
64239
65410
  };
64240
65411
  }
64241
- simpleRouteJson = withFixedFanoutTraces(
64242
- simpleRouteJson,
64243
- fixedFanoutTraceIds
64244
- );
65412
+ if (phaseAutorouterConfig.preset !== "bus_lanes") {
65413
+ simpleRouteJson = withFixedFanoutTraces(
65414
+ simpleRouteJson,
65415
+ fixedFanoutTraceIds
65416
+ );
65417
+ }
64245
65418
  simpleRouteJson = Group_applyDrcTolerancesToSimpleRouteJson(
64246
65419
  simpleRouteJson,
64247
65420
  routingPhasePlan.drcTolerances
64248
65421
  );
64249
65422
  simpleRouteJson.allowViaInPad = phaseAutorouterConfig.allowViaInPad;
64250
65423
  const getPrecomputedRoutingResult = usesPreviousStageOutput ? void 0 : routingPhasePlan.getPrecomputedRoutingResult;
65424
+ const preflightRoutingCheckPolicy = this.getInheritedProperty(
65425
+ "preflightRoutingCheckPolicy"
65426
+ );
65427
+ if (routingPhasePlan.isImplicitRemainingPhase && phaseStageIndex === 0 && this.getInheritedProperty("routeRemaining") === void 0 && (preflightRoutingCheckPolicy === "basic" || preflightRoutingCheckPolicy === "conservative") && simpleRouteJson.connections.length > 50) {
65428
+ skippedRemainingPhases.add(routingPhasePlan);
65429
+ db.pcb_autorouting_error.insert({
65430
+ pcb_error_id: `pcb_autorouting_error_remaining_routes_${this.subcircuit_id}`,
65431
+ subcircuit_id: this.subcircuit_id ?? void 0,
65432
+ error_type: "pcb_autorouting_error",
65433
+ message: `Remaining routes left unrouted (over 50 traces remaining and preflightRoutingCheckPolicy="${preflightRoutingCheckPolicy}"). Set <board routeRemaining={true} /> or create <autoroutingphase /> elements for specific connections in the order you'd like to route them. The autorouter may hang unless you create autorouting phases incrementally.`
65434
+ });
65435
+ continue;
65436
+ }
64251
65437
  const simplificationHasNoTraceInput = Boolean(
64252
65438
  isTraceSimplificationPhase && simpleRouteJson.traces?.length === 0
64253
65439
  );
@@ -65413,8 +66599,8 @@ var Led_addPolarityFabricationSymbol = (led) => {
65413
66599
  const a = db.pcb_port.getWhere({ source_port_id: anode.source_port_id });
65414
66600
  const k = db.pcb_port.getWhere({ source_port_id: cathode.source_port_id });
65415
66601
  if (!a || !k) return;
65416
- const distance22 = Math.hypot(k.x - a.x, k.y - a.y);
65417
- if (distance22 < 1e-6) return;
66602
+ const distance23 = Math.hypot(k.x - a.x, k.y - a.y);
66603
+ if (distance23 < 1e-6) return;
65418
66604
  const angle = Math.atan2(k.y - a.y, k.x - a.x);
65419
66605
  const frame = compose14(
65420
66606
  translate14((a.x + k.x) / 2, (a.y + k.y) / 2),
@@ -65428,10 +66614,10 @@ var Led_addPolarityFabricationSymbol = (led) => {
65428
66614
  };
65429
66615
  const notes = [];
65430
66616
  generatedNotes.set(led, notes);
65431
- const halfLength = Math.min(distance22 * 0.2, 0.4);
66617
+ const halfLength = Math.min(distance23 * 0.2, 0.4);
65432
66618
  const halfHeight = halfLength * 0.75;
65433
66619
  const routes = [
65434
- [point6(-distance22 / 2, 0), point6(-halfLength, 0)],
66620
+ [point6(-distance23 / 2, 0), point6(-halfLength, 0)],
65435
66621
  [
65436
66622
  point6(-halfLength, -halfHeight),
65437
66623
  point6(halfLength, 0),
@@ -65439,7 +66625,7 @@ var Led_addPolarityFabricationSymbol = (led) => {
65439
66625
  point6(-halfLength, -halfHeight)
65440
66626
  ],
65441
66627
  [point6(halfLength, -halfHeight), point6(halfLength, halfHeight)],
65442
- [point6(halfLength, 0), point6(distance22 / 2, 0)]
66628
+ [point6(halfLength, 0), point6(distance23 / 2, 0)]
65443
66629
  ];
65444
66630
  for (const route of routes) {
65445
66631
  notes.push(
@@ -67163,8 +68349,8 @@ import {
67163
68349
  var EPSILON24 = 1e-9;
67164
68350
  var isWireRoutePoint = (routePoint) => routePoint.route_type === "wire";
67165
68351
  var isPointOnSegment2 = (p, a, b) => {
67166
- const cross3 = (p.y - a.y) * (b.x - a.x) - (p.x - a.x) * (b.y - a.y);
67167
- if (Math.abs(cross3) > EPSILON24) return false;
68352
+ const cross4 = (p.y - a.y) * (b.x - a.x) - (p.x - a.x) * (b.y - a.y);
68353
+ if (Math.abs(cross4) > EPSILON24) return false;
67168
68354
  const dot = (p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y);
67169
68355
  if (dot < -EPSILON24) return false;
67170
68356
  const squaredLength = (b.x - a.x) ** 2 + (b.y - a.y) ** 2;
@@ -67225,8 +68411,8 @@ var isTraceConnectedToSourceNet = (trace, sourceNetId, sourceTraceById) => {
67225
68411
  var isSegmentFullyInsideCopperPour = (start, end, pour) => {
67226
68412
  const dx = end.x - start.x;
67227
68413
  const dy = end.y - start.y;
67228
- const length7 = Math.hypot(dx, dy);
67229
- if (length7 <= EPSILON24) return false;
68414
+ const length8 = Math.hypot(dx, dy);
68415
+ if (length8 <= EPSILON24) return false;
67230
68416
  const samples = [0, 0.25, 0.5, 0.75, 1];
67231
68417
  return samples.every(
67232
68418
  (t) => isPointInCopperPour(
@@ -67694,13 +68880,13 @@ var getPlatedHolePolygon = (pad, componentRotation = 0) => {
67694
68880
  );
67695
68881
  };
67696
68882
  var getTraceSegmentPolygon = (start, end, startWidth, endWidth = startWidth) => {
67697
- const length7 = Math.hypot(end.x - start.x, end.y - start.y);
68883
+ const length8 = Math.hypot(end.x - start.x, end.y - start.y);
67698
68884
  const r1 = startWidth / 2;
67699
68885
  const r2 = endWidth / 2;
67700
- if (length7 <= Math.abs(r2 - r1))
68886
+ if (length8 <= Math.abs(r2 - r1))
67701
68887
  return circlePolygon(r1 >= r2 ? start : end, Math.max(r1, r2));
67702
68888
  const angle = Math.atan2(end.y - start.y, end.x - start.x);
67703
- const tangent = Math.acos((r1 - r2) / length7);
68889
+ const tangent = Math.acos((r1 - r2) / length8);
67704
68890
  const first = new Arc(
67705
68891
  new Point(start.x, start.y),
67706
68892
  r1,
@@ -68005,7 +69191,7 @@ var Board_doInitialPcbPlacementDesignRuleChecks = (board) => {
68005
69191
 
68006
69192
  // lib/components/normal-components/board-castellated-hole.ts
68007
69193
  import {
68008
- distance as distance16
69194
+ distance as distance17
68009
69195
  } from "circuit-json";
68010
69196
  var CASTELLATED_HOLE_ENDPOINT_TOLERANCE_MM = 1e-6;
68011
69197
  var getConnectionTargets2 = (connectsTo) => {
@@ -68026,8 +69212,8 @@ var BoardCastellatedHole = class _BoardCastellatedHole extends PlatedHole {
68026
69212
  constructor(outlinePoint, outlinePointIndex) {
68027
69213
  const name = `castellated_hole_${outlinePointIndex + 1}`;
68028
69214
  const connectionTargets = getConnectionTargets2(outlinePoint.connectsTo);
68029
- const holeDiameter = distance16.parse(outlinePoint.holeDiameter);
68030
- const padDiameter = distance16.parse(outlinePoint.padDiameter);
69215
+ const holeDiameter = distance17.parse(outlinePoint.holeDiameter);
69216
+ const padDiameter = distance17.parse(outlinePoint.padDiameter);
68031
69217
  super({
68032
69218
  shape: "circle",
68033
69219
  holeDiameter,
@@ -68871,11 +70057,11 @@ var MountedBoard = class extends Subcircuit {
68871
70057
 
68872
70058
  // lib/components/normal-components/Panel.ts
68873
70059
  import { panelProps } from "@tscircuit/props";
68874
- import { distance as distance20 } from "circuit-json";
70060
+ import { distance as distance21 } from "circuit-json";
68875
70061
 
68876
70062
  // lib/components/normal-components/Subpanel.ts
68877
70063
  import { subpanelProps } from "@tscircuit/props";
68878
- import { distance as distance19 } from "circuit-json";
70064
+ import { distance as distance20 } from "circuit-json";
68879
70065
  import { compose as compose17, identity as identity8, translate as translate17 } from "transformation-matrix";
68880
70066
 
68881
70067
  // lib/utils/panels/generate-panel-tabs-and-mouse-bites.ts
@@ -69139,15 +70325,15 @@ function generatePanelOutlineRoutingCutouts(boards, options) {
69139
70325
  }
69140
70326
 
69141
70327
  // lib/utils/panels/pack-into-grid.ts
69142
- import { distance as distance18 } from "circuit-json";
70328
+ import { distance as distance19 } from "circuit-json";
69143
70329
 
69144
70330
  // lib/utils/panels/get-board-dimensions-from-props.ts
69145
70331
  import { getBoundsFromPoints as getBoundsFromPoints11 } from "@tscircuit/math-utils";
69146
- import { distance as distance17 } from "circuit-json";
70332
+ import { distance as distance18 } from "circuit-json";
69147
70333
  var getBoardDimensionsFromProps = (board) => {
69148
70334
  const props = board._parsedProps;
69149
- let width = props.width != null ? distance17.parse(props.width) : void 0;
69150
- let height = props.height != null ? distance17.parse(props.height) : void 0;
70335
+ let width = props.width != null ? distance18.parse(props.width) : void 0;
70336
+ let height = props.height != null ? distance18.parse(props.height) : void 0;
69151
70337
  if ((width === void 0 || height === void 0) && props.outline?.length) {
69152
70338
  const outlineBounds = getBoundsFromPoints11(props.outline);
69153
70339
  if (outlineBounds) {
@@ -69191,8 +70377,8 @@ function packIntoGrid({
69191
70377
  }
69192
70378
  let cols;
69193
70379
  let rows;
69194
- const minCellWidth = cellWidth ? distance18.parse(cellWidth) : 0;
69195
- const minCellHeight = cellHeight ? distance18.parse(cellHeight) : 0;
70380
+ const minCellWidth = cellWidth ? distance19.parse(cellWidth) : 0;
70381
+ const minCellHeight = cellHeight ? distance19.parse(cellHeight) : 0;
69196
70382
  if (col !== void 0) {
69197
70383
  cols = col;
69198
70384
  rows = row ?? Math.ceil(itemsWithDims.length / cols);
@@ -69291,8 +70477,8 @@ function getItemDimensions(item, db) {
69291
70477
  const props = subpanel._parsedProps;
69292
70478
  if (props.width !== void 0 && props.height !== void 0) {
69293
70479
  return {
69294
- width: distance18.parse(props.width),
69295
- height: distance18.parse(props.height)
70480
+ width: distance19.parse(props.width),
70481
+ height: distance19.parse(props.height)
69296
70482
  };
69297
70483
  }
69298
70484
  const directBoards = subpanel._getDirectBoardChildren();
@@ -69319,7 +70505,7 @@ function getItemDimensions(item, db) {
69319
70505
  return getBoardDimensionsFromProps(directBoards[0]);
69320
70506
  }
69321
70507
  if (subpanel._cachedGridWidth > 0 && subpanel._cachedGridHeight > 0) {
69322
- const edgePadding = distance18.parse(props.edgePadding ?? 5);
70508
+ const edgePadding = distance19.parse(props.edgePadding ?? 5);
69323
70509
  return {
69324
70510
  width: subpanel._cachedGridWidth + edgePadding * 2,
69325
70511
  height: subpanel._cachedGridHeight + edgePadding * 2
@@ -69477,17 +70663,17 @@ var Subpanel = class _Subpanel extends Group5 {
69477
70663
  edgePaddingTop: edgePaddingTopProp,
69478
70664
  edgePaddingBottom: edgePaddingBottomProp
69479
70665
  } = this._parsedProps;
69480
- const edgePadding = distance19.parse(edgePaddingProp ?? 5);
69481
- const edgePaddingLeft = distance19.parse(edgePaddingLeftProp ?? edgePadding);
69482
- const edgePaddingRight = distance19.parse(
70666
+ const edgePadding = distance20.parse(edgePaddingProp ?? 5);
70667
+ const edgePaddingLeft = distance20.parse(edgePaddingLeftProp ?? edgePadding);
70668
+ const edgePaddingRight = distance20.parse(
69483
70669
  edgePaddingRightProp ?? edgePadding
69484
70670
  );
69485
- const edgePaddingTop = distance19.parse(edgePaddingTopProp ?? edgePadding);
69486
- const edgePaddingBottom = distance19.parse(
70671
+ const edgePaddingTop = distance20.parse(edgePaddingTopProp ?? edgePadding);
70672
+ const edgePaddingBottom = distance20.parse(
69487
70673
  edgePaddingBottomProp ?? edgePadding
69488
70674
  );
69489
- const panelWidth = distance19.parse(this._parsedProps.width);
69490
- const panelHeight = distance19.parse(this._parsedProps.height);
70675
+ const panelWidth = distance20.parse(this._parsedProps.width);
70676
+ const panelHeight = distance20.parse(this._parsedProps.height);
69491
70677
  availablePanelWidth = panelWidth - edgePaddingLeft - edgePaddingRight;
69492
70678
  availablePanelHeight = panelHeight - edgePaddingTop - edgePaddingBottom;
69493
70679
  }
@@ -69562,8 +70748,8 @@ var Subpanel = class _Subpanel extends Group5 {
69562
70748
  if (!this.pcb_group_id) return;
69563
70749
  if (hasExplicitWidth && hasExplicitHeight) {
69564
70750
  db.pcb_group.update(this.pcb_group_id, {
69565
- width: distance19.parse(this._parsedProps.width),
69566
- height: distance19.parse(this._parsedProps.height)
70751
+ width: distance20.parse(this._parsedProps.width),
70752
+ height: distance20.parse(this._parsedProps.height)
69567
70753
  });
69568
70754
  } else if (gridWidth > 0 || gridHeight > 0) {
69569
70755
  const {
@@ -69573,18 +70759,18 @@ var Subpanel = class _Subpanel extends Group5 {
69573
70759
  edgePaddingTop: edgePaddingTopProp,
69574
70760
  edgePaddingBottom: edgePaddingBottomProp
69575
70761
  } = this._parsedProps;
69576
- const edgePadding = distance19.parse(edgePaddingProp ?? 5);
69577
- const edgePaddingLeft = distance19.parse(edgePaddingLeftProp ?? edgePadding);
69578
- const edgePaddingRight = distance19.parse(
70762
+ const edgePadding = distance20.parse(edgePaddingProp ?? 5);
70763
+ const edgePaddingLeft = distance20.parse(edgePaddingLeftProp ?? edgePadding);
70764
+ const edgePaddingRight = distance20.parse(
69579
70765
  edgePaddingRightProp ?? edgePadding
69580
70766
  );
69581
- const edgePaddingTop = distance19.parse(edgePaddingTopProp ?? edgePadding);
69582
- const edgePaddingBottom = distance19.parse(
70767
+ const edgePaddingTop = distance20.parse(edgePaddingTopProp ?? edgePadding);
70768
+ const edgePaddingBottom = distance20.parse(
69583
70769
  edgePaddingBottomProp ?? edgePadding
69584
70770
  );
69585
70771
  db.pcb_group.update(this.pcb_group_id, {
69586
- width: hasExplicitWidth ? distance19.parse(this._parsedProps.width) : gridWidth + edgePaddingLeft + edgePaddingRight,
69587
- height: hasExplicitHeight ? distance19.parse(this._parsedProps.height) : gridHeight + edgePaddingTop + edgePaddingBottom
70772
+ width: hasExplicitWidth ? distance20.parse(this._parsedProps.width) : gridWidth + edgePaddingLeft + edgePaddingRight,
70773
+ height: hasExplicitHeight ? distance20.parse(this._parsedProps.height) : gridHeight + edgePaddingTop + edgePaddingBottom
69588
70774
  });
69589
70775
  }
69590
70776
  }
@@ -69672,8 +70858,8 @@ var Panel = class extends Subpanel {
69672
70858
  const { db } = this.root;
69673
70859
  const props = this._parsedProps;
69674
70860
  const inserted = db.pcb_panel.insert({
69675
- width: props.width !== void 0 ? distance20.parse(props.width) : 0,
69676
- height: props.height !== void 0 ? distance20.parse(props.height) : 0,
70861
+ width: props.width !== void 0 ? distance21.parse(props.width) : 0,
70862
+ height: props.height !== void 0 ? distance21.parse(props.height) : 0,
69677
70863
  thickness: 1.6,
69678
70864
  center: this._getGlobalPcbPositionBeforeLayout(),
69679
70865
  covered_with_solder_mask: !(props.noSolderMask ?? false)
@@ -69692,8 +70878,8 @@ var Panel = class extends Subpanel {
69692
70878
  if (!this.pcb_panel_id) return;
69693
70879
  if (hasExplicitWidth && hasExplicitHeight) {
69694
70880
  db.pcb_panel.update(this.pcb_panel_id, {
69695
- width: distance20.parse(this._parsedProps.width),
69696
- height: distance20.parse(this._parsedProps.height)
70881
+ width: distance21.parse(this._parsedProps.width),
70882
+ height: distance21.parse(this._parsedProps.height)
69697
70883
  });
69698
70884
  } else if (gridWidth > 0 || gridHeight > 0) {
69699
70885
  const {
@@ -69703,18 +70889,18 @@ var Panel = class extends Subpanel {
69703
70889
  edgePaddingTop: edgePaddingTopProp,
69704
70890
  edgePaddingBottom: edgePaddingBottomProp
69705
70891
  } = this._parsedProps;
69706
- const edgePadding = distance20.parse(edgePaddingProp ?? 5);
69707
- const edgePaddingLeft = distance20.parse(edgePaddingLeftProp ?? edgePadding);
69708
- const edgePaddingRight = distance20.parse(
70892
+ const edgePadding = distance21.parse(edgePaddingProp ?? 5);
70893
+ const edgePaddingLeft = distance21.parse(edgePaddingLeftProp ?? edgePadding);
70894
+ const edgePaddingRight = distance21.parse(
69709
70895
  edgePaddingRightProp ?? edgePadding
69710
70896
  );
69711
- const edgePaddingTop = distance20.parse(edgePaddingTopProp ?? edgePadding);
69712
- const edgePaddingBottom = distance20.parse(
70897
+ const edgePaddingTop = distance21.parse(edgePaddingTopProp ?? edgePadding);
70898
+ const edgePaddingBottom = distance21.parse(
69713
70899
  edgePaddingBottomProp ?? edgePadding
69714
70900
  );
69715
70901
  db.pcb_panel.update(this.pcb_panel_id, {
69716
- width: hasExplicitWidth ? distance20.parse(this._parsedProps.width) : gridWidth + edgePaddingLeft + edgePaddingRight,
69717
- height: hasExplicitHeight ? distance20.parse(this._parsedProps.height) : gridHeight + edgePaddingTop + edgePaddingBottom
70902
+ width: hasExplicitWidth ? distance21.parse(this._parsedProps.width) : gridWidth + edgePaddingLeft + edgePaddingRight,
70903
+ height: hasExplicitHeight ? distance21.parse(this._parsedProps.height) : gridHeight + edgePaddingTop + edgePaddingBottom
69718
70904
  });
69719
70905
  }
69720
70906
  }
@@ -69725,8 +70911,8 @@ var Panel = class extends Subpanel {
69725
70911
  const props = this._parsedProps;
69726
70912
  const currentPanel = db.pcb_panel.get(this.pcb_panel_id);
69727
70913
  db.pcb_panel.update(this.pcb_panel_id, {
69728
- width: props.width !== void 0 ? distance20.parse(props.width) : currentPanel?.width,
69729
- height: props.height !== void 0 ? distance20.parse(props.height) : currentPanel?.height,
70914
+ width: props.width !== void 0 ? distance21.parse(props.width) : currentPanel?.width,
70915
+ height: props.height !== void 0 ? distance21.parse(props.height) : currentPanel?.height,
69730
70916
  center: this._getGlobalPcbPositionBeforeLayout(),
69731
70917
  covered_with_solder_mask: !(props.noSolderMask ?? false)
69732
70918
  });
@@ -72032,7 +73218,7 @@ var validateWindingBreakoutInput = (input) => {
72032
73218
  };
72033
73219
 
72034
73220
  // node_modules/@tscircuit/winding-breakout-point-solver/lib/solvers/GatePlacementSolver.ts
72035
- import { BaseSolver as BaseSolver6 } from "@tscircuit/solver-utils";
73221
+ import { BaseSolver as BaseSolver7 } from "@tscircuit/solver-utils";
72036
73222
 
72037
73223
  // node_modules/@tscircuit/winding-breakout-point-solver/lib/gate-placement/place-breakout-gates.ts
72038
73224
  var GEOMETRY_EPSILON = 1e-9;
@@ -72562,7 +73748,7 @@ var getSharedGateSlotCount = (points) => new Set(
72562
73748
  (point6) => `${point6.regionId}:${point6.x.toFixed(9)}:${point6.y.toFixed(9)}`
72563
73749
  )
72564
73750
  ).size;
72565
- var GatePlacementSolver = class extends BaseSolver6 {
73751
+ var GatePlacementSolver = class extends BaseSolver7 {
72566
73752
  constructor(params) {
72567
73753
  super();
72568
73754
  this.params = params;
@@ -72701,7 +73887,7 @@ var GatePlacementSolver = class extends BaseSolver6 {
72701
73887
  };
72702
73888
 
72703
73889
  // node_modules/@tscircuit/winding-breakout-point-solver/lib/solvers/ReferenceOrderingSolver.ts
72704
- import { BaseSolver as BaseSolver7 } from "@tscircuit/solver-utils";
73890
+ import { BaseSolver as BaseSolver8 } from "@tscircuit/solver-utils";
72705
73891
 
72706
73892
  // node_modules/@tscircuit/winding-breakout-point-solver/lib/ordering/get-natural-winding-order.ts
72707
73893
  var getNaturalWindingOrder = (region, connections) => connections.map((connection) => {
@@ -72768,7 +73954,7 @@ var getReferenceWindingOrder = (naturalOrder, atomicGroups, buses) => {
72768
73954
  };
72769
73955
 
72770
73956
  // node_modules/@tscircuit/winding-breakout-point-solver/lib/solvers/ReferenceOrderingSolver.ts
72771
- var ReferenceOrderingSolver = class extends BaseSolver7 {
73957
+ var ReferenceOrderingSolver = class extends BaseSolver8 {
72772
73958
  constructor(params) {
72773
73959
  super();
72774
73960
  this.params = params;
@@ -74113,7 +75299,7 @@ var SilkscreenGraphic = class extends PrimitiveComponent2 {
74113
75299
 
74114
75300
  // lib/components/primitive-components/Fiducial.ts
74115
75301
  import "zod";
74116
- import { distance as distance21 } from "circuit-json";
75302
+ import { distance as distance22 } from "circuit-json";
74117
75303
  import { fiducialProps as fiducialProps2 } from "@tscircuit/props";
74118
75304
  var Fiducial = class extends PrimitiveComponent2 {
74119
75305
  pcb_smtpad_id = null;
@@ -74138,15 +75324,15 @@ var Fiducial = class extends PrimitiveComponent2 {
74138
75324
  shape: "circle",
74139
75325
  x: position.x,
74140
75326
  y: position.y,
74141
- radius: distance21.parse(props.padDiameter) / 2,
74142
- soldermask_margin: props.soldermaskPullback ? distance21.parse(props.soldermaskPullback) : distance21.parse(props.padDiameter) / 2,
75327
+ radius: distance22.parse(props.padDiameter) / 2,
75328
+ soldermask_margin: props.soldermaskPullback ? distance22.parse(props.soldermaskPullback) : distance22.parse(props.padDiameter) / 2,
74143
75329
  is_covered_with_solder_mask: true
74144
75330
  });
74145
75331
  this.pcb_smtpad_id = pcb_smtpad.pcb_smtpad_id;
74146
75332
  }
74147
75333
  getPcbSize() {
74148
75334
  const { _parsedProps: props } = this;
74149
- const d = distance21.parse(props.padDiameter);
75335
+ const d = distance22.parse(props.padDiameter);
74150
75336
  return { width: d, height: d };
74151
75337
  }
74152
75338
  _setPositionFromLayout(newCenter) {