@tscircuit/core 0.0.2025 → 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 +3 -0
- package/dist/index.js +1225 -138
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -6277,10 +6277,10 @@ function addTraceTeardrops(route, contacts) {
|
|
|
6277
6277
|
result.push(start);
|
|
6278
6278
|
continue;
|
|
6279
6279
|
}
|
|
6280
|
-
const atDistance = (
|
|
6280
|
+
const atDistance = (distance25) => ({
|
|
6281
6281
|
route_type: "wire",
|
|
6282
|
-
x: start.x + (end.x - start.x) *
|
|
6283
|
-
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,
|
|
6284
6284
|
width: start.width,
|
|
6285
6285
|
layer: start.layer
|
|
6286
6286
|
});
|
|
@@ -6803,11 +6803,11 @@ function Trace_doInitialPcbTraceRender(trace) {
|
|
|
6803
6803
|
const closestNetBPort = getClosest(netAPort, netBPorts);
|
|
6804
6804
|
const netAPortPosition = netAPort._getGlobalPcbPositionBeforeLayout();
|
|
6805
6805
|
const netBPortPosition = closestNetBPort._getGlobalPcbPositionBeforeLayout();
|
|
6806
|
-
const
|
|
6806
|
+
const distance25 = Math.sqrt(
|
|
6807
6807
|
(netAPortPosition.x - netBPortPosition.x) ** 2 + (netAPortPosition.y - netBPortPosition.y) ** 2
|
|
6808
6808
|
);
|
|
6809
|
-
if (
|
|
6810
|
-
closestDistance =
|
|
6809
|
+
if (distance25 < closestDistance) {
|
|
6810
|
+
closestDistance = distance25;
|
|
6811
6811
|
closestPortPair = [netAPort, closestNetBPort];
|
|
6812
6812
|
}
|
|
6813
6813
|
}
|
|
@@ -13018,7 +13018,7 @@ var createComponentsFromCircuitJson = ({
|
|
|
13018
13018
|
const schematicSymbolId = schematicComponent?.schematic_symbol_id;
|
|
13019
13019
|
const parentSymbol = typeof schematicSymbolId === "string" ? schematicSymbolsByImportedId.get(schematicSymbolId) : void 0;
|
|
13020
13020
|
if (parentSymbol && schematicComponent?.is_box_with_pins === true && elm.center && elm.side_of_component) {
|
|
13021
|
-
const
|
|
13021
|
+
const distance25 = elm.distance_from_component_edge;
|
|
13022
13022
|
const facingDirection = getFacingDirectionFromSide(
|
|
13023
13023
|
elm.side_of_component
|
|
13024
13024
|
);
|
|
@@ -13026,8 +13026,8 @@ var createComponentsFromCircuitJson = ({
|
|
|
13026
13026
|
const directionVector = getUnitVectorFromDirection2(facingDirection);
|
|
13027
13027
|
const stemDirection = elm.facing_direction === facingDirection ? 1 : -1;
|
|
13028
13028
|
const portCenter = {
|
|
13029
|
-
x: elm.center.x + directionVector.x *
|
|
13030
|
-
y: elm.center.y + directionVector.y *
|
|
13029
|
+
x: elm.center.x + directionVector.x * distance25 * stemDirection,
|
|
13030
|
+
y: elm.center.y + directionVector.y * distance25 * stemDirection
|
|
13031
13031
|
};
|
|
13032
13032
|
const sourcePort = sourcePortsByImportedId.get(elm.source_port_id);
|
|
13033
13033
|
const aliases = Array.from(
|
|
@@ -17796,15 +17796,15 @@ var Capacitor_addPolarityFabricationSymbols = (capacitor) => {
|
|
|
17796
17796
|
source_port_id: negatives[0].source_port_id
|
|
17797
17797
|
});
|
|
17798
17798
|
if (!positive || !negative) return;
|
|
17799
|
-
const
|
|
17800
|
-
if (
|
|
17799
|
+
const distance25 = Math.hypot(negative.x - positive.x, negative.y - positive.y);
|
|
17800
|
+
if (distance25 < 1e-6) return;
|
|
17801
17801
|
const frame = compose7(
|
|
17802
17802
|
translate5((positive.x + negative.x) / 2, (positive.y + negative.y) / 2),
|
|
17803
17803
|
rotate4(Math.atan2(negative.y - positive.y, negative.x - positive.x))
|
|
17804
17804
|
);
|
|
17805
17805
|
const point6 = (x, y) => applyToPoint26(frame, { x, y });
|
|
17806
|
-
const offset =
|
|
17807
|
-
const halfSize = Math.min(
|
|
17806
|
+
const offset = distance25 / 4;
|
|
17807
|
+
const halfSize = Math.min(distance25 * 0.12, 0.3);
|
|
17808
17808
|
const routes = [
|
|
17809
17809
|
[point6(-offset - halfSize, 0), point6(-offset + halfSize, 0)],
|
|
17810
17810
|
[point6(-offset, -halfSize), point6(-offset, halfSize)],
|
|
@@ -19629,7 +19629,7 @@ import {
|
|
|
19629
19629
|
groupProps
|
|
19630
19630
|
} from "@tscircuit/props";
|
|
19631
19631
|
import {
|
|
19632
|
-
distance as
|
|
19632
|
+
distance as distance18
|
|
19633
19633
|
} from "circuit-json";
|
|
19634
19634
|
import Debug15 from "debug";
|
|
19635
19635
|
|
|
@@ -38873,9 +38873,9 @@ function getFreeBoundaryTracks(params) {
|
|
|
38873
38873
|
for (const via of vias) {
|
|
38874
38874
|
if (!via.spanLayers.includes(targetLayer2)) continue;
|
|
38875
38875
|
const radius = via.diameter / 2 + traceWidth / 2 + clearance;
|
|
38876
|
-
const
|
|
38877
|
-
if (
|
|
38878
|
-
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);
|
|
38879
38879
|
blocked.push([via.center[along] - extent, via.center[along] + extent]);
|
|
38880
38880
|
}
|
|
38881
38881
|
const gaps = [];
|
|
@@ -50566,6 +50566,8 @@ function getPresetAutoroutingConfig(autorouterConfig, platformConfig) {
|
|
|
50566
50566
|
};
|
|
50567
50567
|
case "bus-lanes":
|
|
50568
50568
|
return { local: true, groupMode: "subcircuit", preset: "bus_lanes" };
|
|
50569
|
+
case "dogbone":
|
|
50570
|
+
return { local: true, groupMode: "subcircuit", preset: "dogbone" };
|
|
50569
50571
|
case "fanout":
|
|
50570
50572
|
return {
|
|
50571
50573
|
local: true,
|
|
@@ -50628,11 +50630,1027 @@ function getPresetAutoroutingConfig(autorouterConfig, platformConfig) {
|
|
|
50628
50630
|
}
|
|
50629
50631
|
}
|
|
50630
50632
|
|
|
50631
|
-
// 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";
|
|
50632
51197
|
import { BaseSolver as BaseSolver4 } from "@tscircuit/solver-utils";
|
|
50633
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
|
+
|
|
50634
51652
|
// node_modules/@tscircuit/bus-lanes-solver/lib/geometry.ts
|
|
50635
|
-
var
|
|
51653
|
+
var distance14 = (a, b) => Math.hypot(a.x - b.x, a.y - b.y);
|
|
50636
51654
|
function pointSegmentDistance(p, edge) {
|
|
50637
51655
|
const [a, b] = edge, dx = b.x - a.x, dy = b.y - a.y, t = Math.max(
|
|
50638
51656
|
0,
|
|
@@ -50643,10 +51661,10 @@ function pointSegmentDistance(p, edge) {
|
|
|
50643
51661
|
);
|
|
50644
51662
|
return Math.hypot(p.x - a.x - t * dx, p.y - a.y - t * dy);
|
|
50645
51663
|
}
|
|
50646
|
-
var
|
|
51664
|
+
var cross3 = (a, b, c) => (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
|
50647
51665
|
function segmentDistance(ab, cd) {
|
|
50648
51666
|
const [a, b] = ab, [c, d] = cd;
|
|
50649
|
-
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)
|
|
50650
51668
|
return 0;
|
|
50651
51669
|
return Math.min(
|
|
50652
51670
|
pointSegmentDistance(a, cd),
|
|
@@ -50655,10 +51673,10 @@ function segmentDistance(ab, cd) {
|
|
|
50655
51673
|
pointSegmentDistance(d, ab)
|
|
50656
51674
|
);
|
|
50657
51675
|
}
|
|
50658
|
-
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);
|
|
50659
51677
|
function simplify(path) {
|
|
50660
51678
|
return path.filter(
|
|
50661
|
-
(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
|
|
50662
51680
|
);
|
|
50663
51681
|
}
|
|
50664
51682
|
|
|
@@ -50702,7 +51720,7 @@ function fixedCopper(input) {
|
|
|
50702
51720
|
throw Error("Fixed trace changes layer without a via");
|
|
50703
51721
|
result.push({ a: p, b: q, radius: p.width / 2, layer: p.layer, owners });
|
|
50704
51722
|
}
|
|
50705
|
-
if (q && p.route_type !== q.route_type &&
|
|
51723
|
+
if (q && p.route_type !== q.route_type && distance14(p, q) > 1e-10) {
|
|
50706
51724
|
const wire = p.route_type === "wire" ? p : q;
|
|
50707
51725
|
result.push({
|
|
50708
51726
|
a: p,
|
|
@@ -50822,7 +51840,7 @@ var VectorScene = class {
|
|
|
50822
51840
|
);
|
|
50823
51841
|
} else {
|
|
50824
51842
|
const r = c.radius + this.margin + epsilon;
|
|
50825
|
-
for (const p of
|
|
51843
|
+
for (const p of distance14(c.a, c.b) < 1e-9 ? [c.a] : [c.a, c.b])
|
|
50826
51844
|
for (const [x, y] of [
|
|
50827
51845
|
[1, k],
|
|
50828
51846
|
[k, 1],
|
|
@@ -50910,7 +51928,7 @@ var VectorVisibilitySearch = class {
|
|
|
50910
51928
|
this.start = start;
|
|
50911
51929
|
this.end = end;
|
|
50912
51930
|
this.vertices = [start, end];
|
|
50913
|
-
this.open.push({ id: 0, g: 0, f:
|
|
51931
|
+
this.open.push({ id: 0, g: 0, f: distance14(start, end) });
|
|
50914
51932
|
this.best.set(0, 0);
|
|
50915
51933
|
}
|
|
50916
51934
|
scene;
|
|
@@ -50970,7 +51988,7 @@ var VectorVisibilitySearch = class {
|
|
|
50970
51988
|
const from = this.vertices[cur.id];
|
|
50971
51989
|
for (let id = 1; id < this.vertices.length; id++) {
|
|
50972
51990
|
if (id === cur.id) continue;
|
|
50973
|
-
const to = this.vertices[id], lower = cur.g +
|
|
51991
|
+
const to = this.vertices[id], lower = cur.g + distance14(from, to);
|
|
50974
51992
|
if (lower >= (this.best.get(id) ?? Infinity) - 1e-9) continue;
|
|
50975
51993
|
const candidates = connectors(from, to).sort(
|
|
50976
51994
|
(a, b) => length(a) - length(b)
|
|
@@ -50983,7 +52001,7 @@ var VectorVisibilitySearch = class {
|
|
|
50983
52001
|
this.open.push({
|
|
50984
52002
|
id,
|
|
50985
52003
|
g,
|
|
50986
|
-
f: g +
|
|
52004
|
+
f: g + distance14(to, this.end),
|
|
50987
52005
|
parent: cur,
|
|
50988
52006
|
edge: path
|
|
50989
52007
|
});
|
|
@@ -51225,9 +52243,9 @@ function tuneLengths(input, traces, targets) {
|
|
|
51225
52243
|
const clearance = input.minTraceToPadEdgeClearance ?? input.defaultObstacleMargin ?? 0.075;
|
|
51226
52244
|
const returnSpacing = Math.max(width + clearance, 3 * width);
|
|
51227
52245
|
const pitch = 2 * (returnSpacing + 2 * width);
|
|
51228
|
-
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);
|
|
51229
52247
|
for (const { i } of segments) {
|
|
51230
|
-
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);
|
|
51231
52249
|
if (span < 0.01) continue;
|
|
51232
52250
|
const ux = (b.x - a.x) / span, uy = (b.y - a.y) / span;
|
|
51233
52251
|
const maximumTeeth = Math.floor(span * 0.9 / pitch);
|
|
@@ -51327,7 +52345,7 @@ function tuneLengths(input, traces, targets) {
|
|
|
51327
52345
|
function tuningPathIsSelfClear(path, required) {
|
|
51328
52346
|
const cumulative = [0];
|
|
51329
52347
|
for (let i = 1; i < path.length; i++)
|
|
51330
|
-
cumulative.push(cumulative[i - 1] +
|
|
52348
|
+
cumulative.push(cumulative[i - 1] + distance14(path[i - 1], path[i]));
|
|
51331
52349
|
for (let i = 0; i < path.length - 1; i++)
|
|
51332
52350
|
for (let j = i + 2; j < path.length - 1; j++) {
|
|
51333
52351
|
if (cumulative[j] - cumulative[i + 1] <= required + 1e-8) continue;
|
|
@@ -51365,7 +52383,7 @@ var layerColors = {
|
|
|
51365
52383
|
var layerColor = (layer) => layerColors[layer] ?? "#64748b";
|
|
51366
52384
|
|
|
51367
52385
|
// node_modules/@tscircuit/bus-lanes-solver/lib/bus-lanes-solver.ts
|
|
51368
|
-
var BusLanesSolver = class extends
|
|
52386
|
+
var BusLanesSolver = class extends BaseSolver8 {
|
|
51369
52387
|
input;
|
|
51370
52388
|
options;
|
|
51371
52389
|
phase = "validate";
|
|
@@ -51575,7 +52593,7 @@ var BusLanesSolver = class extends BaseSolver4 {
|
|
|
51575
52593
|
const t = this.traces.find((t2) => t2.connection_name === c.name);
|
|
51576
52594
|
if (!t) throw Error("Missing lane");
|
|
51577
52595
|
const scene = this.scene(c);
|
|
51578
|
-
if (
|
|
52596
|
+
if (distance14(t.route[0], c.pointsToConnect[0]) > 1e-8 || distance14(t.route.at(-1), c.pointsToConnect[1]) > 1e-8)
|
|
51579
52597
|
throw Error("Broken lane endpoints");
|
|
51580
52598
|
for (let i = 1; i < t.route.length; i++) {
|
|
51581
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);
|
|
@@ -51640,7 +52658,7 @@ var BusLanesSolver = class extends BaseSolver4 {
|
|
|
51640
52658
|
});
|
|
51641
52659
|
continue;
|
|
51642
52660
|
}
|
|
51643
|
-
if (
|
|
52661
|
+
if (distance14(c.a, c.b) < 1e-9)
|
|
51644
52662
|
circles.push({
|
|
51645
52663
|
center: c.a,
|
|
51646
52664
|
radius: c.radius,
|
|
@@ -51801,7 +52819,7 @@ import { CreateFdmEnclosureSolver } from "@tscircuit/create-fdm-enclosure";
|
|
|
51801
52819
|
// node_modules/@tscircuit/implicit-copper-pour-solver/lib/implicit-copper-pour-pipeline-solver.ts
|
|
51802
52820
|
import {
|
|
51803
52821
|
BasePipelineSolver,
|
|
51804
|
-
BaseSolver as
|
|
52822
|
+
BaseSolver as BaseSolver9,
|
|
51805
52823
|
definePipelineStep
|
|
51806
52824
|
} from "@tscircuit/solver-utils";
|
|
51807
52825
|
|
|
@@ -51819,7 +52837,7 @@ import { applyToPoint as applyToPoint30, compose as compose11, scale as scale2,
|
|
|
51819
52837
|
// node_modules/@tscircuit/implicit-copper-pour-solver/lib/geometry.ts
|
|
51820
52838
|
import {
|
|
51821
52839
|
clamp,
|
|
51822
|
-
distance as
|
|
52840
|
+
distance as distance15,
|
|
51823
52841
|
doBoundsOverlap as doBoundsOverlap2,
|
|
51824
52842
|
getBoundsFromPoints as getBoundsFromPoints3,
|
|
51825
52843
|
getUnitVectorFromPointAToB as getUnitVectorFromPointAToB2,
|
|
@@ -51868,7 +52886,7 @@ var distanceToPolygon = (point6, polygon) => {
|
|
|
51868
52886
|
var distanceToPrimitive = (primitive, px, py) => {
|
|
51869
52887
|
const point6 = { x: px, y: py };
|
|
51870
52888
|
if (primitive.kind === "circle") {
|
|
51871
|
-
return Math.max(
|
|
52889
|
+
return Math.max(distance15(point6, primitive) - primitive.radius, 0);
|
|
51872
52890
|
}
|
|
51873
52891
|
if (primitive.kind === "segment") {
|
|
51874
52892
|
return Math.max(
|
|
@@ -51919,7 +52937,7 @@ var getClosestPointOnPrimitive = (primitive, point6) => {
|
|
|
51919
52937
|
const start = primitive.points[index];
|
|
51920
52938
|
const end = primitive.points[(index + 1) % primitive.points.length];
|
|
51921
52939
|
const candidate = pointToSegmentClosestPoint(point6, start, end);
|
|
51922
|
-
const candidateDistance =
|
|
52940
|
+
const candidateDistance = distance15(point6, candidate);
|
|
51923
52941
|
if (candidateDistance < bestDistance) {
|
|
51924
52942
|
bestDistance = candidateDistance;
|
|
51925
52943
|
closestPoint = candidate;
|
|
@@ -52975,7 +53993,7 @@ var prepareCircuitJson = (input) => {
|
|
|
52975
53993
|
|
|
52976
53994
|
// node_modules/@tscircuit/implicit-copper-pour-solver/lib/simplify-polygon-edges.ts
|
|
52977
53995
|
import {
|
|
52978
|
-
distance as
|
|
53996
|
+
distance as distance16,
|
|
52979
53997
|
doSegmentsIntersect,
|
|
52980
53998
|
onSegment,
|
|
52981
53999
|
pointToSegmentDistance as pointToSegmentDistance2
|
|
@@ -53038,8 +54056,8 @@ var simplifyOpenPath = (points, tolerance) => {
|
|
|
53038
54056
|
const point6 = points[index];
|
|
53039
54057
|
const next = points[index + 1];
|
|
53040
54058
|
const shortestLeg = Math.min(
|
|
53041
|
-
|
|
53042
|
-
|
|
54059
|
+
distance16(point6, previous),
|
|
54060
|
+
distance16(next, point6)
|
|
53043
54061
|
);
|
|
53044
54062
|
if (repeatsTurnDirection || shortestLeg > tolerance) {
|
|
53045
54063
|
anchorIndices.push(index);
|
|
@@ -53088,8 +54106,8 @@ var normalizePolygon = (polygon) => {
|
|
|
53088
54106
|
};
|
|
53089
54107
|
var isPointStrictlyInsideSegment = (point6, start, end) => {
|
|
53090
54108
|
if (pointsEqual(point6, start) || pointsEqual(point6, end)) return false;
|
|
53091
|
-
const
|
|
53092
|
-
if (Math.abs(
|
|
54109
|
+
const cross5 = crossProduct(start, end, point6);
|
|
54110
|
+
if (Math.abs(cross5) > 1e-9) return false;
|
|
53093
54111
|
return onSegment(start, point6, end);
|
|
53094
54112
|
};
|
|
53095
54113
|
var splitEdgesAtVertices = (polygon, allVertices) => {
|
|
@@ -53441,7 +54459,7 @@ var mergeSolverGraphics = (source, overlay) => {
|
|
|
53441
54459
|
};
|
|
53442
54460
|
|
|
53443
54461
|
// node_modules/@tscircuit/implicit-copper-pour-solver/lib/implicit-copper-pour-pipeline-solver.ts
|
|
53444
|
-
var PrepareCircuitJsonSolver = class extends
|
|
54462
|
+
var PrepareCircuitJsonSolver = class extends BaseSolver9 {
|
|
53445
54463
|
constructor(input) {
|
|
53446
54464
|
super();
|
|
53447
54465
|
this.input = input;
|
|
@@ -53457,7 +54475,7 @@ var PrepareCircuitJsonSolver = class extends BaseSolver5 {
|
|
|
53457
54475
|
return this.output;
|
|
53458
54476
|
}
|
|
53459
54477
|
};
|
|
53460
|
-
var AssignGridCellsSolver = class extends
|
|
54478
|
+
var AssignGridCellsSolver = class extends BaseSolver9 {
|
|
53461
54479
|
constructor(input) {
|
|
53462
54480
|
super();
|
|
53463
54481
|
this.input = input;
|
|
@@ -53480,7 +54498,7 @@ var AssignGridCellsSolver = class extends BaseSolver5 {
|
|
|
53480
54498
|
return this.output;
|
|
53481
54499
|
}
|
|
53482
54500
|
};
|
|
53483
|
-
var TracePowerPolygonsSolver = class extends
|
|
54501
|
+
var TracePowerPolygonsSolver = class extends BaseSolver9 {
|
|
53484
54502
|
constructor(input) {
|
|
53485
54503
|
super();
|
|
53486
54504
|
this.input = input;
|
|
@@ -53516,7 +54534,7 @@ var TracePowerPolygonsSolver = class extends BaseSolver5 {
|
|
|
53516
54534
|
};
|
|
53517
54535
|
}
|
|
53518
54536
|
};
|
|
53519
|
-
var SimplifyPolygonEdgesSolver = class extends
|
|
54537
|
+
var SimplifyPolygonEdgesSolver = class extends BaseSolver9 {
|
|
53520
54538
|
constructor(input) {
|
|
53521
54539
|
super();
|
|
53522
54540
|
this.input = input;
|
|
@@ -53654,13 +54672,13 @@ import { LayoutPipelineSolver } from "@tscircuit/matchpack";
|
|
|
53654
54672
|
import { SchematicTracePipelineSolver } from "@tscircuit/schematic-trace-solver";
|
|
53655
54673
|
|
|
53656
54674
|
// node_modules/@tscircuit/via-stitch-solver/lib/solvers/ViaStitchSolver.ts
|
|
53657
|
-
import { BaseSolver as
|
|
54675
|
+
import { BaseSolver as BaseSolver10 } from "@tscircuit/solver-utils";
|
|
53658
54676
|
|
|
53659
54677
|
// node_modules/@tscircuit/via-stitch-solver/lib/geometry/brep-point-containment.ts
|
|
53660
54678
|
var POINT_EPSILON = 1e-8;
|
|
53661
54679
|
var isPointOnSegment = (point6, start, end) => {
|
|
53662
|
-
const
|
|
53663
|
-
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;
|
|
53664
54682
|
const dot = (point6.x - start.x) * (end.x - start.x) + (point6.y - start.y) * (end.y - start.y);
|
|
53665
54683
|
if (dot < -POINT_EPSILON) return false;
|
|
53666
54684
|
const squaredLength = (end.x - start.x) ** 2 + (end.y - start.y) ** 2;
|
|
@@ -53981,7 +54999,7 @@ var getGridCoordinates = ({
|
|
|
53981
54999
|
}
|
|
53982
55000
|
return coordinates;
|
|
53983
55001
|
};
|
|
53984
|
-
var ViaStitchSolver = class extends
|
|
55002
|
+
var ViaStitchSolver = class extends BaseSolver10 {
|
|
53985
55003
|
constructor(input) {
|
|
53986
55004
|
super();
|
|
53987
55005
|
this.input = input;
|
|
@@ -54128,6 +55146,7 @@ var ViaStitchSolver = class extends BaseSolver6 {
|
|
|
54128
55146
|
// lib/solvers.ts
|
|
54129
55147
|
import { PackSolver2 } from "calculate-packing";
|
|
54130
55148
|
var SOLVERS = {
|
|
55149
|
+
DogboneFanoutSolver,
|
|
54131
55150
|
BusLanesSolver,
|
|
54132
55151
|
PackSolver2,
|
|
54133
55152
|
LayoutPipelineSolver,
|
|
@@ -54591,6 +55610,27 @@ var localAutorouterStrategies = /* @__PURE__ */ new Map([
|
|
|
54591
55610
|
createFanoutAutorouterStrategy("single_layer_fanout")
|
|
54592
55611
|
],
|
|
54593
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
|
+
],
|
|
54594
55634
|
[
|
|
54595
55635
|
"bus_lanes",
|
|
54596
55636
|
{
|
|
@@ -61067,19 +62107,19 @@ import Debug12 from "debug";
|
|
|
61067
62107
|
import { isValidElement as isValidElement2 } from "react";
|
|
61068
62108
|
|
|
61069
62109
|
// lib/components/primitive-components/Group/Group_doInitialSchematicTraceRender/segment-intersection.ts
|
|
61070
|
-
var
|
|
62110
|
+
var cross4 = (ax, ay, bx, by) => {
|
|
61071
62111
|
return ax * by - ay * bx;
|
|
61072
62112
|
};
|
|
61073
62113
|
function segmentIntersection(p1, p2, q1, q2, tol) {
|
|
61074
62114
|
const r = { x: p2.x - p1.x, y: p2.y - p1.y };
|
|
61075
62115
|
const s = { x: q2.x - q1.x, y: q2.y - q1.y };
|
|
61076
|
-
const rxs =
|
|
62116
|
+
const rxs = cross4(r.x, r.y, s.x, s.y);
|
|
61077
62117
|
const q_p = { x: q1.x - p1.x, y: q1.y - p1.y };
|
|
61078
|
-
const q_pxr =
|
|
62118
|
+
const q_pxr = cross4(q_p.x, q_p.y, r.x, r.y);
|
|
61079
62119
|
if (Math.abs(rxs) < tol && Math.abs(q_pxr) < tol) return null;
|
|
61080
62120
|
if (Math.abs(rxs) < tol && Math.abs(q_pxr) >= tol) return null;
|
|
61081
|
-
const t =
|
|
61082
|
-
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;
|
|
61083
62123
|
if (t < -tol || t > 1 + tol || u < -tol || u > 1 + tol) return null;
|
|
61084
62124
|
return { x: p1.x + t * r.x, y: p1.y + t * r.y };
|
|
61085
62125
|
}
|
|
@@ -61272,8 +62312,8 @@ function isParallel(e1, e2, tol = TOL2) {
|
|
|
61272
62312
|
const L1 = Math.hypot(v1.x, v1.y);
|
|
61273
62313
|
const L2 = Math.hypot(v2.x, v2.y);
|
|
61274
62314
|
if (L1 < tol || L2 < tol) return true;
|
|
61275
|
-
const
|
|
61276
|
-
return Math.abs(
|
|
62315
|
+
const cross5 = v1.x * v2.y - v1.y * v2.x;
|
|
62316
|
+
return Math.abs(cross5) <= tol * L1 * L2;
|
|
61277
62317
|
}
|
|
61278
62318
|
function incidentEdgesAtPoint(trace, p, tol = TOL2) {
|
|
61279
62319
|
return trace.edges.filter(
|
|
@@ -64540,6 +65580,16 @@ function Group_getRoutingPhasePlans(group) {
|
|
|
64540
65580
|
nets: nets.filter((net) => breakoutByNet.get(net) === breakout),
|
|
64541
65581
|
traces: breakoutTraces
|
|
64542
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
|
+
}
|
|
64543
65593
|
if (breakout._parsedProps.pcbTracePaths !== void 0) {
|
|
64544
65594
|
breakoutPlans.push({
|
|
64545
65595
|
...breakoutPlan,
|
|
@@ -64612,7 +65662,7 @@ function Group_getRoutingPhasePlans(group) {
|
|
|
64612
65662
|
var package_default = {
|
|
64613
65663
|
name: "@tscircuit/core",
|
|
64614
65664
|
type: "module",
|
|
64615
|
-
version: "0.0.
|
|
65665
|
+
version: "0.0.2025",
|
|
64616
65666
|
types: "dist/index.d.ts",
|
|
64617
65667
|
main: "dist/index.js",
|
|
64618
65668
|
module: "dist/index.js",
|
|
@@ -64653,6 +65703,7 @@ var package_default = {
|
|
|
64653
65703
|
"@tscircuit/common": "^0.0.20",
|
|
64654
65704
|
"@tscircuit/copper-pour-solver": "^0.0.62",
|
|
64655
65705
|
"@tscircuit/create-fdm-enclosure": "0.0.4",
|
|
65706
|
+
"@tscircuit/dogbone-solver": "https://jscdn.tscircuit.com/@tscircuit/dogbone-solver/0.0.1.tgz",
|
|
64656
65707
|
"@tscircuit/fanout-solver": "0.0.78",
|
|
64657
65708
|
"@tscircuit/flex-utils": "https://jscdn.tscircuit.com/@tscircuit/flex-utils/0.0.5.tgz",
|
|
64658
65709
|
"@tscircuit/footprinter": "^0.0.426",
|
|
@@ -65080,7 +66131,6 @@ function Group_syncFanoutExitsWithGlobalConnections({
|
|
|
65080
66131
|
) ? [{ globalConnection, pointIndex, previousPoint }] : []
|
|
65081
66132
|
)
|
|
65082
66133
|
);
|
|
65083
|
-
if (globalPointMatches.length === 0) continue;
|
|
65084
66134
|
const changedPointIndex = outputConnection.pointsToConnect.findIndex(
|
|
65085
66135
|
(outputPoint, pointIndex) => {
|
|
65086
66136
|
const inputPoint = inputConnection.pointsToConnect[pointIndex];
|
|
@@ -65089,6 +66139,14 @@ function Group_syncFanoutExitsWithGlobalConnections({
|
|
|
65089
66139
|
);
|
|
65090
66140
|
if (changedPointIndex < 0) continue;
|
|
65091
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
|
+
}
|
|
65092
66150
|
for (const {
|
|
65093
66151
|
globalConnection,
|
|
65094
66152
|
pointIndex,
|
|
@@ -65349,7 +66407,7 @@ function getAccumulatedPcbTracesWithStageOutputReplacements({
|
|
|
65349
66407
|
}
|
|
65350
66408
|
|
|
65351
66409
|
// lib/components/primitive-components/Group/get-source-trace-id-for-routed-trace.ts
|
|
65352
|
-
import { distance as
|
|
66410
|
+
import { distance as distance17, pointToSegmentDistance as pointToSegmentDistance3 } from "@tscircuit/math-utils";
|
|
65353
66411
|
import { ConnectivityMap as ConnectivityMap5 } from "circuit-json-to-connectivity-map";
|
|
65354
66412
|
var POINT_EPSILON2 = 1e-6;
|
|
65355
66413
|
function isPointOnWireSegment({
|
|
@@ -65371,7 +66429,7 @@ function isPointInSmtPad({
|
|
|
65371
66429
|
return Math.abs(point6.x - pad.x) <= pad.width / 2 + traceWidth / 2 && Math.abs(point6.y - pad.y) <= pad.height / 2 + traceWidth / 2;
|
|
65372
66430
|
}
|
|
65373
66431
|
if (pad.shape === "circle") {
|
|
65374
|
-
return
|
|
66432
|
+
return distance17(point6, pad) <= pad.radius + traceWidth / 2;
|
|
65375
66433
|
}
|
|
65376
66434
|
return false;
|
|
65377
66435
|
}
|
|
@@ -65380,7 +66438,7 @@ function isPointInPlatedHole({
|
|
|
65380
66438
|
hole,
|
|
65381
66439
|
traceWidth = 0
|
|
65382
66440
|
}) {
|
|
65383
|
-
return
|
|
66441
|
+
return distance17(point6, hole) <= (hole.outer_diameter ?? hole.hole_diameter ?? 0) / 2 + traceWidth / 2;
|
|
65384
66442
|
}
|
|
65385
66443
|
function getEndpointSourcePortIdsFromGeometry(db, trace) {
|
|
65386
66444
|
const sourcePortIds = /* @__PURE__ */ new Set();
|
|
@@ -65393,7 +66451,7 @@ function getEndpointSourcePortIdsFromGeometry(db, trace) {
|
|
|
65393
66451
|
if (!pcbPort.source_port_id) continue;
|
|
65394
66452
|
if (endpoint.layer && pcbPort.layers && !pcbPort.layers.includes(endpoint.layer))
|
|
65395
66453
|
continue;
|
|
65396
|
-
if (
|
|
66454
|
+
if (distance17(endpoint, pcbPort) <= 0.01) {
|
|
65397
66455
|
sourcePortIds.add(pcbPort.source_port_id);
|
|
65398
66456
|
}
|
|
65399
66457
|
}
|
|
@@ -66077,8 +67135,8 @@ var Group5 = class extends NormalComponent {
|
|
|
66077
67135
|
const groupProps2 = props;
|
|
66078
67136
|
const hasOutline = groupProps2.outline && groupProps2.outline.length > 0;
|
|
66079
67137
|
const numericOutline = hasOutline ? groupProps2.outline.map((point6) => ({
|
|
66080
|
-
x:
|
|
66081
|
-
y:
|
|
67138
|
+
x: distance18.parse(point6.x),
|
|
67139
|
+
y: distance18.parse(point6.y)
|
|
66082
67140
|
})) : void 0;
|
|
66083
67141
|
const ctx = this.props;
|
|
66084
67142
|
const anchorPosition = this._getGlobalPcbPositionBeforeLayout();
|
|
@@ -66116,8 +67174,8 @@ var Group5 = class extends NormalComponent {
|
|
|
66116
67174
|
const hasExplicitPositioning = this._parsedProps.pcbX !== void 0 || this._parsedProps.pcbY !== void 0;
|
|
66117
67175
|
if (hasOutline) {
|
|
66118
67176
|
const numericOutline = props.outline.map((point6) => ({
|
|
66119
|
-
x:
|
|
66120
|
-
y:
|
|
67177
|
+
x: distance18.parse(point6.x),
|
|
67178
|
+
y: distance18.parse(point6.y)
|
|
66121
67179
|
}));
|
|
66122
67180
|
const outlineBounds = getBoundsFromPoints9(numericOutline);
|
|
66123
67181
|
if (!outlineBounds) return;
|
|
@@ -66518,7 +67576,9 @@ var Group5 = class extends NormalComponent {
|
|
|
66518
67576
|
routingPhaseDebugLabels.set(plan, `${label} ${ordinal}`);
|
|
66519
67577
|
}
|
|
66520
67578
|
const hasFanoutStage = routingStages.some(
|
|
66521
|
-
({ autorouterConfig: autorouterConfig2 }) => ["fanout", "single_layer_fanout"].includes(
|
|
67579
|
+
({ autorouterConfig: autorouterConfig2 }) => ["fanout", "single_layer_fanout", "dogbone"].includes(
|
|
67580
|
+
autorouterConfig2.preset ?? ""
|
|
67581
|
+
)
|
|
66522
67582
|
);
|
|
66523
67583
|
const fanoutPourNetMap = hasFanoutStage ? Group_getFanoutPourNetMap(this, routingPhasePlans) : void 0;
|
|
66524
67584
|
let { simpleRouteJson: baseSimpleRouteJson } = getSimpleRouteJsonFromCircuitJson({
|
|
@@ -66941,6 +68001,11 @@ var Group5 = class extends NormalComponent {
|
|
|
66941
68001
|
} else {
|
|
66942
68002
|
autorouter = localAutorouterStrategy.create({
|
|
66943
68003
|
simpleRouteJson,
|
|
68004
|
+
onSolverEnded: (event) => this.root?.emit("solver:ended", {
|
|
68005
|
+
...event,
|
|
68006
|
+
type: "solver:ended",
|
|
68007
|
+
componentName: this.getString()
|
|
68008
|
+
}),
|
|
66944
68009
|
commonAutorouterOptions,
|
|
66945
68010
|
busFanoutDirections: routingPhasePlan.busFanoutDirections,
|
|
66946
68011
|
fanoutBounds: routingPhasePlan.fanoutBounds,
|
|
@@ -66996,7 +68061,7 @@ var Group5 = class extends NormalComponent {
|
|
|
66996
68061
|
traces = await routingPromise;
|
|
66997
68062
|
}
|
|
66998
68063
|
let transformedSimpleRouteJson = precomputedOutputSimpleRouteJson ?? autorouter?.getOutputSimpleRouteJson?.();
|
|
66999
|
-
if (transformedSimpleRouteJson && !usesPreviousStageOutput && ["fanout", "single_layer_fanout"].includes(
|
|
68064
|
+
if (transformedSimpleRouteJson && !usesPreviousStageOutput && ["fanout", "single_layer_fanout", "dogbone"].includes(
|
|
67000
68065
|
phaseAutorouterConfig.preset ?? ""
|
|
67001
68066
|
) && routingPhasePlan.routingPcbGroupId) {
|
|
67002
68067
|
const synchronizedFanout = Group_syncFanoutExitsWithGlobalConnections(
|
|
@@ -67016,10 +68081,14 @@ var Group5 = class extends NormalComponent {
|
|
|
67016
68081
|
if (!breakoutPoint) continue;
|
|
67017
68082
|
db.pcb_breakout_point.update(breakoutPoint.pcb_breakout_point_id, {
|
|
67018
68083
|
x: synchronizedPoint.fanoutExitPoint.x,
|
|
67019
|
-
y: synchronizedPoint.fanoutExitPoint.y
|
|
68084
|
+
y: synchronizedPoint.fanoutExitPoint.y,
|
|
68085
|
+
layer: synchronizedPoint.fanoutExitPoint.layer
|
|
67020
68086
|
});
|
|
67021
68087
|
}
|
|
67022
68088
|
}
|
|
68089
|
+
if (localAutorouterStrategy.preserveOutputTraces) {
|
|
68090
|
+
for (const trace of traces) fixedTraceIds.add(trace.pcb_trace_id);
|
|
68091
|
+
}
|
|
67023
68092
|
let stageOutputTraces = traces;
|
|
67024
68093
|
if (transformedSimpleRouteJson?.traces) {
|
|
67025
68094
|
stageOutputTraces = transformedSimpleRouteJson.traces;
|
|
@@ -67056,7 +68125,7 @@ var Group5 = class extends NormalComponent {
|
|
|
67056
68125
|
subcircuit: this,
|
|
67057
68126
|
input: simpleRouteJson,
|
|
67058
68127
|
traces,
|
|
67059
|
-
isFanout: ["fanout", "single_layer_fanout"].includes(
|
|
68128
|
+
isFanout: ["fanout", "single_layer_fanout", "dogbone"].includes(
|
|
67060
68129
|
phaseAutorouterConfig.preset ?? ""
|
|
67061
68130
|
)
|
|
67062
68131
|
});
|
|
@@ -67989,8 +69058,8 @@ var Led_addPolarityFabricationSymbol = (led) => {
|
|
|
67989
69058
|
const a = db.pcb_port.getWhere({ source_port_id: anode.source_port_id });
|
|
67990
69059
|
const k = db.pcb_port.getWhere({ source_port_id: cathode.source_port_id });
|
|
67991
69060
|
if (!a || !k) return;
|
|
67992
|
-
const
|
|
67993
|
-
if (
|
|
69061
|
+
const distance25 = Math.hypot(k.x - a.x, k.y - a.y);
|
|
69062
|
+
if (distance25 < 1e-6) return;
|
|
67994
69063
|
const angle = Math.atan2(k.y - a.y, k.x - a.x);
|
|
67995
69064
|
const frame = compose15(
|
|
67996
69065
|
translate15((a.x + k.x) / 2, (a.y + k.y) / 2),
|
|
@@ -68004,10 +69073,10 @@ var Led_addPolarityFabricationSymbol = (led) => {
|
|
|
68004
69073
|
};
|
|
68005
69074
|
const notes = [];
|
|
68006
69075
|
generatedNotes.set(led, notes);
|
|
68007
|
-
const halfLength = Math.min(
|
|
69076
|
+
const halfLength = Math.min(distance25 * 0.2, 0.4);
|
|
68008
69077
|
const halfHeight = halfLength * 0.75;
|
|
68009
69078
|
const routes = [
|
|
68010
|
-
[point6(-
|
|
69079
|
+
[point6(-distance25 / 2, 0), point6(-halfLength, 0)],
|
|
68011
69080
|
[
|
|
68012
69081
|
point6(-halfLength, -halfHeight),
|
|
68013
69082
|
point6(halfLength, 0),
|
|
@@ -68015,7 +69084,7 @@ var Led_addPolarityFabricationSymbol = (led) => {
|
|
|
68015
69084
|
point6(-halfLength, -halfHeight)
|
|
68016
69085
|
],
|
|
68017
69086
|
[point6(halfLength, -halfHeight), point6(halfLength, halfHeight)],
|
|
68018
|
-
[point6(halfLength, 0), point6(
|
|
69087
|
+
[point6(halfLength, 0), point6(distance25 / 2, 0)]
|
|
68019
69088
|
];
|
|
68020
69089
|
for (const route of routes) {
|
|
68021
69090
|
notes.push(
|
|
@@ -69949,7 +71018,7 @@ var Board_doInitialPcbPlacementDesignRuleChecks = (board) => {
|
|
|
69949
71018
|
|
|
69950
71019
|
// lib/components/normal-components/Board/board-castellated-hole.ts
|
|
69951
71020
|
import {
|
|
69952
|
-
distance as
|
|
71021
|
+
distance as distance19
|
|
69953
71022
|
} from "circuit-json";
|
|
69954
71023
|
var CASTELLATED_HOLE_ENDPOINT_TOLERANCE_MM = 1e-6;
|
|
69955
71024
|
var getConnectionTargets2 = (connectsTo) => {
|
|
@@ -69970,8 +71039,8 @@ var BoardCastellatedHole = class _BoardCastellatedHole extends PlatedHole {
|
|
|
69970
71039
|
constructor(outlinePoint, outlinePointIndex) {
|
|
69971
71040
|
const name = `castellated_hole_${outlinePointIndex + 1}`;
|
|
69972
71041
|
const connectionTargets = getConnectionTargets2(outlinePoint.connectsTo);
|
|
69973
|
-
const holeDiameter =
|
|
69974
|
-
const padDiameter =
|
|
71042
|
+
const holeDiameter = distance19.parse(outlinePoint.holeDiameter);
|
|
71043
|
+
const padDiameter = distance19.parse(outlinePoint.padDiameter);
|
|
69975
71044
|
super({
|
|
69976
71045
|
shape: "circle",
|
|
69977
71046
|
holeDiameter,
|
|
@@ -70721,11 +71790,11 @@ var MountedBoard = class extends Subcircuit {
|
|
|
70721
71790
|
|
|
70722
71791
|
// lib/components/normal-components/Panel.ts
|
|
70723
71792
|
import { panelProps } from "@tscircuit/props";
|
|
70724
|
-
import { distance as
|
|
71793
|
+
import { distance as distance23 } from "circuit-json";
|
|
70725
71794
|
|
|
70726
71795
|
// lib/components/normal-components/Subpanel.ts
|
|
70727
71796
|
import { subpanelProps } from "@tscircuit/props";
|
|
70728
|
-
import { distance as
|
|
71797
|
+
import { distance as distance22 } from "circuit-json";
|
|
70729
71798
|
import { compose as compose17, identity as identity9, translate as translate17 } from "transformation-matrix";
|
|
70730
71799
|
|
|
70731
71800
|
// lib/utils/panels/generate-panel-tabs-and-mouse-bites.ts
|
|
@@ -70989,15 +72058,15 @@ function generatePanelOutlineRoutingCutouts(boards, options) {
|
|
|
70989
72058
|
}
|
|
70990
72059
|
|
|
70991
72060
|
// lib/utils/panels/pack-into-grid.ts
|
|
70992
|
-
import { distance as
|
|
72061
|
+
import { distance as distance21 } from "circuit-json";
|
|
70993
72062
|
|
|
70994
72063
|
// lib/utils/panels/get-board-dimensions-from-props.ts
|
|
70995
72064
|
import { getBoundsFromPoints as getBoundsFromPoints11 } from "@tscircuit/math-utils";
|
|
70996
|
-
import { distance as
|
|
72065
|
+
import { distance as distance20 } from "circuit-json";
|
|
70997
72066
|
var getBoardDimensionsFromProps = (board) => {
|
|
70998
72067
|
const props = board._parsedProps;
|
|
70999
|
-
let width = props.width != null ?
|
|
71000
|
-
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;
|
|
71001
72070
|
if ((width === void 0 || height === void 0) && props.outline?.length) {
|
|
71002
72071
|
const outlineBounds = getBoundsFromPoints11(props.outline);
|
|
71003
72072
|
if (outlineBounds) {
|
|
@@ -71041,8 +72110,8 @@ function packIntoGrid({
|
|
|
71041
72110
|
}
|
|
71042
72111
|
let cols;
|
|
71043
72112
|
let rows;
|
|
71044
|
-
const minCellWidth = cellWidth ?
|
|
71045
|
-
const minCellHeight = cellHeight ?
|
|
72113
|
+
const minCellWidth = cellWidth ? distance21.parse(cellWidth) : 0;
|
|
72114
|
+
const minCellHeight = cellHeight ? distance21.parse(cellHeight) : 0;
|
|
71046
72115
|
if (col !== void 0) {
|
|
71047
72116
|
cols = col;
|
|
71048
72117
|
rows = row ?? Math.ceil(itemsWithDims.length / cols);
|
|
@@ -71141,8 +72210,8 @@ function getItemDimensions(item, db) {
|
|
|
71141
72210
|
const props = subpanel._parsedProps;
|
|
71142
72211
|
if (props.width !== void 0 && props.height !== void 0) {
|
|
71143
72212
|
return {
|
|
71144
|
-
width:
|
|
71145
|
-
height:
|
|
72213
|
+
width: distance21.parse(props.width),
|
|
72214
|
+
height: distance21.parse(props.height)
|
|
71146
72215
|
};
|
|
71147
72216
|
}
|
|
71148
72217
|
const directBoards = subpanel._getDirectBoardChildren();
|
|
@@ -71169,7 +72238,7 @@ function getItemDimensions(item, db) {
|
|
|
71169
72238
|
return getBoardDimensionsFromProps(directBoards[0]);
|
|
71170
72239
|
}
|
|
71171
72240
|
if (subpanel._cachedGridWidth > 0 && subpanel._cachedGridHeight > 0) {
|
|
71172
|
-
const edgePadding =
|
|
72241
|
+
const edgePadding = distance21.parse(props.edgePadding ?? 5);
|
|
71173
72242
|
return {
|
|
71174
72243
|
width: subpanel._cachedGridWidth + edgePadding * 2,
|
|
71175
72244
|
height: subpanel._cachedGridHeight + edgePadding * 2
|
|
@@ -71327,17 +72396,17 @@ var Subpanel = class _Subpanel extends Group5 {
|
|
|
71327
72396
|
edgePaddingTop: edgePaddingTopProp,
|
|
71328
72397
|
edgePaddingBottom: edgePaddingBottomProp
|
|
71329
72398
|
} = this._parsedProps;
|
|
71330
|
-
const edgePadding =
|
|
71331
|
-
const edgePaddingLeft =
|
|
71332
|
-
const edgePaddingRight =
|
|
72399
|
+
const edgePadding = distance22.parse(edgePaddingProp ?? 5);
|
|
72400
|
+
const edgePaddingLeft = distance22.parse(edgePaddingLeftProp ?? edgePadding);
|
|
72401
|
+
const edgePaddingRight = distance22.parse(
|
|
71333
72402
|
edgePaddingRightProp ?? edgePadding
|
|
71334
72403
|
);
|
|
71335
|
-
const edgePaddingTop =
|
|
71336
|
-
const edgePaddingBottom =
|
|
72404
|
+
const edgePaddingTop = distance22.parse(edgePaddingTopProp ?? edgePadding);
|
|
72405
|
+
const edgePaddingBottom = distance22.parse(
|
|
71337
72406
|
edgePaddingBottomProp ?? edgePadding
|
|
71338
72407
|
);
|
|
71339
|
-
const panelWidth =
|
|
71340
|
-
const panelHeight =
|
|
72408
|
+
const panelWidth = distance22.parse(this._parsedProps.width);
|
|
72409
|
+
const panelHeight = distance22.parse(this._parsedProps.height);
|
|
71341
72410
|
availablePanelWidth = panelWidth - edgePaddingLeft - edgePaddingRight;
|
|
71342
72411
|
availablePanelHeight = panelHeight - edgePaddingTop - edgePaddingBottom;
|
|
71343
72412
|
}
|
|
@@ -71412,8 +72481,8 @@ var Subpanel = class _Subpanel extends Group5 {
|
|
|
71412
72481
|
if (!this.pcb_group_id) return;
|
|
71413
72482
|
if (hasExplicitWidth && hasExplicitHeight) {
|
|
71414
72483
|
db.pcb_group.update(this.pcb_group_id, {
|
|
71415
|
-
width:
|
|
71416
|
-
height:
|
|
72484
|
+
width: distance22.parse(this._parsedProps.width),
|
|
72485
|
+
height: distance22.parse(this._parsedProps.height)
|
|
71417
72486
|
});
|
|
71418
72487
|
} else if (gridWidth > 0 || gridHeight > 0) {
|
|
71419
72488
|
const {
|
|
@@ -71423,18 +72492,18 @@ var Subpanel = class _Subpanel extends Group5 {
|
|
|
71423
72492
|
edgePaddingTop: edgePaddingTopProp,
|
|
71424
72493
|
edgePaddingBottom: edgePaddingBottomProp
|
|
71425
72494
|
} = this._parsedProps;
|
|
71426
|
-
const edgePadding =
|
|
71427
|
-
const edgePaddingLeft =
|
|
71428
|
-
const edgePaddingRight =
|
|
72495
|
+
const edgePadding = distance22.parse(edgePaddingProp ?? 5);
|
|
72496
|
+
const edgePaddingLeft = distance22.parse(edgePaddingLeftProp ?? edgePadding);
|
|
72497
|
+
const edgePaddingRight = distance22.parse(
|
|
71429
72498
|
edgePaddingRightProp ?? edgePadding
|
|
71430
72499
|
);
|
|
71431
|
-
const edgePaddingTop =
|
|
71432
|
-
const edgePaddingBottom =
|
|
72500
|
+
const edgePaddingTop = distance22.parse(edgePaddingTopProp ?? edgePadding);
|
|
72501
|
+
const edgePaddingBottom = distance22.parse(
|
|
71433
72502
|
edgePaddingBottomProp ?? edgePadding
|
|
71434
72503
|
);
|
|
71435
72504
|
db.pcb_group.update(this.pcb_group_id, {
|
|
71436
|
-
width: hasExplicitWidth ?
|
|
71437
|
-
height: hasExplicitHeight ?
|
|
72505
|
+
width: hasExplicitWidth ? distance22.parse(this._parsedProps.width) : gridWidth + edgePaddingLeft + edgePaddingRight,
|
|
72506
|
+
height: hasExplicitHeight ? distance22.parse(this._parsedProps.height) : gridHeight + edgePaddingTop + edgePaddingBottom
|
|
71438
72507
|
});
|
|
71439
72508
|
}
|
|
71440
72509
|
}
|
|
@@ -71522,8 +72591,8 @@ var Panel = class extends Subpanel {
|
|
|
71522
72591
|
const { db } = this.root;
|
|
71523
72592
|
const props = this._parsedProps;
|
|
71524
72593
|
const inserted = db.pcb_panel.insert({
|
|
71525
|
-
width: props.width !== void 0 ?
|
|
71526
|
-
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,
|
|
71527
72596
|
thickness: 1.6,
|
|
71528
72597
|
center: this._getGlobalPcbPositionBeforeLayout(),
|
|
71529
72598
|
covered_with_solder_mask: !(props.noSolderMask ?? false)
|
|
@@ -71542,8 +72611,8 @@ var Panel = class extends Subpanel {
|
|
|
71542
72611
|
if (!this.pcb_panel_id) return;
|
|
71543
72612
|
if (hasExplicitWidth && hasExplicitHeight) {
|
|
71544
72613
|
db.pcb_panel.update(this.pcb_panel_id, {
|
|
71545
|
-
width:
|
|
71546
|
-
height:
|
|
72614
|
+
width: distance23.parse(this._parsedProps.width),
|
|
72615
|
+
height: distance23.parse(this._parsedProps.height)
|
|
71547
72616
|
});
|
|
71548
72617
|
} else if (gridWidth > 0 || gridHeight > 0) {
|
|
71549
72618
|
const {
|
|
@@ -71553,18 +72622,18 @@ var Panel = class extends Subpanel {
|
|
|
71553
72622
|
edgePaddingTop: edgePaddingTopProp,
|
|
71554
72623
|
edgePaddingBottom: edgePaddingBottomProp
|
|
71555
72624
|
} = this._parsedProps;
|
|
71556
|
-
const edgePadding =
|
|
71557
|
-
const edgePaddingLeft =
|
|
71558
|
-
const edgePaddingRight =
|
|
72625
|
+
const edgePadding = distance23.parse(edgePaddingProp ?? 5);
|
|
72626
|
+
const edgePaddingLeft = distance23.parse(edgePaddingLeftProp ?? edgePadding);
|
|
72627
|
+
const edgePaddingRight = distance23.parse(
|
|
71559
72628
|
edgePaddingRightProp ?? edgePadding
|
|
71560
72629
|
);
|
|
71561
|
-
const edgePaddingTop =
|
|
71562
|
-
const edgePaddingBottom =
|
|
72630
|
+
const edgePaddingTop = distance23.parse(edgePaddingTopProp ?? edgePadding);
|
|
72631
|
+
const edgePaddingBottom = distance23.parse(
|
|
71563
72632
|
edgePaddingBottomProp ?? edgePadding
|
|
71564
72633
|
);
|
|
71565
72634
|
db.pcb_panel.update(this.pcb_panel_id, {
|
|
71566
|
-
width: hasExplicitWidth ?
|
|
71567
|
-
height: hasExplicitHeight ?
|
|
72635
|
+
width: hasExplicitWidth ? distance23.parse(this._parsedProps.width) : gridWidth + edgePaddingLeft + edgePaddingRight,
|
|
72636
|
+
height: hasExplicitHeight ? distance23.parse(this._parsedProps.height) : gridHeight + edgePaddingTop + edgePaddingBottom
|
|
71568
72637
|
});
|
|
71569
72638
|
}
|
|
71570
72639
|
}
|
|
@@ -71575,8 +72644,8 @@ var Panel = class extends Subpanel {
|
|
|
71575
72644
|
const props = this._parsedProps;
|
|
71576
72645
|
const currentPanel = db.pcb_panel.get(this.pcb_panel_id);
|
|
71577
72646
|
db.pcb_panel.update(this.pcb_panel_id, {
|
|
71578
|
-
width: props.width !== void 0 ?
|
|
71579
|
-
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,
|
|
71580
72649
|
center: this._getGlobalPcbPositionBeforeLayout(),
|
|
71581
72650
|
covered_with_solder_mask: !(props.noSolderMask ?? false)
|
|
71582
72651
|
});
|
|
@@ -73398,7 +74467,7 @@ var PcbNoteDimension = class extends PrimitiveComponent2 {
|
|
|
73398
74467
|
import { breakoutProps } from "@tscircuit/props";
|
|
73399
74468
|
|
|
73400
74469
|
// lib/components/primitive-components/AutoplacedBreakoutPoint.ts
|
|
73401
|
-
import { layer_ref as
|
|
74470
|
+
import { layer_ref as layer_ref4 } from "circuit-json";
|
|
73402
74471
|
var AutoplacedBreakoutPoint = class extends BaseBreakoutPoint {
|
|
73403
74472
|
get config() {
|
|
73404
74473
|
return {
|
|
@@ -73412,7 +74481,7 @@ var AutoplacedBreakoutPoint = class extends BaseBreakoutPoint {
|
|
|
73412
74481
|
position
|
|
73413
74482
|
}) {
|
|
73414
74483
|
this.matchedSourceTraceId = sourceTraceId;
|
|
73415
|
-
this._renderPcbBreakoutPoint({ layer:
|
|
74484
|
+
this._renderPcbBreakoutPoint({ layer: layer_ref4.parse(layer) });
|
|
73416
74485
|
this._setPositionFromLayout(position);
|
|
73417
74486
|
}
|
|
73418
74487
|
};
|
|
@@ -74124,7 +75193,7 @@ var validateWindingBreakoutInput = (input) => {
|
|
|
74124
75193
|
};
|
|
74125
75194
|
|
|
74126
75195
|
// node_modules/@tscircuit/winding-breakout-point-solver/lib/solvers/GatePlacementSolver.ts
|
|
74127
|
-
import { BaseSolver as
|
|
75196
|
+
import { BaseSolver as BaseSolver11 } from "@tscircuit/solver-utils";
|
|
74128
75197
|
|
|
74129
75198
|
// node_modules/@tscircuit/winding-breakout-point-solver/lib/gate-placement/place-breakout-gates.ts
|
|
74130
75199
|
var GEOMETRY_EPSILON = 1e-9;
|
|
@@ -74654,7 +75723,7 @@ var getSharedGateSlotCount = (points) => new Set(
|
|
|
74654
75723
|
(point6) => `${point6.regionId}:${point6.x.toFixed(9)}:${point6.y.toFixed(9)}`
|
|
74655
75724
|
)
|
|
74656
75725
|
).size;
|
|
74657
|
-
var GatePlacementSolver = class extends
|
|
75726
|
+
var GatePlacementSolver = class extends BaseSolver11 {
|
|
74658
75727
|
constructor(params) {
|
|
74659
75728
|
super();
|
|
74660
75729
|
this.params = params;
|
|
@@ -74793,7 +75862,7 @@ var GatePlacementSolver = class extends BaseSolver7 {
|
|
|
74793
75862
|
};
|
|
74794
75863
|
|
|
74795
75864
|
// node_modules/@tscircuit/winding-breakout-point-solver/lib/solvers/ReferenceOrderingSolver.ts
|
|
74796
|
-
import { BaseSolver as
|
|
75865
|
+
import { BaseSolver as BaseSolver12 } from "@tscircuit/solver-utils";
|
|
74797
75866
|
|
|
74798
75867
|
// node_modules/@tscircuit/winding-breakout-point-solver/lib/ordering/get-natural-winding-order.ts
|
|
74799
75868
|
var getNaturalWindingOrder = (region, connections) => connections.map((connection) => {
|
|
@@ -74860,7 +75929,7 @@ var getReferenceWindingOrder = (naturalOrder, atomicGroups, buses) => {
|
|
|
74860
75929
|
};
|
|
74861
75930
|
|
|
74862
75931
|
// node_modules/@tscircuit/winding-breakout-point-solver/lib/solvers/ReferenceOrderingSolver.ts
|
|
74863
|
-
var ReferenceOrderingSolver = class extends
|
|
75932
|
+
var ReferenceOrderingSolver = class extends BaseSolver12 {
|
|
74864
75933
|
constructor(params) {
|
|
74865
75934
|
super();
|
|
74866
75935
|
this.params = params;
|
|
@@ -75912,6 +76981,10 @@ var solveImplicitBreakoutPoints = (breakout, solverFn) => {
|
|
|
75912
76981
|
|
|
75913
76982
|
// lib/components/primitive-components/Breakout/Breakout.ts
|
|
75914
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
|
+
}
|
|
75915
76988
|
get isRoutingDirective() {
|
|
75916
76989
|
return true;
|
|
75917
76990
|
}
|
|
@@ -75970,7 +77043,7 @@ var Breakout = class extends Group5 {
|
|
|
75970
77043
|
for (const port of result.ports) {
|
|
75971
77044
|
const isInside = breakoutPortSet.has(port);
|
|
75972
77045
|
const hasOutsidePort = result.ports.some((p) => !breakoutPortSet.has(p));
|
|
75973
|
-
if (!isInside || !hasOutsidePort) continue;
|
|
77046
|
+
if (!isInside || !hasOutsidePort && !this.isDogboneFanout) continue;
|
|
75974
77047
|
if (manuallyMappedPorts.has(port)) continue;
|
|
75975
77048
|
if (autoPlacedPorts.has(port)) continue;
|
|
75976
77049
|
autoPlacedPorts.add(port);
|
|
@@ -75983,6 +77056,20 @@ var Breakout = class extends Group5 {
|
|
|
75983
77056
|
}
|
|
75984
77057
|
doInitialPcbAutoplaceBreakoutPoints() {
|
|
75985
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
|
+
}
|
|
75986
77073
|
const ownAutorouter = this.props.autorouter;
|
|
75987
77074
|
const inheritedAutorouter = ownAutorouter === void 0 ? this.parent?.getInheritedProperty("autorouter") : void 0;
|
|
75988
77075
|
const implicitBreakoutPointSolverFn = typeof ownAutorouter === "object" ? ownAutorouter.implicitBreakoutPointSolverFn : typeof inheritedAutorouter === "object" ? inheritedAutorouter.implicitBreakoutPointSolverFn : void 0;
|
|
@@ -76205,7 +77292,7 @@ var SilkscreenGraphic = class extends PrimitiveComponent2 {
|
|
|
76205
77292
|
|
|
76206
77293
|
// lib/components/primitive-components/Fiducial.ts
|
|
76207
77294
|
import "zod";
|
|
76208
|
-
import { distance as
|
|
77295
|
+
import { distance as distance24 } from "circuit-json";
|
|
76209
77296
|
import { fiducialProps as fiducialProps2 } from "@tscircuit/props";
|
|
76210
77297
|
var Fiducial = class extends PrimitiveComponent2 {
|
|
76211
77298
|
pcb_smtpad_id = null;
|
|
@@ -76230,15 +77317,15 @@ var Fiducial = class extends PrimitiveComponent2 {
|
|
|
76230
77317
|
shape: "circle",
|
|
76231
77318
|
x: position.x,
|
|
76232
77319
|
y: position.y,
|
|
76233
|
-
radius:
|
|
76234
|
-
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,
|
|
76235
77322
|
is_covered_with_solder_mask: true
|
|
76236
77323
|
});
|
|
76237
77324
|
this.pcb_smtpad_id = pcb_smtpad.pcb_smtpad_id;
|
|
76238
77325
|
}
|
|
76239
77326
|
getPcbSize() {
|
|
76240
77327
|
const { _parsedProps: props } = this;
|
|
76241
|
-
const d =
|
|
77328
|
+
const d = distance24.parse(props.padDiameter);
|
|
76242
77329
|
return { width: d, height: d };
|
|
76243
77330
|
}
|
|
76244
77331
|
_setPositionFromLayout(newCenter) {
|
|
@@ -76282,15 +77369,15 @@ import {
|
|
|
76282
77369
|
} from "@tscircuit/copper-pour-solver";
|
|
76283
77370
|
|
|
76284
77371
|
// lib/components/primitive-components/CopperPour/utils/mark-trace-segments-inside-copper-pour.ts
|
|
76285
|
-
var
|
|
77372
|
+
var EPSILON26 = 1e-9;
|
|
76286
77373
|
var isWireRoutePoint = (routePoint) => routePoint.route_type === "wire";
|
|
76287
77374
|
var isPointOnSegment2 = (p, a, b) => {
|
|
76288
|
-
const
|
|
76289
|
-
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;
|
|
76290
77377
|
const dot = (p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y);
|
|
76291
|
-
if (dot < -
|
|
77378
|
+
if (dot < -EPSILON26) return false;
|
|
76292
77379
|
const squaredLength = (b.x - a.x) ** 2 + (b.y - a.y) ** 2;
|
|
76293
|
-
if (dot - squaredLength >
|
|
77380
|
+
if (dot - squaredLength > EPSILON26) return false;
|
|
76294
77381
|
return true;
|
|
76295
77382
|
};
|
|
76296
77383
|
var isPointInRing2 = (point6, ring) => {
|
|
@@ -76314,7 +77401,7 @@ var isPointInRectPour = (p, pour) => {
|
|
|
76314
77401
|
const dy = p.y - center.y;
|
|
76315
77402
|
const localX = dx * cosR - dy * sinR;
|
|
76316
77403
|
const localY = dx * sinR + dy * cosR;
|
|
76317
|
-
return Math.abs(localX) <= width / 2 +
|
|
77404
|
+
return Math.abs(localX) <= width / 2 + EPSILON26 && Math.abs(localY) <= height / 2 + EPSILON26;
|
|
76318
77405
|
};
|
|
76319
77406
|
var isPointInBrepPour = (p, pour) => {
|
|
76320
77407
|
const outerRing = pour.brep_shape.outer_ring.vertices.map((v) => ({
|
|
@@ -76348,7 +77435,7 @@ var isSegmentFullyInsideCopperPour = (start, end, pour) => {
|
|
|
76348
77435
|
const dx = end.x - start.x;
|
|
76349
77436
|
const dy = end.y - start.y;
|
|
76350
77437
|
const length8 = Math.hypot(dx, dy);
|
|
76351
|
-
if (length8 <=
|
|
77438
|
+
if (length8 <= EPSILON26) return false;
|
|
76352
77439
|
const samples = [0, 0.25, 0.5, 0.75, 1];
|
|
76353
77440
|
return samples.every(
|
|
76354
77441
|
(t) => isPointInCopperPour(
|