@tscircuit/fanout-solver 0.0.29 → 0.0.30
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/lib/fanout-solver.ts +44 -11
- package/lib/prepare-buses.ts +26 -0
- package/lib/types.ts +4 -0
- package/package.json +1 -1
package/lib/fanout-solver.ts
CHANGED
|
@@ -259,7 +259,7 @@ function getBusDepthInRows(bus: PreparedBus): number {
|
|
|
259
259
|
)
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
function
|
|
262
|
+
function createInitialLayerAssignment(params: {
|
|
263
263
|
buses: PreparedBus[]
|
|
264
264
|
escapeLayers: string[]
|
|
265
265
|
escapeLayersByBusId: Readonly<Record<string, readonly string[]>>
|
|
@@ -314,16 +314,16 @@ function createPreferredLayerAssignment(params: {
|
|
|
314
314
|
}
|
|
315
315
|
|
|
316
316
|
function prioritizeLayerAssignment(params: {
|
|
317
|
-
|
|
317
|
+
initialAssignment: Readonly<Record<string, string>>
|
|
318
318
|
generatedAssignments: Array<Readonly<Record<string, string>>>
|
|
319
319
|
maxAssignments: number
|
|
320
320
|
}): Array<Readonly<Record<string, string>>> {
|
|
321
|
-
const {
|
|
322
|
-
const
|
|
321
|
+
const { initialAssignment, generatedAssignments, maxAssignments } = params
|
|
322
|
+
const initialKey = JSON.stringify(initialAssignment)
|
|
323
323
|
return [
|
|
324
|
-
|
|
324
|
+
initialAssignment,
|
|
325
325
|
...generatedAssignments.filter(
|
|
326
|
-
(assignment) => JSON.stringify(assignment) !==
|
|
326
|
+
(assignment) => JSON.stringify(assignment) !== initialKey,
|
|
327
327
|
),
|
|
328
328
|
].slice(0, maxAssignments)
|
|
329
329
|
}
|
|
@@ -335,7 +335,12 @@ function getCandidateEscapeLayersForBus(params: {
|
|
|
335
335
|
staticClearanceCache: RouteBusStaticClearanceCache
|
|
336
336
|
}): string[] {
|
|
337
337
|
const { bus, srj, config, staticClearanceCache } = params
|
|
338
|
-
const
|
|
338
|
+
const busAllowedLayers = bus.allowedLayers
|
|
339
|
+
const allowedEscapeLayers =
|
|
340
|
+
busAllowedLayers === undefined
|
|
341
|
+
? config.escapeLayers
|
|
342
|
+
: config.escapeLayers.filter((layer) => busAllowedLayers.includes(layer))
|
|
343
|
+
const individuallyRoutableLayers = allowedEscapeLayers.filter(
|
|
339
344
|
(targetLayer) =>
|
|
340
345
|
routeBus({
|
|
341
346
|
srj,
|
|
@@ -357,9 +362,11 @@ function getCandidateEscapeLayersForBus(params: {
|
|
|
357
362
|
// route this bus by itself cannot become viable later in an assignment.
|
|
358
363
|
// Preserve the original candidates when none route so impossible problems
|
|
359
364
|
// still produce the usual failed-solver result instead of throwing here.
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
365
|
+
const candidateLayers =
|
|
366
|
+
individuallyRoutableLayers.length > 0
|
|
367
|
+
? individuallyRoutableLayers
|
|
368
|
+
: allowedEscapeLayers
|
|
369
|
+
return candidateLayers
|
|
363
370
|
}
|
|
364
371
|
|
|
365
372
|
export class FanoutSolver extends BaseSolver {
|
|
@@ -400,6 +407,24 @@ export class FanoutSolver extends BaseSolver {
|
|
|
400
407
|
this.config = resolveConfig(inputSrj, options)
|
|
401
408
|
this.preparedBuses = prepareFanoutBuses(inputSrj, options)
|
|
402
409
|
for (const bus of this.preparedBuses) {
|
|
410
|
+
for (const allowedLayer of bus.allowedLayers ?? []) {
|
|
411
|
+
if (!this.config.layerNames.includes(allowedLayer)) {
|
|
412
|
+
throw new Error(
|
|
413
|
+
`FanoutSolver: bus "${bus.busId}" allows unavailable layer "${allowedLayer}"`,
|
|
414
|
+
)
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (
|
|
418
|
+
bus.termination.type === "boundary" &&
|
|
419
|
+
bus.allowedLayers !== undefined &&
|
|
420
|
+
!bus.allowedLayers.some((layer) =>
|
|
421
|
+
this.config.escapeLayers.includes(layer),
|
|
422
|
+
)
|
|
423
|
+
) {
|
|
424
|
+
throw new Error(
|
|
425
|
+
`FanoutSolver: bus "${bus.busId}" has no allowed layer in escapeLayers`,
|
|
426
|
+
)
|
|
427
|
+
}
|
|
403
428
|
if (bus.termination.type !== "plane") continue
|
|
404
429
|
const planeLayer = bus.termination.layer
|
|
405
430
|
if (!this.config.layerNames.includes(planeLayer)) {
|
|
@@ -407,6 +432,14 @@ export class FanoutSolver extends BaseSolver {
|
|
|
407
432
|
`FanoutSolver: plane-terminated bus "${bus.busId}" targets unavailable layer "${planeLayer}"`,
|
|
408
433
|
)
|
|
409
434
|
}
|
|
435
|
+
if (
|
|
436
|
+
bus.allowedLayers !== undefined &&
|
|
437
|
+
!bus.allowedLayers.includes(planeLayer)
|
|
438
|
+
) {
|
|
439
|
+
throw new Error(
|
|
440
|
+
`FanoutSolver: plane-terminated bus "${bus.busId}" targets disallowed layer "${planeLayer}"`,
|
|
441
|
+
)
|
|
442
|
+
}
|
|
410
443
|
if (
|
|
411
444
|
bus.connections.some(
|
|
412
445
|
(connection) => connection.sourceLayer === planeLayer,
|
|
@@ -454,7 +487,7 @@ export class FanoutSolver extends BaseSolver {
|
|
|
454
487
|
...fixedPlaneAssignments,
|
|
455
488
|
}))
|
|
456
489
|
this.layerAssignments = prioritizeLayerAssignment({
|
|
457
|
-
|
|
490
|
+
initialAssignment: createInitialLayerAssignment({
|
|
458
491
|
buses: this.preparedBuses,
|
|
459
492
|
escapeLayers: this.config.escapeLayers,
|
|
460
493
|
escapeLayersByBusId,
|
package/lib/prepare-buses.ts
CHANGED
|
@@ -407,6 +407,26 @@ function resolvePreferredExit(
|
|
|
407
407
|
return value
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
+
function resolveAllowedLayers(
|
|
411
|
+
busId: string,
|
|
412
|
+
allowedLayers: readonly string[] | undefined,
|
|
413
|
+
): string[] | undefined {
|
|
414
|
+
if (allowedLayers === undefined) return undefined
|
|
415
|
+
if (allowedLayers.length === 0) {
|
|
416
|
+
throw new Error(
|
|
417
|
+
`FanoutSolver: bus "${busId}" must allow at least one layer`,
|
|
418
|
+
)
|
|
419
|
+
}
|
|
420
|
+
for (const layer of allowedLayers) {
|
|
421
|
+
if (typeof layer !== "string" || layer.length === 0) {
|
|
422
|
+
throw new Error(
|
|
423
|
+
`FanoutSolver: bus "${busId}" has an invalid allowed layer`,
|
|
424
|
+
)
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return [...new Set(allowedLayers)]
|
|
428
|
+
}
|
|
429
|
+
|
|
410
430
|
export function resolveAvailableBoundaryRegions(
|
|
411
431
|
value: readonly FanoutAvailableCornerAndSideInput[] | undefined,
|
|
412
432
|
): AvailableBoundaryRegion[] | undefined {
|
|
@@ -491,6 +511,10 @@ function resolveBusSpecs(
|
|
|
491
511
|
(requestedBus as FanoutBusSpec).preferredExit ??
|
|
492
512
|
options.defaultPreferredExit,
|
|
493
513
|
)
|
|
514
|
+
const allowedLayers = resolveAllowedLayers(
|
|
515
|
+
requestedBus.busId,
|
|
516
|
+
(requestedBus as FanoutBusSpec).allowedLayers,
|
|
517
|
+
)
|
|
494
518
|
if (termination.type === "plane" && preferredExit !== undefined) {
|
|
495
519
|
throw new Error(
|
|
496
520
|
`FanoutSolver: plane-terminated bus "${requestedBus.busId}" cannot also specify preferredExit`,
|
|
@@ -506,6 +530,7 @@ function resolveBusSpecs(
|
|
|
506
530
|
(requestedBus as FanoutBusSpec).direction ??
|
|
507
531
|
options.defaultDirection,
|
|
508
532
|
preferredExit,
|
|
533
|
+
...(allowedLayers === undefined ? {} : { allowedLayers }),
|
|
509
534
|
termination,
|
|
510
535
|
})
|
|
511
536
|
}
|
|
@@ -1020,6 +1045,7 @@ export function prepareFanoutBuses(
|
|
|
1020
1045
|
busId: busSpec.busId,
|
|
1021
1046
|
direction: resolvedExit.direction,
|
|
1022
1047
|
preferredExit: resolvedExit.preferredExit,
|
|
1048
|
+
allowedLayers: busSpec.allowedLayers,
|
|
1023
1049
|
termination: busSpec.termination ?? { type: "boundary" },
|
|
1024
1050
|
connections: preparedConnections,
|
|
1025
1051
|
componentId: sourceGrid.componentId,
|
package/lib/types.ts
CHANGED
|
@@ -76,6 +76,8 @@ export interface FanoutBusSpec extends SimpleRouteBus {
|
|
|
76
76
|
sourceComponentId?: string
|
|
77
77
|
direction?: FanoutDirection
|
|
78
78
|
preferredExit?: FanoutBorderTarget
|
|
79
|
+
/** Layers to which this bus is allowed to escape. */
|
|
80
|
+
allowedLayers?: readonly string[]
|
|
79
81
|
/**
|
|
80
82
|
* Preferred downstream routing point for each connection after it leaves the
|
|
81
83
|
* fanout boundary. This is routing guidance only and does not replace the
|
|
@@ -244,6 +246,8 @@ export interface PreparedBus {
|
|
|
244
246
|
busId: string
|
|
245
247
|
direction: FanoutDirection
|
|
246
248
|
preferredExit?: FanoutBorderTarget
|
|
249
|
+
/** Layers to which this bus is allowed to escape. */
|
|
250
|
+
allowedLayers?: readonly string[]
|
|
247
251
|
termination: FanoutBusTermination
|
|
248
252
|
connections: PreparedConnection[]
|
|
249
253
|
componentId: string
|