@tscircuit/capacity-autorouter 0.0.49 → 0.0.50
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 +145 -39
- package/dist/index.js +550 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -965,7 +965,7 @@ var calculateOptimalCapacityDepth = (initialWidth, targetMinCapacity = 0.5, maxD
|
|
|
965
965
|
};
|
|
966
966
|
|
|
967
967
|
// lib/data-structures/ObstacleTree.ts
|
|
968
|
-
var
|
|
968
|
+
var ObstacleSpatialHashIndex = class {
|
|
969
969
|
constructor(obstacles) {
|
|
970
970
|
this.obstacles = obstacles;
|
|
971
971
|
this.buckets = /* @__PURE__ */ new Map();
|
|
@@ -1109,7 +1109,7 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1109
1109
|
];
|
|
1110
1110
|
this.finishedNodes = [];
|
|
1111
1111
|
this.nodeToXYOverlappingObstaclesMap = /* @__PURE__ */ new Map();
|
|
1112
|
-
this.obstacleTree = new
|
|
1112
|
+
this.obstacleTree = new ObstacleSpatialHashIndex(this.srj.obstacles);
|
|
1113
1113
|
this.targets = this.computeTargets();
|
|
1114
1114
|
this.targetTree = new TargetTree(this.targets);
|
|
1115
1115
|
}
|
|
@@ -8806,6 +8806,537 @@ var CapacityMeshEdgeSolver2_NodeTreeOptimization = class extends CapacityMeshEdg
|
|
|
8806
8806
|
}
|
|
8807
8807
|
};
|
|
8808
8808
|
|
|
8809
|
+
// lib/data-structures/HighDensityRouteSpatialIndex.ts
|
|
8810
|
+
var getSegmentBounds2 = (segment) => {
|
|
8811
|
+
return {
|
|
8812
|
+
minX: Math.min(segment[0].x, segment[1].x),
|
|
8813
|
+
maxX: Math.max(segment[0].x, segment[1].x),
|
|
8814
|
+
minY: Math.min(segment[0].y, segment[1].y),
|
|
8815
|
+
maxY: Math.max(segment[0].y, segment[1].y)
|
|
8816
|
+
};
|
|
8817
|
+
};
|
|
8818
|
+
function computeDistSq(p1, p2) {
|
|
8819
|
+
const dx = p1.x - p2.x;
|
|
8820
|
+
const dy = p1.y - p2.y;
|
|
8821
|
+
return dx * dx + dy * dy;
|
|
8822
|
+
}
|
|
8823
|
+
function pointToSegmentDistanceSq(p, a, b) {
|
|
8824
|
+
const l2 = computeDistSq(a, b);
|
|
8825
|
+
if (l2 === 0) return computeDistSq(p, a);
|
|
8826
|
+
let t = ((p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y)) / l2;
|
|
8827
|
+
t = Math.max(0, Math.min(1, t));
|
|
8828
|
+
const projection = {
|
|
8829
|
+
x: a.x + t * (b.x - a.x),
|
|
8830
|
+
y: a.y + t * (b.y - a.y)
|
|
8831
|
+
};
|
|
8832
|
+
return computeDistSq(p, projection);
|
|
8833
|
+
}
|
|
8834
|
+
function segmentToSegmentDistanceSq(a, b, c, d) {
|
|
8835
|
+
if (doSegmentsIntersect(a, b, c, d)) {
|
|
8836
|
+
return 0;
|
|
8837
|
+
}
|
|
8838
|
+
const pA = { x: a.x, y: a.y };
|
|
8839
|
+
const pB = { x: b.x, y: b.y };
|
|
8840
|
+
const pC = { x: c.x, y: c.y };
|
|
8841
|
+
const pD = { x: d.x, y: d.y };
|
|
8842
|
+
return Math.min(
|
|
8843
|
+
pointToSegmentDistanceSq(pA, pC, pD),
|
|
8844
|
+
pointToSegmentDistanceSq(pB, pC, pD),
|
|
8845
|
+
pointToSegmentDistanceSq(pC, pA, pB),
|
|
8846
|
+
pointToSegmentDistanceSq(pD, pA, pB)
|
|
8847
|
+
);
|
|
8848
|
+
}
|
|
8849
|
+
var HighDensityRouteSpatialIndex = class {
|
|
8850
|
+
segmentBuckets;
|
|
8851
|
+
viaBuckets;
|
|
8852
|
+
// New: Store vias
|
|
8853
|
+
routes;
|
|
8854
|
+
CELL_SIZE;
|
|
8855
|
+
constructor(routes, cellSize = 1) {
|
|
8856
|
+
this.segmentBuckets = /* @__PURE__ */ new Map();
|
|
8857
|
+
this.viaBuckets = /* @__PURE__ */ new Map();
|
|
8858
|
+
this.routes = /* @__PURE__ */ new Map();
|
|
8859
|
+
this.CELL_SIZE = cellSize;
|
|
8860
|
+
const epsilon = 1e-9;
|
|
8861
|
+
for (const route of routes) {
|
|
8862
|
+
if (!route || !route.connectionName) {
|
|
8863
|
+
console.warn("Skipping route with missing data:", route);
|
|
8864
|
+
continue;
|
|
8865
|
+
}
|
|
8866
|
+
if (this.routes.has(route.connectionName)) {
|
|
8867
|
+
console.warn(
|
|
8868
|
+
`Skipping duplicate route connectionName: ${route.connectionName}`
|
|
8869
|
+
);
|
|
8870
|
+
continue;
|
|
8871
|
+
}
|
|
8872
|
+
this.routes.set(route.connectionName, route);
|
|
8873
|
+
if (route.route && route.route.length >= 2) {
|
|
8874
|
+
for (let i = 0; i < route.route.length - 1; i++) {
|
|
8875
|
+
const p1 = route.route[i];
|
|
8876
|
+
const p2 = route.route[i + 1];
|
|
8877
|
+
if (p1.x === p2.x && p1.y === p2.y) continue;
|
|
8878
|
+
const segment = [p1, p2];
|
|
8879
|
+
const bounds = getSegmentBounds2(segment);
|
|
8880
|
+
const segmentInfo = {
|
|
8881
|
+
segmentId: `${route.connectionName}-seg-${i}`,
|
|
8882
|
+
segment,
|
|
8883
|
+
parentRoute: route
|
|
8884
|
+
};
|
|
8885
|
+
const minIndexX = Math.floor(bounds.minX / this.CELL_SIZE);
|
|
8886
|
+
const maxIndexX = Math.floor((bounds.maxX + epsilon) / this.CELL_SIZE);
|
|
8887
|
+
const minIndexY = Math.floor(bounds.minY / this.CELL_SIZE);
|
|
8888
|
+
const maxIndexY = Math.floor((bounds.maxY + epsilon) / this.CELL_SIZE);
|
|
8889
|
+
for (let ix = minIndexX; ix <= maxIndexX; ix++) {
|
|
8890
|
+
for (let iy = minIndexY; iy <= maxIndexY; iy++) {
|
|
8891
|
+
const bucketKey = `${ix}x${iy}`;
|
|
8892
|
+
let bucketList = this.segmentBuckets.get(bucketKey);
|
|
8893
|
+
if (!bucketList) {
|
|
8894
|
+
bucketList = [];
|
|
8895
|
+
this.segmentBuckets.set(bucketKey, bucketList);
|
|
8896
|
+
}
|
|
8897
|
+
bucketList.push(segmentInfo);
|
|
8898
|
+
}
|
|
8899
|
+
}
|
|
8900
|
+
}
|
|
8901
|
+
}
|
|
8902
|
+
if (route.vias && route.vias.length > 0) {
|
|
8903
|
+
for (let i = 0; i < route.vias.length; i++) {
|
|
8904
|
+
const via = route.vias[i];
|
|
8905
|
+
if (via === void 0 || via === null) continue;
|
|
8906
|
+
const storedVia = {
|
|
8907
|
+
viaId: `${route.connectionName}-via-${i}`,
|
|
8908
|
+
x: via.x,
|
|
8909
|
+
y: via.y,
|
|
8910
|
+
parentRoute: route
|
|
8911
|
+
};
|
|
8912
|
+
const ix = Math.floor(via.x / this.CELL_SIZE);
|
|
8913
|
+
const iy = Math.floor(via.y / this.CELL_SIZE);
|
|
8914
|
+
const bucketKey = `${ix}x${iy}`;
|
|
8915
|
+
let bucketList = this.viaBuckets.get(bucketKey);
|
|
8916
|
+
if (!bucketList) {
|
|
8917
|
+
bucketList = [];
|
|
8918
|
+
this.viaBuckets.set(bucketKey, bucketList);
|
|
8919
|
+
}
|
|
8920
|
+
bucketList.push(storedVia);
|
|
8921
|
+
}
|
|
8922
|
+
}
|
|
8923
|
+
}
|
|
8924
|
+
}
|
|
8925
|
+
/**
|
|
8926
|
+
* Finds routes that potentially conflict with a given line segment within a margin.
|
|
8927
|
+
* Checks both segments and vias.
|
|
8928
|
+
* @param segmentStart Start point of the query segment.
|
|
8929
|
+
* @param segmentEnd End point of the query segment.
|
|
8930
|
+
* @param margin The minimum required clearance distance from the query segment's centerline.
|
|
8931
|
+
* @returns An array of conflicting routes and their minimum distance to the segment.
|
|
8932
|
+
*/
|
|
8933
|
+
getConflictingRoutesForSegment(segmentStart, segmentEnd, margin) {
|
|
8934
|
+
const querySegment = [segmentStart, segmentEnd];
|
|
8935
|
+
const bounds = getSegmentBounds2(querySegment);
|
|
8936
|
+
const searchMinX = bounds.minX - margin;
|
|
8937
|
+
const searchMinY = bounds.minY - margin;
|
|
8938
|
+
const searchMaxX = bounds.maxX + margin;
|
|
8939
|
+
const searchMaxY = bounds.maxY + margin;
|
|
8940
|
+
const epsilon = 1e-9;
|
|
8941
|
+
const minIndexX = Math.floor(searchMinX / this.CELL_SIZE);
|
|
8942
|
+
const maxIndexX = Math.floor((searchMaxX + epsilon) / this.CELL_SIZE);
|
|
8943
|
+
const minIndexY = Math.floor(searchMinY / this.CELL_SIZE);
|
|
8944
|
+
const maxIndexY = Math.floor((searchMaxY + epsilon) / this.CELL_SIZE);
|
|
8945
|
+
const conflictingRouteData = /* @__PURE__ */ new Map();
|
|
8946
|
+
const checkedSegments = /* @__PURE__ */ new Set();
|
|
8947
|
+
const checkedVias = /* @__PURE__ */ new Set();
|
|
8948
|
+
const queryP1 = { x: segmentStart.x, y: segmentStart.y };
|
|
8949
|
+
const queryP2 = { x: segmentEnd.x, y: segmentEnd.y };
|
|
8950
|
+
for (let ix = minIndexX; ix <= maxIndexX; ix++) {
|
|
8951
|
+
for (let iy = minIndexY; iy <= maxIndexY; iy++) {
|
|
8952
|
+
const bucketKey = `${ix}x${iy}`;
|
|
8953
|
+
const segmentBucketList = this.segmentBuckets.get(bucketKey);
|
|
8954
|
+
if (segmentBucketList) {
|
|
8955
|
+
for (const segmentInfo of segmentBucketList) {
|
|
8956
|
+
if (checkedSegments.has(segmentInfo.segmentId)) continue;
|
|
8957
|
+
checkedSegments.add(segmentInfo.segmentId);
|
|
8958
|
+
const route = segmentInfo.parentRoute;
|
|
8959
|
+
const [p1, p2] = segmentInfo.segment;
|
|
8960
|
+
const requiredSeparation = margin + route.traceThickness / 2;
|
|
8961
|
+
const requiredSeparationSq = requiredSeparation * requiredSeparation;
|
|
8962
|
+
const distSq = segmentToSegmentDistanceSq(
|
|
8963
|
+
segmentStart,
|
|
8964
|
+
segmentEnd,
|
|
8965
|
+
p1,
|
|
8966
|
+
p2
|
|
8967
|
+
);
|
|
8968
|
+
if (distSq < requiredSeparationSq) {
|
|
8969
|
+
const routeName = route.connectionName;
|
|
8970
|
+
const existing = conflictingRouteData.get(routeName);
|
|
8971
|
+
if (!existing || distSq < existing.minDistSq) {
|
|
8972
|
+
conflictingRouteData.set(routeName, {
|
|
8973
|
+
route,
|
|
8974
|
+
minDistSq: distSq
|
|
8975
|
+
});
|
|
8976
|
+
}
|
|
8977
|
+
}
|
|
8978
|
+
}
|
|
8979
|
+
}
|
|
8980
|
+
const viaBucketList = this.viaBuckets.get(bucketKey);
|
|
8981
|
+
if (viaBucketList) {
|
|
8982
|
+
for (const viaInfo of viaBucketList) {
|
|
8983
|
+
if (checkedVias.has(viaInfo.viaId)) continue;
|
|
8984
|
+
checkedVias.add(viaInfo.viaId);
|
|
8985
|
+
const route = viaInfo.parentRoute;
|
|
8986
|
+
const viaPoint = { x: viaInfo.x, y: viaInfo.y };
|
|
8987
|
+
const requiredSeparation = margin + route.viaDiameter / 2;
|
|
8988
|
+
const requiredSeparationSq = requiredSeparation * requiredSeparation;
|
|
8989
|
+
const distSq = pointToSegmentDistanceSq(viaPoint, queryP1, queryP2);
|
|
8990
|
+
if (distSq < requiredSeparationSq) {
|
|
8991
|
+
const routeName = route.connectionName;
|
|
8992
|
+
const existing = conflictingRouteData.get(routeName);
|
|
8993
|
+
if (!existing || distSq < existing.minDistSq) {
|
|
8994
|
+
conflictingRouteData.set(routeName, {
|
|
8995
|
+
route,
|
|
8996
|
+
minDistSq: distSq
|
|
8997
|
+
});
|
|
8998
|
+
}
|
|
8999
|
+
}
|
|
9000
|
+
}
|
|
9001
|
+
}
|
|
9002
|
+
}
|
|
9003
|
+
}
|
|
9004
|
+
const results = [];
|
|
9005
|
+
for (const data of conflictingRouteData.values()) {
|
|
9006
|
+
results.push({
|
|
9007
|
+
conflictingRoute: data.route,
|
|
9008
|
+
distance: Math.sqrt(data.minDistSq)
|
|
9009
|
+
});
|
|
9010
|
+
}
|
|
9011
|
+
return results;
|
|
9012
|
+
}
|
|
9013
|
+
/**
|
|
9014
|
+
* Finds routes that pass near a given point within a margin.
|
|
9015
|
+
* Checks both segments and vias.
|
|
9016
|
+
* @param point The query point {x, y}. Z is ignored.
|
|
9017
|
+
* @param margin The minimum required clearance distance from the query point.
|
|
9018
|
+
* @returns An array of conflicting routes and their minimum distance to the point.
|
|
9019
|
+
*/
|
|
9020
|
+
getConflictingRoutesNearPoint(point, margin) {
|
|
9021
|
+
const searchMinX = point.x - margin;
|
|
9022
|
+
const searchMinY = point.y - margin;
|
|
9023
|
+
const searchMaxX = point.x + margin;
|
|
9024
|
+
const searchMaxY = point.y + margin;
|
|
9025
|
+
const epsilon = 1e-9;
|
|
9026
|
+
const minIndexX = Math.floor(searchMinX / this.CELL_SIZE);
|
|
9027
|
+
const maxIndexX = Math.floor((searchMaxX + epsilon) / this.CELL_SIZE);
|
|
9028
|
+
const minIndexY = Math.floor(searchMinY / this.CELL_SIZE);
|
|
9029
|
+
const maxIndexY = Math.floor((searchMaxY + epsilon) / this.CELL_SIZE);
|
|
9030
|
+
const conflictingRouteData = /* @__PURE__ */ new Map();
|
|
9031
|
+
const checkedSegments = /* @__PURE__ */ new Set();
|
|
9032
|
+
const checkedVias = /* @__PURE__ */ new Set();
|
|
9033
|
+
for (let ix = minIndexX; ix <= maxIndexX; ix++) {
|
|
9034
|
+
for (let iy = minIndexY; iy <= maxIndexY; iy++) {
|
|
9035
|
+
const bucketKey = `${ix}x${iy}`;
|
|
9036
|
+
const segmentBucketList = this.segmentBuckets.get(bucketKey);
|
|
9037
|
+
if (segmentBucketList) {
|
|
9038
|
+
for (const segmentInfo of segmentBucketList) {
|
|
9039
|
+
if (checkedSegments.has(segmentInfo.segmentId)) continue;
|
|
9040
|
+
checkedSegments.add(segmentInfo.segmentId);
|
|
9041
|
+
const route = segmentInfo.parentRoute;
|
|
9042
|
+
const p1 = {
|
|
9043
|
+
x: segmentInfo.segment[0].x,
|
|
9044
|
+
y: segmentInfo.segment[0].y
|
|
9045
|
+
};
|
|
9046
|
+
const p2 = {
|
|
9047
|
+
x: segmentInfo.segment[1].x,
|
|
9048
|
+
y: segmentInfo.segment[1].y
|
|
9049
|
+
};
|
|
9050
|
+
const requiredSeparation = margin + route.traceThickness / 2;
|
|
9051
|
+
const requiredSeparationSq = requiredSeparation * requiredSeparation;
|
|
9052
|
+
const distSq = pointToSegmentDistanceSq(point, p1, p2);
|
|
9053
|
+
if (distSq < requiredSeparationSq) {
|
|
9054
|
+
const routeName = route.connectionName;
|
|
9055
|
+
const existing = conflictingRouteData.get(routeName);
|
|
9056
|
+
if (!existing || distSq < existing.minDistSq) {
|
|
9057
|
+
conflictingRouteData.set(routeName, {
|
|
9058
|
+
route,
|
|
9059
|
+
minDistSq: distSq
|
|
9060
|
+
});
|
|
9061
|
+
}
|
|
9062
|
+
}
|
|
9063
|
+
}
|
|
9064
|
+
}
|
|
9065
|
+
const viaBucketList = this.viaBuckets.get(bucketKey);
|
|
9066
|
+
if (viaBucketList) {
|
|
9067
|
+
for (const viaInfo of viaBucketList) {
|
|
9068
|
+
if (checkedVias.has(viaInfo.viaId)) continue;
|
|
9069
|
+
checkedVias.add(viaInfo.viaId);
|
|
9070
|
+
const route = viaInfo.parentRoute;
|
|
9071
|
+
const viaPoint = { x: viaInfo.x, y: viaInfo.y };
|
|
9072
|
+
const requiredSeparation = margin + route.viaDiameter / 2;
|
|
9073
|
+
const requiredSeparationSq = requiredSeparation * requiredSeparation;
|
|
9074
|
+
const distSq = computeDistSq(point, viaPoint);
|
|
9075
|
+
if (distSq < requiredSeparationSq) {
|
|
9076
|
+
const routeName = route.connectionName;
|
|
9077
|
+
const existing = conflictingRouteData.get(routeName);
|
|
9078
|
+
if (!existing || distSq < existing.minDistSq) {
|
|
9079
|
+
conflictingRouteData.set(routeName, {
|
|
9080
|
+
route,
|
|
9081
|
+
minDistSq: distSq
|
|
9082
|
+
});
|
|
9083
|
+
}
|
|
9084
|
+
}
|
|
9085
|
+
}
|
|
9086
|
+
}
|
|
9087
|
+
}
|
|
9088
|
+
}
|
|
9089
|
+
const results = [];
|
|
9090
|
+
for (const data of conflictingRouteData.values()) {
|
|
9091
|
+
results.push({
|
|
9092
|
+
conflictingRoute: data.route,
|
|
9093
|
+
distance: Math.sqrt(data.minDistSq)
|
|
9094
|
+
});
|
|
9095
|
+
}
|
|
9096
|
+
return results;
|
|
9097
|
+
}
|
|
9098
|
+
};
|
|
9099
|
+
|
|
9100
|
+
// lib/solvers/UselessViaRemovalSolver/SingleRouteUselessViaRemovalSolver.ts
|
|
9101
|
+
var SingleRouteUselessViaRemovalSolver = class extends BaseSolver {
|
|
9102
|
+
obstacleSHI;
|
|
9103
|
+
hdRouteSHI;
|
|
9104
|
+
unsimplifiedRoute;
|
|
9105
|
+
routeSections;
|
|
9106
|
+
currentSectionIndex;
|
|
9107
|
+
TRACE_THICKNESS = 0.15;
|
|
9108
|
+
OBSTACLE_MARGIN = 0.1;
|
|
9109
|
+
constructor(params) {
|
|
9110
|
+
super();
|
|
9111
|
+
this.currentSectionIndex = 1;
|
|
9112
|
+
this.obstacleSHI = params.obstacleSHI;
|
|
9113
|
+
this.hdRouteSHI = params.hdRouteSHI;
|
|
9114
|
+
this.unsimplifiedRoute = params.unsimplifiedRoute;
|
|
9115
|
+
this.routeSections = this.breakRouteIntoSections(this.unsimplifiedRoute);
|
|
9116
|
+
}
|
|
9117
|
+
breakRouteIntoSections(route) {
|
|
9118
|
+
const routeSections = [];
|
|
9119
|
+
const routePoints = route.route;
|
|
9120
|
+
if (routePoints.length === 0) return [];
|
|
9121
|
+
let currentSection = {
|
|
9122
|
+
startIndex: 0,
|
|
9123
|
+
endIndex: -1,
|
|
9124
|
+
z: routePoints[0].z,
|
|
9125
|
+
points: [routePoints[0]]
|
|
9126
|
+
};
|
|
9127
|
+
for (let i = 1; i < routePoints.length; i++) {
|
|
9128
|
+
if (routePoints[i].z === currentSection.z) {
|
|
9129
|
+
currentSection.points.push(routePoints[i]);
|
|
9130
|
+
} else {
|
|
9131
|
+
currentSection.endIndex = i - 1;
|
|
9132
|
+
routeSections.push(currentSection);
|
|
9133
|
+
currentSection = {
|
|
9134
|
+
startIndex: i,
|
|
9135
|
+
endIndex: -1,
|
|
9136
|
+
z: routePoints[i].z,
|
|
9137
|
+
points: [routePoints[i]]
|
|
9138
|
+
};
|
|
9139
|
+
}
|
|
9140
|
+
}
|
|
9141
|
+
currentSection.endIndex = routePoints.length - 1;
|
|
9142
|
+
routeSections.push(currentSection);
|
|
9143
|
+
return routeSections;
|
|
9144
|
+
}
|
|
9145
|
+
_step() {
|
|
9146
|
+
if (this.currentSectionIndex >= this.routeSections.length - 1) {
|
|
9147
|
+
this.solved = true;
|
|
9148
|
+
return;
|
|
9149
|
+
}
|
|
9150
|
+
const prevSection = this.routeSections[this.currentSectionIndex - 1];
|
|
9151
|
+
const currentSection = this.routeSections[this.currentSectionIndex];
|
|
9152
|
+
const nextSection = this.routeSections[this.currentSectionIndex + 1];
|
|
9153
|
+
if (prevSection.z !== nextSection.z) {
|
|
9154
|
+
this.currentSectionIndex++;
|
|
9155
|
+
return;
|
|
9156
|
+
}
|
|
9157
|
+
const targetZ = prevSection.z;
|
|
9158
|
+
if (this.canSectionMoveToLayer({ currentSection, targetZ })) {
|
|
9159
|
+
currentSection.z = targetZ;
|
|
9160
|
+
currentSection.points = currentSection.points.map((p) => ({
|
|
9161
|
+
...p,
|
|
9162
|
+
z: targetZ
|
|
9163
|
+
}));
|
|
9164
|
+
this.currentSectionIndex += 2;
|
|
9165
|
+
return;
|
|
9166
|
+
}
|
|
9167
|
+
this.currentSectionIndex++;
|
|
9168
|
+
return;
|
|
9169
|
+
}
|
|
9170
|
+
canSectionMoveToLayer({
|
|
9171
|
+
currentSection,
|
|
9172
|
+
targetZ
|
|
9173
|
+
}) {
|
|
9174
|
+
for (let i = 0; i < currentSection.points.length - 1; i++) {
|
|
9175
|
+
const A = { ...currentSection.points[i], z: targetZ };
|
|
9176
|
+
const B = { ...currentSection.points[i + 1], z: targetZ };
|
|
9177
|
+
const conflictingRoutes = this.hdRouteSHI.getConflictingRoutesForSegment(
|
|
9178
|
+
A,
|
|
9179
|
+
B,
|
|
9180
|
+
this.TRACE_THICKNESS
|
|
9181
|
+
);
|
|
9182
|
+
for (const { conflictingRoute, distance: distance6 } of conflictingRoutes) {
|
|
9183
|
+
if (conflictingRoute.connectionName === this.unsimplifiedRoute.connectionName)
|
|
9184
|
+
continue;
|
|
9185
|
+
if (distance6 < this.TRACE_THICKNESS + conflictingRoute.traceThickness) {
|
|
9186
|
+
return false;
|
|
9187
|
+
}
|
|
9188
|
+
}
|
|
9189
|
+
const segmentBox = {
|
|
9190
|
+
centerX: (A.x + B.x) / 2,
|
|
9191
|
+
centerY: (A.y + B.y) / 2,
|
|
9192
|
+
width: Math.abs(A.x - B.x),
|
|
9193
|
+
height: Math.abs(A.y - B.y)
|
|
9194
|
+
};
|
|
9195
|
+
const obstacles = this.obstacleSHI.getNodesInArea(
|
|
9196
|
+
segmentBox.centerX,
|
|
9197
|
+
segmentBox.centerY,
|
|
9198
|
+
segmentBox.width,
|
|
9199
|
+
segmentBox.height
|
|
9200
|
+
);
|
|
9201
|
+
for (const obstacle of obstacles) {
|
|
9202
|
+
const distToObstacle = segmentToBoxMinDistance(A, B, obstacle);
|
|
9203
|
+
if (distToObstacle < this.TRACE_THICKNESS + this.OBSTACLE_MARGIN) {
|
|
9204
|
+
return false;
|
|
9205
|
+
}
|
|
9206
|
+
}
|
|
9207
|
+
}
|
|
9208
|
+
return true;
|
|
9209
|
+
}
|
|
9210
|
+
getOptimizedHdRoute() {
|
|
9211
|
+
const route = this.routeSections.flatMap((section) => section.points);
|
|
9212
|
+
const vias = [];
|
|
9213
|
+
for (let i = 0; i < route.length - 1; i++) {
|
|
9214
|
+
if (route[i].z !== route[i + 1].z) {
|
|
9215
|
+
vias.push({
|
|
9216
|
+
x: route[i].x,
|
|
9217
|
+
y: route[i].y
|
|
9218
|
+
});
|
|
9219
|
+
}
|
|
9220
|
+
}
|
|
9221
|
+
return {
|
|
9222
|
+
connectionName: this.unsimplifiedRoute.connectionName,
|
|
9223
|
+
route,
|
|
9224
|
+
traceThickness: this.unsimplifiedRoute.traceThickness,
|
|
9225
|
+
vias,
|
|
9226
|
+
viaDiameter: this.unsimplifiedRoute.viaDiameter
|
|
9227
|
+
};
|
|
9228
|
+
}
|
|
9229
|
+
visualize() {
|
|
9230
|
+
const graphics = {
|
|
9231
|
+
circles: [],
|
|
9232
|
+
lines: [],
|
|
9233
|
+
points: [],
|
|
9234
|
+
rects: [],
|
|
9235
|
+
coordinateSystem: "cartesian",
|
|
9236
|
+
title: "Single Route Useless Via Removal Solver"
|
|
9237
|
+
};
|
|
9238
|
+
for (let i = 0; i < this.routeSections.length; i++) {
|
|
9239
|
+
const section = this.routeSections[i];
|
|
9240
|
+
graphics.lines.push({
|
|
9241
|
+
points: section.points,
|
|
9242
|
+
strokeWidth: this.TRACE_THICKNESS,
|
|
9243
|
+
strokeColor: i === this.currentSectionIndex ? "orange" : section.z === 0 ? "red" : "blue"
|
|
9244
|
+
});
|
|
9245
|
+
}
|
|
9246
|
+
return graphics;
|
|
9247
|
+
}
|
|
9248
|
+
};
|
|
9249
|
+
|
|
9250
|
+
// lib/solvers/UselessViaRemovalSolver/UselessViaRemovalSolver.ts
|
|
9251
|
+
var UselessViaRemovalSolver = class extends BaseSolver {
|
|
9252
|
+
constructor(input) {
|
|
9253
|
+
super();
|
|
9254
|
+
this.input = input;
|
|
9255
|
+
this.unsimplifiedHdRoutes = input.unsimplifiedHdRoutes;
|
|
9256
|
+
this.optimizedHdRoutes = [];
|
|
9257
|
+
this.unprocessedRoutes = [...input.unsimplifiedHdRoutes];
|
|
9258
|
+
this.obstacleSHI = new ObstacleSpatialHashIndex(input.obstacles);
|
|
9259
|
+
this.hdRouteSHI = new HighDensityRouteSpatialIndex(
|
|
9260
|
+
this.unsimplifiedHdRoutes
|
|
9261
|
+
);
|
|
9262
|
+
}
|
|
9263
|
+
unsimplifiedHdRoutes;
|
|
9264
|
+
optimizedHdRoutes;
|
|
9265
|
+
unprocessedRoutes;
|
|
9266
|
+
activeSubSolver = null;
|
|
9267
|
+
obstacleSHI = null;
|
|
9268
|
+
hdRouteSHI = null;
|
|
9269
|
+
_step() {
|
|
9270
|
+
if (this.activeSubSolver) {
|
|
9271
|
+
this.activeSubSolver.step();
|
|
9272
|
+
if (this.activeSubSolver.solved) {
|
|
9273
|
+
this.optimizedHdRoutes.push(this.activeSubSolver.getOptimizedHdRoute());
|
|
9274
|
+
this.activeSubSolver = null;
|
|
9275
|
+
} else if (this.activeSubSolver.failed || this.activeSubSolver.error) {
|
|
9276
|
+
this.error = this.activeSubSolver.error;
|
|
9277
|
+
this.failed = true;
|
|
9278
|
+
}
|
|
9279
|
+
return;
|
|
9280
|
+
}
|
|
9281
|
+
const unprocessedRoute = this.unprocessedRoutes.shift();
|
|
9282
|
+
if (!unprocessedRoute) {
|
|
9283
|
+
this.solved = true;
|
|
9284
|
+
return;
|
|
9285
|
+
}
|
|
9286
|
+
this.activeSubSolver = new SingleRouteUselessViaRemovalSolver({
|
|
9287
|
+
hdRouteSHI: this.hdRouteSHI,
|
|
9288
|
+
obstacleSHI: this.obstacleSHI,
|
|
9289
|
+
unsimplifiedRoute: unprocessedRoute
|
|
9290
|
+
});
|
|
9291
|
+
}
|
|
9292
|
+
getOptimizedHdRoutes() {
|
|
9293
|
+
return this.optimizedHdRoutes;
|
|
9294
|
+
}
|
|
9295
|
+
visualize() {
|
|
9296
|
+
const visualization = {
|
|
9297
|
+
lines: [],
|
|
9298
|
+
points: [],
|
|
9299
|
+
rects: [],
|
|
9300
|
+
circles: [],
|
|
9301
|
+
coordinateSystem: "cartesian",
|
|
9302
|
+
title: "Useless Via Removal Solver"
|
|
9303
|
+
};
|
|
9304
|
+
for (const route of this.optimizedHdRoutes) {
|
|
9305
|
+
if (route.route.length === 0) continue;
|
|
9306
|
+
const color = this.input.colorMap[route.connectionName] || "#888888";
|
|
9307
|
+
for (let i = 0; i < route.route.length - 1; i++) {
|
|
9308
|
+
const current = route.route[i];
|
|
9309
|
+
const next = route.route[i + 1];
|
|
9310
|
+
if (current.z === next.z) {
|
|
9311
|
+
visualization.lines.push({
|
|
9312
|
+
points: [
|
|
9313
|
+
{ x: current.x, y: current.y },
|
|
9314
|
+
{ x: next.x, y: next.y }
|
|
9315
|
+
],
|
|
9316
|
+
strokeColor: current.z === 0 ? "red" : "blue",
|
|
9317
|
+
strokeWidth: route.traceThickness,
|
|
9318
|
+
label: `${route.connectionName} (z=${current.z})`
|
|
9319
|
+
});
|
|
9320
|
+
}
|
|
9321
|
+
}
|
|
9322
|
+
for (const via of route.vias) {
|
|
9323
|
+
visualization.circles.push({
|
|
9324
|
+
center: { x: via.x, y: via.y },
|
|
9325
|
+
radius: route.viaDiameter / 2,
|
|
9326
|
+
fill: "rgba(255, 0, 255, 0.5)",
|
|
9327
|
+
label: `${route.connectionName} via`
|
|
9328
|
+
});
|
|
9329
|
+
}
|
|
9330
|
+
}
|
|
9331
|
+
if (this.activeSubSolver) {
|
|
9332
|
+
visualization.lines.push(
|
|
9333
|
+
...this.activeSubSolver.visualize().lines ?? []
|
|
9334
|
+
);
|
|
9335
|
+
}
|
|
9336
|
+
return visualization;
|
|
9337
|
+
}
|
|
9338
|
+
};
|
|
9339
|
+
|
|
8809
9340
|
// lib/solvers/AutoroutingPipelineSolver.ts
|
|
8810
9341
|
function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
|
|
8811
9342
|
return {
|
|
@@ -8851,6 +9382,7 @@ var AutoroutingPipelineSolver = class extends BaseSolver {
|
|
|
8851
9382
|
highDensityStitchSolver;
|
|
8852
9383
|
singleLayerNodeMerger;
|
|
8853
9384
|
strawSolver;
|
|
9385
|
+
uselessViaRemovalSolver;
|
|
8854
9386
|
multiSimplifiedPathSolver;
|
|
8855
9387
|
startTimeOfPhase;
|
|
8856
9388
|
endTimeOfPhase;
|
|
@@ -9006,12 +9538,24 @@ var AutoroutingPipelineSolver = class extends BaseSolver {
|
|
|
9006
9538
|
}
|
|
9007
9539
|
]
|
|
9008
9540
|
),
|
|
9541
|
+
definePipelineStep(
|
|
9542
|
+
"uselessViaRemovalSolver",
|
|
9543
|
+
UselessViaRemovalSolver,
|
|
9544
|
+
(cms) => [
|
|
9545
|
+
{
|
|
9546
|
+
unsimplifiedHdRoutes: cms.highDensityStitchSolver.mergedHdRoutes,
|
|
9547
|
+
obstacles: cms.srj.obstacles,
|
|
9548
|
+
colorMap: cms.colorMap,
|
|
9549
|
+
layerCount: cms.srj.layerCount
|
|
9550
|
+
}
|
|
9551
|
+
]
|
|
9552
|
+
),
|
|
9009
9553
|
definePipelineStep(
|
|
9010
9554
|
"multiSimplifiedPathSolver",
|
|
9011
9555
|
MultiSimplifiedPathSolver,
|
|
9012
9556
|
(cms) => [
|
|
9013
9557
|
{
|
|
9014
|
-
unsimplifiedHdRoutes: cms.highDensityStitchSolver.mergedHdRoutes,
|
|
9558
|
+
unsimplifiedHdRoutes: cms.uselessViaRemovalSolver?.getOptimizedHdRoutes() || cms.highDensityStitchSolver.mergedHdRoutes,
|
|
9015
9559
|
obstacles: cms.srj.obstacles,
|
|
9016
9560
|
connMap: cms.connMap,
|
|
9017
9561
|
colorMap: cms.colorMap
|
|
@@ -9070,6 +9614,7 @@ var AutoroutingPipelineSolver = class extends BaseSolver {
|
|
|
9070
9614
|
const segmentOptimizationViz = this.unravelMultiSectionSolver?.visualize() ?? this.segmentToPointOptimizer?.visualize();
|
|
9071
9615
|
const highDensityViz = this.highDensityRouteSolver?.visualize();
|
|
9072
9616
|
const highDensityStitchViz = this.highDensityStitchSolver?.visualize();
|
|
9617
|
+
const uselessViaRemovalViz = this.uselessViaRemovalSolver?.visualize();
|
|
9073
9618
|
const simplifiedPathSolverViz = this.multiSimplifiedPathSolver?.visualize();
|
|
9074
9619
|
const problemViz = {
|
|
9075
9620
|
points: [
|
|
@@ -9122,6 +9667,7 @@ var AutoroutingPipelineSolver = class extends BaseSolver {
|
|
|
9122
9667
|
segmentOptimizationViz,
|
|
9123
9668
|
highDensityViz ? combineVisualizations(problemViz, highDensityViz) : null,
|
|
9124
9669
|
highDensityStitchViz,
|
|
9670
|
+
uselessViaRemovalViz,
|
|
9125
9671
|
simplifiedPathSolverViz,
|
|
9126
9672
|
this.solved ? combineVisualizations(
|
|
9127
9673
|
problemViz,
|
|
@@ -9184,7 +9730,7 @@ var AutoroutingPipelineSolver = class extends BaseSolver {
|
|
|
9184
9730
|
return match ? match[1] : mstConnectionName;
|
|
9185
9731
|
}
|
|
9186
9732
|
_getOutputHdRoutes() {
|
|
9187
|
-
return this.multiSimplifiedPathSolver?.simplifiedHdRoutes ?? this.highDensityStitchSolver.mergedHdRoutes;
|
|
9733
|
+
return this.multiSimplifiedPathSolver?.simplifiedHdRoutes ?? this.uselessViaRemovalSolver?.getOptimizedHdRoutes() ?? this.highDensityStitchSolver.mergedHdRoutes;
|
|
9188
9734
|
}
|
|
9189
9735
|
/**
|
|
9190
9736
|
* Returns the SimpleRouteJson with routes converted to SimplifiedPcbTraces
|