@tscircuit/capacity-autorouter 0.0.1

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.
@@ -0,0 +1,818 @@
1
+ import { GraphicsObject } from 'graphics-debug';
2
+ import { ConnectivityMap } from 'circuit-json-to-connectivity-map';
3
+
4
+ type TraceId = string;
5
+ interface SimpleRouteJson {
6
+ layerCount: number;
7
+ minTraceWidth: number;
8
+ obstacles: Obstacle[];
9
+ connections: Array<SimpleRouteConnection>;
10
+ bounds: {
11
+ minX: number;
12
+ maxX: number;
13
+ minY: number;
14
+ maxY: number;
15
+ };
16
+ traces?: SimplifiedPcbTraces;
17
+ }
18
+ interface Obstacle {
19
+ type: "rect";
20
+ layers: string[];
21
+ center: {
22
+ x: number;
23
+ y: number;
24
+ };
25
+ width: number;
26
+ height: number;
27
+ connectedTo: TraceId[];
28
+ }
29
+ interface SimpleRouteConnection {
30
+ name: string;
31
+ pointsToConnect: Array<{
32
+ x: number;
33
+ y: number;
34
+ layer: string;
35
+ }>;
36
+ }
37
+ type SimplifiedPcbTraces = Array<{
38
+ type: "pcb_trace";
39
+ pcb_trace_id: TraceId;
40
+ route: Array<{
41
+ route_type: "wire";
42
+ x: number;
43
+ y: number;
44
+ width: number;
45
+ layer: string;
46
+ } | {
47
+ route_type: "via";
48
+ x: number;
49
+ y: number;
50
+ to_layer: string;
51
+ from_layer: string;
52
+ }>;
53
+ }>;
54
+
55
+ type CapacityMeshNodeId = string;
56
+ interface CapacityMeshNode {
57
+ capacityMeshNodeId: string;
58
+ center: {
59
+ x: number;
60
+ y: number;
61
+ };
62
+ width: number;
63
+ height: number;
64
+ layer: string;
65
+ _depth?: number;
66
+ _completelyInsideObstacle?: boolean;
67
+ _containsObstacle?: boolean;
68
+ _containsTarget?: boolean;
69
+ _targetConnectionName?: string;
70
+ _parent?: CapacityMeshNode;
71
+ }
72
+ interface CapacityMeshEdge {
73
+ capacityMeshEdgeId: string;
74
+ nodeIds: [CapacityMeshNodeId, CapacityMeshNodeId];
75
+ }
76
+
77
+ type CapacityPathId = string;
78
+ interface CapacityPath {
79
+ capacityPathId: CapacityPathId;
80
+ connectionName: string;
81
+ nodeIds: CapacityMeshNodeId[];
82
+ }
83
+
84
+ declare class BaseSolver {
85
+ MAX_ITERATIONS: number;
86
+ solved: boolean;
87
+ failed: boolean;
88
+ iterations: number;
89
+ progress: number;
90
+ error: string | null;
91
+ /** DO NOT OVERRIDE! Override _step() instead */
92
+ step(): void;
93
+ _step(): void;
94
+ solve(): void;
95
+ visualize(): GraphicsObject;
96
+ }
97
+
98
+ declare class CapacityMeshEdgeSolver extends BaseSolver {
99
+ nodes: CapacityMeshNode[];
100
+ edges: Array<CapacityMeshEdge>;
101
+ constructor(nodes: CapacityMeshNode[]);
102
+ getNextCapacityMeshEdgeId(): string;
103
+ step(): void;
104
+ private areNodesBordering;
105
+ visualize(): GraphicsObject;
106
+ }
107
+
108
+ interface CapacityMeshNodeSolverOptions {
109
+ capacityDepth?: number;
110
+ }
111
+ declare class CapacityMeshNodeSolver extends BaseSolver {
112
+ srj: SimpleRouteJson;
113
+ opts: CapacityMeshNodeSolverOptions;
114
+ unfinishedNodes: CapacityMeshNode[];
115
+ finishedNodes: CapacityMeshNode[];
116
+ nodeToOverlappingObstaclesMap: Map<CapacityMeshNodeId, Obstacle[]>;
117
+ MAX_DEPTH: number;
118
+ constructor(srj: SimpleRouteJson, opts?: CapacityMeshNodeSolverOptions);
119
+ _nextNodeCounter: number;
120
+ getNextNodeId(): string;
121
+ getCapacityFromDepth(depth: number): number;
122
+ getTargetNameIfNodeContainsTarget(node: CapacityMeshNode): string | null;
123
+ getOverlappingObstacles(node: CapacityMeshNode): Obstacle[];
124
+ /**
125
+ * Checks if the given mesh node overlaps with any obstacle.
126
+ * We treat both obstacles and nodes as axis‐aligned rectangles.
127
+ */
128
+ doesNodeOverlapObstacle(node: CapacityMeshNode): boolean;
129
+ /**
130
+ * Checks if the entire node is contained within any obstacle.
131
+ */
132
+ isNodeCompletelyInsideObstacle(node: CapacityMeshNode): boolean;
133
+ getChildNodes(parent: CapacityMeshNode): CapacityMeshNode[];
134
+ shouldNodeBeSubdivided(node: CapacityMeshNode): boolean;
135
+ _step(): void;
136
+ /**
137
+ * Creates a GraphicsObject to visualize the mesh, its nodes, obstacles, and connection points.
138
+ *
139
+ * - Mesh nodes are rendered as rectangles.
140
+ * - Nodes that have an obstacle intersection are outlined in red.
141
+ * - Other nodes are outlined in green.
142
+ * - Lines are drawn from a node to its parent.
143
+ * - Obstacles are drawn as semi-transparent red rectangles.
144
+ * - Points for each connection’s pointsToConnect are drawn in a unique color.
145
+ */
146
+ visualize(): GraphicsObject;
147
+ }
148
+
149
+ interface CapacityHyperParameters {
150
+ VIA_DIAMETER: number;
151
+ TRACE_WIDTH: number;
152
+ MAX_CAPACITY_FACTOR: number;
153
+ }
154
+
155
+ type Candidate = {
156
+ prevCandidate: Candidate | null;
157
+ node: CapacityMeshNode;
158
+ f: number;
159
+ g: number;
160
+ h: number;
161
+ };
162
+ declare class CapacityPathingSolver extends BaseSolver {
163
+ connectionsWithNodes: Array<{
164
+ connection: SimpleRouteConnection;
165
+ nodes: CapacityMeshNode[];
166
+ path?: CapacityMeshNode[];
167
+ }>;
168
+ usedNodeCapacityMap: Map<CapacityMeshNodeId, number>;
169
+ simpleRouteJson: SimpleRouteJson;
170
+ nodes: CapacityMeshNode[];
171
+ edges: CapacityMeshEdge[];
172
+ GREEDY_MULTIPLIER: number;
173
+ nodeMap: Map<CapacityMeshNodeId, CapacityMeshNode>;
174
+ nodeEdgeMap: Map<CapacityMeshNodeId, CapacityMeshEdge[]>;
175
+ connectionNameToGoalNodeIds: Map<string, CapacityMeshNodeId[]>;
176
+ colorMap: Record<string, string>;
177
+ maxDepthOfNodes: number;
178
+ activeCandidateStraightLineDistance?: number;
179
+ hyperParameters: Partial<CapacityHyperParameters>;
180
+ constructor({ simpleRouteJson, nodes, edges, colorMap, MAX_ITERATIONS, hyperParameters, }: {
181
+ simpleRouteJson: SimpleRouteJson;
182
+ nodes: CapacityMeshNode[];
183
+ edges: CapacityMeshEdge[];
184
+ colorMap?: Record<string, string>;
185
+ MAX_ITERATIONS?: number;
186
+ hyperParameters?: Partial<CapacityHyperParameters>;
187
+ });
188
+ getTotalCapacity(node: CapacityMeshNode): number;
189
+ getConnectionsWithNodes(): {
190
+ connectionsWithNodes: {
191
+ connection: SimpleRouteConnection;
192
+ nodes: CapacityMeshNode[];
193
+ pathFound: boolean;
194
+ }[];
195
+ connectionNameToGoalNodeIds: Map<string, string[]>;
196
+ };
197
+ currentConnectionIndex: number;
198
+ candidates?: Array<Candidate> | null;
199
+ visitedNodes?: Set<CapacityMeshNodeId> | null;
200
+ computeG(prevCandidate: Candidate, node: CapacityMeshNode, endGoal: CapacityMeshNode): number;
201
+ computeH(prevCandidate: Candidate, node: CapacityMeshNode, endGoal: CapacityMeshNode): number;
202
+ getBacktrackedPath(candidate: Candidate): CapacityMeshNode[];
203
+ getNeighboringNodes(node: CapacityMeshNode): CapacityMeshNode[];
204
+ getCapacityPaths(): CapacityPath[];
205
+ doesNodeHaveCapacityForTrace(node: CapacityMeshNode): boolean;
206
+ canTravelThroughObstacle(node: CapacityMeshNode, connectionName: string): boolean;
207
+ getDistanceBetweenNodes(A: CapacityMeshNode, B: CapacityMeshNode): number;
208
+ reduceCapacityAlongPath(nextConnection: {
209
+ path?: CapacityMeshNode[];
210
+ }): void;
211
+ _step(): void;
212
+ visualize(): GraphicsObject;
213
+ }
214
+
215
+ interface NodePortSegment {
216
+ capacityMeshNodeId: string;
217
+ nodePortSegmentId?: string;
218
+ start: {
219
+ x: number;
220
+ y: number;
221
+ };
222
+ end: {
223
+ x: number;
224
+ y: number;
225
+ };
226
+ connectionNames: string[];
227
+ }
228
+
229
+ /**
230
+ * Each Node is a square. The capacity paths indicate the nodes the trace will
231
+ * travel through. We want to find the "Port Segment" that each capacity path
232
+ * will take for each node.
233
+ */
234
+ declare class CapacityEdgeToPortSegmentSolver extends BaseSolver {
235
+ nodes: CapacityMeshNode[];
236
+ edges: CapacityMeshEdge[];
237
+ capacityPaths: CapacityPath[];
238
+ nodeMap: Map<CapacityMeshNodeId, CapacityMeshNode>;
239
+ nodeEdgeMap: Map<CapacityMeshNodeId, CapacityMeshEdge[]>;
240
+ unprocessedNodeIds: CapacityMeshNodeId[];
241
+ nodePortSegments: Map<CapacityMeshNodeId, NodePortSegment[]>;
242
+ colorMap: Record<string, string>;
243
+ constructor({ nodes, edges, capacityPaths, colorMap, }: {
244
+ nodes: CapacityMeshNode[];
245
+ edges: CapacityMeshEdge[];
246
+ capacityPaths: CapacityPath[];
247
+ colorMap?: Record<string, string>;
248
+ });
249
+ step(): void;
250
+ visualize(): GraphicsObject;
251
+ }
252
+
253
+ type PortPoint = {
254
+ connectionName: string;
255
+ x: number;
256
+ y: number;
257
+ z: number;
258
+ };
259
+ type NodeWithPortPoints = {
260
+ capacityMeshNodeId: string;
261
+ center: {
262
+ x: number;
263
+ y: number;
264
+ };
265
+ width: number;
266
+ height: number;
267
+ portPoints: PortPoint[];
268
+ };
269
+ /**
270
+ * A path for a wire in high-density intra-node routing.
271
+ *
272
+ * Wires travel along a route, and are placed to avoid other
273
+ * wires at the same z-level. Any time a z level is changed,
274
+ * you must place a via.
275
+ *
276
+ * z is an integer corresponding to the layer index
277
+ *
278
+ * z=0: top layer for 2 layer boards
279
+ * z=1: bottom layer for 2 layer boards
280
+ *
281
+ * z must be an integer
282
+ */
283
+ type HighDensityIntraNodeRoute = {
284
+ connectionName: string;
285
+ traceThickness: number;
286
+ viaDiameter: number;
287
+ route: Array<{
288
+ x: number;
289
+ y: number;
290
+ z: number;
291
+ }>;
292
+ vias: Array<{
293
+ x: number;
294
+ y: number;
295
+ }>;
296
+ };
297
+
298
+ interface SegmentWithAssignedPoints extends NodePortSegment {
299
+ assignedPoints?: {
300
+ connectionName: string;
301
+ point: {
302
+ x: number;
303
+ y: number;
304
+ z: number;
305
+ };
306
+ }[];
307
+ }
308
+ /**
309
+ * CapacitySegmentToPointSolver:
310
+ *
311
+ * In each step, the solver iterates over all unsolved segments (segments
312
+ * without points assigned for each connection). For each segment:
313
+ *
314
+ * - If there is only one connection, it assigns the center as the point.
315
+ * - If there are two connections, it attempts to determine the ordering using
316
+ * other segments within the node. If no ordering can be determined, it does nothing.
317
+ *
318
+ * If an iteration produces no new assignments, the solver picks the segment with
319
+ * the fewest connections and assigns points evenly spaced along the segment,
320
+ * ordering them alphabetically.
321
+ */
322
+ declare class CapacitySegmentToPointSolver extends BaseSolver {
323
+ unsolvedSegments: SegmentWithAssignedPoints[];
324
+ solvedSegments: (NodePortSegment & {
325
+ assignedPoints: {
326
+ connectionName: string;
327
+ point: {
328
+ x: number;
329
+ y: number;
330
+ z: number;
331
+ };
332
+ }[];
333
+ })[];
334
+ nodeMap: Record<string, CapacityMeshNode>;
335
+ colorMap: Record<string, string>;
336
+ constructor({ segments, colorMap, nodes, }: {
337
+ segments: NodePortSegment[];
338
+ colorMap?: Record<string, string>;
339
+ /**
340
+ * This isn't used by the algorithm, but allows associating metadata
341
+ * for the result datatype (the center, width, height of the node)
342
+ */
343
+ nodes: CapacityMeshNode[];
344
+ });
345
+ /**
346
+ * Perform one iteration step.
347
+ */
348
+ _step(): void;
349
+ /**
350
+ * Return the assigned points for each segment.
351
+ */
352
+ getNodesWithPortPoints(): NodeWithPortPoints[];
353
+ /**
354
+ * Return a GraphicsObject that visualizes the segments with assigned points.
355
+ */
356
+ visualize(): GraphicsObject;
357
+ }
358
+
359
+ interface HighDensityHyperParameters {
360
+ FUTURE_CONNECTION_PROX_TRACE_PENALTY_FACTOR: number;
361
+ FUTURE_CONNECTION_PROX_VIA_PENALTY_FACTOR: number;
362
+ FUTURE_CONNECTION_PROXIMITY_VD: number;
363
+ MISALIGNED_DIST_PENALTY_FACTOR: number;
364
+ VIA_PENALTY_FACTOR_2: number;
365
+ SHUFFLE_SEED: number;
366
+ CELL_SIZE_FACTOR: number;
367
+ FLIP_TRACE_ALIGNMENT_DIRECTION: boolean;
368
+ }
369
+
370
+ type FutureConnection = {
371
+ connectionName: string;
372
+ points: {
373
+ x: number;
374
+ y: number;
375
+ }[];
376
+ };
377
+ type Node = {
378
+ x: number;
379
+ y: number;
380
+ z: number;
381
+ g: number;
382
+ h: number;
383
+ f: number;
384
+ parent: Node | null;
385
+ };
386
+ declare class SingleHighDensityRouteSolver extends BaseSolver {
387
+ obstacleRoutes: HighDensityIntraNodeRoute[];
388
+ bounds: {
389
+ minX: number;
390
+ maxX: number;
391
+ minY: number;
392
+ maxY: number;
393
+ };
394
+ boundsSize: {
395
+ width: number;
396
+ height: number;
397
+ };
398
+ boundsCenter: {
399
+ x: number;
400
+ y: number;
401
+ };
402
+ A: {
403
+ x: number;
404
+ y: number;
405
+ z: number;
406
+ };
407
+ B: {
408
+ x: number;
409
+ y: number;
410
+ z: number;
411
+ };
412
+ straightLineDistance: number;
413
+ viaDiameter: number;
414
+ traceThickness: number;
415
+ obstacleMargin: number;
416
+ layerCount: number;
417
+ minCellSize: number;
418
+ cellStep: number;
419
+ GREEDY_MULTIPLER: number;
420
+ numRoutes: number;
421
+ VIA_PENALTY_FACTOR: number;
422
+ CELL_SIZE_FACTOR: number;
423
+ exploredNodes: Set<string>;
424
+ candidates: Node[];
425
+ connectionName: string;
426
+ solvedPath: HighDensityIntraNodeRoute | null;
427
+ futureConnections: FutureConnection[];
428
+ hyperParameters: Partial<HighDensityHyperParameters>;
429
+ connMap?: ConnectivityMap;
430
+ /** For debugging/animating the exploration */
431
+ debug_exploredNodesOrdered: string[];
432
+ debug_nodesTooCloseToObstacle: Set<string>;
433
+ debug_nodePathToParentIntersectsObstacle: Set<string>;
434
+ debugEnabled: boolean;
435
+ constructor(opts: {
436
+ connectionName: string;
437
+ obstacleRoutes: HighDensityIntraNodeRoute[];
438
+ minDistBetweenEnteringPoints: number;
439
+ bounds: {
440
+ minX: number;
441
+ maxX: number;
442
+ minY: number;
443
+ maxY: number;
444
+ };
445
+ A: {
446
+ x: number;
447
+ y: number;
448
+ z: number;
449
+ };
450
+ B: {
451
+ x: number;
452
+ y: number;
453
+ z: number;
454
+ };
455
+ viaDiameter?: number;
456
+ traceThickness?: number;
457
+ obstacleMargin?: number;
458
+ layerCount?: number;
459
+ futureConnections?: FutureConnection[];
460
+ hyperParameters?: Partial<HighDensityHyperParameters>;
461
+ connMap?: ConnectivityMap;
462
+ });
463
+ handleSimpleCases(): void;
464
+ get viaPenaltyDistance(): number;
465
+ isNodeTooCloseToObstacle(node: Node, margin?: number, isVia?: boolean): boolean;
466
+ isNodeTooCloseToEdge(node: Node): boolean;
467
+ doesPathToParentIntersectObstacle(node: Node): boolean;
468
+ computeH(node: Node): number;
469
+ computeG(node: Node): number;
470
+ computeF(g: number, h: number): number;
471
+ getNodeKey(node: Node): string;
472
+ getNeighbors(node: Node): Node[];
473
+ getNodePath(node: Node): Node[];
474
+ getViasInNodePath(node: Node): {
475
+ x: number;
476
+ y: number;
477
+ }[];
478
+ setSolvedPath(node: Node): void;
479
+ computeProgress(currentNode: Node, goalDist: number, isOnLayer: boolean): number;
480
+ _step(): void;
481
+ visualize(): GraphicsObject;
482
+ }
483
+
484
+ declare class SingleIntraNodeRouteSolver extends BaseSolver {
485
+ nodeWithPortPoints: NodeWithPortPoints;
486
+ colorMap: Record<string, string>;
487
+ unsolvedConnections: {
488
+ connectionName: string;
489
+ points: {
490
+ x: number;
491
+ y: number;
492
+ z: number;
493
+ }[];
494
+ }[];
495
+ totalConnections: number;
496
+ solvedRoutes: HighDensityIntraNodeRoute[];
497
+ failedSolvers: SingleHighDensityRouteSolver[];
498
+ hyperParameters: Partial<HighDensityHyperParameters>;
499
+ minDistBetweenEnteringPoints: number;
500
+ activeSolver: SingleHighDensityRouteSolver | null;
501
+ connMap?: ConnectivityMap;
502
+ constructor(params: {
503
+ nodeWithPortPoints: NodeWithPortPoints;
504
+ colorMap?: Record<string, string>;
505
+ hyperParameters?: Partial<HighDensityHyperParameters>;
506
+ connMap?: ConnectivityMap;
507
+ });
508
+ computeProgress(): number;
509
+ _step(): void;
510
+ visualize(): GraphicsObject;
511
+ }
512
+
513
+ type SupervisedSolver<T extends BaseSolver> = {
514
+ hyperParameters: any;
515
+ solver: T;
516
+ h: number;
517
+ g: number;
518
+ f: number;
519
+ };
520
+ type HyperParameterDef = {
521
+ name: string;
522
+ possibleValues: Array<any>;
523
+ };
524
+ /**
525
+ * The HyperParameterSupervisorSolver is a solver that solves a problem by
526
+ * running competing solvers with different hyperparameters.
527
+ *
528
+ * As solvers make progress, the supervisor will allow the best solvers to run
529
+ * for more iterations, prioritizing the solvers that are working the best.
530
+ */
531
+ declare class HyperParameterSupervisorSolver<T extends BaseSolver> extends BaseSolver {
532
+ GREEDY_MULTIPLIER: number;
533
+ MIN_SUBSTEPS: number;
534
+ supervisedSolvers?: Array<SupervisedSolver<T>>;
535
+ getHyperParameterDefs(): Array<HyperParameterDef>;
536
+ getCombinationDefs(): Array<Array<string>> | null;
537
+ getHyperParameterCombinations(hyperParameterDefs?: Array<HyperParameterDef>): Array<Record<string, any>>;
538
+ initializeSolvers(): void;
539
+ generateSolver(hyperParameters: any): T;
540
+ computeG(solver: T): number;
541
+ computeH(solver: T): number;
542
+ computeF(g: number, h: number): number;
543
+ getSupervisedSolverWithBestFitness(): SupervisedSolver<T> | null;
544
+ _step(): void;
545
+ onSolve(solver: SupervisedSolver<T>): void;
546
+ visualize(): GraphicsObject;
547
+ }
548
+
549
+ declare class HyperSingleIntraNodeSolver extends HyperParameterSupervisorSolver<SingleIntraNodeRouteSolver> {
550
+ constructorParams: ConstructorParameters<typeof SingleIntraNodeRouteSolver>[0];
551
+ solvedRoutes: HighDensityIntraNodeRoute[];
552
+ nodeWithPortPoints: NodeWithPortPoints;
553
+ constructor(opts: ConstructorParameters<typeof SingleIntraNodeRouteSolver>[0]);
554
+ getCombinationDefs(): string[][];
555
+ getHyperParameterDefs(): ({
556
+ name: string;
557
+ possibleValues: ({
558
+ FUTURE_CONNECTION_PROX_TRACE_PENALTY_FACTOR: number;
559
+ FUTURE_CONNECTION_PROX_VIA_PENALTY_FACTOR: number;
560
+ FUTURE_CONNECTION_PROXIMITY_VD: number;
561
+ MISALIGNED_DIST_PENALTY_FACTOR: number;
562
+ VIA_PENALTY_FACTOR_2?: undefined;
563
+ } | {
564
+ FUTURE_CONNECTION_PROX_TRACE_PENALTY_FACTOR: number;
565
+ FUTURE_CONNECTION_PROX_VIA_PENALTY_FACTOR: number;
566
+ FUTURE_CONNECTION_PROXIMITY_VD: number;
567
+ MISALIGNED_DIST_PENALTY_FACTOR: number;
568
+ VIA_PENALTY_FACTOR_2: number;
569
+ })[];
570
+ } | {
571
+ name: string;
572
+ possibleValues: {
573
+ SHUFFLE_SEED: number;
574
+ }[];
575
+ } | {
576
+ name: string;
577
+ possibleValues: {
578
+ CELL_SIZE_FACTOR: number;
579
+ }[];
580
+ } | {
581
+ name: string;
582
+ possibleValues: {
583
+ FLIP_TRACE_ALIGNMENT_DIRECTION: boolean;
584
+ }[];
585
+ } | {
586
+ name: string;
587
+ possibleValues: {
588
+ CELL_SIZE_FACTOR: number;
589
+ VIA_PENALTY_FACTOR_2: number;
590
+ }[];
591
+ })[];
592
+ computeG(solver: SingleIntraNodeRouteSolver): number;
593
+ computeH(solver: SingleIntraNodeRouteSolver): number;
594
+ generateSolver(hyperParameters: any): SingleIntraNodeRouteSolver;
595
+ onSolve(solver: SupervisedSolver<SingleIntraNodeRouteSolver>): void;
596
+ }
597
+
598
+ declare class HighDensityRouteSolver extends BaseSolver {
599
+ unsolvedNodePortPoints: NodeWithPortPoints[];
600
+ routes: HighDensityIntraNodeRoute[];
601
+ colorMap: Record<string, string>;
602
+ readonly defaultViaDiameter = 0.6;
603
+ readonly defaultTraceThickness = 0.15;
604
+ failedSolvers: (SingleIntraNodeRouteSolver | HyperSingleIntraNodeSolver)[];
605
+ activeSubSolver: SingleIntraNodeRouteSolver | HyperSingleIntraNodeSolver | null;
606
+ connMap?: ConnectivityMap;
607
+ constructor({ nodePortPoints, colorMap, connMap, }: {
608
+ nodePortPoints: NodeWithPortPoints[];
609
+ colorMap?: Record<string, string>;
610
+ connMap?: ConnectivityMap;
611
+ });
612
+ /**
613
+ * Each iteration, pop an unsolved node and attempt to find the routes inside
614
+ * of it.
615
+ */
616
+ _step(): void;
617
+ visualize(): GraphicsObject;
618
+ }
619
+
620
+ declare class CapacityNodeTargetMerger extends BaseSolver {
621
+ nodes: CapacityMeshNode[];
622
+ connMap: ConnectivityMap;
623
+ unprocessedObstacles: Obstacle[];
624
+ newNodes: CapacityMeshNode[];
625
+ removedNodeIds: Set<string>;
626
+ constructor(nodes: CapacityMeshNode[], obstacles: Obstacle[], connMap: ConnectivityMap);
627
+ _step(): void;
628
+ visualize(): GraphicsObject;
629
+ }
630
+
631
+ type NodePortSegmentId = string;
632
+ interface ChangeLayerOperation {
633
+ op: "changeLayer";
634
+ segmentId: string;
635
+ pointIndex: number;
636
+ newLayer: number;
637
+ /** Operation is mutated and oldLayer is added to allow reversal */
638
+ oldLayer?: number;
639
+ cost?: number;
640
+ }
641
+ interface SwitchOperation {
642
+ op: "switch";
643
+ segmentId: string;
644
+ point1Index: number;
645
+ point2Index: number;
646
+ cost?: number;
647
+ }
648
+ interface CombinedOperation {
649
+ op: "combined";
650
+ subOperations: Array<SwitchOperation | ChangeLayerOperation>;
651
+ cost?: number;
652
+ }
653
+ type Operation = ChangeLayerOperation | SwitchOperation | CombinedOperation;
654
+ /**
655
+ * Use simulated annealing to try to improve the placement of points (via
656
+ * swapping with points on the same segment) or changing the layer.
657
+ *
658
+ * We have the following pieces of information:
659
+ * - NodePortSegment with nodePortSegmentId
660
+ * - A "neighbor" NodePortSegmentWithAssignedPoints has one change
661
+ * - A change can be flipping a point to the opposite layer
662
+ * - A change can also be switching the position of two points
663
+ * - We represent the operations used to change from an original scene
664
+ * with a list of operations [SEG1_CL(1, 1), SEG1_SW(1, 2), SEG2_CL(2, 0)]
665
+ * - CN indicates the capacity node to edit
666
+ * - The SW operation "switches" the x/y location of two points
667
+ * - The CL operation changes the layer of the point
668
+ * - When choosing edits to make, we are biased to operate on nodes that have a
669
+ * high cost and biased against operating on nodes we've operated on a lot
670
+ * - Each step, we generate an operation and use the standard simulated
671
+ * annealing function to determine if we should perform the operation
672
+ */
673
+ declare class CapacitySegmentPointOptimizer extends BaseSolver {
674
+ assignedSegments: SegmentWithAssignedPoints[];
675
+ colorMap: Record<string, string>;
676
+ nodeMap: Map<CapacityMeshNodeId, CapacityMeshNode>;
677
+ nodeIdToSegmentIds: Map<string, string[]>;
678
+ segmentIdToNodeIds: Map<string, string[]>;
679
+ currentMutatedSegments: Map<NodePortSegmentId, SegmentWithAssignedPoints>;
680
+ allSegmentIds: string[];
681
+ lastAppliedOperation: Operation | null;
682
+ lastCreatedOperation: Operation | null;
683
+ currentNodeCosts: Map<CapacityMeshNodeId, number>;
684
+ lastAcceptedIteration: number;
685
+ currentCost: number;
686
+ randomSeed: number;
687
+ numNodes: number;
688
+ probabilityOfFailure: number;
689
+ nodesThatCantFitVias: Set<CapacityMeshNodeId>;
690
+ mutableSegments: Set<NodePortSegmentId>;
691
+ VIA_DIAMETER: number;
692
+ OBSTACLE_MARGIN: number;
693
+ MAX_OPERATIONS_PER_MUTATION: number;
694
+ MAX_NODE_CHAIN_PER_MUTATION: number;
695
+ NOOP_ITERATIONS_BEFORE_EARLY_STOP: number;
696
+ constructor({ assignedSegments, colorMap, nodes, }: {
697
+ assignedSegments: NodePortSegment[];
698
+ colorMap?: Record<string, string>;
699
+ /**
700
+ * This isn't used by the algorithm, but allows associating metadata
701
+ * for the result datatype (the center, width, height of the node)
702
+ */
703
+ nodes: CapacityMeshNode[];
704
+ });
705
+ random(): number;
706
+ /**
707
+ * The cost is the "probability of failure" of the node.
708
+ */
709
+ computeNodeCost(nodeId: CapacityMeshNodeId): number;
710
+ /**
711
+ * Number of traces that can go through this node if they are completely
712
+ * straight without crossings
713
+ */
714
+ getUsedTraceCapacity(nodeId: CapacityMeshNodeId): number;
715
+ /**
716
+ * Granular via capacity is a consideration of capacity that includes...
717
+ * - The number of traces
718
+ * - The number of trace crossings (0-2 vias per trace crossing)
719
+ * - Empirically, each crossing typically results in 0.82 vias
720
+ * - e.g. 17 traces would typically have 51 crossings & 42 vias
721
+ * - The number of layer changes (at least 1 via per layer change)
722
+ * - We don't know how a entry/exit being on separated layers effects
723
+ * the capacity/number of vias yet
724
+ *
725
+ * - Generally minimizing the number of crossings is pretty good, if there
726
+ * is no trace crossing you basically don't have any used capacity
727
+ * - If the entry/exit layer is different, you're guaranteed to have at least
728
+ * one via
729
+ *
730
+ * - Total capacity is computed by estimating the number of vias that could
731
+ * be created using the formula (viaFitAcross / 2) ** 1.1
732
+ */
733
+ getUsedViaCapacity(nodeId: CapacityMeshNodeId): number;
734
+ getRandomWeightedNodeId(): CapacityMeshNodeId;
735
+ getRandomWeightedSegmentId(): string;
736
+ getMutableSegments(): Set<string>;
737
+ isSegmentMutable(segmentId: string): boolean;
738
+ getRandomOperationForSegment(randomSegmentId: string): SwitchOperation | ChangeLayerOperation | null;
739
+ getNodesNearNode(nodeId: CapacityMeshNodeId, hops?: number): CapacityMeshNodeId[];
740
+ getRandomCombinedOperationNearNode(nodeId: CapacityMeshNodeId): CombinedOperation;
741
+ /**
742
+ * A combined operation can perform multiple operations on a single node, this
743
+ * allows it to reach outcomes that may not be beneficial with since
744
+ * operations
745
+ */
746
+ getRandomCombinedOperationOnSingleNode(max?: number): CombinedOperation;
747
+ getRandomOperation(): Operation;
748
+ /**
749
+ * We compute "overall probability of failure" as our overall cost, then
750
+ * linearize it to make it easier to work with
751
+ */
752
+ computeCurrentCost(): {
753
+ cost: number;
754
+ nodeCosts: Map<CapacityMeshNodeId, number>;
755
+ probabilityOfFailure: number;
756
+ linearizedCost: number;
757
+ };
758
+ applyOperation(op: Operation): void;
759
+ reverseOperation(op: Operation): void;
760
+ isNewCostAcceptable(oldPf: number, newPf: number): boolean;
761
+ /**
762
+ * FOR OUTPUT: Return the assigned points for each segment.
763
+ */
764
+ getNodesWithPortPoints(): NodeWithPortPoints[];
765
+ _step(): void;
766
+ visualize(): GraphicsObject;
767
+ }
768
+
769
+ interface CapacityMeshSolverOptions {
770
+ capacityDepth?: number;
771
+ targetMinCapacity?: number;
772
+ }
773
+ declare class CapacityMeshSolver extends BaseSolver {
774
+ srj: SimpleRouteJson;
775
+ opts: CapacityMeshSolverOptions;
776
+ nodeSolver: CapacityMeshNodeSolver;
777
+ nodeTargetMerger?: CapacityNodeTargetMerger;
778
+ edgeSolver?: CapacityMeshEdgeSolver;
779
+ pathingSolver?: CapacityPathingSolver;
780
+ edgeToPortSegmentSolver?: CapacityEdgeToPortSegmentSolver;
781
+ colorMap: Record<string, string>;
782
+ segmentToPointSolver?: CapacitySegmentToPointSolver;
783
+ segmentToPointOptimizer?: CapacitySegmentPointOptimizer;
784
+ highDensityRouteSolver?: HighDensityRouteSolver;
785
+ activeSolver?: BaseSolver | null;
786
+ connMap: ConnectivityMap;
787
+ constructor(srj: SimpleRouteJson, opts?: CapacityMeshSolverOptions);
788
+ _step(): void;
789
+ visualize(): GraphicsObject;
790
+ /**
791
+ * Simplifies a route by merging consecutive points along the same line
792
+ */
793
+ private simplifyRoute;
794
+ /**
795
+ * Returns the SimpleRouteJson with routes converted to SimplifiedPcbTraces
796
+ */
797
+ getOutputSimpleRouteJson(): SimpleRouteJson;
798
+ }
799
+
800
+ /**
801
+ * Calculate the capacity of a node based on its width
802
+ * @param nodeOrWidth The node or width to calculate capacity for
803
+ * @param maxCapacityFactor Optional multiplier to adjust capacity
804
+ * @returns The calculated capacity
805
+ */
806
+ declare const getTunedTotalCapacity1: (nodeOrWidth: CapacityMeshNode | {
807
+ width: number;
808
+ }, maxCapacityFactor?: number) => number;
809
+ /**
810
+ * Calculate the optimal subdivision depth to reach a target minimum capacity
811
+ * @param initialWidth The initial width of the top-level node
812
+ * @param targetMinCapacity The minimum capacity target (default 0.5)
813
+ * @param maxDepth Maximum allowed depth (default 10)
814
+ * @returns The optimal capacity depth
815
+ */
816
+ declare const calculateOptimalCapacityDepth: (initialWidth: number, targetMinCapacity?: number, maxDepth?: number) => number;
817
+
818
+ export { CapacityMeshSolver, calculateOptimalCapacityDepth, getTunedTotalCapacity1 };