@tscircuit/capacity-autorouter 0.0.20 → 0.0.21
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 +8 -3
- package/dist/index.js +93 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ interface SimpleRouteJson {
|
|
|
18
18
|
interface Obstacle {
|
|
19
19
|
type: "rect";
|
|
20
20
|
layers: string[];
|
|
21
|
+
zLayers?: number[];
|
|
21
22
|
center: {
|
|
22
23
|
x: number;
|
|
23
24
|
y: number;
|
|
@@ -72,6 +73,7 @@ interface CapacityMeshNode {
|
|
|
72
73
|
_containsObstacle?: boolean;
|
|
73
74
|
_containsTarget?: boolean;
|
|
74
75
|
_targetConnectionName?: string;
|
|
76
|
+
_shouldBeInGraph?: boolean;
|
|
75
77
|
_parent?: CapacityMeshNode;
|
|
76
78
|
}
|
|
77
79
|
interface CapacityMeshEdge {
|
|
@@ -110,6 +112,7 @@ declare class CapacityMeshEdgeSolver extends BaseSolver {
|
|
|
110
112
|
getNextCapacityMeshEdgeId(): string;
|
|
111
113
|
step(): void;
|
|
112
114
|
private areNodesBordering;
|
|
115
|
+
private doNodesHaveSharedLayer;
|
|
113
116
|
visualize(): GraphicsObject;
|
|
114
117
|
}
|
|
115
118
|
|
|
@@ -127,7 +130,8 @@ declare class CapacityMeshNodeSolver extends BaseSolver {
|
|
|
127
130
|
opts: CapacityMeshNodeSolverOptions;
|
|
128
131
|
unfinishedNodes: CapacityMeshNode[];
|
|
129
132
|
finishedNodes: CapacityMeshNode[];
|
|
130
|
-
|
|
133
|
+
nodeToXYOverlappingObstaclesMap: Map<CapacityMeshNodeId, Obstacle[]>;
|
|
134
|
+
layerCount: number;
|
|
131
135
|
MAX_DEPTH: number;
|
|
132
136
|
targets: Target[];
|
|
133
137
|
constructor(srj: SimpleRouteJson, opts?: CapacityMeshNodeSolverOptions);
|
|
@@ -135,7 +139,8 @@ declare class CapacityMeshNodeSolver extends BaseSolver {
|
|
|
135
139
|
getNextNodeId(): string;
|
|
136
140
|
getCapacityFromDepth(depth: number): number;
|
|
137
141
|
getTargetIfNodeContainsTarget(node: CapacityMeshNode): Target | null;
|
|
138
|
-
|
|
142
|
+
getXYOverlappingObstacles(node: CapacityMeshNode): Obstacle[];
|
|
143
|
+
getXYZOverlappingObstacles(node: CapacityMeshNode): Obstacle[];
|
|
139
144
|
/**
|
|
140
145
|
* Checks if the given mesh node overlaps with any obstacle.
|
|
141
146
|
* We treat both obstacles and nodes as axis‐aligned rectangles.
|
|
@@ -146,7 +151,7 @@ declare class CapacityMeshNodeSolver extends BaseSolver {
|
|
|
146
151
|
*/
|
|
147
152
|
isNodeCompletelyInsideObstacle(node: CapacityMeshNode): boolean;
|
|
148
153
|
getChildNodes(parent: CapacityMeshNode): CapacityMeshNode[];
|
|
149
|
-
|
|
154
|
+
shouldNodeBeXYSubdivided(node: CapacityMeshNode): boolean;
|
|
150
155
|
_step(): void;
|
|
151
156
|
/**
|
|
152
157
|
* Creates a GraphicsObject to visualize the mesh, its nodes, obstacles, and connection points.
|
package/dist/index.js
CHANGED
|
@@ -140,7 +140,7 @@ var CapacityMeshEdgeSolver = class extends BaseSolver {
|
|
|
140
140
|
this.edges = [];
|
|
141
141
|
for (let i = 0; i < this.nodes.length; i++) {
|
|
142
142
|
for (let j = i + 1; j < this.nodes.length; j++) {
|
|
143
|
-
if (this.areNodesBordering(this.nodes[i], this.nodes[j])) {
|
|
143
|
+
if (this.areNodesBordering(this.nodes[i], this.nodes[j]) && this.doNodesHaveSharedLayer(this.nodes[i], this.nodes[j])) {
|
|
144
144
|
this.edges.push({
|
|
145
145
|
capacityMeshEdgeId: this.getNextCapacityMeshEdgeId(),
|
|
146
146
|
nodeIds: [
|
|
@@ -194,16 +194,35 @@ var CapacityMeshEdgeSolver = class extends BaseSolver {
|
|
|
194
194
|
const shareHorizontalBorder = (Math.abs(n1Bottom - n2Top) < epsilon || Math.abs(n1Top - n2Bottom) < epsilon) && Math.min(n1Right, n2Right) - Math.max(n1Left, n2Left) >= epsilon;
|
|
195
195
|
return shareVerticalBorder || shareHorizontalBorder;
|
|
196
196
|
}
|
|
197
|
+
doNodesHaveSharedLayer(node1, node2) {
|
|
198
|
+
return node1.availableZ.some((z) => node2.availableZ.includes(z));
|
|
199
|
+
}
|
|
197
200
|
visualize() {
|
|
198
201
|
const graphics = {
|
|
199
202
|
lines: [],
|
|
200
203
|
points: [],
|
|
201
|
-
rects: this.nodes.map((node) =>
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
rects: this.nodes.map((node) => {
|
|
205
|
+
const lowestZ = Math.min(...node.availableZ);
|
|
206
|
+
return {
|
|
207
|
+
width: Math.max(node.width - 2, node.width * 0.8),
|
|
208
|
+
height: Math.max(node.height - 2, node.height * 0.8),
|
|
209
|
+
center: {
|
|
210
|
+
x: node.center.x + lowestZ * node.width * 0.05,
|
|
211
|
+
y: node.center.y - lowestZ * node.width * 0.05
|
|
212
|
+
},
|
|
213
|
+
fill: node._containsObstacle ? "rgba(255,0,0,0.1)" : {
|
|
214
|
+
"0,1": "rgba(0,0,0,0.1)",
|
|
215
|
+
"0": "rgba(0,200,200, 0.1)",
|
|
216
|
+
"1": "rgba(0,0,200, 0.1)"
|
|
217
|
+
}[node.availableZ.join(",")] ?? "rgba(0,200,200,0.1)",
|
|
218
|
+
label: [
|
|
219
|
+
node.capacityMeshNodeId,
|
|
220
|
+
`availableZ: ${node.availableZ.join(",")}`,
|
|
221
|
+
`target? ${node._containsTarget ?? false}`,
|
|
222
|
+
`obs? ${node._containsObstacle ?? false}`
|
|
223
|
+
].join("\n")
|
|
224
|
+
};
|
|
225
|
+
}),
|
|
207
226
|
circles: []
|
|
208
227
|
};
|
|
209
228
|
for (const edge of this.edges) {
|
|
@@ -1074,7 +1093,14 @@ function doRectsOverlap(rect1, rect2) {
|
|
|
1074
1093
|
return rect1Left <= rect2Right && rect1Right >= rect2Left && rect1Top <= rect2Bottom && rect1Bottom >= rect2Top;
|
|
1075
1094
|
}
|
|
1076
1095
|
|
|
1077
|
-
// lib/
|
|
1096
|
+
// lib/utils/mapLayerNameToZ.ts
|
|
1097
|
+
var mapLayerNameToZ = (layerName, layerCount) => {
|
|
1098
|
+
if (layerName === "top") return 0;
|
|
1099
|
+
if (layerName === "bottom") return layerCount - 1;
|
|
1100
|
+
return parseInt(layerName.slice(5));
|
|
1101
|
+
};
|
|
1102
|
+
|
|
1103
|
+
// lib/solvers/CapacityMeshSolver/CapacityMeshNodeSolver1.ts
|
|
1078
1104
|
var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
1079
1105
|
constructor(srj, opts = {}) {
|
|
1080
1106
|
super();
|
|
@@ -1082,6 +1108,16 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1082
1108
|
this.opts = opts;
|
|
1083
1109
|
this.MAX_DEPTH = opts?.capacityDepth ?? this.MAX_DEPTH;
|
|
1084
1110
|
this.MAX_ITERATIONS = 1e5;
|
|
1111
|
+
this.layerCount = srj.layerCount ?? 2;
|
|
1112
|
+
for (const obstacle of srj.obstacles) {
|
|
1113
|
+
if (!obstacle.zLayers) {
|
|
1114
|
+
const zLayers = [];
|
|
1115
|
+
for (const layer of obstacle.layers) {
|
|
1116
|
+
zLayers.push(mapLayerNameToZ(layer, srj.layerCount));
|
|
1117
|
+
}
|
|
1118
|
+
obstacle.zLayers = zLayers;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1085
1121
|
const boundsCenter = {
|
|
1086
1122
|
x: (srj.bounds.minX + srj.bounds.maxX) / 2,
|
|
1087
1123
|
y: (srj.bounds.minY + srj.bounds.maxY) / 2
|
|
@@ -1106,7 +1142,7 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1106
1142
|
}
|
|
1107
1143
|
];
|
|
1108
1144
|
this.finishedNodes = [];
|
|
1109
|
-
this.
|
|
1145
|
+
this.nodeToXYOverlappingObstaclesMap = /* @__PURE__ */ new Map();
|
|
1110
1146
|
this.targets = this.srj.connections.flatMap(
|
|
1111
1147
|
(c) => c.pointsToConnect.map((p) => ({
|
|
1112
1148
|
...p,
|
|
@@ -1117,7 +1153,8 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1117
1153
|
}
|
|
1118
1154
|
unfinishedNodes;
|
|
1119
1155
|
finishedNodes;
|
|
1120
|
-
|
|
1156
|
+
nodeToXYOverlappingObstaclesMap;
|
|
1157
|
+
layerCount;
|
|
1121
1158
|
// targetObstacleMap: Record<string, { obstacle: Obstacle, node: CapacityMeshNode }>
|
|
1122
1159
|
MAX_DEPTH = 4;
|
|
1123
1160
|
targets;
|
|
@@ -1129,7 +1166,7 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1129
1166
|
return (this.MAX_DEPTH - depth + 1) ** 2;
|
|
1130
1167
|
}
|
|
1131
1168
|
getTargetIfNodeContainsTarget(node) {
|
|
1132
|
-
const overlappingObstacles = this.
|
|
1169
|
+
const overlappingObstacles = this.getXYOverlappingObstacles(node);
|
|
1133
1170
|
for (const target of this.targets) {
|
|
1134
1171
|
const targetObstacle = overlappingObstacles.find(
|
|
1135
1172
|
(o) => isPointInRect(target, o)
|
|
@@ -1145,8 +1182,8 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1145
1182
|
}
|
|
1146
1183
|
return null;
|
|
1147
1184
|
}
|
|
1148
|
-
|
|
1149
|
-
const cachedObstacles = this.
|
|
1185
|
+
getXYOverlappingObstacles(node) {
|
|
1186
|
+
const cachedObstacles = this.nodeToXYOverlappingObstaclesMap.get(
|
|
1150
1187
|
node.capacityMeshNodeId
|
|
1151
1188
|
);
|
|
1152
1189
|
if (cachedObstacles) {
|
|
@@ -1157,7 +1194,7 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1157
1194
|
const nodeRight = node.center.x + node.width / 2;
|
|
1158
1195
|
const nodeTop = node.center.y - node.height / 2;
|
|
1159
1196
|
const nodeBottom = node.center.y + node.height / 2;
|
|
1160
|
-
const obstacles = node._parent ? this.
|
|
1197
|
+
const obstacles = node._parent ? this.getXYOverlappingObstacles(node._parent) : this.srj.obstacles;
|
|
1161
1198
|
for (const obstacle of obstacles) {
|
|
1162
1199
|
const obsLeft = obstacle.center.x - obstacle.width / 2;
|
|
1163
1200
|
const obsRight = obstacle.center.x + obstacle.width / 2;
|
|
@@ -1165,20 +1202,38 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1165
1202
|
const obsBottom = obstacle.center.y + obstacle.height / 2;
|
|
1166
1203
|
if (nodeRight >= obsLeft && nodeLeft <= obsRight && nodeBottom >= obsTop && nodeTop <= obsBottom) {
|
|
1167
1204
|
overlappingObstacles.push(obstacle);
|
|
1205
|
+
continue;
|
|
1206
|
+
}
|
|
1207
|
+
if (nodeLeft >= obsLeft && nodeRight <= obsRight && nodeTop >= obsTop && nodeBottom <= obsBottom) {
|
|
1208
|
+
overlappingObstacles.push(obstacle);
|
|
1209
|
+
continue;
|
|
1210
|
+
}
|
|
1211
|
+
if (obsLeft >= nodeLeft && obsRight <= nodeRight && obsTop >= nodeTop && obsBottom <= nodeBottom) {
|
|
1212
|
+
overlappingObstacles.push(obstacle);
|
|
1168
1213
|
}
|
|
1169
1214
|
}
|
|
1170
|
-
this.
|
|
1215
|
+
this.nodeToXYOverlappingObstaclesMap.set(
|
|
1171
1216
|
node.capacityMeshNodeId,
|
|
1172
1217
|
overlappingObstacles
|
|
1173
1218
|
);
|
|
1174
1219
|
return overlappingObstacles;
|
|
1175
1220
|
}
|
|
1221
|
+
getXYZOverlappingObstacles(node) {
|
|
1222
|
+
const xyOverlappingObstacles = this.getXYOverlappingObstacles(node);
|
|
1223
|
+
const xyzOverlappingObstacles = [];
|
|
1224
|
+
for (const obstacle of xyOverlappingObstacles) {
|
|
1225
|
+
if (node.availableZ.some((z) => obstacle.zLayers.includes(z))) {
|
|
1226
|
+
xyzOverlappingObstacles.push(obstacle);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
return xyzOverlappingObstacles;
|
|
1230
|
+
}
|
|
1176
1231
|
/**
|
|
1177
1232
|
* Checks if the given mesh node overlaps with any obstacle.
|
|
1178
1233
|
* We treat both obstacles and nodes as axis‐aligned rectangles.
|
|
1179
1234
|
*/
|
|
1180
1235
|
doesNodeOverlapObstacle(node) {
|
|
1181
|
-
const overlappingObstacles = this.
|
|
1236
|
+
const overlappingObstacles = this.getXYZOverlappingObstacles(node);
|
|
1182
1237
|
if (overlappingObstacles.length > 0) {
|
|
1183
1238
|
return true;
|
|
1184
1239
|
}
|
|
@@ -1195,7 +1250,7 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1195
1250
|
* Checks if the entire node is contained within any obstacle.
|
|
1196
1251
|
*/
|
|
1197
1252
|
isNodeCompletelyInsideObstacle(node) {
|
|
1198
|
-
const overlappingObstacles = this.
|
|
1253
|
+
const overlappingObstacles = this.getXYZOverlappingObstacles(node);
|
|
1199
1254
|
const nodeLeft = node.center.x - node.width / 2;
|
|
1200
1255
|
const nodeRight = node.center.x + node.width / 2;
|
|
1201
1256
|
const nodeTop = node.center.y - node.height / 2;
|
|
@@ -1263,7 +1318,7 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1263
1318
|
}
|
|
1264
1319
|
return childNodes;
|
|
1265
1320
|
}
|
|
1266
|
-
|
|
1321
|
+
shouldNodeBeXYSubdivided(node) {
|
|
1267
1322
|
if (node._depth >= this.MAX_DEPTH) return false;
|
|
1268
1323
|
if (node._containsTarget) return true;
|
|
1269
1324
|
if (node._containsObstacle && !node._completelyInsideObstacle) return true;
|
|
@@ -1279,7 +1334,7 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1279
1334
|
const finishedNewNodes = [];
|
|
1280
1335
|
const unfinishedNewNodes = [];
|
|
1281
1336
|
for (const newNode of newNodes) {
|
|
1282
|
-
const shouldBeSubdivided = this.
|
|
1337
|
+
const shouldBeSubdivided = this.shouldNodeBeXYSubdivided(newNode);
|
|
1283
1338
|
if (shouldBeSubdivided) {
|
|
1284
1339
|
unfinishedNewNodes.push(newNode);
|
|
1285
1340
|
} else if (!shouldBeSubdivided && !newNode._containsObstacle) {
|
|
@@ -1317,20 +1372,33 @@ var CapacityMeshNodeSolver = class extends BaseSolver {
|
|
|
1317
1372
|
height: obstacle.height,
|
|
1318
1373
|
fill: "rgba(255,0,0,0.3)",
|
|
1319
1374
|
stroke: "red",
|
|
1320
|
-
label: "obstacle"
|
|
1375
|
+
label: ["obstacle", obstacle.zLayers.join(",")].join("\n")
|
|
1321
1376
|
});
|
|
1322
1377
|
}
|
|
1323
1378
|
const allNodes = [...this.finishedNodes, ...this.unfinishedNodes];
|
|
1324
1379
|
for (const node of allNodes) {
|
|
1380
|
+
const lowestZ = Math.min(...node.availableZ);
|
|
1325
1381
|
graphics.rects.push({
|
|
1326
|
-
center:
|
|
1382
|
+
center: {
|
|
1383
|
+
x: node.center.x + lowestZ * node.width * 0.05,
|
|
1384
|
+
y: node.center.y - lowestZ * node.width * 0.05
|
|
1385
|
+
},
|
|
1327
1386
|
width: Math.max(node.width - 2, node.width * 0.8),
|
|
1328
1387
|
height: Math.max(node.height - 2, node.height * 0.8),
|
|
1329
|
-
fill: node._containsObstacle ? "rgba(255,0,0,0.1)" :
|
|
1330
|
-
|
|
1331
|
-
|
|
1388
|
+
fill: node._containsObstacle ? "rgba(255,0,0,0.1)" : {
|
|
1389
|
+
"0,1": "rgba(0,0,0,0.1)",
|
|
1390
|
+
"0": "rgba(0,200,200, 0.1)",
|
|
1391
|
+
"1": "rgba(0,0,200, 0.1)"
|
|
1392
|
+
}[node.availableZ.join(",")] ?? "rgba(0,200,200,0.1)",
|
|
1393
|
+
label: [
|
|
1394
|
+
node.capacityMeshNodeId,
|
|
1395
|
+
`availableZ: ${node.availableZ.join(",")}`,
|
|
1396
|
+
`target? ${node._containsTarget ?? false}`,
|
|
1397
|
+
`obs? ${node._containsObstacle ?? false}`
|
|
1398
|
+
].join("\n")
|
|
1332
1399
|
});
|
|
1333
1400
|
}
|
|
1401
|
+
graphics.rects.sort((a, b) => a.center.y - b.center.y);
|
|
1334
1402
|
this.srj.connections.forEach((connection, index) => {
|
|
1335
1403
|
const color = COLORS[index % COLORS.length];
|
|
1336
1404
|
for (const pt of connection.pointsToConnect) {
|
|
@@ -3717,13 +3785,6 @@ var convertHdRouteToSimplifiedRoute = (hdRoute, layerCount) => {
|
|
|
3717
3785
|
return result;
|
|
3718
3786
|
};
|
|
3719
3787
|
|
|
3720
|
-
// lib/utils/mapLayerNameToZ.ts
|
|
3721
|
-
var mapLayerNameToZ = (layerName, layerCount) => {
|
|
3722
|
-
if (layerName === "top") return 0;
|
|
3723
|
-
if (layerName === "bottom") return layerCount - 1;
|
|
3724
|
-
return parseInt(layerName.slice(5));
|
|
3725
|
-
};
|
|
3726
|
-
|
|
3727
3788
|
// lib/solvers/RouteStitchingSolver/SingleHighDensityRouteStitchSolver.ts
|
|
3728
3789
|
var SingleHighDensityRouteStitchSolver = class extends BaseSolver {
|
|
3729
3790
|
mergedHdRoute;
|
|
@@ -5197,7 +5258,7 @@ Layer: ${segmentPoint.z}`,
|
|
|
5197
5258
|
}
|
|
5198
5259
|
};
|
|
5199
5260
|
|
|
5200
|
-
// lib/solvers/
|
|
5261
|
+
// lib/solvers/AutoroutingPipelineSolver.ts
|
|
5201
5262
|
function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
|
|
5202
5263
|
return {
|
|
5203
5264
|
solverName,
|