@tscircuit/core 0.0.2024 → 0.0.2026
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.d.ts +9 -6
- package/dist/index.js +1286 -140
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -3280,10 +3280,70 @@ var getAxisAlignedRectFromPolygon = (points) => {
|
|
|
3280
3280
|
};
|
|
3281
3281
|
};
|
|
3282
3282
|
|
|
3283
|
+
// lib/utils/obstacles/getCircumscribedRectFromCircularPcbKeepoutOutline.ts
|
|
3284
|
+
var MINIMUM_CIRCULAR_OUTLINE_POINT_COUNT = 12;
|
|
3285
|
+
var MINIMUM_CIRCULAR_OUTLINE_ASPECT_RATIO = 0.99;
|
|
3286
|
+
var MAXIMUM_RADIUS_VARIATION_RATIO = 1e-3;
|
|
3287
|
+
var getCircumscribedRectFromCircularPcbKeepoutOutline = (keepout) => {
|
|
3288
|
+
const { outline, stroke_width } = keepout;
|
|
3289
|
+
const firstPoint = outline[0];
|
|
3290
|
+
const lastPoint = outline.at(-1);
|
|
3291
|
+
if (!firstPoint || !lastPoint) return null;
|
|
3292
|
+
if (firstPoint.x !== lastPoint.x || firstPoint.y !== lastPoint.y) return null;
|
|
3293
|
+
const centerlinePoints = outline.slice(0, -1);
|
|
3294
|
+
if (centerlinePoints.length < MINIMUM_CIRCULAR_OUTLINE_POINT_COUNT)
|
|
3295
|
+
return null;
|
|
3296
|
+
const minX = Math.min(...centerlinePoints.map(({ x }) => x));
|
|
3297
|
+
const maxX = Math.max(...centerlinePoints.map(({ x }) => x));
|
|
3298
|
+
const minY = Math.min(...centerlinePoints.map(({ y }) => y));
|
|
3299
|
+
const maxY = Math.max(...centerlinePoints.map(({ y }) => y));
|
|
3300
|
+
const centerlineWidth = maxX - minX;
|
|
3301
|
+
const centerlineHeight = maxY - minY;
|
|
3302
|
+
const centerlineDiameter = Math.max(centerlineWidth, centerlineHeight);
|
|
3303
|
+
if (!Number.isFinite(centerlineDiameter) || centerlineDiameter <= 0 || Math.min(centerlineWidth, centerlineHeight) / centerlineDiameter < MINIMUM_CIRCULAR_OUTLINE_ASPECT_RATIO) {
|
|
3304
|
+
return null;
|
|
3305
|
+
}
|
|
3306
|
+
const center = { x: (minX + maxX) / 2, y: (minY + maxY) / 2 };
|
|
3307
|
+
const radii = centerlinePoints.map(
|
|
3308
|
+
({ x, y }) => Math.hypot(x - center.x, y - center.y)
|
|
3309
|
+
);
|
|
3310
|
+
if (Math.max(...radii) - Math.min(...radii) > centerlineDiameter * MAXIMUM_RADIUS_VARIATION_RATIO) {
|
|
3311
|
+
return null;
|
|
3312
|
+
}
|
|
3313
|
+
const angles = centerlinePoints.map(
|
|
3314
|
+
({ x, y }) => Math.atan2(y - center.y, x - center.x)
|
|
3315
|
+
);
|
|
3316
|
+
const angularSteps = angles.map((angle, angleIndex) => {
|
|
3317
|
+
const nextAngle = angles[(angleIndex + 1) % angles.length];
|
|
3318
|
+
return Math.atan2(Math.sin(nextAngle - angle), Math.cos(nextAngle - angle));
|
|
3319
|
+
});
|
|
3320
|
+
const traversalDirection = Math.sign(angularSteps[0]);
|
|
3321
|
+
if (traversalDirection === 0 || angularSteps.some(
|
|
3322
|
+
(angularStep) => Math.sign(angularStep) !== traversalDirection || Math.abs(angularStep) > 4 * Math.PI / centerlinePoints.length
|
|
3323
|
+
)) {
|
|
3324
|
+
return null;
|
|
3325
|
+
}
|
|
3326
|
+
const outerDiameter = centerlineDiameter + stroke_width;
|
|
3327
|
+
return { center, width: outerDiameter, height: outerDiameter };
|
|
3328
|
+
};
|
|
3329
|
+
|
|
3283
3330
|
// lib/utils/obstacles/getObstaclesFromPcbKeepoutOutline.ts
|
|
3284
3331
|
var getObstaclesFromPcbKeepoutOutline = (keepout, componentId) => {
|
|
3285
3332
|
const { outline, stroke_width } = keepout;
|
|
3286
3333
|
if (!Number.isFinite(stroke_width) || stroke_width <= 0) return [];
|
|
3334
|
+
const circularKeepoutRect = getCircumscribedRectFromCircularPcbKeepoutOutline(keepout);
|
|
3335
|
+
if (circularKeepoutRect) {
|
|
3336
|
+
return [
|
|
3337
|
+
{
|
|
3338
|
+
obstacleId: keepout.pcb_keepout_id,
|
|
3339
|
+
componentId,
|
|
3340
|
+
type: "rect",
|
|
3341
|
+
layers: keepout.layers,
|
|
3342
|
+
...circularKeepoutRect,
|
|
3343
|
+
connectedTo: []
|
|
3344
|
+
}
|
|
3345
|
+
];
|
|
3346
|
+
}
|
|
3287
3347
|
const obstacles = [];
|
|
3288
3348
|
for (let segmentIndex = 0; segmentIndex < outline.length - 1; segmentIndex++) {
|
|
3289
3349
|
const start = outline[segmentIndex];
|
|
@@ -3548,8 +3608,7 @@ var getObstaclesFromCircuitJson = (circuitJson, connMap) => {
|
|
|
3548
3608
|
if (element.shape === "circle") {
|
|
3549
3609
|
obstacles.push({
|
|
3550
3610
|
componentId: pcbComponentId,
|
|
3551
|
-
|
|
3552
|
-
type: "oval",
|
|
3611
|
+
type: "rect",
|
|
3553
3612
|
layers: element.layers,
|
|
3554
3613
|
center: {
|
|
3555
3614
|
x: element.center.x,
|
|
@@ -6218,10 +6277,10 @@ function addTraceTeardrops(route, contacts) {
|
|
|
6218
6277
|
result.push(start);
|
|
6219
6278
|
continue;
|
|
6220
6279
|
}
|
|
6221
|
-
const atDistance = (
|
|
6280
|
+
const atDistance = (distance25) => ({
|
|
6222
6281
|
route_type: "wire",
|
|
6223
|
-
x: start.x + (end.x - start.x) *
|
|
6224
|
-
y: start.y + (end.y - start.y) *
|
|
6282
|
+
x: start.x + (end.x - start.x) * distance25 / length8,
|
|
6283
|
+
y: start.y + (end.y - start.y) * distance25 / length8,
|
|
6225
6284
|
width: start.width,
|
|
6226
6285
|
layer: start.layer
|
|
6227
6286
|
});
|
|
@@ -6744,11 +6803,11 @@ function Trace_doInitialPcbTraceRender(trace) {
|
|
|
6744
6803
|
const closestNetBPort = getClosest(netAPort, netBPorts);
|
|
6745
6804
|
const netAPortPosition = netAPort._getGlobalPcbPositionBeforeLayout();
|
|
6746
6805
|
const netBPortPosition = closestNetBPort._getGlobalPcbPositionBeforeLayout();
|
|
6747
|
-
const
|
|
6806
|
+
const distance25 = Math.sqrt(
|
|
6748
6807
|
(netAPortPosition.x - netBPortPosition.x) ** 2 + (netAPortPosition.y - netBPortPosition.y) ** 2
|
|
6749
6808
|
);
|
|
6750
|
-
if (
|
|
6751
|
-
closestDistance =
|
|
6809
|
+
if (distance25 < closestDistance) {
|
|
6810
|
+
closestDistance = distance25;
|
|
6752
6811
|
closestPortPair = [netAPort, closestNetBPort];
|
|
6753
6812
|
}
|
|
6754
6813
|
}
|
|
@@ -12959,7 +13018,7 @@ var createComponentsFromCircuitJson = ({
|
|
|
12959
13018
|
const schematicSymbolId = schematicComponent?.schematic_symbol_id;
|
|
12960
13019
|
const parentSymbol = typeof schematicSymbolId === "string" ? schematicSymbolsByImportedId.get(schematicSymbolId) : void 0;
|
|
12961
13020
|
if (parentSymbol && schematicComponent?.is_box_with_pins === true && elm.center && elm.side_of_component) {
|
|
12962
|
-
const
|
|
13021
|
+
const distance25 = elm.distance_from_component_edge;
|
|
12963
13022
|
const facingDirection = getFacingDirectionFromSide(
|
|
12964
13023
|
elm.side_of_component
|
|
12965
13024
|
);
|
|
@@ -12967,8 +13026,8 @@ var createComponentsFromCircuitJson = ({
|
|
|
12967
13026
|
const directionVector = getUnitVectorFromDirection2(facingDirection);
|
|
12968
13027
|
const stemDirection = elm.facing_direction === facingDirection ? 1 : -1;
|
|
12969
13028
|
const portCenter = {
|
|
12970
|
-
x: elm.center.x + directionVector.x *
|
|
12971
|
-
y: elm.center.y + directionVector.y *
|
|
13029
|
+
x: elm.center.x + directionVector.x * distance25 * stemDirection,
|
|
13030
|
+
y: elm.center.y + directionVector.y * distance25 * stemDirection
|
|
12972
13031
|
};
|
|
12973
13032
|
const sourcePort = sourcePortsByImportedId.get(elm.source_port_id);
|
|
12974
13033
|
const aliases = Array.from(
|
|
@@ -17737,15 +17796,15 @@ var Capacitor_addPolarityFabricationSymbols = (capacitor) => {
|
|
|
17737
17796
|
source_port_id: negatives[0].source_port_id
|
|
17738
17797
|
});
|
|
17739
17798
|
if (!positive || !negative) return;
|
|
17740
|
-
const
|
|
17741
|
-
if (
|
|
17799
|
+
const distance25 = Math.hypot(negative.x - positive.x, negative.y - positive.y);
|
|
17800
|
+
if (distance25 < 1e-6) return;
|
|
17742
17801
|
const frame = compose7(
|
|
17743
17802
|
translate5((positive.x + negative.x) / 2, (positive.y + negative.y) / 2),
|
|
17744
17803
|
rotate4(Math.atan2(negative.y - positive.y, negative.x - positive.x))
|
|
17745
17804
|
);
|
|
17746
17805
|
const point6 = (x, y) => applyToPoint26(frame, { x, y });
|
|
17747
|
-
const offset =
|
|
17748
|
-
const halfSize = Math.min(
|
|
17806
|
+
const offset = distance25 / 4;
|
|
17807
|
+
const halfSize = Math.min(distance25 * 0.12, 0.3);
|
|
17749
17808
|
const routes = [
|
|
17750
17809
|
[point6(-offset - halfSize, 0), point6(-offset + halfSize, 0)],
|
|
17751
17810
|
[point6(-offset, -halfSize), point6(-offset, halfSize)],
|
|
@@ -19570,7 +19629,7 @@ import {
|
|
|
19570
19629
|
groupProps
|
|
19571
19630
|
} from "@tscircuit/props";
|
|
19572
19631
|
import {
|
|
19573
|
-
distance as
|
|
19632
|
+
distance as distance18
|
|
19574
19633
|
} from "circuit-json";
|
|
19575
19634
|
import Debug15 from "debug";
|
|
19576
19635
|
|
|
@@ -38814,9 +38873,9 @@ function getFreeBoundaryTracks(params) {
|
|
|
38814
38873
|
for (const via of vias) {
|
|
38815
38874
|
if (!via.spanLayers.includes(targetLayer2)) continue;
|
|
38816
38875
|
const radius = via.diameter / 2 + traceWidth / 2 + clearance;
|
|
38817
|
-
const
|
|
38818
|
-
if (
|
|
38819
|
-
const extent = Math.sqrt(radius * radius -
|
|
38876
|
+
const distance25 = Math.abs(via.center[across] - edge);
|
|
38877
|
+
if (distance25 >= radius) continue;
|
|
38878
|
+
const extent = Math.sqrt(radius * radius - distance25 * distance25);
|
|
38820
38879
|
blocked.push([via.center[along] - extent, via.center[along] + extent]);
|
|
38821
38880
|
}
|
|
38822
38881
|
const gaps = [];
|
|
@@ -50507,6 +50566,8 @@ function getPresetAutoroutingConfig(autorouterConfig, platformConfig) {
|
|
|
50507
50566
|
};
|
|
50508
50567
|
case "bus-lanes":
|
|
50509
50568
|
return { local: true, groupMode: "subcircuit", preset: "bus_lanes" };
|
|
50569
|
+
case "dogbone":
|
|
50570
|
+
return { local: true, groupMode: "subcircuit", preset: "dogbone" };
|
|
50510
50571
|
case "fanout":
|
|
50511
50572
|
return {
|
|
50512
50573
|
local: true,
|
|
@@ -50569,11 +50630,1027 @@ function getPresetAutoroutingConfig(autorouterConfig, platformConfig) {
|
|
|
50569
50630
|
}
|
|
50570
50631
|
}
|
|
50571
50632
|
|
|
50572
|
-
// node_modules/@tscircuit/
|
|
50633
|
+
// node_modules/@tscircuit/dogbone-solver/lib/dogbone-fanout-solver.ts
|
|
50634
|
+
import { BaseSolver as BaseSolver7 } from "@tscircuit/solver-utils";
|
|
50635
|
+
|
|
50636
|
+
// node_modules/@tscircuit/dogbone-solver/node_modules/@tscircuit/fanout-solver/lib/geometry.ts
|
|
50637
|
+
var EPSILON24 = 1e-9;
|
|
50638
|
+
function obstacleIsCircular2(obstacle) {
|
|
50639
|
+
return obstacle.shape === "circle";
|
|
50640
|
+
}
|
|
50641
|
+
function toObstacleLocalPoint2(point6, obstacle) {
|
|
50642
|
+
const rotationRadians = -(obstacle.ccwRotationDegrees ?? 0) * Math.PI / 180;
|
|
50643
|
+
const dx = point6.x - obstacle.center.x;
|
|
50644
|
+
const dy = point6.y - obstacle.center.y;
|
|
50645
|
+
return {
|
|
50646
|
+
x: dx * Math.cos(rotationRadians) - dy * Math.sin(rotationRadians),
|
|
50647
|
+
y: dx * Math.sin(rotationRadians) + dy * Math.cos(rotationRadians)
|
|
50648
|
+
};
|
|
50649
|
+
}
|
|
50650
|
+
function distance12(a, b) {
|
|
50651
|
+
return Math.hypot(a.x - b.x, a.y - b.y);
|
|
50652
|
+
}
|
|
50653
|
+
function distancePointToSegment2(point6, start, end) {
|
|
50654
|
+
const dx = end.x - start.x;
|
|
50655
|
+
const dy = end.y - start.y;
|
|
50656
|
+
const lengthSquared = dx * dx + dy * dy;
|
|
50657
|
+
const rawT = lengthSquared < EPSILON24 ? 0 : ((point6.x - start.x) * dx + (point6.y - start.y) * dy) / lengthSquared;
|
|
50658
|
+
const t = Math.max(0, Math.min(1, rawT));
|
|
50659
|
+
return Math.hypot(point6.x - (start.x + t * dx), point6.y - (start.y + t * dy));
|
|
50660
|
+
}
|
|
50661
|
+
function cross2(origin, a, b) {
|
|
50662
|
+
return (a.x - origin.x) * (b.y - origin.y) - (a.y - origin.y) * (b.x - origin.x);
|
|
50663
|
+
}
|
|
50664
|
+
function segmentsProperlyCross2(a, b, c, d) {
|
|
50665
|
+
const d1 = cross2(c, d, a);
|
|
50666
|
+
const d2 = cross2(c, d, b);
|
|
50667
|
+
const d3 = cross2(a, b, c);
|
|
50668
|
+
const d4 = cross2(a, b, d);
|
|
50669
|
+
return (d1 > 0 && d2 < 0 || d1 < 0 && d2 > 0) && (d3 > 0 && d4 < 0 || d3 < 0 && d4 > 0);
|
|
50670
|
+
}
|
|
50671
|
+
function distanceSegmentToSegment2(firstStart, firstEnd, secondStart, secondEnd) {
|
|
50672
|
+
if (segmentsProperlyCross2(firstStart, firstEnd, secondStart, secondEnd)) {
|
|
50673
|
+
return 0;
|
|
50674
|
+
}
|
|
50675
|
+
return Math.min(
|
|
50676
|
+
distancePointToSegment2(firstStart, secondStart, secondEnd),
|
|
50677
|
+
distancePointToSegment2(firstEnd, secondStart, secondEnd),
|
|
50678
|
+
distancePointToSegment2(secondStart, firstStart, firstEnd),
|
|
50679
|
+
distancePointToSegment2(secondEnd, firstStart, firstEnd)
|
|
50680
|
+
);
|
|
50681
|
+
}
|
|
50682
|
+
function distancePointToObstacle2(point6, obstacle) {
|
|
50683
|
+
if (obstacleIsCircular2(obstacle)) {
|
|
50684
|
+
return Math.max(0, distance12(point6, obstacle.center) - obstacle.width / 2);
|
|
50685
|
+
}
|
|
50686
|
+
const localPoint = toObstacleLocalPoint2(point6, obstacle);
|
|
50687
|
+
const dx = Math.max(Math.abs(localPoint.x) - obstacle.width / 2, 0);
|
|
50688
|
+
const dy = Math.max(Math.abs(localPoint.y) - obstacle.height / 2, 0);
|
|
50689
|
+
return Math.hypot(dx, dy);
|
|
50690
|
+
}
|
|
50691
|
+
function distanceSegmentToObstacle2(segment2, obstacle) {
|
|
50692
|
+
if (obstacleIsCircular2(obstacle)) {
|
|
50693
|
+
return Math.max(
|
|
50694
|
+
0,
|
|
50695
|
+
distancePointToSegment2(obstacle.center, segment2.start, segment2.end) - obstacle.width / 2
|
|
50696
|
+
);
|
|
50697
|
+
}
|
|
50698
|
+
const localStart = toObstacleLocalPoint2(segment2.start, obstacle);
|
|
50699
|
+
const localEnd = toObstacleLocalPoint2(segment2.end, obstacle);
|
|
50700
|
+
if (Math.abs(localStart.x) <= obstacle.width / 2 + EPSILON24 && Math.abs(localStart.y) <= obstacle.height / 2 + EPSILON24) {
|
|
50701
|
+
return 0;
|
|
50702
|
+
}
|
|
50703
|
+
if (Math.abs(localEnd.x) <= obstacle.width / 2 + EPSILON24 && Math.abs(localEnd.y) <= obstacle.height / 2 + EPSILON24) {
|
|
50704
|
+
return 0;
|
|
50705
|
+
}
|
|
50706
|
+
const minX = -obstacle.width / 2;
|
|
50707
|
+
const maxX = obstacle.width / 2;
|
|
50708
|
+
const minY = -obstacle.height / 2;
|
|
50709
|
+
const maxY = obstacle.height / 2;
|
|
50710
|
+
const corners = [
|
|
50711
|
+
{ x: minX, y: minY },
|
|
50712
|
+
{ x: maxX, y: minY },
|
|
50713
|
+
{ x: maxX, y: maxY },
|
|
50714
|
+
{ x: minX, y: maxY }
|
|
50715
|
+
];
|
|
50716
|
+
let minimumDistance = Number.POSITIVE_INFINITY;
|
|
50717
|
+
for (let index = 0; index < corners.length; index++) {
|
|
50718
|
+
minimumDistance = Math.min(
|
|
50719
|
+
minimumDistance,
|
|
50720
|
+
distanceSegmentToSegment2(
|
|
50721
|
+
localStart,
|
|
50722
|
+
localEnd,
|
|
50723
|
+
corners[index],
|
|
50724
|
+
corners[(index + 1) % corners.length]
|
|
50725
|
+
)
|
|
50726
|
+
);
|
|
50727
|
+
}
|
|
50728
|
+
return minimumDistance;
|
|
50729
|
+
}
|
|
50730
|
+
function segmentsAreClear2(first, second, clearance) {
|
|
50731
|
+
if (first.layer !== second.layer) return true;
|
|
50732
|
+
const requiredDistance = (first.width + second.width) / 2 + clearance;
|
|
50733
|
+
const broadPhaseDistance = requiredDistance + EPSILON24;
|
|
50734
|
+
if (Math.min(first.start.x, first.end.x) - Math.max(second.start.x, second.end.x) > broadPhaseDistance || Math.min(second.start.x, second.end.x) - Math.max(first.start.x, first.end.x) > broadPhaseDistance || Math.min(first.start.y, first.end.y) - Math.max(second.start.y, second.end.y) > broadPhaseDistance || Math.min(second.start.y, second.end.y) - Math.max(first.start.y, first.end.y) > broadPhaseDistance) {
|
|
50735
|
+
return true;
|
|
50736
|
+
}
|
|
50737
|
+
return distanceSegmentToSegment2(
|
|
50738
|
+
first.start,
|
|
50739
|
+
first.end,
|
|
50740
|
+
second.start,
|
|
50741
|
+
second.end
|
|
50742
|
+
) >= requiredDistance - EPSILON24;
|
|
50743
|
+
}
|
|
50744
|
+
|
|
50745
|
+
// node_modules/@tscircuit/dogbone-solver/node_modules/@tscircuit/fanout-solver/lib/match-component-dogbone-via-sites.ts
|
|
50746
|
+
var EPSILON25 = 1e-9;
|
|
50747
|
+
var DEFAULT_MAXIMUM_SEARCH_STATES2 = 1e5;
|
|
50748
|
+
function assertGeometryRules2(rules) {
|
|
50749
|
+
for (const [name, value] of [
|
|
50750
|
+
["viaDiameter", rules.viaDiameter],
|
|
50751
|
+
["traceWidth", rules.traceWidth]
|
|
50752
|
+
]) {
|
|
50753
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
50754
|
+
throw new Error(
|
|
50755
|
+
`FanoutSolver: dogbone ${name} must be a positive finite number, received ${value}`
|
|
50756
|
+
);
|
|
50757
|
+
}
|
|
50758
|
+
}
|
|
50759
|
+
if (!Number.isFinite(rules.clearance) || rules.clearance < 0) {
|
|
50760
|
+
throw new Error(
|
|
50761
|
+
`FanoutSolver: dogbone clearance must be a non-negative finite number, received ${rules.clearance}`
|
|
50762
|
+
);
|
|
50763
|
+
}
|
|
50764
|
+
if (rules.viaHoleDiameter !== void 0 && (!Number.isFinite(rules.viaHoleDiameter) || rules.viaHoleDiameter <= 0)) {
|
|
50765
|
+
throw new Error(
|
|
50766
|
+
`FanoutSolver: dogbone viaHoleDiameter must be a positive finite number, received ${rules.viaHoleDiameter}`
|
|
50767
|
+
);
|
|
50768
|
+
}
|
|
50769
|
+
if (rules.holeToHoleClearance !== void 0 && (!Number.isFinite(rules.holeToHoleClearance) || rules.holeToHoleClearance < 0)) {
|
|
50770
|
+
throw new Error(
|
|
50771
|
+
`FanoutSolver: dogbone holeToHoleClearance must be a non-negative finite number, received ${rules.holeToHoleClearance}`
|
|
50772
|
+
);
|
|
50773
|
+
}
|
|
50774
|
+
if (rules.holeToHoleClearance !== void 0 && rules.viaHoleDiameter === void 0) {
|
|
50775
|
+
throw new Error(
|
|
50776
|
+
"FanoutSolver: dogbone holeToHoleClearance requires viaHoleDiameter"
|
|
50777
|
+
);
|
|
50778
|
+
}
|
|
50779
|
+
const maximumSearchStates = rules.maximumSearchStates ?? DEFAULT_MAXIMUM_SEARCH_STATES2;
|
|
50780
|
+
if (!Number.isInteger(maximumSearchStates) || maximumSearchStates < 1) {
|
|
50781
|
+
throw new Error(
|
|
50782
|
+
`FanoutSolver: dogbone maximumSearchStates must be a positive integer, received ${maximumSearchStates}`
|
|
50783
|
+
);
|
|
50784
|
+
}
|
|
50785
|
+
return maximumSearchStates;
|
|
50786
|
+
}
|
|
50787
|
+
function uniqueSortedCoordinates2(values) {
|
|
50788
|
+
const result = [];
|
|
50789
|
+
for (const value of values.toSorted((first, second) => first - second)) {
|
|
50790
|
+
if (!Number.isFinite(value)) continue;
|
|
50791
|
+
if (result.length === 0 || Math.abs(result.at(-1) - value) > EPSILON25) {
|
|
50792
|
+
result.push(value);
|
|
50793
|
+
}
|
|
50794
|
+
}
|
|
50795
|
+
return result;
|
|
50796
|
+
}
|
|
50797
|
+
function getComponentMatchingInputs2(preparedBuses) {
|
|
50798
|
+
const byComponent = /* @__PURE__ */ new Map();
|
|
50799
|
+
const componentByConnectionIndex = /* @__PURE__ */ new Map();
|
|
50800
|
+
const obstacleSetByComponentId = /* @__PURE__ */ new Map();
|
|
50801
|
+
for (const bus of preparedBuses) {
|
|
50802
|
+
let component = byComponent.get(bus.componentId);
|
|
50803
|
+
if (!component) {
|
|
50804
|
+
component = {
|
|
50805
|
+
componentId: bus.componentId,
|
|
50806
|
+
connections: [],
|
|
50807
|
+
obstacles: [],
|
|
50808
|
+
xCoordinates: [],
|
|
50809
|
+
yCoordinates: [],
|
|
50810
|
+
pitchX: Number.POSITIVE_INFINITY,
|
|
50811
|
+
pitchY: Number.POSITIVE_INFINITY
|
|
50812
|
+
};
|
|
50813
|
+
byComponent.set(bus.componentId, component);
|
|
50814
|
+
obstacleSetByComponentId.set(bus.componentId, /* @__PURE__ */ new Set());
|
|
50815
|
+
}
|
|
50816
|
+
component.xCoordinates.push(...bus.xCoordinates);
|
|
50817
|
+
component.yCoordinates.push(...bus.yCoordinates);
|
|
50818
|
+
if (Number.isFinite(bus.pitchX) && bus.pitchX > EPSILON25) {
|
|
50819
|
+
component.pitchX = Math.min(component.pitchX, bus.pitchX);
|
|
50820
|
+
}
|
|
50821
|
+
if (Number.isFinite(bus.pitchY) && bus.pitchY > EPSILON25) {
|
|
50822
|
+
component.pitchY = Math.min(component.pitchY, bus.pitchY);
|
|
50823
|
+
}
|
|
50824
|
+
const obstacleSet = obstacleSetByComponentId.get(bus.componentId);
|
|
50825
|
+
for (const obstacle of bus.componentObstacles) {
|
|
50826
|
+
if (!obstacleSet.has(obstacle)) {
|
|
50827
|
+
obstacleSet.add(obstacle);
|
|
50828
|
+
component.obstacles.push(obstacle);
|
|
50829
|
+
}
|
|
50830
|
+
}
|
|
50831
|
+
for (const preparedConnection of bus.connections) {
|
|
50832
|
+
const existingComponent = componentByConnectionIndex.get(
|
|
50833
|
+
preparedConnection.connectionIndex
|
|
50834
|
+
);
|
|
50835
|
+
if (existingComponent !== void 0) {
|
|
50836
|
+
if (existingComponent !== bus.componentId) {
|
|
50837
|
+
throw new Error(
|
|
50838
|
+
`FanoutSolver: connection index ${preparedConnection.connectionIndex} belongs to multiple components`
|
|
50839
|
+
);
|
|
50840
|
+
}
|
|
50841
|
+
continue;
|
|
50842
|
+
}
|
|
50843
|
+
componentByConnectionIndex.set(
|
|
50844
|
+
preparedConnection.connectionIndex,
|
|
50845
|
+
bus.componentId
|
|
50846
|
+
);
|
|
50847
|
+
component.connections.push({
|
|
50848
|
+
preparedConnection,
|
|
50849
|
+
busId: bus.busId,
|
|
50850
|
+
direction: bus.direction,
|
|
50851
|
+
terminationType: bus.termination.type
|
|
50852
|
+
});
|
|
50853
|
+
}
|
|
50854
|
+
}
|
|
50855
|
+
return [...byComponent.values()].map((component) => ({
|
|
50856
|
+
...component,
|
|
50857
|
+
connections: component.connections.toSorted(
|
|
50858
|
+
(first, second) => first.preparedConnection.connectionIndex - second.preparedConnection.connectionIndex
|
|
50859
|
+
),
|
|
50860
|
+
xCoordinates: uniqueSortedCoordinates2(component.xCoordinates),
|
|
50861
|
+
yCoordinates: uniqueSortedCoordinates2(component.yCoordinates)
|
|
50862
|
+
})).toSorted(
|
|
50863
|
+
(first, second) => first.componentId.localeCompare(second.componentId)
|
|
50864
|
+
);
|
|
50865
|
+
}
|
|
50866
|
+
function getInterstitialCoordinates2(params) {
|
|
50867
|
+
const { coordinates, pitch } = params;
|
|
50868
|
+
if (coordinates.length === 0 || !Number.isFinite(pitch) || pitch <= EPSILON25) {
|
|
50869
|
+
return [];
|
|
50870
|
+
}
|
|
50871
|
+
const interstitialCoordinates = [coordinates[0] - pitch / 2];
|
|
50872
|
+
for (let index = 1; index < coordinates.length; index++) {
|
|
50873
|
+
interstitialCoordinates.push(
|
|
50874
|
+
(coordinates[index - 1] + coordinates[index]) / 2
|
|
50875
|
+
);
|
|
50876
|
+
}
|
|
50877
|
+
interstitialCoordinates.push(coordinates.at(-1) + pitch / 2);
|
|
50878
|
+
return uniqueSortedCoordinates2(interstitialCoordinates);
|
|
50879
|
+
}
|
|
50880
|
+
function getAdjacentInterstitialCoordinates2(params) {
|
|
50881
|
+
const { sourceCoordinate, coordinates, pitch } = params;
|
|
50882
|
+
const interstitialCoordinates = getInterstitialCoordinates2({
|
|
50883
|
+
coordinates,
|
|
50884
|
+
pitch
|
|
50885
|
+
});
|
|
50886
|
+
const before = interstitialCoordinates.filter((coordinate) => coordinate < sourceCoordinate - EPSILON25).at(-1);
|
|
50887
|
+
const after = interstitialCoordinates.find(
|
|
50888
|
+
(coordinate) => coordinate > sourceCoordinate + EPSILON25
|
|
50889
|
+
);
|
|
50890
|
+
return [before, after].filter(
|
|
50891
|
+
(coordinate) => coordinate !== void 0
|
|
50892
|
+
);
|
|
50893
|
+
}
|
|
50894
|
+
function directSegmentIsStraightOr452(start, end) {
|
|
50895
|
+
const absoluteX = Math.abs(end.x - start.x);
|
|
50896
|
+
const absoluteY = Math.abs(end.y - start.y);
|
|
50897
|
+
return absoluteX <= EPSILON25 || absoluteY <= EPSILON25 || Math.abs(absoluteX - absoluteY) <= EPSILON25;
|
|
50898
|
+
}
|
|
50899
|
+
function getOutwardRank2(params) {
|
|
50900
|
+
const { source, site, direction } = params;
|
|
50901
|
+
const outwardDisplacement = direction === "right" ? site.x - source.x : direction === "left" ? source.x - site.x : direction === "up" ? site.y - source.y : source.y - site.y;
|
|
50902
|
+
return outwardDisplacement > EPSILON25 ? 0 : Math.abs(outwardDisplacement) <= EPSILON25 ? 1 : 2;
|
|
50903
|
+
}
|
|
50904
|
+
function viaSiteClearsObstacles2(params) {
|
|
50905
|
+
const { point: point6, obstacles, viaDiameter, clearance } = params;
|
|
50906
|
+
const requiredClearance = viaDiameter / 2 + clearance;
|
|
50907
|
+
return obstacles.every(
|
|
50908
|
+
(obstacle) => distancePointToObstacle2(point6, obstacle) >= requiredClearance - EPSILON25
|
|
50909
|
+
);
|
|
50910
|
+
}
|
|
50911
|
+
function sourceSegmentClearsOtherObstacles2(params) {
|
|
50912
|
+
const { segment: segment2, sourceObstacle, obstacles, clearance } = params;
|
|
50913
|
+
const requiredClearance = segment2.width / 2 + clearance;
|
|
50914
|
+
return obstacles.every(
|
|
50915
|
+
(obstacle) => obstacle === sourceObstacle || distanceSegmentToObstacle2(segment2, obstacle) >= requiredClearance - EPSILON25
|
|
50916
|
+
);
|
|
50917
|
+
}
|
|
50918
|
+
function getConnectionCandidates2(params) {
|
|
50919
|
+
const { connection, component, rules } = params;
|
|
50920
|
+
const { preparedConnection, direction } = connection;
|
|
50921
|
+
const obstacles = rules.additionalObstacles ? [.../* @__PURE__ */ new Set([...component.obstacles, ...rules.additionalObstacles])] : component.obstacles;
|
|
50922
|
+
const source = {
|
|
50923
|
+
x: preparedConnection.sourcePoint.x,
|
|
50924
|
+
y: preparedConnection.sourcePoint.y
|
|
50925
|
+
};
|
|
50926
|
+
const adjacentX = getAdjacentInterstitialCoordinates2({
|
|
50927
|
+
sourceCoordinate: source.x,
|
|
50928
|
+
coordinates: component.xCoordinates,
|
|
50929
|
+
pitch: component.pitchX
|
|
50930
|
+
});
|
|
50931
|
+
const adjacentY = getAdjacentInterstitialCoordinates2({
|
|
50932
|
+
sourceCoordinate: source.y,
|
|
50933
|
+
coordinates: component.yCoordinates,
|
|
50934
|
+
pitch: component.pitchY
|
|
50935
|
+
});
|
|
50936
|
+
const rawPoints = [
|
|
50937
|
+
...adjacentX.map((x) => ({ x, y: source.y })),
|
|
50938
|
+
...adjacentY.map((y) => ({ x: source.x, y })),
|
|
50939
|
+
...adjacentX.flatMap((x) => adjacentY.map((y) => ({ x, y })))
|
|
50940
|
+
];
|
|
50941
|
+
const uniquePoints2 = [];
|
|
50942
|
+
for (const point6 of rawPoints) {
|
|
50943
|
+
if (!uniquePoints2.some((candidate) => distance12(candidate, point6) <= EPSILON25)) {
|
|
50944
|
+
uniquePoints2.push(point6);
|
|
50945
|
+
}
|
|
50946
|
+
}
|
|
50947
|
+
const fixedViaPoint = rules.fixedViaPointsByConnectionIndex?.get(
|
|
50948
|
+
preparedConnection.connectionIndex
|
|
50949
|
+
);
|
|
50950
|
+
const candidatePoints = fixedViaPoint ? [fixedViaPoint] : uniquePoints2;
|
|
50951
|
+
const candidates = [];
|
|
50952
|
+
for (const point6 of candidatePoints) {
|
|
50953
|
+
if (connection.terminationType === "plane" && !directSegmentIsStraightOr452(source, point6)) {
|
|
50954
|
+
continue;
|
|
50955
|
+
}
|
|
50956
|
+
if (!viaSiteClearsObstacles2({
|
|
50957
|
+
point: point6,
|
|
50958
|
+
obstacles,
|
|
50959
|
+
viaDiameter: rules.viaDiameter,
|
|
50960
|
+
clearance: rules.clearance
|
|
50961
|
+
})) {
|
|
50962
|
+
continue;
|
|
50963
|
+
}
|
|
50964
|
+
const sourceSegment = {
|
|
50965
|
+
start: source,
|
|
50966
|
+
end: point6,
|
|
50967
|
+
width: rules.traceWidth,
|
|
50968
|
+
layer: preparedConnection.sourceLayer
|
|
50969
|
+
};
|
|
50970
|
+
if (!sourceSegmentClearsOtherObstacles2({
|
|
50971
|
+
segment: sourceSegment,
|
|
50972
|
+
sourceObstacle: preparedConnection.sourceObstacle,
|
|
50973
|
+
obstacles,
|
|
50974
|
+
clearance: rules.clearance
|
|
50975
|
+
})) {
|
|
50976
|
+
continue;
|
|
50977
|
+
}
|
|
50978
|
+
const candidateClearsRoutedCopper = (rules.blockingSegments ?? []).every(
|
|
50979
|
+
(blocker) => {
|
|
50980
|
+
if (blocker.connectionIndex === preparedConnection.connectionIndex) {
|
|
50981
|
+
return true;
|
|
50982
|
+
}
|
|
50983
|
+
if (rules.canShareCopper?.(
|
|
50984
|
+
preparedConnection.connectionIndex,
|
|
50985
|
+
blocker.connectionIndex
|
|
50986
|
+
)) {
|
|
50987
|
+
return true;
|
|
50988
|
+
}
|
|
50989
|
+
const viaToTraceClearance = rules.viaDiameter / 2 + blocker.segment.width / 2 + rules.clearance;
|
|
50990
|
+
if (distancePointToSegment2(
|
|
50991
|
+
point6,
|
|
50992
|
+
blocker.segment.start,
|
|
50993
|
+
blocker.segment.end
|
|
50994
|
+
) < viaToTraceClearance - EPSILON25) {
|
|
50995
|
+
return false;
|
|
50996
|
+
}
|
|
50997
|
+
return blocker.segment.layer !== sourceSegment.layer || segmentsAreClear2(sourceSegment, blocker.segment, rules.clearance);
|
|
50998
|
+
}
|
|
50999
|
+
);
|
|
51000
|
+
if (!candidateClearsRoutedCopper) continue;
|
|
51001
|
+
const candidateClearsRoutedVias = (rules.blockingVias ?? []).every(
|
|
51002
|
+
(blocker) => {
|
|
51003
|
+
if (blocker.connectionIndex === preparedConnection.connectionIndex) {
|
|
51004
|
+
return true;
|
|
51005
|
+
}
|
|
51006
|
+
if (distance12(point6, blocker.center) < (rules.viaDiameter + blocker.diameter) / 2 + rules.clearance - EPSILON25) {
|
|
51007
|
+
return false;
|
|
51008
|
+
}
|
|
51009
|
+
if (blocker.spanLayers.includes(sourceSegment.layer) && distancePointToSegment2(
|
|
51010
|
+
blocker.center,
|
|
51011
|
+
sourceSegment.start,
|
|
51012
|
+
sourceSegment.end
|
|
51013
|
+
) < blocker.diameter / 2 + sourceSegment.width / 2 + rules.clearance - EPSILON25) {
|
|
51014
|
+
return false;
|
|
51015
|
+
}
|
|
51016
|
+
return true;
|
|
51017
|
+
}
|
|
51018
|
+
);
|
|
51019
|
+
if (!candidateClearsRoutedVias) continue;
|
|
51020
|
+
candidates.push({
|
|
51021
|
+
connectionIndex: preparedConnection.connectionIndex,
|
|
51022
|
+
point: point6,
|
|
51023
|
+
sourceSegment,
|
|
51024
|
+
outwardRank: getOutwardRank2({ source, site: point6, direction })
|
|
51025
|
+
});
|
|
51026
|
+
}
|
|
51027
|
+
const perpendicularCoordinates = direction === "left" || direction === "right" ? component.yCoordinates : component.xCoordinates;
|
|
51028
|
+
const sourcePerpendicularCoordinate = direction === "left" || direction === "right" ? source.y : source.x;
|
|
51029
|
+
const perpendicularGridIndex = perpendicularCoordinates.findIndex(
|
|
51030
|
+
(coordinate) => Math.abs(coordinate - sourcePerpendicularCoordinate) <= EPSILON25
|
|
51031
|
+
);
|
|
51032
|
+
const planeCheckerboardSide = perpendicularGridIndex >= 0 ? perpendicularGridIndex % 2 === 0 ? 1 : -1 : void 0;
|
|
51033
|
+
const preferredPerpendicularSide = connection.terminationType === "boundary" ? rules.preferredBoundaryPerpendicularSideByBusId?.get(connection.busId) : rules.preferPlaneCheckerboardSites && connection.terminationType === "plane" && (direction === "left" || direction === "right") ? planeCheckerboardSide : void 0;
|
|
51034
|
+
const preferOutward = connection.terminationType === "boundary" ? rules.preferBoundaryOutwardByBusId?.get(connection.busId) ?? true : true;
|
|
51035
|
+
const preferredViaPoint = rules.preferredViaPointsByConnectionIndex?.get(
|
|
51036
|
+
preparedConnection.connectionIndex
|
|
51037
|
+
);
|
|
51038
|
+
const getPerpendicularPreferenceRank = (candidate) => {
|
|
51039
|
+
if (preferredPerpendicularSide === void 0) return 0;
|
|
51040
|
+
const displacement = direction === "left" || direction === "right" ? candidate.point.y - source.y : candidate.point.x - source.x;
|
|
51041
|
+
return displacement * preferredPerpendicularSide > EPSILON25 ? 0 : Math.abs(displacement) <= EPSILON25 ? 1 : 2;
|
|
51042
|
+
};
|
|
51043
|
+
return candidates.toSorted(
|
|
51044
|
+
(first, second) => (preferredViaPoint ? Number(distance12(first.point, preferredViaPoint) > EPSILON25) - Number(distance12(second.point, preferredViaPoint) > EPSILON25) : 0) || (preferOutward ? first.outwardRank - second.outwardRank : second.outwardRank - first.outwardRank) || getPerpendicularPreferenceRank(first) - getPerpendicularPreferenceRank(second) || distance12(source, first.point) - distance12(source, second.point) || first.point.x - second.point.x || first.point.y - second.point.y
|
|
51045
|
+
);
|
|
51046
|
+
}
|
|
51047
|
+
function candidatesAreMutuallyClear2(params) {
|
|
51048
|
+
const { first, second, rules } = params;
|
|
51049
|
+
const canShareCopper = rules.canShareCopper?.(first.connectionIndex, second.connectionIndex) ?? false;
|
|
51050
|
+
const requiredHoleSeparation = rules.viaHoleDiameter ? rules.viaHoleDiameter + (rules.holeToHoleClearance ?? rules.clearance) : 0;
|
|
51051
|
+
const requiredViaSeparation = canShareCopper ? requiredHoleSeparation : Math.max(rules.viaDiameter + rules.clearance, requiredHoleSeparation);
|
|
51052
|
+
if (distance12(first.point, second.point) < requiredViaSeparation - EPSILON25) {
|
|
51053
|
+
return false;
|
|
51054
|
+
}
|
|
51055
|
+
const requiredViaToTraceClearance = rules.viaDiameter / 2 + rules.traceWidth / 2 + rules.clearance;
|
|
51056
|
+
if (!canShareCopper) {
|
|
51057
|
+
if (distancePointToSegment2(
|
|
51058
|
+
first.point,
|
|
51059
|
+
second.sourceSegment.start,
|
|
51060
|
+
second.sourceSegment.end
|
|
51061
|
+
) < requiredViaToTraceClearance - EPSILON25 || distancePointToSegment2(
|
|
51062
|
+
second.point,
|
|
51063
|
+
first.sourceSegment.start,
|
|
51064
|
+
first.sourceSegment.end
|
|
51065
|
+
) < requiredViaToTraceClearance - EPSILON25) {
|
|
51066
|
+
return false;
|
|
51067
|
+
}
|
|
51068
|
+
}
|
|
51069
|
+
if (canShareCopper) return true;
|
|
51070
|
+
return segmentsAreClear2(
|
|
51071
|
+
first.sourceSegment,
|
|
51072
|
+
second.sourceSegment,
|
|
51073
|
+
rules.clearance
|
|
51074
|
+
);
|
|
51075
|
+
}
|
|
51076
|
+
function matchComponent2(params) {
|
|
51077
|
+
const { component, rules, consumeSearchState } = params;
|
|
51078
|
+
const entries = component.connections.map(
|
|
51079
|
+
(connection) => ({
|
|
51080
|
+
connection,
|
|
51081
|
+
candidates: getConnectionCandidates2({ connection, component, rules })
|
|
51082
|
+
})
|
|
51083
|
+
);
|
|
51084
|
+
if (entries.some((entry) => entry.candidates.length === 0)) return null;
|
|
51085
|
+
const compatibilityCache = /* @__PURE__ */ new Map();
|
|
51086
|
+
const candidatesAreCompatible = (first, second) => {
|
|
51087
|
+
const cached = compatibilityCache.get(first)?.get(second);
|
|
51088
|
+
if (cached !== void 0) return cached;
|
|
51089
|
+
const compatible = candidatesAreMutuallyClear2({ first, second, rules });
|
|
51090
|
+
const firstCache = compatibilityCache.get(first) ?? /* @__PURE__ */ new Map();
|
|
51091
|
+
firstCache.set(second, compatible);
|
|
51092
|
+
compatibilityCache.set(first, firstCache);
|
|
51093
|
+
const secondCache = compatibilityCache.get(second) ?? /* @__PURE__ */ new Map();
|
|
51094
|
+
secondCache.set(first, compatible);
|
|
51095
|
+
compatibilityCache.set(second, secondCache);
|
|
51096
|
+
return compatible;
|
|
51097
|
+
};
|
|
51098
|
+
const forcedCandidates = entries.flatMap(
|
|
51099
|
+
(entry) => entry.candidates.length === 1 ? [entry.candidates[0]] : []
|
|
51100
|
+
);
|
|
51101
|
+
for (let candidateIndex = 0; candidateIndex < forcedCandidates.length; candidateIndex++) {
|
|
51102
|
+
const candidate = forcedCandidates[candidateIndex];
|
|
51103
|
+
for (let previousIndex = 0; previousIndex < candidateIndex; previousIndex++) {
|
|
51104
|
+
if (!candidatesAreCompatible(candidate, forcedCandidates[previousIndex])) {
|
|
51105
|
+
return null;
|
|
51106
|
+
}
|
|
51107
|
+
}
|
|
51108
|
+
}
|
|
51109
|
+
const assignedCandidates = new Map(
|
|
51110
|
+
forcedCandidates.map((candidate) => [candidate.connectionIndex, candidate])
|
|
51111
|
+
);
|
|
51112
|
+
const remaining = new Set(
|
|
51113
|
+
entries.flatMap(
|
|
51114
|
+
(entry) => entry.candidates.length > 1 ? [entry.connection.preparedConnection.connectionIndex] : []
|
|
51115
|
+
)
|
|
51116
|
+
);
|
|
51117
|
+
const entryByConnectionIndex = new Map(
|
|
51118
|
+
entries.map((entry) => [
|
|
51119
|
+
entry.connection.preparedConnection.connectionIndex,
|
|
51120
|
+
entry
|
|
51121
|
+
])
|
|
51122
|
+
);
|
|
51123
|
+
if (remaining.size === 0) {
|
|
51124
|
+
return new Map(
|
|
51125
|
+
[...assignedCandidates.entries()].map(([connectionIndex, candidate]) => [
|
|
51126
|
+
connectionIndex,
|
|
51127
|
+
{ ...candidate.point }
|
|
51128
|
+
])
|
|
51129
|
+
);
|
|
51130
|
+
}
|
|
51131
|
+
const getViableCandidates = (entry) => entry.candidates.filter(
|
|
51132
|
+
(candidate) => [...assignedCandidates.values()].every(
|
|
51133
|
+
(assignedCandidate) => candidatesAreCompatible(candidate, assignedCandidate)
|
|
51134
|
+
)
|
|
51135
|
+
);
|
|
51136
|
+
const augmentMatching = () => {
|
|
51137
|
+
if (!consumeSearchState()) return false;
|
|
51138
|
+
if (remaining.size === 0) return true;
|
|
51139
|
+
let selectedEntry;
|
|
51140
|
+
let selectedCandidates = [];
|
|
51141
|
+
for (const connectionIndex2 of [...remaining].toSorted(
|
|
51142
|
+
(first, second) => first - second
|
|
51143
|
+
)) {
|
|
51144
|
+
const entry = entryByConnectionIndex.get(connectionIndex2);
|
|
51145
|
+
const viableCandidates = getViableCandidates(entry);
|
|
51146
|
+
if (viableCandidates.length === 0) return false;
|
|
51147
|
+
if (!selectedEntry || viableCandidates.length < selectedCandidates.length || viableCandidates.length === selectedCandidates.length && connectionIndex2 < selectedEntry.connection.preparedConnection.connectionIndex) {
|
|
51148
|
+
selectedEntry = entry;
|
|
51149
|
+
selectedCandidates = viableCandidates;
|
|
51150
|
+
}
|
|
51151
|
+
}
|
|
51152
|
+
const connectionIndex = selectedEntry.connection.preparedConnection.connectionIndex;
|
|
51153
|
+
remaining.delete(connectionIndex);
|
|
51154
|
+
for (const candidate of selectedCandidates) {
|
|
51155
|
+
assignedCandidates.set(connectionIndex, candidate);
|
|
51156
|
+
if (augmentMatching()) return true;
|
|
51157
|
+
assignedCandidates.delete(connectionIndex);
|
|
51158
|
+
}
|
|
51159
|
+
remaining.add(connectionIndex);
|
|
51160
|
+
return false;
|
|
51161
|
+
};
|
|
51162
|
+
if (!augmentMatching()) return null;
|
|
51163
|
+
return new Map(
|
|
51164
|
+
[...assignedCandidates.entries()].toSorted(([first], [second]) => first - second).map(([connectionIndex, candidate]) => [
|
|
51165
|
+
connectionIndex,
|
|
51166
|
+
{ ...candidate.point }
|
|
51167
|
+
])
|
|
51168
|
+
);
|
|
51169
|
+
}
|
|
51170
|
+
function matchComponentDogboneViaSites2(preparedBuses, rules) {
|
|
51171
|
+
const maximumSearchStates = assertGeometryRules2(rules);
|
|
51172
|
+
if (preparedBuses.length === 0) return /* @__PURE__ */ new Map();
|
|
51173
|
+
let consumedSearchStates = 0;
|
|
51174
|
+
const consumeSearchState = () => {
|
|
51175
|
+
consumedSearchStates++;
|
|
51176
|
+
return consumedSearchStates <= maximumSearchStates;
|
|
51177
|
+
};
|
|
51178
|
+
const result = /* @__PURE__ */ new Map();
|
|
51179
|
+
for (const component of getComponentMatchingInputs2(preparedBuses)) {
|
|
51180
|
+
const componentResult = matchComponent2({
|
|
51181
|
+
component,
|
|
51182
|
+
rules,
|
|
51183
|
+
consumeSearchState
|
|
51184
|
+
});
|
|
51185
|
+
if (!componentResult) return null;
|
|
51186
|
+
for (const [connectionIndex, point6] of componentResult) {
|
|
51187
|
+
result.set(connectionIndex, point6);
|
|
51188
|
+
}
|
|
51189
|
+
}
|
|
51190
|
+
return result;
|
|
51191
|
+
}
|
|
51192
|
+
|
|
51193
|
+
// node_modules/@tscircuit/dogbone-solver/node_modules/@tscircuit/fanout-solver/lib/route-reserved-via-buses.ts
|
|
51194
|
+
import {
|
|
51195
|
+
PortfolioSingleIntraNodeSolver as PortfolioSingleIntraNodeSolver2
|
|
51196
|
+
} from "@tscircuit/capacity-autorouter";
|
|
50573
51197
|
import { BaseSolver as BaseSolver4 } from "@tscircuit/solver-utils";
|
|
50574
51198
|
|
|
51199
|
+
// node_modules/@tscircuit/dogbone-solver/node_modules/@tscircuit/fanout-solver/lib/subsolver-request.ts
|
|
51200
|
+
import { BaseSolver as BaseSolver5 } from "@tscircuit/solver-utils";
|
|
51201
|
+
|
|
51202
|
+
// node_modules/@tscircuit/dogbone-solver/node_modules/@tscircuit/fanout-solver/lib/fanout-solver.ts
|
|
51203
|
+
import { BaseSolver as BaseSolver6 } from "@tscircuit/solver-utils";
|
|
51204
|
+
import { mergeGraphics as mergeGraphics2 } from "graphics-debug";
|
|
51205
|
+
|
|
51206
|
+
// node_modules/@tscircuit/dogbone-solver/node_modules/@tscircuit/fanout-solver/lib/match-angularly-ordered-local-vias.ts
|
|
51207
|
+
var TAU2 = Math.PI * 2;
|
|
51208
|
+
|
|
51209
|
+
// node_modules/@tscircuit/dogbone-solver/node_modules/@tscircuit/fanout-solver/lib/route-single-layer-adaptive-exits.ts
|
|
51210
|
+
var FANOUT_FLOW_DEBUG_ENABLED2 = globalThis.process?.env?.FANOUT_FLOW_DEBUG === "1";
|
|
51211
|
+
|
|
51212
|
+
// node_modules/@tscircuit/dogbone-solver/node_modules/@tscircuit/fanout-solver/lib/runtime-process.ts
|
|
51213
|
+
var getRuntimeProcess2 = (runtime) => runtime.process ?? { env: {} };
|
|
51214
|
+
|
|
51215
|
+
// node_modules/@tscircuit/dogbone-solver/node_modules/@tscircuit/fanout-solver/lib/fanout-solver.ts
|
|
51216
|
+
var process2 = getRuntimeProcess2(globalThis);
|
|
51217
|
+
|
|
51218
|
+
// node_modules/@tscircuit/dogbone-solver/lib/dogbone-fanout-solver.ts
|
|
51219
|
+
import { layer_ref as layer_ref3 } from "circuit-json";
|
|
51220
|
+
|
|
51221
|
+
// node_modules/@tscircuit/dogbone-solver/lib/get-via-board-layers.ts
|
|
51222
|
+
function getViaBoardLayers2(layerCount) {
|
|
51223
|
+
if (layerCount <= 1) return ["top"];
|
|
51224
|
+
return [
|
|
51225
|
+
"top",
|
|
51226
|
+
...Array.from(
|
|
51227
|
+
{ length: layerCount - 2 },
|
|
51228
|
+
(_, index) => `inner${index + 1}`
|
|
51229
|
+
),
|
|
51230
|
+
"bottom"
|
|
51231
|
+
];
|
|
51232
|
+
}
|
|
51233
|
+
|
|
51234
|
+
// node_modules/@tscircuit/dogbone-solver/lib/dogbone-fanout-solver.ts
|
|
51235
|
+
var DogboneFanoutSolver = class extends BaseSolver7 {
|
|
51236
|
+
constructor(params) {
|
|
51237
|
+
super();
|
|
51238
|
+
this.params = params;
|
|
51239
|
+
this.MAX_ITERATIONS = params.input.connections.length * 2 + 10;
|
|
51240
|
+
this.steps = this.solveSteps();
|
|
51241
|
+
}
|
|
51242
|
+
params;
|
|
51243
|
+
phase = "preparing";
|
|
51244
|
+
sources = [];
|
|
51245
|
+
sites = /* @__PURE__ */ new Map();
|
|
51246
|
+
traces = [];
|
|
51247
|
+
steps;
|
|
51248
|
+
getConstructorParams() {
|
|
51249
|
+
return [this.params];
|
|
51250
|
+
}
|
|
51251
|
+
_step() {
|
|
51252
|
+
try {
|
|
51253
|
+
if (this.steps.next().done) {
|
|
51254
|
+
this.phase = "complete";
|
|
51255
|
+
this.progress = 1;
|
|
51256
|
+
this.solved = true;
|
|
51257
|
+
}
|
|
51258
|
+
} catch (error) {
|
|
51259
|
+
this.error = error instanceof Error ? error.message : String(error);
|
|
51260
|
+
this.failed = true;
|
|
51261
|
+
}
|
|
51262
|
+
}
|
|
51263
|
+
getOutput() {
|
|
51264
|
+
if (!this.solved)
|
|
51265
|
+
throw new Error(this.error ?? "Dogbone fanout is not solved");
|
|
51266
|
+
return this.traces;
|
|
51267
|
+
}
|
|
51268
|
+
visualize() {
|
|
51269
|
+
return {
|
|
51270
|
+
rects: this.params.input.obstacles.filter((pad) => pad.shape !== "circle").map((pad) => ({
|
|
51271
|
+
center: pad.center,
|
|
51272
|
+
width: pad.width,
|
|
51273
|
+
height: pad.height,
|
|
51274
|
+
fill: "rgba(200,40,40,0.3)"
|
|
51275
|
+
})),
|
|
51276
|
+
circles: [
|
|
51277
|
+
...this.params.input.obstacles.filter((pad) => pad.shape === "circle").map((pad) => ({
|
|
51278
|
+
center: pad.center,
|
|
51279
|
+
radius: pad.width / 2,
|
|
51280
|
+
fill: "rgba(200,40,40,0.3)"
|
|
51281
|
+
})),
|
|
51282
|
+
...[...this.sites.values()].map((center) => ({
|
|
51283
|
+
center,
|
|
51284
|
+
radius: (this.params.input.minViaPadDiameter ?? this.params.input.minViaDiameter ?? 0.6) / 2,
|
|
51285
|
+
fill: "orange"
|
|
51286
|
+
}))
|
|
51287
|
+
],
|
|
51288
|
+
lines: [...this.sites].map(([index, via]) => ({
|
|
51289
|
+
points: [this.sources[index].port, via],
|
|
51290
|
+
strokeColor: "orange",
|
|
51291
|
+
strokeWidth: this.params.input.minTraceWidth
|
|
51292
|
+
}))
|
|
51293
|
+
};
|
|
51294
|
+
}
|
|
51295
|
+
*solveSteps() {
|
|
51296
|
+
const { input, fanoutRoutingLayers } = this.params;
|
|
51297
|
+
const traceWidth = input.minTraceWidth;
|
|
51298
|
+
const clearance = input.minTraceToPadEdgeClearance ?? 0.1;
|
|
51299
|
+
const viaDiameter = input.minViaPadDiameter ?? input.minViaDiameter ?? 0.6;
|
|
51300
|
+
const holeDiameter = input.minViaHoleDiameter ?? 0.3;
|
|
51301
|
+
const viaClearance = input.minViaEdgeToPadEdgeClearance ?? clearance;
|
|
51302
|
+
const boardEdgeClearance = input.minBoardEdgeClearance ?? clearance;
|
|
51303
|
+
const holeClearance = input.minViaHoleEdgeToViaHoleEdgeClearance ?? clearance;
|
|
51304
|
+
this.sources = input.connections.map((connection) => {
|
|
51305
|
+
const point6 = connection.pointsToConnect.find(
|
|
51306
|
+
(point7) => point7.pcb_port_id && input.obstacles.some(
|
|
51307
|
+
(pad2) => pad2.componentId && Math.hypot(pad2.center.x - point7.x, pad2.center.y - point7.y) < 1e-6
|
|
51308
|
+
)
|
|
51309
|
+
);
|
|
51310
|
+
if (!point6?.pcb_port_id)
|
|
51311
|
+
throw new Error(
|
|
51312
|
+
`Dogbone connection ${connection.name} has no source pad`
|
|
51313
|
+
);
|
|
51314
|
+
const pad = input.obstacles.find(
|
|
51315
|
+
(pad2) => pad2.componentId && Math.hypot(pad2.center.x - point6.x, pad2.center.y - point6.y) < 1e-6
|
|
51316
|
+
);
|
|
51317
|
+
return {
|
|
51318
|
+
port: {
|
|
51319
|
+
pcb_port_id: point6.pcb_port_id,
|
|
51320
|
+
pcb_component_id: pad.componentId,
|
|
51321
|
+
x: point6.x,
|
|
51322
|
+
y: point6.y,
|
|
51323
|
+
layers: [layer_ref3.parse(point6.layer)]
|
|
51324
|
+
},
|
|
51325
|
+
sourceTraceId: connection.source_trace_id
|
|
51326
|
+
};
|
|
51327
|
+
});
|
|
51328
|
+
const sources = this.sources;
|
|
51329
|
+
const layers = getViaBoardLayers2(input.layerCount);
|
|
51330
|
+
const allowedLayers = fanoutRoutingLayers ?? layers;
|
|
51331
|
+
if (allowedLayers.some(
|
|
51332
|
+
(layer) => !layers.some((boardLayer) => boardLayer === layer)
|
|
51333
|
+
))
|
|
51334
|
+
throw new Error("Dogbone fanout targets an unavailable board layer");
|
|
51335
|
+
const buses = [];
|
|
51336
|
+
const targets = sources.map(({ port, sourceTraceId }, connectionIndex) => {
|
|
51337
|
+
const sourceObstacle = input.obstacles.find(
|
|
51338
|
+
(obstacle) => obstacle.componentId === port.pcb_component_id && Math.hypot(obstacle.center.x - port.x, obstacle.center.y - port.y) < 1e-6
|
|
51339
|
+
);
|
|
51340
|
+
if (!sourceObstacle)
|
|
51341
|
+
throw new Error(
|
|
51342
|
+
"Dogbone fanout requires a component pad at each source port"
|
|
51343
|
+
);
|
|
51344
|
+
if (port.layers.length !== 1 || !layers.includes(port.layers[0])) {
|
|
51345
|
+
throw new Error("Dogbone fanout requires a single-layer SMT source pad");
|
|
51346
|
+
}
|
|
51347
|
+
const sourceLayer = port.layers[0];
|
|
51348
|
+
const targetLayer2 = allowedLayers.find((layer) => layer !== sourceLayer);
|
|
51349
|
+
if (!targetLayer2)
|
|
51350
|
+
throw new Error(
|
|
51351
|
+
"Dogbone fanout requires a legal layer distinct from the source pad"
|
|
51352
|
+
);
|
|
51353
|
+
let bus = buses.find(
|
|
51354
|
+
(bus2) => bus2.componentId === sourceObstacle.componentId
|
|
51355
|
+
);
|
|
51356
|
+
if (!bus) {
|
|
51357
|
+
const pads = input.obstacles.filter(
|
|
51358
|
+
(obstacle) => obstacle.componentId === sourceObstacle.componentId
|
|
51359
|
+
);
|
|
51360
|
+
const xs = [
|
|
51361
|
+
...new Set(pads.map((pad) => Math.round(pad.center.x * 1e6) / 1e6))
|
|
51362
|
+
].sort((a, b) => a - b);
|
|
51363
|
+
const ys = [
|
|
51364
|
+
...new Set(pads.map((pad) => Math.round(pad.center.y * 1e6) / 1e6))
|
|
51365
|
+
].sort((a, b) => a - b);
|
|
51366
|
+
if (xs.length < 2 || ys.length < 2)
|
|
51367
|
+
throw new Error("Dogbone fanout requires a two-dimensional pad grid");
|
|
51368
|
+
const pitch = (coordinates) => Math.min(...coordinates.slice(1).map((v, i) => v - coordinates[i]));
|
|
51369
|
+
bus = {
|
|
51370
|
+
busId: sourceObstacle.componentId,
|
|
51371
|
+
componentId: sourceObstacle.componentId,
|
|
51372
|
+
componentObstacles: pads,
|
|
51373
|
+
connections: [],
|
|
51374
|
+
direction: "down",
|
|
51375
|
+
termination: { type: "boundary" },
|
|
51376
|
+
componentBounds: {
|
|
51377
|
+
minX: xs[0],
|
|
51378
|
+
maxX: xs.at(-1),
|
|
51379
|
+
minY: ys[0],
|
|
51380
|
+
maxY: ys.at(-1)
|
|
51381
|
+
},
|
|
51382
|
+
sharedBoundary: input.bounds,
|
|
51383
|
+
xCoordinates: xs,
|
|
51384
|
+
yCoordinates: ys,
|
|
51385
|
+
pitchX: pitch(xs),
|
|
51386
|
+
pitchY: pitch(ys)
|
|
51387
|
+
};
|
|
51388
|
+
buses.push(bus);
|
|
51389
|
+
}
|
|
51390
|
+
const sourcePoint = {
|
|
51391
|
+
x: port.x,
|
|
51392
|
+
y: port.y,
|
|
51393
|
+
layer: sourceLayer,
|
|
51394
|
+
pcb_port_id: port.pcb_port_id
|
|
51395
|
+
};
|
|
51396
|
+
bus.connections.push({
|
|
51397
|
+
connectionIndex,
|
|
51398
|
+
connection: { name: port.pcb_port_id, pointsToConnect: [sourcePoint] },
|
|
51399
|
+
sourcePoint,
|
|
51400
|
+
sourcePointIndex: 0,
|
|
51401
|
+
sourceLayer,
|
|
51402
|
+
sourceObstacle,
|
|
51403
|
+
targetPoint: sourcePoint
|
|
51404
|
+
});
|
|
51405
|
+
return { sourceTraceId, port, sourceLayer, targetLayer: targetLayer2 };
|
|
51406
|
+
});
|
|
51407
|
+
const sites = this.sites;
|
|
51408
|
+
const blockingSegments = [];
|
|
51409
|
+
const blockingVias = [];
|
|
51410
|
+
for (const bus of buses) {
|
|
51411
|
+
this.phase = "assigning_sites";
|
|
51412
|
+
yield;
|
|
51413
|
+
const assignment = matchComponentDogboneViaSites2([bus], {
|
|
51414
|
+
traceWidth,
|
|
51415
|
+
clearance: Math.max(clearance, viaClearance),
|
|
51416
|
+
viaDiameter,
|
|
51417
|
+
viaHoleDiameter: holeDiameter,
|
|
51418
|
+
holeToHoleClearance: holeClearance,
|
|
51419
|
+
additionalObstacles: input.obstacles,
|
|
51420
|
+
blockingSegments,
|
|
51421
|
+
blockingVias
|
|
51422
|
+
});
|
|
51423
|
+
if (!assignment || assignment.size !== bus.connections.length)
|
|
51424
|
+
throw new Error(
|
|
51425
|
+
"No complete local dogbone assignment within clearance/search limits"
|
|
51426
|
+
);
|
|
51427
|
+
for (const connection of bus.connections) {
|
|
51428
|
+
const connectionIndex = connection.connectionIndex;
|
|
51429
|
+
const via = assignment.get(connectionIndex);
|
|
51430
|
+
sites.set(connectionIndex, via);
|
|
51431
|
+
blockingSegments.push({
|
|
51432
|
+
connectionIndex,
|
|
51433
|
+
segment: {
|
|
51434
|
+
start: connection.sourcePoint,
|
|
51435
|
+
end: via,
|
|
51436
|
+
width: traceWidth,
|
|
51437
|
+
layer: connection.sourceLayer
|
|
51438
|
+
}
|
|
51439
|
+
});
|
|
51440
|
+
blockingVias.push({
|
|
51441
|
+
connectionIndex,
|
|
51442
|
+
center: via,
|
|
51443
|
+
diameter: viaDiameter,
|
|
51444
|
+
spanLayers: layers
|
|
51445
|
+
});
|
|
51446
|
+
}
|
|
51447
|
+
}
|
|
51448
|
+
this.phase = "creating_traces";
|
|
51449
|
+
for (const [
|
|
51450
|
+
index,
|
|
51451
|
+
{ sourceTraceId, port, sourceLayer, targetLayer: targetLayer2 }
|
|
51452
|
+
] of targets.entries()) {
|
|
51453
|
+
const via = sites.get(index);
|
|
51454
|
+
const dx = Math.abs(via.x - port.x), dy = Math.abs(via.y - port.y);
|
|
51455
|
+
if (dx > 1e-6 && dy > 1e-6 && Math.abs(dx - dy) > 1e-6)
|
|
51456
|
+
throw new Error(
|
|
51457
|
+
"Dogbone fanout requires straight or 45-degree local escapes"
|
|
51458
|
+
);
|
|
51459
|
+
const margin = viaDiameter / 2 + boardEdgeClearance;
|
|
51460
|
+
if (via.x < input.bounds.minX + margin || via.x > input.bounds.maxX - margin || via.y < input.bounds.minY + margin || via.y > input.bounds.maxY - margin)
|
|
51461
|
+
throw new Error("Dogbone via violates board-edge clearance");
|
|
51462
|
+
this.traces.push({
|
|
51463
|
+
type: "pcb_trace",
|
|
51464
|
+
pcb_trace_id: `dogbone_${port.pcb_port_id}`,
|
|
51465
|
+
source_trace_id: sourceTraceId,
|
|
51466
|
+
connectsTo: [port.pcb_port_id],
|
|
51467
|
+
route: [
|
|
51468
|
+
{
|
|
51469
|
+
route_type: "wire",
|
|
51470
|
+
x: port.x,
|
|
51471
|
+
y: port.y,
|
|
51472
|
+
layer: sourceLayer,
|
|
51473
|
+
width: traceWidth
|
|
51474
|
+
},
|
|
51475
|
+
{ route_type: "wire", ...via, layer: sourceLayer, width: traceWidth },
|
|
51476
|
+
{
|
|
51477
|
+
route_type: "via",
|
|
51478
|
+
...via,
|
|
51479
|
+
from_layer: sourceLayer,
|
|
51480
|
+
to_layer: targetLayer2,
|
|
51481
|
+
via_diameter: viaDiameter,
|
|
51482
|
+
via_hole_diameter: holeDiameter,
|
|
51483
|
+
layers
|
|
51484
|
+
},
|
|
51485
|
+
{ route_type: "wire", ...via, layer: targetLayer2, width: traceWidth },
|
|
51486
|
+
{ route_type: "wire", ...via, layer: targetLayer2, width: traceWidth }
|
|
51487
|
+
]
|
|
51488
|
+
});
|
|
51489
|
+
this.progress = (index + 1) / targets.length;
|
|
51490
|
+
yield;
|
|
51491
|
+
}
|
|
51492
|
+
}
|
|
51493
|
+
};
|
|
51494
|
+
|
|
51495
|
+
// node_modules/@tscircuit/dogbone-solver/lib/dogbone-autorouter.ts
|
|
51496
|
+
var DogboneAutorouter = class {
|
|
51497
|
+
constructor(params, callbacks = {}) {
|
|
51498
|
+
this.params = params;
|
|
51499
|
+
this.callbacks = callbacks;
|
|
51500
|
+
this.solver = new DogboneFanoutSolver(params);
|
|
51501
|
+
}
|
|
51502
|
+
params;
|
|
51503
|
+
callbacks;
|
|
51504
|
+
get input() {
|
|
51505
|
+
return this.params.input;
|
|
51506
|
+
}
|
|
51507
|
+
isRouting = false;
|
|
51508
|
+
solver;
|
|
51509
|
+
timer;
|
|
51510
|
+
listeners = [];
|
|
51511
|
+
finishSolver() {
|
|
51512
|
+
this.callbacks.onSolverEnded?.({
|
|
51513
|
+
solverName: "DogboneFanoutSolver",
|
|
51514
|
+
solved: this.solver.solved,
|
|
51515
|
+
failed: this.solver.failed,
|
|
51516
|
+
iterations: this.solver.iterations,
|
|
51517
|
+
error: this.solver.error
|
|
51518
|
+
});
|
|
51519
|
+
}
|
|
51520
|
+
getTraces() {
|
|
51521
|
+
return this.solver.getOutput().map((trace) => {
|
|
51522
|
+
const connection = this.params.input.connections.find(
|
|
51523
|
+
(connection2) => connection2.pointsToConnect.some(
|
|
51524
|
+
(point6) => point6.pcb_port_id && trace.connectsTo?.includes(point6.pcb_port_id)
|
|
51525
|
+
)
|
|
51526
|
+
);
|
|
51527
|
+
if (!connection)
|
|
51528
|
+
throw new Error("Dogbone fanout lost its source connection");
|
|
51529
|
+
return {
|
|
51530
|
+
...trace,
|
|
51531
|
+
connection_name: connection.name,
|
|
51532
|
+
source_trace_id: connection.source_trace_id,
|
|
51533
|
+
connectsTo: [
|
|
51534
|
+
.../* @__PURE__ */ new Set([
|
|
51535
|
+
...trace.connectsTo ?? [],
|
|
51536
|
+
...connection.routingPcbGroupId ? connection.pointsToConnect.flatMap(
|
|
51537
|
+
(point6) => !point6.pcb_port_id && point6.pointId ? [point6.pointId] : []
|
|
51538
|
+
) : [`${trace.pcb_trace_id}_exit`]
|
|
51539
|
+
])
|
|
51540
|
+
]
|
|
51541
|
+
};
|
|
51542
|
+
});
|
|
51543
|
+
}
|
|
51544
|
+
getOutputSimpleRouteJson() {
|
|
51545
|
+
const traces = this.getTraces();
|
|
51546
|
+
return {
|
|
51547
|
+
...this.params.input,
|
|
51548
|
+
traces,
|
|
51549
|
+
connections: this.params.input.connections.map((connection) => {
|
|
51550
|
+
const trace = traces.find(
|
|
51551
|
+
(trace2) => trace2.connection_name === connection.name
|
|
51552
|
+
);
|
|
51553
|
+
if (!trace) return connection;
|
|
51554
|
+
const exit = trace.route.at(-1);
|
|
51555
|
+
if (exit.route_type !== "wire")
|
|
51556
|
+
throw new Error("Dogbone fanout lost its exit");
|
|
51557
|
+
return {
|
|
51558
|
+
...connection,
|
|
51559
|
+
pointsToConnect: connection.pointsToConnect.map((point6) => {
|
|
51560
|
+
const isHandoff = !point6.pcb_port_id;
|
|
51561
|
+
const relocate = connection.routingPcbGroupId ? isHandoff : Boolean(
|
|
51562
|
+
point6.pcb_port_id && trace.connectsTo?.includes(point6.pcb_port_id)
|
|
51563
|
+
);
|
|
51564
|
+
if (!relocate) return point6;
|
|
51565
|
+
if (connection.routingPcbGroupId)
|
|
51566
|
+
return { ...point6, x: exit.x, y: exit.y, layer: exit.layer };
|
|
51567
|
+
const { pcb_port_id: _port, ...handoff } = point6;
|
|
51568
|
+
return {
|
|
51569
|
+
...handoff,
|
|
51570
|
+
pointId: `${trace.pcb_trace_id}_exit`,
|
|
51571
|
+
x: exit.x,
|
|
51572
|
+
y: exit.y,
|
|
51573
|
+
layer: exit.layer
|
|
51574
|
+
};
|
|
51575
|
+
})
|
|
51576
|
+
};
|
|
51577
|
+
})
|
|
51578
|
+
};
|
|
51579
|
+
}
|
|
51580
|
+
on(event, callback) {
|
|
51581
|
+
this.listeners.push({
|
|
51582
|
+
event,
|
|
51583
|
+
callback
|
|
51584
|
+
});
|
|
51585
|
+
}
|
|
51586
|
+
emit(event) {
|
|
51587
|
+
for (const listener of this.listeners)
|
|
51588
|
+
if (listener.event === event.type) listener.callback(event);
|
|
51589
|
+
}
|
|
51590
|
+
start() {
|
|
51591
|
+
if (this.isRouting) return;
|
|
51592
|
+
this.isRouting = true;
|
|
51593
|
+
this.callbacks.onSolverStarted?.({
|
|
51594
|
+
solverName: "DogboneFanoutSolver",
|
|
51595
|
+
solverParams: this.params,
|
|
51596
|
+
solverConstructorArgs: this.solver.getConstructorParams()
|
|
51597
|
+
});
|
|
51598
|
+
const tick = () => {
|
|
51599
|
+
if (!this.isRouting) return;
|
|
51600
|
+
try {
|
|
51601
|
+
for (let i = 0; i < 16 && !this.solver.solved && !this.solver.failed; i++)
|
|
51602
|
+
this.solver.step();
|
|
51603
|
+
this.emit({
|
|
51604
|
+
type: "progress",
|
|
51605
|
+
steps: this.solver.iterations,
|
|
51606
|
+
progress: this.solver.progress,
|
|
51607
|
+
phase: this.solver.phase,
|
|
51608
|
+
debugGraphics: this.solver.visualize()
|
|
51609
|
+
});
|
|
51610
|
+
if (!this.isRouting) return;
|
|
51611
|
+
if (this.solver.failed) {
|
|
51612
|
+
this.finishSolver();
|
|
51613
|
+
this.isRouting = false;
|
|
51614
|
+
this.emit({
|
|
51615
|
+
type: "error",
|
|
51616
|
+
error: new Error(this.solver.error ?? "Dogbone routing failed")
|
|
51617
|
+
});
|
|
51618
|
+
return;
|
|
51619
|
+
}
|
|
51620
|
+
if (this.solver.solved) {
|
|
51621
|
+
this.finishSolver();
|
|
51622
|
+
this.isRouting = false;
|
|
51623
|
+
this.emit({ type: "complete", traces: this.getTraces() });
|
|
51624
|
+
return;
|
|
51625
|
+
}
|
|
51626
|
+
this.timer = setTimeout(tick, 0);
|
|
51627
|
+
} catch (error) {
|
|
51628
|
+
this.isRouting = false;
|
|
51629
|
+
this.emit({
|
|
51630
|
+
type: "error",
|
|
51631
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
51632
|
+
});
|
|
51633
|
+
}
|
|
51634
|
+
};
|
|
51635
|
+
this.timer = setTimeout(tick, 0);
|
|
51636
|
+
}
|
|
51637
|
+
stop() {
|
|
51638
|
+
this.isRouting = false;
|
|
51639
|
+
if (this.timer) clearTimeout(this.timer);
|
|
51640
|
+
}
|
|
51641
|
+
solveSync() {
|
|
51642
|
+
this.solver.solve();
|
|
51643
|
+
if (this.solver.failed)
|
|
51644
|
+
throw new Error(this.solver.error ?? "Dogbone routing failed");
|
|
51645
|
+
return this.getTraces();
|
|
51646
|
+
}
|
|
51647
|
+
};
|
|
51648
|
+
|
|
51649
|
+
// node_modules/@tscircuit/bus-lanes-solver/lib/bus-lanes-solver.ts
|
|
51650
|
+
import { BaseSolver as BaseSolver8 } from "@tscircuit/solver-utils";
|
|
51651
|
+
|
|
50575
51652
|
// node_modules/@tscircuit/bus-lanes-solver/lib/geometry.ts
|
|
50576
|
-
var
|
|
51653
|
+
var distance14 = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
|
|
50577
51654
|
function pointSegmentDistance(p, edge) {
|
|
50578
51655
|
const [a, b] = edge, dx = b.x - a.x, dy = b.y - a.y, t = Math.max(
|
|
50579
51656
|
0,
|
|
@@ -50584,10 +51661,10 @@ function pointSegmentDistance(p, edge) {
|
|
|
50584
51661
|
);
|
|
50585
51662
|
return Math.hypot(p.x - a.x - t * dx, p.y - a.y - t * dy);
|
|
50586
51663
|
}
|
|
50587
|
-
var
|
|
51664
|
+
var cross3 = (a, b, c) => (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
|
50588
51665
|
function segmentDistance(ab, cd) {
|
|
50589
51666
|
const [a, b] = ab, [c, d] = cd;
|
|
50590
|
-
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) &&
|
|
51667
|
+
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) && cross3(a, b, c) * cross3(a, b, d) <= 0 && cross3(c, d, a) * cross3(c, d, b) <= 0)
|
|
50591
51668
|
return 0;
|
|
50592
51669
|
return Math.min(
|
|
50593
51670
|
pointSegmentDistance(a, cd),
|
|
@@ -50596,10 +51673,10 @@ function segmentDistance(ab, cd) {
|
|
|
50596
51673
|
pointSegmentDistance(d, ab)
|
|
50597
51674
|
);
|
|
50598
51675
|
}
|
|
50599
|
-
var length = (path) => path.slice(1).reduce((sum, p, i) => sum +
|
|
51676
|
+
var length = (path) => path.slice(1).reduce((sum, p, i) => sum + distance14(path[i], p), 0);
|
|
50600
51677
|
function simplify(path) {
|
|
50601
51678
|
return path.filter(
|
|
50602
|
-
(p, i) => !i || i === path.length - 1 || Math.abs(
|
|
51679
|
+
(p, i) => !i || i === path.length - 1 || Math.abs(cross3(path[i - 1], p, path[i + 1])) > 1e-9
|
|
50603
51680
|
);
|
|
50604
51681
|
}
|
|
50605
51682
|
|
|
@@ -50643,7 +51720,7 @@ function fixedCopper(input) {
|
|
|
50643
51720
|
throw Error("Fixed trace changes layer without a via");
|
|
50644
51721
|
result.push({ a: p, b: q, radius: p.width / 2, layer: p.layer, owners });
|
|
50645
51722
|
}
|
|
50646
|
-
if (q && p.route_type !== q.route_type &&
|
|
51723
|
+
if (q && p.route_type !== q.route_type && distance14(p, q) > 1e-10) {
|
|
50647
51724
|
const wire = p.route_type === "wire" ? p : q;
|
|
50648
51725
|
result.push({
|
|
50649
51726
|
a: p,
|
|
@@ -50763,7 +51840,7 @@ var VectorScene = class {
|
|
|
50763
51840
|
);
|
|
50764
51841
|
} else {
|
|
50765
51842
|
const r = c.radius + this.margin + epsilon;
|
|
50766
|
-
for (const p of
|
|
51843
|
+
for (const p of distance14(c.a, c.b) < 1e-9 ? [c.a] : [c.a, c.b])
|
|
50767
51844
|
for (const [x, y] of [
|
|
50768
51845
|
[1, k],
|
|
50769
51846
|
[k, 1],
|
|
@@ -50851,7 +51928,7 @@ var VectorVisibilitySearch = class {
|
|
|
50851
51928
|
this.start = start;
|
|
50852
51929
|
this.end = end;
|
|
50853
51930
|
this.vertices = [start, end];
|
|
50854
|
-
this.open.push({ id: 0, g: 0, f:
|
|
51931
|
+
this.open.push({ id: 0, g: 0, f: distance14(start, end) });
|
|
50855
51932
|
this.best.set(0, 0);
|
|
50856
51933
|
}
|
|
50857
51934
|
scene;
|
|
@@ -50911,7 +51988,7 @@ var VectorVisibilitySearch = class {
|
|
|
50911
51988
|
const from = this.vertices[cur.id];
|
|
50912
51989
|
for (let id = 1; id < this.vertices.length; id++) {
|
|
50913
51990
|
if (id === cur.id) continue;
|
|
50914
|
-
const to = this.vertices[id], lower = cur.g +
|
|
51991
|
+
const to = this.vertices[id], lower = cur.g + distance14(from, to);
|
|
50915
51992
|
if (lower >= (this.best.get(id) ?? Infinity) - 1e-9) continue;
|
|
50916
51993
|
const candidates = connectors(from, to).sort(
|
|
50917
51994
|
(a, b) => length(a) - length(b)
|
|
@@ -50924,7 +52001,7 @@ var VectorVisibilitySearch = class {
|
|
|
50924
52001
|
this.open.push({
|
|
50925
52002
|
id,
|
|
50926
52003
|
g,
|
|
50927
|
-
f: g +
|
|
52004
|
+
f: g + distance14(to, this.end),
|
|
50928
52005
|
parent: cur,
|
|
50929
52006
|
edge: path
|
|
50930
52007
|
});
|
|
@@ -51166,9 +52243,9 @@ function tuneLengths(input, traces, targets) {
|
|
|
51166
52243
|
const clearance = input.minTraceToPadEdgeClearance ?? input.defaultObstacleMargin ?? 0.075;
|
|
51167
52244
|
const returnSpacing = Math.max(width + clearance, 3 * width);
|
|
51168
52245
|
const pitch = 2 * (returnSpacing + 2 * width);
|
|
51169
|
-
const segments = t.route.slice(1).map((p, i) => ({ i, span:
|
|
52246
|
+
const segments = t.route.slice(1).map((p, i) => ({ i, span: distance14(t.route[i], p) })).sort((a, b) => b.span - a.span);
|
|
51170
52247
|
for (const { i } of segments) {
|
|
51171
|
-
const a = t.route[i], b = t.route[i + 1], span =
|
|
52248
|
+
const a = t.route[i], b = t.route[i + 1], span = distance14(a, b);
|
|
51172
52249
|
if (span < 0.01) continue;
|
|
51173
52250
|
const ux = (b.x - a.x) / span, uy = (b.y - a.y) / span;
|
|
51174
52251
|
const maximumTeeth = Math.floor(span * 0.9 / pitch);
|
|
@@ -51268,7 +52345,7 @@ function tuneLengths(input, traces, targets) {
|
|
|
51268
52345
|
function tuningPathIsSelfClear(path, required) {
|
|
51269
52346
|
const cumulative = [0];
|
|
51270
52347
|
for (let i = 1; i < path.length; i++)
|
|
51271
|
-
cumulative.push(cumulative[i - 1] +
|
|
52348
|
+
cumulative.push(cumulative[i - 1] + distance14(path[i - 1], path[i]));
|
|
51272
52349
|
for (let i = 0; i < path.length - 1; i++)
|
|
51273
52350
|
for (let j = i + 2; j < path.length - 1; j++) {
|
|
51274
52351
|
if (cumulative[j] - cumulative[i + 1] <= required + 1e-8) continue;
|
|
@@ -51306,7 +52383,7 @@ var layerColors = {
|
|
|
51306
52383
|
var layerColor = (layer) => layerColors[layer] ?? "#64748b";
|
|
51307
52384
|
|
|
51308
52385
|
// node_modules/@tscircuit/bus-lanes-solver/lib/bus-lanes-solver.ts
|
|
51309
|
-
var BusLanesSolver = class extends
|
|
52386
|
+
var BusLanesSolver = class extends BaseSolver8 {
|
|
51310
52387
|
input;
|
|
51311
52388
|
options;
|
|
51312
52389
|
phase = "validate";
|
|
@@ -51516,7 +52593,7 @@ var BusLanesSolver = class extends BaseSolver4 {
|
|
|
51516
52593
|
const t = this.traces.find((t2) => t2.connection_name === c.name);
|
|
51517
52594
|
if (!t) throw Error("Missing lane");
|
|
51518
52595
|
const scene = this.scene(c);
|
|
51519
|
-
if (
|
|
52596
|
+
if (distance14(t.route[0], c.pointsToConnect[0]) > 1e-8 || distance14(t.route.at(-1), c.pointsToConnect[1]) > 1e-8)
|
|
51520
52597
|
throw Error("Broken lane endpoints");
|
|
51521
52598
|
for (let i = 1; i < t.route.length; i++) {
|
|
51522
52599
|
const a = t.route[i - 1], b = t.route[i], dx = Math.abs(a.x - b.x), dy = Math.abs(a.y - b.y);
|
|
@@ -51581,7 +52658,7 @@ var BusLanesSolver = class extends BaseSolver4 {
|
|
|
51581
52658
|
});
|
|
51582
52659
|
continue;
|
|
51583
52660
|
}
|
|
51584
|
-
if (
|
|
52661
|
+
if (distance14(c.a, c.b) < 1e-9)
|
|
51585
52662
|
circles.push({
|
|
51586
52663
|
center: c.a,
|
|
51587
52664
|
radius: c.radius,
|
|
@@ -51742,7 +52819,7 @@ import { CreateFdmEnclosureSolver } from "@tscircuit/create-fdm-enclosure";
|
|
|
51742
52819
|
// node_modules/@tscircuit/implicit-copper-pour-solver/lib/implicit-copper-pour-pipeline-solver.ts
|
|
51743
52820
|
import {
|
|
51744
52821
|
BasePipelineSolver,
|
|
51745
|
-
BaseSolver as
|
|
52822
|
+
BaseSolver as BaseSolver9,
|
|
51746
52823
|
definePipelineStep
|
|
51747
52824
|
} from "@tscircuit/solver-utils";
|
|
51748
52825
|
|
|
@@ -51760,7 +52837,7 @@ import { applyToPoint as applyToPoint30, compose as compose11, scale as scale2,
|
|
|
51760
52837
|
// node_modules/@tscircuit/implicit-copper-pour-solver/lib/geometry.ts
|
|
51761
52838
|
import {
|
|
51762
52839
|
clamp,
|
|
51763
|
-
distance as
|
|
52840
|
+
distance as distance15,
|
|
51764
52841
|
doBoundsOverlap as doBoundsOverlap2,
|
|
51765
52842
|
getBoundsFromPoints as getBoundsFromPoints3,
|
|
51766
52843
|
getUnitVectorFromPointAToB as getUnitVectorFromPointAToB2,
|
|
@@ -51809,7 +52886,7 @@ var distanceToPolygon = (point6, polygon) => {
|
|
|
51809
52886
|
var distanceToPrimitive = (primitive, px, py) => {
|
|
51810
52887
|
const point6 = { x: px, y: py };
|
|
51811
52888
|
if (primitive.kind === "circle") {
|
|
51812
|
-
return Math.max(
|
|
52889
|
+
return Math.max(distance15(point6, primitive) - primitive.radius, 0);
|
|
51813
52890
|
}
|
|
51814
52891
|
if (primitive.kind === "segment") {
|
|
51815
52892
|
return Math.max(
|
|
@@ -51860,7 +52937,7 @@ var getClosestPointOnPrimitive = (primitive, point6) => {
|
|
|
51860
52937
|
const start = primitive.points[index];
|
|
51861
52938
|
const end = primitive.points[(index + 1) % primitive.points.length];
|
|
51862
52939
|
const candidate = pointToSegmentClosestPoint(point6, start, end);
|
|
51863
|
-
const candidateDistance =
|
|
52940
|
+
const candidateDistance = distance15(point6, candidate);
|
|
51864
52941
|
if (candidateDistance < bestDistance) {
|
|
51865
52942
|
bestDistance = candidateDistance;
|
|
51866
52943
|
closestPoint = candidate;
|
|
@@ -52916,7 +53993,7 @@ var prepareCircuitJson = (input) => {
|
|
|
52916
53993
|
|
|
52917
53994
|
// node_modules/@tscircuit/implicit-copper-pour-solver/lib/simplify-polygon-edges.ts
|
|
52918
53995
|
import {
|
|
52919
|
-
distance as
|
|
53996
|
+
distance as distance16,
|
|
52920
53997
|
doSegmentsIntersect,
|
|
52921
53998
|
onSegment,
|
|
52922
53999
|
pointToSegmentDistance as pointToSegmentDistance2
|
|
@@ -52979,8 +54056,8 @@ var simplifyOpenPath = (points, tolerance) => {
|
|
|
52979
54056
|
const point6 = points[index];
|
|
52980
54057
|
const next = points[index + 1];
|
|
52981
54058
|
const shortestLeg = Math.min(
|
|
52982
|
-
|
|
52983
|
-
|
|
54059
|
+
distance16(point6, previous),
|
|
54060
|
+
distance16(next, point6)
|
|
52984
54061
|
);
|
|
52985
54062
|
if (repeatsTurnDirection || shortestLeg > tolerance) {
|
|
52986
54063
|
anchorIndices.push(index);
|
|
@@ -53029,8 +54106,8 @@ var normalizePolygon = (polygon) => {
|
|
|
53029
54106
|
};
|
|
53030
54107
|
var isPointStrictlyInsideSegment = (point6, start, end) => {
|
|
53031
54108
|
if (pointsEqual(point6, start) || pointsEqual(point6, end)) return false;
|
|
53032
|
-
const
|
|
53033
|
-
if (Math.abs(
|
|
54109
|
+
const cross5 = crossProduct(start, end, point6);
|
|
54110
|
+
if (Math.abs(cross5) > 1e-9) return false;
|
|
53034
54111
|
return onSegment(start, point6, end);
|
|
53035
54112
|
};
|
|
53036
54113
|
var splitEdgesAtVertices = (polygon, allVertices) => {
|
|
@@ -53382,7 +54459,7 @@ var mergeSolverGraphics = (source, overlay) => {
|
|
|
53382
54459
|
};
|
|
53383
54460
|
|
|
53384
54461
|
// node_modules/@tscircuit/implicit-copper-pour-solver/lib/implicit-copper-pour-pipeline-solver.ts
|
|
53385
|
-
var PrepareCircuitJsonSolver = class extends
|
|
54462
|
+
var PrepareCircuitJsonSolver = class extends BaseSolver9 {
|
|
53386
54463
|
constructor(input) {
|
|
53387
54464
|
super();
|
|
53388
54465
|
this.input = input;
|
|
@@ -53398,7 +54475,7 @@ var PrepareCircuitJsonSolver = class extends BaseSolver5 {
|
|
|
53398
54475
|
return this.output;
|
|
53399
54476
|
}
|
|
53400
54477
|
};
|
|
53401
|
-
var AssignGridCellsSolver = class extends
|
|
54478
|
+
var AssignGridCellsSolver = class extends BaseSolver9 {
|
|
53402
54479
|
constructor(input) {
|
|
53403
54480
|
super();
|
|
53404
54481
|
this.input = input;
|
|
@@ -53421,7 +54498,7 @@ var AssignGridCellsSolver = class extends BaseSolver5 {
|
|
|
53421
54498
|
return this.output;
|
|
53422
54499
|
}
|
|
53423
54500
|
};
|
|
53424
|
-
var TracePowerPolygonsSolver = class extends
|
|
54501
|
+
var TracePowerPolygonsSolver = class extends BaseSolver9 {
|
|
53425
54502
|
constructor(input) {
|
|
53426
54503
|
super();
|
|
53427
54504
|
this.input = input;
|
|
@@ -53457,7 +54534,7 @@ var TracePowerPolygonsSolver = class extends BaseSolver5 {
|
|
|
53457
54534
|
};
|
|
53458
54535
|
}
|
|
53459
54536
|
};
|
|
53460
|
-
var SimplifyPolygonEdgesSolver = class extends
|
|
54537
|
+
var SimplifyPolygonEdgesSolver = class extends BaseSolver9 {
|
|
53461
54538
|
constructor(input) {
|
|
53462
54539
|
super();
|
|
53463
54540
|
this.input = input;
|
|
@@ -53595,13 +54672,13 @@ import { LayoutPipelineSolver } from "@tscircuit/matchpack";
|
|
|
53595
54672
|
import { SchematicTracePipelineSolver } from "@tscircuit/schematic-trace-solver";
|
|
53596
54673
|
|
|
53597
54674
|
// node_modules/@tscircuit/via-stitch-solver/lib/solvers/ViaStitchSolver.ts
|
|
53598
|
-
import { BaseSolver as
|
|
54675
|
+
import { BaseSolver as BaseSolver10 } from "@tscircuit/solver-utils";
|
|
53599
54676
|
|
|
53600
54677
|
// node_modules/@tscircuit/via-stitch-solver/lib/geometry/brep-point-containment.ts
|
|
53601
54678
|
var POINT_EPSILON = 1e-8;
|
|
53602
54679
|
var isPointOnSegment = (point6, start, end) => {
|
|
53603
|
-
const
|
|
53604
|
-
if (Math.abs(
|
|
54680
|
+
const cross5 = (point6.y - start.y) * (end.x - start.x) - (point6.x - start.x) * (end.y - start.y);
|
|
54681
|
+
if (Math.abs(cross5) > POINT_EPSILON) return false;
|
|
53605
54682
|
const dot = (point6.x - start.x) * (end.x - start.x) + (point6.y - start.y) * (end.y - start.y);
|
|
53606
54683
|
if (dot < -POINT_EPSILON) return false;
|
|
53607
54684
|
const squaredLength = (end.x - start.x) ** 2 + (end.y - start.y) ** 2;
|
|
@@ -53922,7 +54999,7 @@ var getGridCoordinates = ({
|
|
|
53922
54999
|
}
|
|
53923
55000
|
return coordinates;
|
|
53924
55001
|
};
|
|
53925
|
-
var ViaStitchSolver = class extends
|
|
55002
|
+
var ViaStitchSolver = class extends BaseSolver10 {
|
|
53926
55003
|
constructor(input) {
|
|
53927
55004
|
super();
|
|
53928
55005
|
this.input = input;
|
|
@@ -54069,6 +55146,7 @@ var ViaStitchSolver = class extends BaseSolver6 {
|
|
|
54069
55146
|
// lib/solvers.ts
|
|
54070
55147
|
import { PackSolver2 } from "calculate-packing";
|
|
54071
55148
|
var SOLVERS = {
|
|
55149
|
+
DogboneFanoutSolver,
|
|
54072
55150
|
BusLanesSolver,
|
|
54073
55151
|
PackSolver2,
|
|
54074
55152
|
LayoutPipelineSolver,
|
|
@@ -54532,6 +55610,27 @@ var localAutorouterStrategies = /* @__PURE__ */ new Map([
|
|
|
54532
55610
|
createFanoutAutorouterStrategy("single_layer_fanout")
|
|
54533
55611
|
],
|
|
54534
55612
|
["fanout", createFanoutAutorouterStrategy("fanout")],
|
|
55613
|
+
[
|
|
55614
|
+
"dogbone",
|
|
55615
|
+
{
|
|
55616
|
+
name: "dogbone",
|
|
55617
|
+
preserveOutputTraces: true,
|
|
55618
|
+
followUpAutorouter: "default",
|
|
55619
|
+
cacheable: false,
|
|
55620
|
+
getSolverName: () => "DogboneFanoutSolver",
|
|
55621
|
+
create: ({
|
|
55622
|
+
simpleRouteJson,
|
|
55623
|
+
fanoutRoutingLayers,
|
|
55624
|
+
onSolverStarted,
|
|
55625
|
+
onSolverEnded
|
|
55626
|
+
}) => {
|
|
55627
|
+
return new DogboneAutorouter(
|
|
55628
|
+
{ input: simpleRouteJson, fanoutRoutingLayers },
|
|
55629
|
+
{ onSolverStarted, onSolverEnded }
|
|
55630
|
+
);
|
|
55631
|
+
}
|
|
55632
|
+
}
|
|
55633
|
+
],
|
|
54535
55634
|
[
|
|
54536
55635
|
"bus_lanes",
|
|
54537
55636
|
{
|
|
@@ -61008,19 +62107,19 @@ import Debug12 from "debug";
|
|
|
61008
62107
|
import { isValidElement as isValidElement2 } from "react";
|
|
61009
62108
|
|
|
61010
62109
|
// lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/segment-intersection.ts
|
|
61011
|
-
var
|
|
62110
|
+
var cross4 = (ax, ay, bx, by) => {
|
|
61012
62111
|
return ax * by - ay * bx;
|
|
61013
62112
|
};
|
|
61014
62113
|
function segmentIntersection(p1, p2, q1, q2, tol) {
|
|
61015
62114
|
const r = { x: p2.x - p1.x, y: p2.y - p1.y };
|
|
61016
62115
|
const s = { x: q2.x - q1.x, y: q2.y - q1.y };
|
|
61017
|
-
const rxs =
|
|
62116
|
+
const rxs = cross4(r.x, r.y, s.x, s.y);
|
|
61018
62117
|
const q_p = { x: q1.x - p1.x, y: q1.y - p1.y };
|
|
61019
|
-
const q_pxr =
|
|
62118
|
+
const q_pxr = cross4(q_p.x, q_p.y, r.x, r.y);
|
|
61020
62119
|
if (Math.abs(rxs) < tol && Math.abs(q_pxr) < tol) return null;
|
|
61021
62120
|
if (Math.abs(rxs) < tol && Math.abs(q_pxr) >= tol) return null;
|
|
61022
|
-
const t =
|
|
61023
|
-
const u =
|
|
62121
|
+
const t = cross4(q_p.x, q_p.y, s.x, s.y) / rxs;
|
|
62122
|
+
const u = cross4(q_p.x, q_p.y, r.x, r.y) / rxs;
|
|
61024
62123
|
if (t < -tol || t > 1 + tol || u < -tol || u > 1 + tol) return null;
|
|
61025
62124
|
return { x: p1.x + t * r.x, y: p1.y + t * r.y };
|
|
61026
62125
|
}
|
|
@@ -61213,8 +62312,8 @@ function isParallel(e1, e2, tol = TOL2) {
|
|
|
61213
62312
|
const L1 = Math.hypot(v1.x, v1.y);
|
|
61214
62313
|
const L2 = Math.hypot(v2.x, v2.y);
|
|
61215
62314
|
if (L1 < tol || L2 < tol) return true;
|
|
61216
|
-
const
|
|
61217
|
-
return Math.abs(
|
|
62315
|
+
const cross5 = v1.x * v2.y - v1.y * v2.x;
|
|
62316
|
+
return Math.abs(cross5) <= tol * L1 * L2;
|
|
61218
62317
|
}
|
|
61219
62318
|
function incidentEdgesAtPoint(trace, p, tol = TOL2) {
|
|
61220
62319
|
return trace.edges.filter(
|
|
@@ -64481,6 +65580,16 @@ function Group_getRoutingPhasePlans(group) {
|
|
|
64481
65580
|
nets: nets.filter((net) => breakoutByNet.get(net) === breakout),
|
|
64482
65581
|
traces: breakoutTraces
|
|
64483
65582
|
};
|
|
65583
|
+
if (breakout.isDogboneFanout && breakoutProps2.pcbTracePaths === void 0) {
|
|
65584
|
+
breakoutPlans.push({
|
|
65585
|
+
...breakoutPlan,
|
|
65586
|
+
// Keep the local fanout outline while allowing dogbone sites outside
|
|
65587
|
+
// the pad bounding box to use the full board routing bounds.
|
|
65588
|
+
routingBounds: void 0,
|
|
65589
|
+
fanoutBounds: routingBounds
|
|
65590
|
+
});
|
|
65591
|
+
continue;
|
|
65592
|
+
}
|
|
64484
65593
|
if (breakout._parsedProps.pcbTracePaths !== void 0) {
|
|
64485
65594
|
breakoutPlans.push({
|
|
64486
65595
|
...breakoutPlan,
|
|
@@ -64553,7 +65662,7 @@ function Group_getRoutingPhasePlans(group) {
|
|
|
64553
65662
|
var package_default = {
|
|
64554
65663
|
name: "@tscircuit/core",
|
|
64555
65664
|
type: "module",
|
|
64556
|
-
version: "0.0.
|
|
65665
|
+
version: "0.0.2025",
|
|
64557
65666
|
types: "dist/index.d.ts",
|
|
64558
65667
|
main: "dist/index.js",
|
|
64559
65668
|
module: "dist/index.js",
|
|
@@ -64594,6 +65703,7 @@ var package_default = {
|
|
|
64594
65703
|
"@tscircuit/common": "^0.0.20",
|
|
64595
65704
|
"@tscircuit/copper-pour-solver": "^0.0.62",
|
|
64596
65705
|
"@tscircuit/create-fdm-enclosure": "0.0.4",
|
|
65706
|
+
"@tscircuit/dogbone-solver": "https://jscdn.tscircuit.com/@tscircuit/dogbone-solver/0.0.1.tgz",
|
|
64597
65707
|
"@tscircuit/fanout-solver": "0.0.78",
|
|
64598
65708
|
"@tscircuit/flex-utils": "https://jscdn.tscircuit.com/@tscircuit/flex-utils/0.0.5.tgz",
|
|
64599
65709
|
"@tscircuit/footprinter": "^0.0.426",
|
|
@@ -65021,7 +66131,6 @@ function Group_syncFanoutExitsWithGlobalConnections({
|
|
|
65021
66131
|
) ? [{ globalConnection, pointIndex, previousPoint }] : []
|
|
65022
66132
|
)
|
|
65023
66133
|
);
|
|
65024
|
-
if (globalPointMatches.length === 0) continue;
|
|
65025
66134
|
const changedPointIndex = outputConnection.pointsToConnect.findIndex(
|
|
65026
66135
|
(outputPoint, pointIndex) => {
|
|
65027
66136
|
const inputPoint = inputConnection.pointsToConnect[pointIndex];
|
|
@@ -65030,6 +66139,14 @@ function Group_syncFanoutExitsWithGlobalConnections({
|
|
|
65030
66139
|
);
|
|
65031
66140
|
if (changedPointIndex < 0) continue;
|
|
65032
66141
|
const fanoutExitPoint = outputConnection.pointsToConnect[changedPointIndex];
|
|
66142
|
+
if (globalPointMatches.length === 0) {
|
|
66143
|
+
synchronizedBreakoutPoints.push({
|
|
66144
|
+
sourceTraceId: inputConnection.source_trace_id,
|
|
66145
|
+
routingPcbGroupId,
|
|
66146
|
+
previousPoint: inputConnection.pointsToConnect[changedPointIndex],
|
|
66147
|
+
fanoutExitPoint
|
|
66148
|
+
});
|
|
66149
|
+
}
|
|
65033
66150
|
for (const {
|
|
65034
66151
|
globalConnection,
|
|
65035
66152
|
pointIndex,
|
|
@@ -65290,7 +66407,7 @@ function getAccumulatedPcbTracesWithStageOutputReplacements({
|
|
|
65290
66407
|
}
|
|
65291
66408
|
|
|
65292
66409
|
// lib/components/primitive-components/Group/get-source-trace-id-for-routed-trace.ts
|
|
65293
|
-
import { distance as
|
|
66410
|
+
import { distance as distance17, pointToSegmentDistance as pointToSegmentDistance3 } from "@tscircuit/math-utils";
|
|
65294
66411
|
import { ConnectivityMap as ConnectivityMap5 } from "circuit-json-to-connectivity-map";
|
|
65295
66412
|
var POINT_EPSILON2 = 1e-6;
|
|
65296
66413
|
function isPointOnWireSegment({
|
|
@@ -65312,7 +66429,7 @@ function isPointInSmtPad({
|
|
|
65312
66429
|
return Math.abs(point6.x - pad.x) <= pad.width / 2 + traceWidth / 2 && Math.abs(point6.y - pad.y) <= pad.height / 2 + traceWidth / 2;
|
|
65313
66430
|
}
|
|
65314
66431
|
if (pad.shape === "circle") {
|
|
65315
|
-
return
|
|
66432
|
+
return distance17(point6, pad) <= pad.radius + traceWidth / 2;
|
|
65316
66433
|
}
|
|
65317
66434
|
return false;
|
|
65318
66435
|
}
|
|
@@ -65321,7 +66438,7 @@ function isPointInPlatedHole({
|
|
|
65321
66438
|
hole,
|
|
65322
66439
|
traceWidth = 0
|
|
65323
66440
|
}) {
|
|
65324
|
-
return
|
|
66441
|
+
return distance17(point6, hole) <= (hole.outer_diameter ?? hole.hole_diameter ?? 0) / 2 + traceWidth / 2;
|
|
65325
66442
|
}
|
|
65326
66443
|
function getEndpointSourcePortIdsFromGeometry(db, trace) {
|
|
65327
66444
|
const sourcePortIds = /* @__PURE__ */ new Set();
|
|
@@ -65334,7 +66451,7 @@ function getEndpointSourcePortIdsFromGeometry(db, trace) {
|
|
|
65334
66451
|
if (!pcbPort.source_port_id) continue;
|
|
65335
66452
|
if (endpoint.layer && pcbPort.layers && !pcbPort.layers.includes(endpoint.layer))
|
|
65336
66453
|
continue;
|
|
65337
|
-
if (
|
|
66454
|
+
if (distance17(endpoint, pcbPort) <= 0.01) {
|
|
65338
66455
|
sourcePortIds.add(pcbPort.source_port_id);
|
|
65339
66456
|
}
|
|
65340
66457
|
}
|
|
@@ -66018,8 +67135,8 @@ var Group5 = class extends NormalComponent {
|
|
|
66018
67135
|
const groupProps2 = props;
|
|
66019
67136
|
const hasOutline = groupProps2.outline && groupProps2.outline.length > 0;
|
|
66020
67137
|
const numericOutline = hasOutline ? groupProps2.outline.map((point6) => ({
|
|
66021
|
-
x:
|
|
66022
|
-
y:
|
|
67138
|
+
x: distance18.parse(point6.x),
|
|
67139
|
+
y: distance18.parse(point6.y)
|
|
66023
67140
|
})) : void 0;
|
|
66024
67141
|
const ctx = this.props;
|
|
66025
67142
|
const anchorPosition = this._getGlobalPcbPositionBeforeLayout();
|
|
@@ -66057,8 +67174,8 @@ var Group5 = class extends NormalComponent {
|
|
|
66057
67174
|
const hasExplicitPositioning = this._parsedProps.pcbX !== void 0 || this._parsedProps.pcbY !== void 0;
|
|
66058
67175
|
if (hasOutline) {
|
|
66059
67176
|
const numericOutline = props.outline.map((point6) => ({
|
|
66060
|
-
x:
|
|
66061
|
-
y:
|
|
67177
|
+
x: distance18.parse(point6.x),
|
|
67178
|
+
y: distance18.parse(point6.y)
|
|
66062
67179
|
}));
|
|
66063
67180
|
const outlineBounds = getBoundsFromPoints9(numericOutline);
|
|
66064
67181
|
if (!outlineBounds) return;
|
|
@@ -66459,7 +67576,9 @@ var Group5 = class extends NormalComponent {
|
|
|
66459
67576
|
routingPhaseDebugLabels.set(plan, `${label} ${ordinal}`);
|
|
66460
67577
|
}
|
|
66461
67578
|
const hasFanoutStage = routingStages.some(
|
|
66462
|
-
({ autorouterConfig: autorouterConfig2 }) => ["fanout", "single_layer_fanout"].includes(
|
|
67579
|
+
({ autorouterConfig: autorouterConfig2 }) => ["fanout", "single_layer_fanout", "dogbone"].includes(
|
|
67580
|
+
autorouterConfig2.preset ?? ""
|
|
67581
|
+
)
|
|
66463
67582
|
);
|
|
66464
67583
|
const fanoutPourNetMap = hasFanoutStage ? Group_getFanoutPourNetMap(this, routingPhasePlans) : void 0;
|
|
66465
67584
|
let { simpleRouteJson: baseSimpleRouteJson } = getSimpleRouteJsonFromCircuitJson({
|
|
@@ -66882,6 +68001,11 @@ var Group5 = class extends NormalComponent {
|
|
|
66882
68001
|
} else {
|
|
66883
68002
|
autorouter = localAutorouterStrategy.create({
|
|
66884
68003
|
simpleRouteJson,
|
|
68004
|
+
onSolverEnded: (event) => this.root?.emit("solver:ended", {
|
|
68005
|
+
...event,
|
|
68006
|
+
type: "solver:ended",
|
|
68007
|
+
componentName: this.getString()
|
|
68008
|
+
}),
|
|
66885
68009
|
commonAutorouterOptions,
|
|
66886
68010
|
busFanoutDirections: routingPhasePlan.busFanoutDirections,
|
|
66887
68011
|
fanoutBounds: routingPhasePlan.fanoutBounds,
|
|
@@ -66937,7 +68061,7 @@ var Group5 = class extends NormalComponent {
|
|
|
66937
68061
|
traces = await routingPromise;
|
|
66938
68062
|
}
|
|
66939
68063
|
let transformedSimpleRouteJson = precomputedOutputSimpleRouteJson ?? autorouter?.getOutputSimpleRouteJson?.();
|
|
66940
|
-
if (transformedSimpleRouteJson && !usesPreviousStageOutput && ["fanout", "single_layer_fanout"].includes(
|
|
68064
|
+
if (transformedSimpleRouteJson && !usesPreviousStageOutput && ["fanout", "single_layer_fanout", "dogbone"].includes(
|
|
66941
68065
|
phaseAutorouterConfig.preset ?? ""
|
|
66942
68066
|
) && routingPhasePlan.routingPcbGroupId) {
|
|
66943
68067
|
const synchronizedFanout = Group_syncFanoutExitsWithGlobalConnections(
|
|
@@ -66957,10 +68081,14 @@ var Group5 = class extends NormalComponent {
|
|
|
66957
68081
|
if (!breakoutPoint) continue;
|
|
66958
68082
|
db.pcb_breakout_point.update(breakoutPoint.pcb_breakout_point_id, {
|
|
66959
68083
|
x: synchronizedPoint.fanoutExitPoint.x,
|
|
66960
|
-
y: synchronizedPoint.fanoutExitPoint.y
|
|
68084
|
+
y: synchronizedPoint.fanoutExitPoint.y,
|
|
68085
|
+
layer: synchronizedPoint.fanoutExitPoint.layer
|
|
66961
68086
|
});
|
|
66962
68087
|
}
|
|
66963
68088
|
}
|
|
68089
|
+
if (localAutorouterStrategy.preserveOutputTraces) {
|
|
68090
|
+
for (const trace of traces) fixedTraceIds.add(trace.pcb_trace_id);
|
|
68091
|
+
}
|
|
66964
68092
|
let stageOutputTraces = traces;
|
|
66965
68093
|
if (transformedSimpleRouteJson?.traces) {
|
|
66966
68094
|
stageOutputTraces = transformedSimpleRouteJson.traces;
|
|
@@ -66997,7 +68125,7 @@ var Group5 = class extends NormalComponent {
|
|
|
66997
68125
|
subcircuit: this,
|
|
66998
68126
|
input: simpleRouteJson,
|
|
66999
68127
|
traces,
|
|
67000
|
-
isFanout: ["fanout", "single_layer_fanout"].includes(
|
|
68128
|
+
isFanout: ["fanout", "single_layer_fanout", "dogbone"].includes(
|
|
67001
68129
|
phaseAutorouterConfig.preset ?? ""
|
|
67002
68130
|
)
|
|
67003
68131
|
});
|
|
@@ -67930,8 +69058,8 @@ var Led_addPolarityFabricationSymbol = (led) => {
|
|
|
67930
69058
|
const a = db.pcb_port.getWhere({ source_port_id: anode.source_port_id });
|
|
67931
69059
|
const k = db.pcb_port.getWhere({ source_port_id: cathode.source_port_id });
|
|
67932
69060
|
if (!a || !k) return;
|
|
67933
|
-
const
|
|
67934
|
-
if (
|
|
69061
|
+
const distance25 = Math.hypot(k.x - a.x, k.y - a.y);
|
|
69062
|
+
if (distance25 < 1e-6) return;
|
|
67935
69063
|
const angle = Math.atan2(k.y - a.y, k.x - a.x);
|
|
67936
69064
|
const frame = compose15(
|
|
67937
69065
|
translate15((a.x + k.x) / 2, (a.y + k.y) / 2),
|
|
@@ -67945,10 +69073,10 @@ var Led_addPolarityFabricationSymbol = (led) => {
|
|
|
67945
69073
|
};
|
|
67946
69074
|
const notes = [];
|
|
67947
69075
|
generatedNotes.set(led, notes);
|
|
67948
|
-
const halfLength = Math.min(
|
|
69076
|
+
const halfLength = Math.min(distance25 * 0.2, 0.4);
|
|
67949
69077
|
const halfHeight = halfLength * 0.75;
|
|
67950
69078
|
const routes = [
|
|
67951
|
-
[point6(-
|
|
69079
|
+
[point6(-distance25 / 2, 0), point6(-halfLength, 0)],
|
|
67952
69080
|
[
|
|
67953
69081
|
point6(-halfLength, -halfHeight),
|
|
67954
69082
|
point6(halfLength, 0),
|
|
@@ -67956,7 +69084,7 @@ var Led_addPolarityFabricationSymbol = (led) => {
|
|
|
67956
69084
|
point6(-halfLength, -halfHeight)
|
|
67957
69085
|
],
|
|
67958
69086
|
[point6(halfLength, -halfHeight), point6(halfLength, halfHeight)],
|
|
67959
|
-
[point6(halfLength, 0), point6(
|
|
69087
|
+
[point6(halfLength, 0), point6(distance25 / 2, 0)]
|
|
67960
69088
|
];
|
|
67961
69089
|
for (const route of routes) {
|
|
67962
69090
|
notes.push(
|
|
@@ -69890,7 +71018,7 @@ var Board_doInitialPcbPlacementDesignRuleChecks = (board) => {
|
|
|
69890
71018
|
|
|
69891
71019
|
// lib/components/normal-components/Board/board-castellated-hole.ts
|
|
69892
71020
|
import {
|
|
69893
|
-
distance as
|
|
71021
|
+
distance as distance19
|
|
69894
71022
|
} from "circuit-json";
|
|
69895
71023
|
var CASTELLATED_HOLE_ENDPOINT_TOLERANCE_MM = 1e-6;
|
|
69896
71024
|
var getConnectionTargets2 = (connectsTo) => {
|
|
@@ -69911,8 +71039,8 @@ var BoardCastellatedHole = class _BoardCastellatedHole extends PlatedHole {
|
|
|
69911
71039
|
constructor(outlinePoint, outlinePointIndex) {
|
|
69912
71040
|
const name = `castellated_hole_${outlinePointIndex + 1}`;
|
|
69913
71041
|
const connectionTargets = getConnectionTargets2(outlinePoint.connectsTo);
|
|
69914
|
-
const holeDiameter =
|
|
69915
|
-
const padDiameter =
|
|
71042
|
+
const holeDiameter = distance19.parse(outlinePoint.holeDiameter);
|
|
71043
|
+
const padDiameter = distance19.parse(outlinePoint.padDiameter);
|
|
69916
71044
|
super({
|
|
69917
71045
|
shape: "circle",
|
|
69918
71046
|
holeDiameter,
|
|
@@ -70662,11 +71790,11 @@ var MountedBoard = class extends Subcircuit {
|
|
|
70662
71790
|
|
|
70663
71791
|
// lib/components/normal-components/Panel.ts
|
|
70664
71792
|
import { panelProps } from "@tscircuit/props";
|
|
70665
|
-
import { distance as
|
|
71793
|
+
import { distance as distance23 } from "circuit-json";
|
|
70666
71794
|
|
|
70667
71795
|
// lib/components/normal-components/Subpanel.ts
|
|
70668
71796
|
import { subpanelProps } from "@tscircuit/props";
|
|
70669
|
-
import { distance as
|
|
71797
|
+
import { distance as distance22 } from "circuit-json";
|
|
70670
71798
|
import { compose as compose17, identity as identity9, translate as translate17 } from "transformation-matrix";
|
|
70671
71799
|
|
|
70672
71800
|
// lib/utils/panels/generate-panel-tabs-and-mouse-bites.ts
|
|
@@ -70930,15 +72058,15 @@ function generatePanelOutlineRoutingCutouts(boards, options) {
|
|
|
70930
72058
|
}
|
|
70931
72059
|
|
|
70932
72060
|
// lib/utils/panels/pack-into-grid.ts
|
|
70933
|
-
import { distance as
|
|
72061
|
+
import { distance as distance21 } from "circuit-json";
|
|
70934
72062
|
|
|
70935
72063
|
// lib/utils/panels/get-board-dimensions-from-props.ts
|
|
70936
72064
|
import { getBoundsFromPoints as getBoundsFromPoints11 } from "@tscircuit/math-utils";
|
|
70937
|
-
import { distance as
|
|
72065
|
+
import { distance as distance20 } from "circuit-json";
|
|
70938
72066
|
var getBoardDimensionsFromProps = (board) => {
|
|
70939
72067
|
const props = board._parsedProps;
|
|
70940
|
-
let width = props.width != null ?
|
|
70941
|
-
let height = props.height != null ?
|
|
72068
|
+
let width = props.width != null ? distance20.parse(props.width) : void 0;
|
|
72069
|
+
let height = props.height != null ? distance20.parse(props.height) : void 0;
|
|
70942
72070
|
if ((width === void 0 || height === void 0) && props.outline?.length) {
|
|
70943
72071
|
const outlineBounds = getBoundsFromPoints11(props.outline);
|
|
70944
72072
|
if (outlineBounds) {
|
|
@@ -70982,8 +72110,8 @@ function packIntoGrid({
|
|
|
70982
72110
|
}
|
|
70983
72111
|
let cols;
|
|
70984
72112
|
let rows;
|
|
70985
|
-
const minCellWidth = cellWidth ?
|
|
70986
|
-
const minCellHeight = cellHeight ?
|
|
72113
|
+
const minCellWidth = cellWidth ? distance21.parse(cellWidth) : 0;
|
|
72114
|
+
const minCellHeight = cellHeight ? distance21.parse(cellHeight) : 0;
|
|
70987
72115
|
if (col !== void 0) {
|
|
70988
72116
|
cols = col;
|
|
70989
72117
|
rows = row ?? Math.ceil(itemsWithDims.length / cols);
|
|
@@ -71082,8 +72210,8 @@ function getItemDimensions(item, db) {
|
|
|
71082
72210
|
const props = subpanel._parsedProps;
|
|
71083
72211
|
if (props.width !== void 0 && props.height !== void 0) {
|
|
71084
72212
|
return {
|
|
71085
|
-
width:
|
|
71086
|
-
height:
|
|
72213
|
+
width: distance21.parse(props.width),
|
|
72214
|
+
height: distance21.parse(props.height)
|
|
71087
72215
|
};
|
|
71088
72216
|
}
|
|
71089
72217
|
const directBoards = subpanel._getDirectBoardChildren();
|
|
@@ -71110,7 +72238,7 @@ function getItemDimensions(item, db) {
|
|
|
71110
72238
|
return getBoardDimensionsFromProps(directBoards[0]);
|
|
71111
72239
|
}
|
|
71112
72240
|
if (subpanel._cachedGridWidth > 0 && subpanel._cachedGridHeight > 0) {
|
|
71113
|
-
const edgePadding =
|
|
72241
|
+
const edgePadding = distance21.parse(props.edgePadding ?? 5);
|
|
71114
72242
|
return {
|
|
71115
72243
|
width: subpanel._cachedGridWidth + edgePadding * 2,
|
|
71116
72244
|
height: subpanel._cachedGridHeight + edgePadding * 2
|
|
@@ -71268,17 +72396,17 @@ var Subpanel = class _Subpanel extends Group5 {
|
|
|
71268
72396
|
edgePaddingTop: edgePaddingTopProp,
|
|
71269
72397
|
edgePaddingBottom: edgePaddingBottomProp
|
|
71270
72398
|
} = this._parsedProps;
|
|
71271
|
-
const edgePadding =
|
|
71272
|
-
const edgePaddingLeft =
|
|
71273
|
-
const edgePaddingRight =
|
|
72399
|
+
const edgePadding = distance22.parse(edgePaddingProp ?? 5);
|
|
72400
|
+
const edgePaddingLeft = distance22.parse(edgePaddingLeftProp ?? edgePadding);
|
|
72401
|
+
const edgePaddingRight = distance22.parse(
|
|
71274
72402
|
edgePaddingRightProp ?? edgePadding
|
|
71275
72403
|
);
|
|
71276
|
-
const edgePaddingTop =
|
|
71277
|
-
const edgePaddingBottom =
|
|
72404
|
+
const edgePaddingTop = distance22.parse(edgePaddingTopProp ?? edgePadding);
|
|
72405
|
+
const edgePaddingBottom = distance22.parse(
|
|
71278
72406
|
edgePaddingBottomProp ?? edgePadding
|
|
71279
72407
|
);
|
|
71280
|
-
const panelWidth =
|
|
71281
|
-
const panelHeight =
|
|
72408
|
+
const panelWidth = distance22.parse(this._parsedProps.width);
|
|
72409
|
+
const panelHeight = distance22.parse(this._parsedProps.height);
|
|
71282
72410
|
availablePanelWidth = panelWidth - edgePaddingLeft - edgePaddingRight;
|
|
71283
72411
|
availablePanelHeight = panelHeight - edgePaddingTop - edgePaddingBottom;
|
|
71284
72412
|
}
|
|
@@ -71353,8 +72481,8 @@ var Subpanel = class _Subpanel extends Group5 {
|
|
|
71353
72481
|
if (!this.pcb_group_id) return;
|
|
71354
72482
|
if (hasExplicitWidth && hasExplicitHeight) {
|
|
71355
72483
|
db.pcb_group.update(this.pcb_group_id, {
|
|
71356
|
-
width:
|
|
71357
|
-
height:
|
|
72484
|
+
width: distance22.parse(this._parsedProps.width),
|
|
72485
|
+
height: distance22.parse(this._parsedProps.height)
|
|
71358
72486
|
});
|
|
71359
72487
|
} else if (gridWidth > 0 || gridHeight > 0) {
|
|
71360
72488
|
const {
|
|
@@ -71364,18 +72492,18 @@ var Subpanel = class _Subpanel extends Group5 {
|
|
|
71364
72492
|
edgePaddingTop: edgePaddingTopProp,
|
|
71365
72493
|
edgePaddingBottom: edgePaddingBottomProp
|
|
71366
72494
|
} = this._parsedProps;
|
|
71367
|
-
const edgePadding =
|
|
71368
|
-
const edgePaddingLeft =
|
|
71369
|
-
const edgePaddingRight =
|
|
72495
|
+
const edgePadding = distance22.parse(edgePaddingProp ?? 5);
|
|
72496
|
+
const edgePaddingLeft = distance22.parse(edgePaddingLeftProp ?? edgePadding);
|
|
72497
|
+
const edgePaddingRight = distance22.parse(
|
|
71370
72498
|
edgePaddingRightProp ?? edgePadding
|
|
71371
72499
|
);
|
|
71372
|
-
const edgePaddingTop =
|
|
71373
|
-
const edgePaddingBottom =
|
|
72500
|
+
const edgePaddingTop = distance22.parse(edgePaddingTopProp ?? edgePadding);
|
|
72501
|
+
const edgePaddingBottom = distance22.parse(
|
|
71374
72502
|
edgePaddingBottomProp ?? edgePadding
|
|
71375
72503
|
);
|
|
71376
72504
|
db.pcb_group.update(this.pcb_group_id, {
|
|
71377
|
-
width: hasExplicitWidth ?
|
|
71378
|
-
height: hasExplicitHeight ?
|
|
72505
|
+
width: hasExplicitWidth ? distance22.parse(this._parsedProps.width) : gridWidth + edgePaddingLeft + edgePaddingRight,
|
|
72506
|
+
height: hasExplicitHeight ? distance22.parse(this._parsedProps.height) : gridHeight + edgePaddingTop + edgePaddingBottom
|
|
71379
72507
|
});
|
|
71380
72508
|
}
|
|
71381
72509
|
}
|
|
@@ -71463,8 +72591,8 @@ var Panel = class extends Subpanel {
|
|
|
71463
72591
|
const { db } = this.root;
|
|
71464
72592
|
const props = this._parsedProps;
|
|
71465
72593
|
const inserted = db.pcb_panel.insert({
|
|
71466
|
-
width: props.width !== void 0 ?
|
|
71467
|
-
height: props.height !== void 0 ?
|
|
72594
|
+
width: props.width !== void 0 ? distance23.parse(props.width) : 0,
|
|
72595
|
+
height: props.height !== void 0 ? distance23.parse(props.height) : 0,
|
|
71468
72596
|
thickness: 1.6,
|
|
71469
72597
|
center: this._getGlobalPcbPositionBeforeLayout(),
|
|
71470
72598
|
covered_with_solder_mask: !(props.noSolderMask ?? false)
|
|
@@ -71483,8 +72611,8 @@ var Panel = class extends Subpanel {
|
|
|
71483
72611
|
if (!this.pcb_panel_id) return;
|
|
71484
72612
|
if (hasExplicitWidth && hasExplicitHeight) {
|
|
71485
72613
|
db.pcb_panel.update(this.pcb_panel_id, {
|
|
71486
|
-
width:
|
|
71487
|
-
height:
|
|
72614
|
+
width: distance23.parse(this._parsedProps.width),
|
|
72615
|
+
height: distance23.parse(this._parsedProps.height)
|
|
71488
72616
|
});
|
|
71489
72617
|
} else if (gridWidth > 0 || gridHeight > 0) {
|
|
71490
72618
|
const {
|
|
@@ -71494,18 +72622,18 @@ var Panel = class extends Subpanel {
|
|
|
71494
72622
|
edgePaddingTop: edgePaddingTopProp,
|
|
71495
72623
|
edgePaddingBottom: edgePaddingBottomProp
|
|
71496
72624
|
} = this._parsedProps;
|
|
71497
|
-
const edgePadding =
|
|
71498
|
-
const edgePaddingLeft =
|
|
71499
|
-
const edgePaddingRight =
|
|
72625
|
+
const edgePadding = distance23.parse(edgePaddingProp ?? 5);
|
|
72626
|
+
const edgePaddingLeft = distance23.parse(edgePaddingLeftProp ?? edgePadding);
|
|
72627
|
+
const edgePaddingRight = distance23.parse(
|
|
71500
72628
|
edgePaddingRightProp ?? edgePadding
|
|
71501
72629
|
);
|
|
71502
|
-
const edgePaddingTop =
|
|
71503
|
-
const edgePaddingBottom =
|
|
72630
|
+
const edgePaddingTop = distance23.parse(edgePaddingTopProp ?? edgePadding);
|
|
72631
|
+
const edgePaddingBottom = distance23.parse(
|
|
71504
72632
|
edgePaddingBottomProp ?? edgePadding
|
|
71505
72633
|
);
|
|
71506
72634
|
db.pcb_panel.update(this.pcb_panel_id, {
|
|
71507
|
-
width: hasExplicitWidth ?
|
|
71508
|
-
height: hasExplicitHeight ?
|
|
72635
|
+
width: hasExplicitWidth ? distance23.parse(this._parsedProps.width) : gridWidth + edgePaddingLeft + edgePaddingRight,
|
|
72636
|
+
height: hasExplicitHeight ? distance23.parse(this._parsedProps.height) : gridHeight + edgePaddingTop + edgePaddingBottom
|
|
71509
72637
|
});
|
|
71510
72638
|
}
|
|
71511
72639
|
}
|
|
@@ -71516,8 +72644,8 @@ var Panel = class extends Subpanel {
|
|
|
71516
72644
|
const props = this._parsedProps;
|
|
71517
72645
|
const currentPanel = db.pcb_panel.get(this.pcb_panel_id);
|
|
71518
72646
|
db.pcb_panel.update(this.pcb_panel_id, {
|
|
71519
|
-
width: props.width !== void 0 ?
|
|
71520
|
-
height: props.height !== void 0 ?
|
|
72647
|
+
width: props.width !== void 0 ? distance23.parse(props.width) : currentPanel?.width,
|
|
72648
|
+
height: props.height !== void 0 ? distance23.parse(props.height) : currentPanel?.height,
|
|
71521
72649
|
center: this._getGlobalPcbPositionBeforeLayout(),
|
|
71522
72650
|
covered_with_solder_mask: !(props.noSolderMask ?? false)
|
|
71523
72651
|
});
|
|
@@ -73339,7 +74467,7 @@ var PcbNoteDimension = class extends PrimitiveComponent2 {
|
|
|
73339
74467
|
import { breakoutProps } from "@tscircuit/props";
|
|
73340
74468
|
|
|
73341
74469
|
// lib/components/primitive-components/AutoplacedBreakoutPoint.ts
|
|
73342
|
-
import { layer_ref as
|
|
74470
|
+
import { layer_ref as layer_ref4 } from "circuit-json";
|
|
73343
74471
|
var AutoplacedBreakoutPoint = class extends BaseBreakoutPoint {
|
|
73344
74472
|
get config() {
|
|
73345
74473
|
return {
|
|
@@ -73353,7 +74481,7 @@ var AutoplacedBreakoutPoint = class extends BaseBreakoutPoint {
|
|
|
73353
74481
|
position
|
|
73354
74482
|
}) {
|
|
73355
74483
|
this.matchedSourceTraceId = sourceTraceId;
|
|
73356
|
-
this._renderPcbBreakoutPoint({ layer:
|
|
74484
|
+
this._renderPcbBreakoutPoint({ layer: layer_ref4.parse(layer) });
|
|
73357
74485
|
this._setPositionFromLayout(position);
|
|
73358
74486
|
}
|
|
73359
74487
|
};
|
|
@@ -74065,7 +75193,7 @@ var validateWindingBreakoutInput = (input) => {
|
|
|
74065
75193
|
};
|
|
74066
75194
|
|
|
74067
75195
|
// node_modules/@tscircuit/winding-breakout-point-solver/lib/solvers/GatePlacementSolver.ts
|
|
74068
|
-
import { BaseSolver as
|
|
75196
|
+
import { BaseSolver as BaseSolver11 } from "@tscircuit/solver-utils";
|
|
74069
75197
|
|
|
74070
75198
|
// node_modules/@tscircuit/winding-breakout-point-solver/lib/gate-placement/place-breakout-gates.ts
|
|
74071
75199
|
var GEOMETRY_EPSILON = 1e-9;
|
|
@@ -74595,7 +75723,7 @@ var getSharedGateSlotCount = (points) => new Set(
|
|
|
74595
75723
|
(point6) => `${point6.regionId}:${point6.x.toFixed(9)}:${point6.y.toFixed(9)}`
|
|
74596
75724
|
)
|
|
74597
75725
|
).size;
|
|
74598
|
-
var GatePlacementSolver = class extends
|
|
75726
|
+
var GatePlacementSolver = class extends BaseSolver11 {
|
|
74599
75727
|
constructor(params) {
|
|
74600
75728
|
super();
|
|
74601
75729
|
this.params = params;
|
|
@@ -74734,7 +75862,7 @@ var GatePlacementSolver = class extends BaseSolver7 {
|
|
|
74734
75862
|
};
|
|
74735
75863
|
|
|
74736
75864
|
// node_modules/@tscircuit/winding-breakout-point-solver/lib/solvers/ReferenceOrderingSolver.ts
|
|
74737
|
-
import { BaseSolver as
|
|
75865
|
+
import { BaseSolver as BaseSolver12 } from "@tscircuit/solver-utils";
|
|
74738
75866
|
|
|
74739
75867
|
// node_modules/@tscircuit/winding-breakout-point-solver/lib/ordering/get-natural-winding-order.ts
|
|
74740
75868
|
var getNaturalWindingOrder = (region, connections) => connections.map((connection) => {
|
|
@@ -74801,7 +75929,7 @@ var getReferenceWindingOrder = (naturalOrder, atomicGroups, buses) => {
|
|
|
74801
75929
|
};
|
|
74802
75930
|
|
|
74803
75931
|
// node_modules/@tscircuit/winding-breakout-point-solver/lib/solvers/ReferenceOrderingSolver.ts
|
|
74804
|
-
var ReferenceOrderingSolver = class extends
|
|
75932
|
+
var ReferenceOrderingSolver = class extends BaseSolver12 {
|
|
74805
75933
|
constructor(params) {
|
|
74806
75934
|
super();
|
|
74807
75935
|
this.params = params;
|
|
@@ -75853,6 +76981,10 @@ var solveImplicitBreakoutPoints = (breakout, solverFn) => {
|
|
|
75853
76981
|
|
|
75854
76982
|
// lib/components/primitive-components/Breakout/Breakout.ts
|
|
75855
76983
|
var Breakout = class extends Group5 {
|
|
76984
|
+
get isDogboneFanout() {
|
|
76985
|
+
const autorouter = this._parsedProps.autorouter;
|
|
76986
|
+
return (typeof autorouter === "string" ? autorouter : autorouter?.preset) === "dogbone";
|
|
76987
|
+
}
|
|
75856
76988
|
get isRoutingDirective() {
|
|
75857
76989
|
return true;
|
|
75858
76990
|
}
|
|
@@ -75911,7 +77043,7 @@ var Breakout = class extends Group5 {
|
|
|
75911
77043
|
for (const port of result.ports) {
|
|
75912
77044
|
const isInside = breakoutPortSet.has(port);
|
|
75913
77045
|
const hasOutsidePort = result.ports.some((p) => !breakoutPortSet.has(p));
|
|
75914
|
-
if (!isInside || !hasOutsidePort) continue;
|
|
77046
|
+
if (!isInside || !hasOutsidePort && !this.isDogboneFanout) continue;
|
|
75915
77047
|
if (manuallyMappedPorts.has(port)) continue;
|
|
75916
77048
|
if (autoPlacedPorts.has(port)) continue;
|
|
75917
77049
|
autoPlacedPorts.add(port);
|
|
@@ -75924,6 +77056,20 @@ var Breakout = class extends Group5 {
|
|
|
75924
77056
|
}
|
|
75925
77057
|
doInitialPcbAutoplaceBreakoutPoints() {
|
|
75926
77058
|
if (this.root?.pcbDisabled) return;
|
|
77059
|
+
if (this.isDogboneFanout && this._parsedProps.pcbTracePaths === void 0) {
|
|
77060
|
+
for (const point6 of this.children) {
|
|
77061
|
+
if (!(point6 instanceof AutoplacedBreakoutPoint)) continue;
|
|
77062
|
+
const port = this.root.db.pcb_port.get(
|
|
77063
|
+
point6.matchedPort.pcb_port_id
|
|
77064
|
+
);
|
|
77065
|
+
point6._applySolvedBreakoutPoint({
|
|
77066
|
+
sourceTraceId: point6.matchedSourceTraceId,
|
|
77067
|
+
layer: port.layers[0],
|
|
77068
|
+
position: port
|
|
77069
|
+
});
|
|
77070
|
+
}
|
|
77071
|
+
return;
|
|
77072
|
+
}
|
|
75927
77073
|
const ownAutorouter = this.props.autorouter;
|
|
75928
77074
|
const inheritedAutorouter = ownAutorouter === void 0 ? this.parent?.getInheritedProperty("autorouter") : void 0;
|
|
75929
77075
|
const implicitBreakoutPointSolverFn = typeof ownAutorouter === "object" ? ownAutorouter.implicitBreakoutPointSolverFn : typeof inheritedAutorouter === "object" ? inheritedAutorouter.implicitBreakoutPointSolverFn : void 0;
|
|
@@ -76146,7 +77292,7 @@ var SilkscreenGraphic = class extends PrimitiveComponent2 {
|
|
|
76146
77292
|
|
|
76147
77293
|
// lib/components/primitive-components/Fiducial.ts
|
|
76148
77294
|
import "zod";
|
|
76149
|
-
import { distance as
|
|
77295
|
+
import { distance as distance24 } from "circuit-json";
|
|
76150
77296
|
import { fiducialProps as fiducialProps2 } from "@tscircuit/props";
|
|
76151
77297
|
var Fiducial = class extends PrimitiveComponent2 {
|
|
76152
77298
|
pcb_smtpad_id = null;
|
|
@@ -76171,15 +77317,15 @@ var Fiducial = class extends PrimitiveComponent2 {
|
|
|
76171
77317
|
shape: "circle",
|
|
76172
77318
|
x: position.x,
|
|
76173
77319
|
y: position.y,
|
|
76174
|
-
radius:
|
|
76175
|
-
soldermask_margin: props.soldermaskPullback ?
|
|
77320
|
+
radius: distance24.parse(props.padDiameter) / 2,
|
|
77321
|
+
soldermask_margin: props.soldermaskPullback ? distance24.parse(props.soldermaskPullback) : distance24.parse(props.padDiameter) / 2,
|
|
76176
77322
|
is_covered_with_solder_mask: true
|
|
76177
77323
|
});
|
|
76178
77324
|
this.pcb_smtpad_id = pcb_smtpad.pcb_smtpad_id;
|
|
76179
77325
|
}
|
|
76180
77326
|
getPcbSize() {
|
|
76181
77327
|
const { _parsedProps: props } = this;
|
|
76182
|
-
const d =
|
|
77328
|
+
const d = distance24.parse(props.padDiameter);
|
|
76183
77329
|
return { width: d, height: d };
|
|
76184
77330
|
}
|
|
76185
77331
|
_setPositionFromLayout(newCenter) {
|
|
@@ -76223,15 +77369,15 @@ import {
|
|
|
76223
77369
|
} from "@tscircuit/copper-pour-solver";
|
|
76224
77370
|
|
|
76225
77371
|
// lib/components/primitive-components/CopperPour/utils/mark-trace-segments-inside-copper-pour.ts
|
|
76226
|
-
var
|
|
77372
|
+
var EPSILON26 = 1e-9;
|
|
76227
77373
|
var isWireRoutePoint = (routePoint) => routePoint.route_type === "wire";
|
|
76228
77374
|
var isPointOnSegment2 = (p, a, b) => {
|
|
76229
|
-
const
|
|
76230
|
-
if (Math.abs(
|
|
77375
|
+
const cross5 = (p.y - a.y) * (b.x - a.x) - (p.x - a.x) * (b.y - a.y);
|
|
77376
|
+
if (Math.abs(cross5) > EPSILON26) return false;
|
|
76231
77377
|
const dot = (p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y);
|
|
76232
|
-
if (dot < -
|
|
77378
|
+
if (dot < -EPSILON26) return false;
|
|
76233
77379
|
const squaredLength = (b.x - a.x) ** 2 + (b.y - a.y) ** 2;
|
|
76234
|
-
if (dot - squaredLength >
|
|
77380
|
+
if (dot - squaredLength > EPSILON26) return false;
|
|
76235
77381
|
return true;
|
|
76236
77382
|
};
|
|
76237
77383
|
var isPointInRing2 = (point6, ring) => {
|
|
@@ -76255,7 +77401,7 @@ var isPointInRectPour = (p, pour) => {
|
|
|
76255
77401
|
const dy = p.y - center.y;
|
|
76256
77402
|
const localX = dx * cosR - dy * sinR;
|
|
76257
77403
|
const localY = dx * sinR + dy * cosR;
|
|
76258
|
-
return Math.abs(localX) <= width / 2 +
|
|
77404
|
+
return Math.abs(localX) <= width / 2 + EPSILON26 && Math.abs(localY) <= height / 2 + EPSILON26;
|
|
76259
77405
|
};
|
|
76260
77406
|
var isPointInBrepPour = (p, pour) => {
|
|
76261
77407
|
const outerRing = pour.brep_shape.outer_ring.vertices.map((v) => ({
|
|
@@ -76289,7 +77435,7 @@ var isSegmentFullyInsideCopperPour = (start, end, pour) => {
|
|
|
76289
77435
|
const dx = end.x - start.x;
|
|
76290
77436
|
const dy = end.y - start.y;
|
|
76291
77437
|
const length8 = Math.hypot(dx, dy);
|
|
76292
|
-
if (length8 <=
|
|
77438
|
+
if (length8 <= EPSILON26) return false;
|
|
76293
77439
|
const samples = [0, 0.25, 0.5, 0.75, 1];
|
|
76294
77440
|
return samples.every(
|
|
76295
77441
|
(t) => isPointInCopperPour(
|