@tscircuit/core 0.0.1832 → 0.0.1834
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.js +1406 -658
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -19990,6 +19990,345 @@ function getFanoutExitPositionConfig(exitPosition) {
|
|
|
19990
19990
|
// node_modules/@tscircuit/fanout-solver/lib/fanout-solver.ts
|
|
19991
19991
|
import { BaseSolver } from "@tscircuit/solver-utils";
|
|
19992
19992
|
|
|
19993
|
+
// node_modules/@tscircuit/fanout-solver/lib/select-compatible-candidates.ts
|
|
19994
|
+
function selectCompatibleCandidates(params) {
|
|
19995
|
+
const candidates = params.candidateSets.flat();
|
|
19996
|
+
const count = candidates.length;
|
|
19997
|
+
const compatibility = new Uint8Array(count * count);
|
|
19998
|
+
const emptyDomainIndices = /* @__PURE__ */ new Set();
|
|
19999
|
+
const revisionParent = /* @__PURE__ */ new Map();
|
|
20000
|
+
let nextIndex = 0;
|
|
20001
|
+
const initialDomains = params.candidateSets.map(
|
|
20002
|
+
(set) => set.map(() => nextIndex++)
|
|
20003
|
+
);
|
|
20004
|
+
let searchStates = 0;
|
|
20005
|
+
const compatible = (first, second) => {
|
|
20006
|
+
const index = Math.min(first, second) * count + Math.max(first, second);
|
|
20007
|
+
const cached = compatibility[index];
|
|
20008
|
+
if (cached) return cached === 1;
|
|
20009
|
+
const result2 = params.areCompatible(candidates[first], candidates[second]);
|
|
20010
|
+
compatibility[index] = result2 ? 1 : 2;
|
|
20011
|
+
return result2;
|
|
20012
|
+
};
|
|
20013
|
+
const neighbors = initialDomains.map(() => []);
|
|
20014
|
+
for (let first = 0; first < initialDomains.length; first++) {
|
|
20015
|
+
for (let second = first + 1; second < initialDomains.length; second++) {
|
|
20016
|
+
if (initialDomains[first].some(
|
|
20017
|
+
(a) => initialDomains[second].some((b) => !compatible(a, b))
|
|
20018
|
+
)) {
|
|
20019
|
+
neighbors[first].push(second);
|
|
20020
|
+
neighbors[second].push(first);
|
|
20021
|
+
}
|
|
20022
|
+
}
|
|
20023
|
+
}
|
|
20024
|
+
const propagate = (domains, indices, changed) => {
|
|
20025
|
+
const queue = [];
|
|
20026
|
+
let allDomainsRemainNonempty = true;
|
|
20027
|
+
for (const first of indices) {
|
|
20028
|
+
if (domains[first].length === 0) {
|
|
20029
|
+
emptyDomainIndices.add(first);
|
|
20030
|
+
allDomainsRemainNonempty = false;
|
|
20031
|
+
continue;
|
|
20032
|
+
}
|
|
20033
|
+
for (const second of neighbors[first]) {
|
|
20034
|
+
if (indices.includes(second) && (changed === void 0 || second === changed))
|
|
20035
|
+
queue.push([first, second]);
|
|
20036
|
+
}
|
|
20037
|
+
}
|
|
20038
|
+
for (let cursor = 0; cursor < queue.length; cursor++) {
|
|
20039
|
+
const [first, second] = queue[cursor];
|
|
20040
|
+
if (domains[first].length === 0 || domains[second].length === 0)
|
|
20041
|
+
continue;
|
|
20042
|
+
const retained = domains[first].filter(
|
|
20043
|
+
(candidate) => domains[second].some((other) => compatible(candidate, other))
|
|
20044
|
+
);
|
|
20045
|
+
if (retained.length === domains[first].length) continue;
|
|
20046
|
+
revisionParent.set(first, second);
|
|
20047
|
+
domains[first] = retained;
|
|
20048
|
+
if (retained.length === 0) {
|
|
20049
|
+
emptyDomainIndices.add(first);
|
|
20050
|
+
allDomainsRemainNonempty = false;
|
|
20051
|
+
continue;
|
|
20052
|
+
}
|
|
20053
|
+
for (const neighbor of neighbors[first]) {
|
|
20054
|
+
if (neighbor !== second && indices.includes(neighbor))
|
|
20055
|
+
queue.push([neighbor, first]);
|
|
20056
|
+
}
|
|
20057
|
+
}
|
|
20058
|
+
return allDomainsRemainNonempty;
|
|
20059
|
+
};
|
|
20060
|
+
const allIndices = initialDomains.map((_, index) => index);
|
|
20061
|
+
const getConflictDomainIndices = (seeds) => {
|
|
20062
|
+
const pending = [...seeds];
|
|
20063
|
+
const seen = new Set(pending);
|
|
20064
|
+
for (let cursor = 0; cursor < pending.length; cursor++) {
|
|
20065
|
+
const parent = revisionParent.get(pending[cursor]);
|
|
20066
|
+
if (parent !== void 0 && !seen.has(parent)) {
|
|
20067
|
+
seen.add(parent);
|
|
20068
|
+
pending.push(parent);
|
|
20069
|
+
}
|
|
20070
|
+
}
|
|
20071
|
+
return [...seen];
|
|
20072
|
+
};
|
|
20073
|
+
const rootDomains = initialDomains.map((domain) => [...domain]);
|
|
20074
|
+
if (!propagate(rootDomains, allIndices)) {
|
|
20075
|
+
return {
|
|
20076
|
+
selection: null,
|
|
20077
|
+
searchStates,
|
|
20078
|
+
emptyDomainIndices: [...emptyDomainIndices],
|
|
20079
|
+
conflictDomainIndices: getConflictDomainIndices([...emptyDomainIndices])
|
|
20080
|
+
};
|
|
20081
|
+
}
|
|
20082
|
+
const findLocallyCompatibleSelection = () => {
|
|
20083
|
+
const restartCount = 12;
|
|
20084
|
+
const stepsPerRestart = Math.max(
|
|
20085
|
+
1,
|
|
20086
|
+
Math.min(5e3, Math.floor(params.maximumSearchStates / restartCount))
|
|
20087
|
+
);
|
|
20088
|
+
for (let restart = 0; restart < restartCount; restart++) {
|
|
20089
|
+
const selection = rootDomains.map(
|
|
20090
|
+
(domain, index) => domain[(restart * 17 + index * 7) % domain.length]
|
|
20091
|
+
);
|
|
20092
|
+
for (let step = 0; step < stepsPerRestart; step++) {
|
|
20093
|
+
const conflictCounts = selection.map(() => 0);
|
|
20094
|
+
for (let first = 0; first < selection.length; first++) {
|
|
20095
|
+
for (const second of neighbors[first]) {
|
|
20096
|
+
if (second <= first || compatible(selection[first], selection[second]))
|
|
20097
|
+
continue;
|
|
20098
|
+
conflictCounts[first]++;
|
|
20099
|
+
conflictCounts[second]++;
|
|
20100
|
+
}
|
|
20101
|
+
}
|
|
20102
|
+
const maximumConflictCount = Math.max(...conflictCounts);
|
|
20103
|
+
if (maximumConflictCount === 0) return selection;
|
|
20104
|
+
const conflicted = conflictCounts.map((conflicts, index) => ({ conflicts, index })).filter(({ conflicts }) => conflicts === maximumConflictCount);
|
|
20105
|
+
const selected = conflicted[(step + restart) % conflicted.length].index;
|
|
20106
|
+
const scored = rootDomains[selected].map((candidate, index) => ({
|
|
20107
|
+
candidate,
|
|
20108
|
+
index,
|
|
20109
|
+
conflicts: neighbors[selected].filter(
|
|
20110
|
+
(neighbor) => !compatible(candidate, selection[neighbor])
|
|
20111
|
+
).length
|
|
20112
|
+
})).toSorted(
|
|
20113
|
+
(first, second) => first.conflicts - second.conflicts || (first.index - step - restart) % rootDomains[selected].length - (second.index - step - restart) % rootDomains[selected].length
|
|
20114
|
+
);
|
|
20115
|
+
selection[selected] = scored[0].candidate;
|
|
20116
|
+
}
|
|
20117
|
+
}
|
|
20118
|
+
return null;
|
|
20119
|
+
};
|
|
20120
|
+
const locallyCompatibleSelection = findLocallyCompatibleSelection();
|
|
20121
|
+
if (locallyCompatibleSelection) {
|
|
20122
|
+
return {
|
|
20123
|
+
selection: locallyCompatibleSelection.map((index) => candidates[index]),
|
|
20124
|
+
searchStates,
|
|
20125
|
+
emptyDomainIndices: [],
|
|
20126
|
+
conflictDomainIndices: []
|
|
20127
|
+
};
|
|
20128
|
+
}
|
|
20129
|
+
const search = (domains, indices, changed) => {
|
|
20130
|
+
if (!propagate(domains, indices, changed)) return null;
|
|
20131
|
+
const pending = new Set(
|
|
20132
|
+
indices.filter((index) => domains[index].length > 1)
|
|
20133
|
+
);
|
|
20134
|
+
const components = [];
|
|
20135
|
+
while (pending.size > 0) {
|
|
20136
|
+
const component = [pending.values().next().value];
|
|
20137
|
+
pending.delete(component[0]);
|
|
20138
|
+
for (let cursor = 0; cursor < component.length; cursor++) {
|
|
20139
|
+
for (const neighbor of neighbors[component[cursor]]) {
|
|
20140
|
+
if (pending.delete(neighbor)) component.push(neighbor);
|
|
20141
|
+
}
|
|
20142
|
+
}
|
|
20143
|
+
components.push(component);
|
|
20144
|
+
}
|
|
20145
|
+
if (components.length > 1) {
|
|
20146
|
+
let combined = domains;
|
|
20147
|
+
for (const component of components) {
|
|
20148
|
+
const solved = search(combined.slice(), component);
|
|
20149
|
+
if (!solved) return null;
|
|
20150
|
+
combined = solved;
|
|
20151
|
+
}
|
|
20152
|
+
return combined;
|
|
20153
|
+
}
|
|
20154
|
+
let selected = -1;
|
|
20155
|
+
for (const index of indices) {
|
|
20156
|
+
if (domains[index].length > 1 && (selected < 0 || domains[index].length < domains[selected].length))
|
|
20157
|
+
selected = index;
|
|
20158
|
+
}
|
|
20159
|
+
if (selected < 0) return domains;
|
|
20160
|
+
const orderedCandidates = domains[selected].map((candidate) => {
|
|
20161
|
+
const supportCounts = neighbors[selected].filter(
|
|
20162
|
+
(neighbor) => indices.includes(neighbor) && domains[neighbor].length > 1
|
|
20163
|
+
).map(
|
|
20164
|
+
(neighbor) => domains[neighbor].filter((other) => compatible(candidate, other)).length
|
|
20165
|
+
);
|
|
20166
|
+
return {
|
|
20167
|
+
candidate,
|
|
20168
|
+
minimumSupport: Math.min(...supportCounts),
|
|
20169
|
+
totalSupport: supportCounts.reduce((sum, count2) => sum + count2, 0)
|
|
20170
|
+
};
|
|
20171
|
+
}).toSorted(
|
|
20172
|
+
(first, second) => second.minimumSupport - first.minimumSupport || second.totalSupport - first.totalSupport
|
|
20173
|
+
);
|
|
20174
|
+
for (const { candidate } of orderedCandidates) {
|
|
20175
|
+
if (searchStates >= params.maximumSearchStates) return null;
|
|
20176
|
+
searchStates++;
|
|
20177
|
+
const nextDomains = domains.slice();
|
|
20178
|
+
nextDomains[selected] = [candidate];
|
|
20179
|
+
const result2 = search(nextDomains, indices, selected);
|
|
20180
|
+
if (result2) return result2;
|
|
20181
|
+
}
|
|
20182
|
+
return null;
|
|
20183
|
+
};
|
|
20184
|
+
const result = search(rootDomains, allIndices);
|
|
20185
|
+
const diagnosticIndices = emptyDomainIndices.size > 0 || result ? [...emptyDomainIndices] : [
|
|
20186
|
+
allIndices.toSorted(
|
|
20187
|
+
(first, second) => neighbors[second].length - neighbors[first].length || rootDomains[first].length - rootDomains[second].length
|
|
20188
|
+
)[0]
|
|
20189
|
+
];
|
|
20190
|
+
return {
|
|
20191
|
+
selection: result?.map((domain) => candidates[domain[0]]) ?? null,
|
|
20192
|
+
searchStates,
|
|
20193
|
+
emptyDomainIndices: diagnosticIndices,
|
|
20194
|
+
conflictDomainIndices: result ? [] : getConflictDomainIndices(diagnosticIndices)
|
|
20195
|
+
};
|
|
20196
|
+
}
|
|
20197
|
+
|
|
20198
|
+
// node_modules/@tscircuit/fanout-solver/lib/refine-adaptive-plane-reservations.ts
|
|
20199
|
+
var EPSILON5 = 1e-9;
|
|
20200
|
+
function refineAdaptivePlaneReservationCore(params) {
|
|
20201
|
+
const core = params.candidateBusIds.filter(
|
|
20202
|
+
(busId) => !params.activeBusIds.has(busId)
|
|
20203
|
+
);
|
|
20204
|
+
if (core.length < 2) return core;
|
|
20205
|
+
const terminalIndex = core.length - 1;
|
|
20206
|
+
const terminal = params.planeBuses.find(
|
|
20207
|
+
(bus) => bus.busId === core[terminalIndex]
|
|
20208
|
+
);
|
|
20209
|
+
const source = terminal?.connections[0]?.sourcePoint;
|
|
20210
|
+
if (!terminal || !source || terminal.connections.length !== 1 || terminal.termination.type !== "plane")
|
|
20211
|
+
return core;
|
|
20212
|
+
const terminalLayer = terminal.termination.layer;
|
|
20213
|
+
const perpendicularAxis = terminal.direction === "up" || terminal.direction === "down" ? "x" : "y";
|
|
20214
|
+
const parallelAxis = perpendicularAxis === "x" ? "y" : "x";
|
|
20215
|
+
const componentCoordinates = perpendicularAxis === "x" ? terminal.xCoordinates : terminal.yCoordinates;
|
|
20216
|
+
const pitch = perpendicularAxis === "x" ? terminal.pitchX : terminal.pitchY;
|
|
20217
|
+
if (componentCoordinates.length === 0 || !Number.isFinite(pitch) || pitch <= EPSILON5) {
|
|
20218
|
+
return core;
|
|
20219
|
+
}
|
|
20220
|
+
const componentCenter = (Math.min(...componentCoordinates) + Math.max(...componentCoordinates)) / 2;
|
|
20221
|
+
const inwardSign = Math.sign(componentCenter - source[perpendicularAxis]);
|
|
20222
|
+
if (inwardSign === 0) return core;
|
|
20223
|
+
const coreIds = new Set(core);
|
|
20224
|
+
const replacement = params.planeBuses.filter((bus) => {
|
|
20225
|
+
const candidateSource = bus.connections[0]?.sourcePoint;
|
|
20226
|
+
if (bus === terminal || bus.componentId !== terminal.componentId || bus.termination.type !== "plane" || bus.termination.layer !== terminalLayer || bus.direction !== terminal.direction || bus.connections.length !== 1 || !candidateSource || params.activeBusIds.has(bus.busId) || coreIds.has(bus.busId) || Math.abs(candidateSource[parallelAxis] - source[parallelAxis]) > EPSILON5) {
|
|
20227
|
+
return false;
|
|
20228
|
+
}
|
|
20229
|
+
const inwardDistance = (candidateSource[perpendicularAxis] - source[perpendicularAxis]) * inwardSign;
|
|
20230
|
+
return inwardDistance > EPSILON5 && inwardDistance <= pitch + EPSILON5;
|
|
20231
|
+
}).toSorted((first, second) => {
|
|
20232
|
+
const firstSource = first.connections[0].sourcePoint;
|
|
20233
|
+
const secondSource = second.connections[0].sourcePoint;
|
|
20234
|
+
return Math.abs(firstSource[perpendicularAxis] - source[perpendicularAxis]) - Math.abs(
|
|
20235
|
+
secondSource[perpendicularAxis] - source[perpendicularAxis]
|
|
20236
|
+
) || first.connections[0].connectionIndex - second.connections[0].connectionIndex;
|
|
20237
|
+
})[0];
|
|
20238
|
+
if (!replacement) return core;
|
|
20239
|
+
return core.map(
|
|
20240
|
+
(busId, index) => index === terminalIndex ? replacement.busId : busId
|
|
20241
|
+
);
|
|
20242
|
+
}
|
|
20243
|
+
|
|
20244
|
+
// node_modules/@tscircuit/fanout-solver/lib/should-use-adaptive-dense-plane-routing.ts
|
|
20245
|
+
function getSourceFieldFacingEdge(buses) {
|
|
20246
|
+
const firstBus = buses[0];
|
|
20247
|
+
if (!firstBus || buses.some((bus) => bus.componentId !== firstBus.componentId))
|
|
20248
|
+
return void 0;
|
|
20249
|
+
const sourcePoints = buses.flatMap(
|
|
20250
|
+
(bus) => bus.connections.map((connection) => connection.sourcePoint)
|
|
20251
|
+
);
|
|
20252
|
+
if (sourcePoints.length === 0) return void 0;
|
|
20253
|
+
const center = sourcePoints.reduce(
|
|
20254
|
+
(sum, point6) => ({ x: sum.x + point6.x, y: sum.y + point6.y }),
|
|
20255
|
+
{ x: 0, y: 0 }
|
|
20256
|
+
);
|
|
20257
|
+
center.x /= sourcePoints.length;
|
|
20258
|
+
center.y /= sourcePoints.length;
|
|
20259
|
+
const { minX, maxX, minY, maxY } = firstBus.componentBounds;
|
|
20260
|
+
const edgesByDistance = [
|
|
20261
|
+
{ edge: "left", distance: Math.abs(center.x - minX) },
|
|
20262
|
+
{ edge: "right", distance: Math.abs(maxX - center.x) },
|
|
20263
|
+
{ edge: "bottom", distance: Math.abs(center.y - minY) },
|
|
20264
|
+
{ edge: "top", distance: Math.abs(maxY - center.y) }
|
|
20265
|
+
].toSorted((first, second) => first.distance - second.distance);
|
|
20266
|
+
if (Math.abs(edgesByDistance[0].distance - edgesByDistance[1].distance) <= 1e-9)
|
|
20267
|
+
return void 0;
|
|
20268
|
+
return edgesByDistance[0].edge;
|
|
20269
|
+
}
|
|
20270
|
+
function shouldUseAdaptiveDensePlaneRouting(buses, allowBlindAndBuriedVias) {
|
|
20271
|
+
if (allowBlindAndBuriedVias) return false;
|
|
20272
|
+
const boundaryBuses = buses.filter(
|
|
20273
|
+
(bus) => bus.termination.type === "boundary"
|
|
20274
|
+
);
|
|
20275
|
+
const singletonBoundaryBuses = boundaryBuses.filter(
|
|
20276
|
+
(bus) => bus.connections.length === 1
|
|
20277
|
+
);
|
|
20278
|
+
const pairBoundaryBuses = boundaryBuses.filter(
|
|
20279
|
+
(bus) => bus.connections.length === 2
|
|
20280
|
+
);
|
|
20281
|
+
const wideBoundaryBuses = boundaryBuses.filter(
|
|
20282
|
+
(bus) => bus.connections.length >= 8
|
|
20283
|
+
);
|
|
20284
|
+
const planeCount = buses.filter(
|
|
20285
|
+
(bus) => bus.termination.type === "plane" && bus.connections.length === 1
|
|
20286
|
+
).length;
|
|
20287
|
+
const sharedExitEdge = wideBoundaryBuses[0]?.exitEdge;
|
|
20288
|
+
if (boundaryBuses.length !== 9 || singletonBoundaryBuses.length !== 3 || pairBoundaryBuses.length !== 3 || wideBoundaryBuses.length !== 3 || planeCount < 64 || sharedExitEdge === void 0 || wideBoundaryBuses.some((bus) => bus.exitEdge !== sharedExitEdge))
|
|
20289
|
+
return false;
|
|
20290
|
+
const sourceFacingEdge = getSourceFieldFacingEdge(wideBoundaryBuses);
|
|
20291
|
+
if (sourceFacingEdge === void 0) return false;
|
|
20292
|
+
const sourceFacesHorizontalEdge = sourceFacingEdge === "left" || sourceFacingEdge === "right";
|
|
20293
|
+
const exitUsesHorizontalEdge = sharedExitEdge === "left" || sharedExitEdge === "right";
|
|
20294
|
+
return sourceFacesHorizontalEdge !== exitUsesHorizontalEdge;
|
|
20295
|
+
}
|
|
20296
|
+
|
|
20297
|
+
// node_modules/@tscircuit/fanout-solver/lib/fanout-solver.ts
|
|
20298
|
+
import { mergeGraphics } from "graphics-debug";
|
|
20299
|
+
|
|
20300
|
+
// node_modules/@tscircuit/fanout-solver/lib/add-via-layer-metadata.ts
|
|
20301
|
+
function addViaLayerMetadataToTrace(params) {
|
|
20302
|
+
const { trace, layerNames, allowBlindAndBuriedVias } = params;
|
|
20303
|
+
return {
|
|
20304
|
+
...trace,
|
|
20305
|
+
route: trace.route.map(
|
|
20306
|
+
(routePoint) => routePoint.route_type === "via" && !allowBlindAndBuriedVias ? {
|
|
20307
|
+
...routePoint,
|
|
20308
|
+
layers: getViaSpanLayers2({
|
|
20309
|
+
fromLayer: routePoint.from_layer,
|
|
20310
|
+
toLayer: routePoint.to_layer,
|
|
20311
|
+
layerNames,
|
|
20312
|
+
allowBlindAndBuriedVias
|
|
20313
|
+
})
|
|
20314
|
+
} : { ...routePoint }
|
|
20315
|
+
)
|
|
20316
|
+
};
|
|
20317
|
+
}
|
|
20318
|
+
function addViaLayerMetadataToSrj(params) {
|
|
20319
|
+
const { srj, layerNames, allowBlindAndBuriedVias } = params;
|
|
20320
|
+
return {
|
|
20321
|
+
...srj,
|
|
20322
|
+
traces: (srj.traces ?? []).map(
|
|
20323
|
+
(trace) => addViaLayerMetadataToTrace({
|
|
20324
|
+
trace,
|
|
20325
|
+
layerNames,
|
|
20326
|
+
allowBlindAndBuriedVias
|
|
20327
|
+
})
|
|
20328
|
+
)
|
|
20329
|
+
};
|
|
20330
|
+
}
|
|
20331
|
+
|
|
19993
20332
|
// node_modules/@tscircuit/fanout-solver/lib/boundary-exit.ts
|
|
19994
20333
|
function getExitEdgeForDirection(direction) {
|
|
19995
20334
|
switch (direction) {
|
|
@@ -20027,117 +20366,284 @@ function getCornerBandSide(exitEdge, preferredExit) {
|
|
|
20027
20366
|
return preferredExit.endsWith("-right") ? "maximum" : "minimum";
|
|
20028
20367
|
}
|
|
20029
20368
|
|
|
20030
|
-
// node_modules/@tscircuit/fanout-solver/lib/
|
|
20031
|
-
function
|
|
20032
|
-
const
|
|
20033
|
-
|
|
20034
|
-
|
|
20035
|
-
|
|
20036
|
-
|
|
20037
|
-
|
|
20038
|
-
vias.push({
|
|
20039
|
-
center: { x: routePoint.x, y: routePoint.y },
|
|
20040
|
-
diameter: routePoint.via_diameter ?? srj.minViaPadDiameter ?? srj.min_via_pad_diameter ?? srj.minViaDiameter ?? srj.minTraceWidth,
|
|
20041
|
-
spanLayers: getRouteViaSpanLayers({
|
|
20042
|
-
fromLayer: routePoint.from_layer,
|
|
20043
|
-
toLayer: routePoint.to_layer,
|
|
20044
|
-
layers: "layers" in routePoint && Array.isArray(routePoint.layers) ? routePoint.layers : void 0,
|
|
20045
|
-
layerNames,
|
|
20046
|
-
allowBlindAndBuriedVias
|
|
20047
|
-
})
|
|
20048
|
-
});
|
|
20049
|
-
previousWire = void 0;
|
|
20050
|
-
continue;
|
|
20051
|
-
}
|
|
20052
|
-
if (routePoint.route_type !== "wire") {
|
|
20053
|
-
previousWire = void 0;
|
|
20054
|
-
continue;
|
|
20055
|
-
}
|
|
20056
|
-
if (previousWire?.layer === routePoint.layer && (previousWire.x !== routePoint.x || previousWire.y !== routePoint.y)) {
|
|
20057
|
-
segments.push({
|
|
20058
|
-
start: { x: previousWire.x, y: previousWire.y },
|
|
20059
|
-
end: { x: routePoint.x, y: routePoint.y },
|
|
20060
|
-
width: Math.max(previousWire.width, routePoint.width),
|
|
20061
|
-
layer: routePoint.layer
|
|
20062
|
-
});
|
|
20369
|
+
// node_modules/@tscircuit/fanout-solver/lib/build-output.ts
|
|
20370
|
+
function createViaObstacle(plan, layerNames, via, viaIndex) {
|
|
20371
|
+
const zLayers = via.spanLayers.map((layer) => {
|
|
20372
|
+
const layerIndex = layerNames.indexOf(layer);
|
|
20373
|
+
if (layerIndex < 0) {
|
|
20374
|
+
throw new Error(
|
|
20375
|
+
`FanoutSolver: via for "${plan.connectionName}" uses unknown layer "${layer}"`
|
|
20376
|
+
);
|
|
20063
20377
|
}
|
|
20064
|
-
|
|
20065
|
-
}
|
|
20378
|
+
return layerIndex;
|
|
20379
|
+
});
|
|
20380
|
+
const outputIds = createFanoutOutputIds(plan);
|
|
20066
20381
|
return {
|
|
20067
|
-
|
|
20068
|
-
|
|
20069
|
-
|
|
20070
|
-
|
|
20382
|
+
obstacleId: viaIndex === "endpoint" ? outputIds.planeEndpointViaObstacleId : viaIndex === 0 ? outputIds.viaObstacleId : `${outputIds.viaObstacleId}:${viaIndex}`,
|
|
20383
|
+
type: "rect",
|
|
20384
|
+
center: via.center,
|
|
20385
|
+
width: via.diameter,
|
|
20386
|
+
height: via.diameter,
|
|
20387
|
+
layers: via.spanLayers,
|
|
20388
|
+
zLayers,
|
|
20389
|
+
__zLayers: zLayers,
|
|
20390
|
+
connectedTo: [plan.connectionName, plan.trace.pcb_trace_id]
|
|
20071
20391
|
};
|
|
20072
20392
|
}
|
|
20073
|
-
function
|
|
20074
|
-
|
|
20075
|
-
|
|
20076
|
-
|
|
20077
|
-
}
|
|
20078
|
-
|
|
20079
|
-
|
|
20080
|
-
|
|
20081
|
-
|
|
20082
|
-
|
|
20083
|
-
|
|
20084
|
-
["viaDiameter", rules.viaDiameter],
|
|
20085
|
-
["traceWidth", rules.traceWidth]
|
|
20086
|
-
]) {
|
|
20087
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
20393
|
+
function buildOutputSimpleRouteJson(params) {
|
|
20394
|
+
const { inputSrj, plans, layerNames } = params;
|
|
20395
|
+
const outputConnections = inputSrj.connections.map((connection) => ({
|
|
20396
|
+
...connection,
|
|
20397
|
+
pointsToConnect: connection.pointsToConnect.map((point6) => ({ ...point6 }))
|
|
20398
|
+
}));
|
|
20399
|
+
const viaObstacles = [];
|
|
20400
|
+
const planeTerminatedConnectionNames = /* @__PURE__ */ new Set();
|
|
20401
|
+
for (const plan of plans) {
|
|
20402
|
+
const connection = outputConnections[plan.connectionIndex];
|
|
20403
|
+
if (!connection) {
|
|
20088
20404
|
throw new Error(
|
|
20089
|
-
`FanoutSolver:
|
|
20405
|
+
`FanoutSolver: output connection index ${plan.connectionIndex} is missing`
|
|
20090
20406
|
);
|
|
20091
20407
|
}
|
|
20092
|
-
|
|
20093
|
-
|
|
20094
|
-
|
|
20095
|
-
|
|
20096
|
-
|
|
20097
|
-
|
|
20098
|
-
|
|
20099
|
-
|
|
20100
|
-
|
|
20101
|
-
);
|
|
20102
|
-
}
|
|
20103
|
-
if (rules.holeToHoleClearance !== void 0 && (!Number.isFinite(rules.holeToHoleClearance) || rules.holeToHoleClearance < 0)) {
|
|
20104
|
-
throw new Error(
|
|
20105
|
-
`FanoutSolver: dogbone holeToHoleClearance must be a non-negative finite number, received ${rules.holeToHoleClearance}`
|
|
20106
|
-
);
|
|
20107
|
-
}
|
|
20108
|
-
if (rules.holeToHoleClearance !== void 0 && rules.viaHoleDiameter === void 0) {
|
|
20109
|
-
throw new Error(
|
|
20110
|
-
"FanoutSolver: dogbone holeToHoleClearance requires viaHoleDiameter"
|
|
20111
|
-
);
|
|
20112
|
-
}
|
|
20113
|
-
const maximumSearchStates = rules.maximumSearchStates ?? DEFAULT_MAXIMUM_SEARCH_STATES;
|
|
20114
|
-
if (!Number.isInteger(maximumSearchStates) || maximumSearchStates < 1) {
|
|
20115
|
-
throw new Error(
|
|
20116
|
-
`FanoutSolver: dogbone maximumSearchStates must be a positive integer, received ${maximumSearchStates}`
|
|
20117
|
-
);
|
|
20118
|
-
}
|
|
20119
|
-
return maximumSearchStates;
|
|
20120
|
-
}
|
|
20121
|
-
function uniqueSortedCoordinates(values) {
|
|
20122
|
-
const result = [];
|
|
20123
|
-
for (const value of values.toSorted((first, second) => first - second)) {
|
|
20124
|
-
if (!Number.isFinite(value)) continue;
|
|
20125
|
-
if (result.length === 0 || Math.abs(result.at(-1) - value) > EPSILON5) {
|
|
20126
|
-
result.push(value);
|
|
20408
|
+
const outputIds = createFanoutOutputIds(plan);
|
|
20409
|
+
connection.pointsToConnect[plan.sourcePointIndex] = {
|
|
20410
|
+
x: plan.exitPoint.x,
|
|
20411
|
+
y: plan.exitPoint.y,
|
|
20412
|
+
layer: plan.targetLayer,
|
|
20413
|
+
pointId: plan.termination.type === "plane" ? outputIds.planeExitPointId : outputIds.boundaryExitPointId
|
|
20414
|
+
};
|
|
20415
|
+
if (plan.termination.type === "plane") {
|
|
20416
|
+
planeTerminatedConnectionNames.add(plan.connectionName);
|
|
20127
20417
|
}
|
|
20128
|
-
|
|
20129
|
-
|
|
20130
|
-
|
|
20131
|
-
|
|
20132
|
-
|
|
20133
|
-
|
|
20134
|
-
|
|
20135
|
-
|
|
20136
|
-
|
|
20137
|
-
|
|
20138
|
-
|
|
20139
|
-
|
|
20140
|
-
|
|
20418
|
+
if (plan.via) {
|
|
20419
|
+
const viaObstacle = createViaObstacle(plan, layerNames, plan.via, 0);
|
|
20420
|
+
if (viaObstacle) viaObstacles.push(viaObstacle);
|
|
20421
|
+
}
|
|
20422
|
+
for (const [additionalViaIndex, via] of (plan.additionalVias ?? []).entries()) {
|
|
20423
|
+
const viaObstacle = createViaObstacle(
|
|
20424
|
+
plan,
|
|
20425
|
+
layerNames,
|
|
20426
|
+
via,
|
|
20427
|
+
additionalViaIndex + 1
|
|
20428
|
+
);
|
|
20429
|
+
if (viaObstacle) viaObstacles.push(viaObstacle);
|
|
20430
|
+
}
|
|
20431
|
+
if (plan.planeEndpointVia) {
|
|
20432
|
+
const endpointViaObstacle = createViaObstacle(
|
|
20433
|
+
plan,
|
|
20434
|
+
layerNames,
|
|
20435
|
+
plan.planeEndpointVia,
|
|
20436
|
+
"endpoint"
|
|
20437
|
+
);
|
|
20438
|
+
if (endpointViaObstacle) viaObstacles.push(endpointViaObstacle);
|
|
20439
|
+
}
|
|
20440
|
+
}
|
|
20441
|
+
const planTraces = plans.flatMap((plan) => [
|
|
20442
|
+
plan.trace,
|
|
20443
|
+
...plan.planeEndpointTrace ? [plan.planeEndpointTrace] : []
|
|
20444
|
+
]);
|
|
20445
|
+
const coordinateRoutePoints = planTraces.flatMap(
|
|
20446
|
+
(trace) => trace.route.filter(
|
|
20447
|
+
(routePoint) => "x" in routePoint && "y" in routePoint
|
|
20448
|
+
)
|
|
20449
|
+
);
|
|
20450
|
+
const boundsMargin = Math.max(
|
|
20451
|
+
inputSrj.defaultObstacleMargin ?? 0,
|
|
20452
|
+
inputSrj.minTraceWidth
|
|
20453
|
+
);
|
|
20454
|
+
const outputBounds = coordinateRoutePoints.length === 0 ? { ...inputSrj.bounds } : {
|
|
20455
|
+
minX: Math.min(
|
|
20456
|
+
inputSrj.bounds.minX,
|
|
20457
|
+
...coordinateRoutePoints.map(
|
|
20458
|
+
(routePoint) => routePoint.x - boundsMargin
|
|
20459
|
+
)
|
|
20460
|
+
),
|
|
20461
|
+
maxX: Math.max(
|
|
20462
|
+
inputSrj.bounds.maxX,
|
|
20463
|
+
...coordinateRoutePoints.map(
|
|
20464
|
+
(routePoint) => routePoint.x + boundsMargin
|
|
20465
|
+
)
|
|
20466
|
+
),
|
|
20467
|
+
minY: Math.min(
|
|
20468
|
+
inputSrj.bounds.minY,
|
|
20469
|
+
...coordinateRoutePoints.map(
|
|
20470
|
+
(routePoint) => routePoint.y - boundsMargin
|
|
20471
|
+
)
|
|
20472
|
+
),
|
|
20473
|
+
maxY: Math.max(
|
|
20474
|
+
inputSrj.bounds.maxY,
|
|
20475
|
+
...coordinateRoutePoints.map(
|
|
20476
|
+
(routePoint) => routePoint.y + boundsMargin
|
|
20477
|
+
)
|
|
20478
|
+
)
|
|
20479
|
+
};
|
|
20480
|
+
return {
|
|
20481
|
+
...inputSrj,
|
|
20482
|
+
fanoutPlaneConnectivity: plans.flatMap(
|
|
20483
|
+
(plan) => plan.termination.type === "plane" ? [
|
|
20484
|
+
{
|
|
20485
|
+
connectionName: plan.connectionName,
|
|
20486
|
+
layer: plan.termination.layer
|
|
20487
|
+
}
|
|
20488
|
+
] : []
|
|
20489
|
+
),
|
|
20490
|
+
bounds: outputBounds,
|
|
20491
|
+
connections: outputConnections.filter(
|
|
20492
|
+
(connection) => !planeTerminatedConnectionNames.has(connection.name)
|
|
20493
|
+
),
|
|
20494
|
+
buses: inputSrj.buses?.map((bus) => ({
|
|
20495
|
+
...bus,
|
|
20496
|
+
connectionNames: bus.connectionNames.filter(
|
|
20497
|
+
(connectionName) => !planeTerminatedConnectionNames.has(connectionName)
|
|
20498
|
+
)
|
|
20499
|
+
})).filter((bus) => bus.connectionNames.length > 0),
|
|
20500
|
+
obstacles: [
|
|
20501
|
+
...inputSrj.obstacles.map((obstacle) => ({
|
|
20502
|
+
...obstacle,
|
|
20503
|
+
center: { ...obstacle.center },
|
|
20504
|
+
layers: [...obstacle.layers],
|
|
20505
|
+
connectedTo: [...obstacle.connectedTo]
|
|
20506
|
+
})),
|
|
20507
|
+
...viaObstacles
|
|
20508
|
+
],
|
|
20509
|
+
traces: [
|
|
20510
|
+
...(inputSrj.traces ?? []).map((trace) => ({
|
|
20511
|
+
...trace,
|
|
20512
|
+
route: trace.route.map((routePoint) => ({ ...routePoint }))
|
|
20513
|
+
})),
|
|
20514
|
+
...planTraces
|
|
20515
|
+
]
|
|
20516
|
+
};
|
|
20517
|
+
}
|
|
20518
|
+
|
|
20519
|
+
// node_modules/@tscircuit/fanout-solver/lib/get-routed-trace-copper.ts
|
|
20520
|
+
function getRoutedTraceCopper(srj, trace, allowBlindAndBuriedVias = true) {
|
|
20521
|
+
const layerNames = getCopperLayerNames(srj.layerCount);
|
|
20522
|
+
const segments = [];
|
|
20523
|
+
const vias = [];
|
|
20524
|
+
let previousWire;
|
|
20525
|
+
for (const routePoint of trace.route) {
|
|
20526
|
+
if (routePoint.route_type === "via") {
|
|
20527
|
+
vias.push({
|
|
20528
|
+
center: { x: routePoint.x, y: routePoint.y },
|
|
20529
|
+
diameter: routePoint.via_diameter ?? srj.minViaPadDiameter ?? srj.min_via_pad_diameter ?? srj.minViaDiameter ?? srj.minTraceWidth,
|
|
20530
|
+
spanLayers: getRouteViaSpanLayers({
|
|
20531
|
+
fromLayer: routePoint.from_layer,
|
|
20532
|
+
toLayer: routePoint.to_layer,
|
|
20533
|
+
layers: "layers" in routePoint && Array.isArray(routePoint.layers) ? routePoint.layers : void 0,
|
|
20534
|
+
layerNames,
|
|
20535
|
+
allowBlindAndBuriedVias
|
|
20536
|
+
})
|
|
20537
|
+
});
|
|
20538
|
+
previousWire = void 0;
|
|
20539
|
+
continue;
|
|
20540
|
+
}
|
|
20541
|
+
if (routePoint.route_type !== "wire") {
|
|
20542
|
+
previousWire = void 0;
|
|
20543
|
+
continue;
|
|
20544
|
+
}
|
|
20545
|
+
if (previousWire?.layer === routePoint.layer && (previousWire.x !== routePoint.x || previousWire.y !== routePoint.y)) {
|
|
20546
|
+
segments.push({
|
|
20547
|
+
start: { x: previousWire.x, y: previousWire.y },
|
|
20548
|
+
end: { x: routePoint.x, y: routePoint.y },
|
|
20549
|
+
width: Math.max(previousWire.width, routePoint.width),
|
|
20550
|
+
layer: routePoint.layer
|
|
20551
|
+
});
|
|
20552
|
+
}
|
|
20553
|
+
previousWire = routePoint;
|
|
20554
|
+
}
|
|
20555
|
+
return {
|
|
20556
|
+
trace,
|
|
20557
|
+
connectionName: trace.connection_name,
|
|
20558
|
+
segments,
|
|
20559
|
+
vias
|
|
20560
|
+
};
|
|
20561
|
+
}
|
|
20562
|
+
function getAllRoutedTraceCopper(srj, allowBlindAndBuriedVias = true) {
|
|
20563
|
+
return (srj.traces ?? []).map(
|
|
20564
|
+
(trace) => getRoutedTraceCopper(srj, trace, allowBlindAndBuriedVias)
|
|
20565
|
+
);
|
|
20566
|
+
}
|
|
20567
|
+
|
|
20568
|
+
// node_modules/@tscircuit/fanout-solver/lib/get-dogbone-side-variants.ts
|
|
20569
|
+
function getDogboneSideVariants(connections, direction) {
|
|
20570
|
+
const axis = direction === "up" || direction === "down" ? "y" : "x";
|
|
20571
|
+
const variants = connections.slice(0, 32).map(({ connectionIndex }) => [connectionIndex]);
|
|
20572
|
+
for (let first = 0; first < connections.length; first++) {
|
|
20573
|
+
for (let second = first + 1; second < connections.length; second++) {
|
|
20574
|
+
if (variants.length >= 32) return variants;
|
|
20575
|
+
const a = connections[first];
|
|
20576
|
+
const b = connections[second];
|
|
20577
|
+
if (Math.abs(a.sourcePoint[axis] - b.sourcePoint[axis]) <= 1e-9) {
|
|
20578
|
+
variants.push([a.connectionIndex, b.connectionIndex]);
|
|
20579
|
+
}
|
|
20580
|
+
}
|
|
20581
|
+
}
|
|
20582
|
+
return variants;
|
|
20583
|
+
}
|
|
20584
|
+
|
|
20585
|
+
// node_modules/@tscircuit/fanout-solver/lib/match-component-dogbone-via-sites.ts
|
|
20586
|
+
var EPSILON6 = 1e-9;
|
|
20587
|
+
var DEFAULT_MAXIMUM_SEARCH_STATES = 1e5;
|
|
20588
|
+
function assertGeometryRules(rules) {
|
|
20589
|
+
for (const [name, value] of [
|
|
20590
|
+
["viaDiameter", rules.viaDiameter],
|
|
20591
|
+
["traceWidth", rules.traceWidth]
|
|
20592
|
+
]) {
|
|
20593
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
20594
|
+
throw new Error(
|
|
20595
|
+
`FanoutSolver: dogbone ${name} must be a positive finite number, received ${value}`
|
|
20596
|
+
);
|
|
20597
|
+
}
|
|
20598
|
+
}
|
|
20599
|
+
if (!Number.isFinite(rules.clearance) || rules.clearance < 0) {
|
|
20600
|
+
throw new Error(
|
|
20601
|
+
`FanoutSolver: dogbone clearance must be a non-negative finite number, received ${rules.clearance}`
|
|
20602
|
+
);
|
|
20603
|
+
}
|
|
20604
|
+
if (rules.viaHoleDiameter !== void 0 && (!Number.isFinite(rules.viaHoleDiameter) || rules.viaHoleDiameter <= 0)) {
|
|
20605
|
+
throw new Error(
|
|
20606
|
+
`FanoutSolver: dogbone viaHoleDiameter must be a positive finite number, received ${rules.viaHoleDiameter}`
|
|
20607
|
+
);
|
|
20608
|
+
}
|
|
20609
|
+
if (rules.holeToHoleClearance !== void 0 && (!Number.isFinite(rules.holeToHoleClearance) || rules.holeToHoleClearance < 0)) {
|
|
20610
|
+
throw new Error(
|
|
20611
|
+
`FanoutSolver: dogbone holeToHoleClearance must be a non-negative finite number, received ${rules.holeToHoleClearance}`
|
|
20612
|
+
);
|
|
20613
|
+
}
|
|
20614
|
+
if (rules.holeToHoleClearance !== void 0 && rules.viaHoleDiameter === void 0) {
|
|
20615
|
+
throw new Error(
|
|
20616
|
+
"FanoutSolver: dogbone holeToHoleClearance requires viaHoleDiameter"
|
|
20617
|
+
);
|
|
20618
|
+
}
|
|
20619
|
+
const maximumSearchStates = rules.maximumSearchStates ?? DEFAULT_MAXIMUM_SEARCH_STATES;
|
|
20620
|
+
if (!Number.isInteger(maximumSearchStates) || maximumSearchStates < 1) {
|
|
20621
|
+
throw new Error(
|
|
20622
|
+
`FanoutSolver: dogbone maximumSearchStates must be a positive integer, received ${maximumSearchStates}`
|
|
20623
|
+
);
|
|
20624
|
+
}
|
|
20625
|
+
return maximumSearchStates;
|
|
20626
|
+
}
|
|
20627
|
+
function uniqueSortedCoordinates(values) {
|
|
20628
|
+
const result = [];
|
|
20629
|
+
for (const value of values.toSorted((first, second) => first - second)) {
|
|
20630
|
+
if (!Number.isFinite(value)) continue;
|
|
20631
|
+
if (result.length === 0 || Math.abs(result.at(-1) - value) > EPSILON6) {
|
|
20632
|
+
result.push(value);
|
|
20633
|
+
}
|
|
20634
|
+
}
|
|
20635
|
+
return result;
|
|
20636
|
+
}
|
|
20637
|
+
function getComponentMatchingInputs(preparedBuses) {
|
|
20638
|
+
const byComponent = /* @__PURE__ */ new Map();
|
|
20639
|
+
const componentByConnectionIndex = /* @__PURE__ */ new Map();
|
|
20640
|
+
const obstacleSetByComponentId = /* @__PURE__ */ new Map();
|
|
20641
|
+
for (const bus of preparedBuses) {
|
|
20642
|
+
let component = byComponent.get(bus.componentId);
|
|
20643
|
+
if (!component) {
|
|
20644
|
+
component = {
|
|
20645
|
+
componentId: bus.componentId,
|
|
20646
|
+
connections: [],
|
|
20141
20647
|
obstacles: [],
|
|
20142
20648
|
xCoordinates: [],
|
|
20143
20649
|
yCoordinates: [],
|
|
@@ -20149,10 +20655,10 @@ function getComponentMatchingInputs(preparedBuses) {
|
|
|
20149
20655
|
}
|
|
20150
20656
|
component.xCoordinates.push(...bus.xCoordinates);
|
|
20151
20657
|
component.yCoordinates.push(...bus.yCoordinates);
|
|
20152
|
-
if (Number.isFinite(bus.pitchX) && bus.pitchX >
|
|
20658
|
+
if (Number.isFinite(bus.pitchX) && bus.pitchX > EPSILON6) {
|
|
20153
20659
|
component.pitchX = Math.min(component.pitchX, bus.pitchX);
|
|
20154
20660
|
}
|
|
20155
|
-
if (Number.isFinite(bus.pitchY) && bus.pitchY >
|
|
20661
|
+
if (Number.isFinite(bus.pitchY) && bus.pitchY > EPSILON6) {
|
|
20156
20662
|
component.pitchY = Math.min(component.pitchY, bus.pitchY);
|
|
20157
20663
|
}
|
|
20158
20664
|
const obstacleSet = obstacleSetByComponentId.get(bus.componentId);
|
|
@@ -20199,7 +20705,7 @@ function getComponentMatchingInputs(preparedBuses) {
|
|
|
20199
20705
|
}
|
|
20200
20706
|
function getInterstitialCoordinates(params) {
|
|
20201
20707
|
const { coordinates, pitch } = params;
|
|
20202
|
-
if (coordinates.length === 0 || !Number.isFinite(pitch) || pitch <=
|
|
20708
|
+
if (coordinates.length === 0 || !Number.isFinite(pitch) || pitch <= EPSILON6) {
|
|
20203
20709
|
return [];
|
|
20204
20710
|
}
|
|
20205
20711
|
const interstitialCoordinates = [coordinates[0] - pitch / 2];
|
|
@@ -20217,9 +20723,9 @@ function getAdjacentInterstitialCoordinates(params) {
|
|
|
20217
20723
|
coordinates,
|
|
20218
20724
|
pitch
|
|
20219
20725
|
});
|
|
20220
|
-
const before = interstitialCoordinates.filter((coordinate) => coordinate < sourceCoordinate -
|
|
20726
|
+
const before = interstitialCoordinates.filter((coordinate) => coordinate < sourceCoordinate - EPSILON6).at(-1);
|
|
20221
20727
|
const after = interstitialCoordinates.find(
|
|
20222
|
-
(coordinate) => coordinate > sourceCoordinate +
|
|
20728
|
+
(coordinate) => coordinate > sourceCoordinate + EPSILON6
|
|
20223
20729
|
);
|
|
20224
20730
|
return [before, after].filter(
|
|
20225
20731
|
(coordinate) => coordinate !== void 0
|
|
@@ -20228,25 +20734,25 @@ function getAdjacentInterstitialCoordinates(params) {
|
|
|
20228
20734
|
function directSegmentIsStraightOr45(start, end) {
|
|
20229
20735
|
const absoluteX = Math.abs(end.x - start.x);
|
|
20230
20736
|
const absoluteY = Math.abs(end.y - start.y);
|
|
20231
|
-
return absoluteX <=
|
|
20737
|
+
return absoluteX <= EPSILON6 || absoluteY <= EPSILON6 || Math.abs(absoluteX - absoluteY) <= EPSILON6;
|
|
20232
20738
|
}
|
|
20233
20739
|
function getOutwardRank(params) {
|
|
20234
20740
|
const { source, site, direction } = params;
|
|
20235
20741
|
const outwardDisplacement = direction === "right" ? site.x - source.x : direction === "left" ? source.x - site.x : direction === "up" ? site.y - source.y : source.y - site.y;
|
|
20236
|
-
return outwardDisplacement >
|
|
20742
|
+
return outwardDisplacement > EPSILON6 ? 0 : Math.abs(outwardDisplacement) <= EPSILON6 ? 1 : 2;
|
|
20237
20743
|
}
|
|
20238
20744
|
function viaSiteClearsObstacles(params) {
|
|
20239
20745
|
const { point: point6, obstacles, viaDiameter, clearance } = params;
|
|
20240
20746
|
const requiredClearance = viaDiameter / 2 + clearance;
|
|
20241
20747
|
return obstacles.every(
|
|
20242
|
-
(obstacle) => distancePointToObstacle(point6, obstacle) >= requiredClearance -
|
|
20748
|
+
(obstacle) => distancePointToObstacle(point6, obstacle) >= requiredClearance - EPSILON6
|
|
20243
20749
|
);
|
|
20244
20750
|
}
|
|
20245
20751
|
function sourceSegmentClearsOtherObstacles(params) {
|
|
20246
20752
|
const { segment: segment2, sourceObstacle, obstacles, clearance } = params;
|
|
20247
20753
|
const requiredClearance = segment2.width / 2 + clearance;
|
|
20248
20754
|
return obstacles.every(
|
|
20249
|
-
(obstacle) => obstacle === sourceObstacle || distanceSegmentToObstacle(segment2, obstacle) >= requiredClearance -
|
|
20755
|
+
(obstacle) => obstacle === sourceObstacle || distanceSegmentToObstacle(segment2, obstacle) >= requiredClearance - EPSILON6
|
|
20250
20756
|
);
|
|
20251
20757
|
}
|
|
20252
20758
|
function getConnectionCandidates(params) {
|
|
@@ -20274,7 +20780,7 @@ function getConnectionCandidates(params) {
|
|
|
20274
20780
|
];
|
|
20275
20781
|
const uniquePoints2 = [];
|
|
20276
20782
|
for (const point6 of rawPoints) {
|
|
20277
|
-
if (!uniquePoints2.some((candidate) => distance9(candidate, point6) <=
|
|
20783
|
+
if (!uniquePoints2.some((candidate) => distance9(candidate, point6) <= EPSILON6)) {
|
|
20278
20784
|
uniquePoints2.push(point6);
|
|
20279
20785
|
}
|
|
20280
20786
|
}
|
|
@@ -20325,7 +20831,7 @@ function getConnectionCandidates(params) {
|
|
|
20325
20831
|
point6,
|
|
20326
20832
|
blocker.segment.start,
|
|
20327
20833
|
blocker.segment.end
|
|
20328
|
-
) < viaToTraceClearance -
|
|
20834
|
+
) < viaToTraceClearance - EPSILON6) {
|
|
20329
20835
|
return false;
|
|
20330
20836
|
}
|
|
20331
20837
|
return blocker.segment.layer !== sourceSegment.layer || segmentsAreClear(sourceSegment, blocker.segment, rules.clearance);
|
|
@@ -20337,14 +20843,14 @@ function getConnectionCandidates(params) {
|
|
|
20337
20843
|
if (blocker.connectionIndex === preparedConnection.connectionIndex) {
|
|
20338
20844
|
return true;
|
|
20339
20845
|
}
|
|
20340
|
-
if (distance9(point6, blocker.center) < (rules.viaDiameter + blocker.diameter) / 2 + rules.clearance -
|
|
20846
|
+
if (distance9(point6, blocker.center) < (rules.viaDiameter + blocker.diameter) / 2 + rules.clearance - EPSILON6) {
|
|
20341
20847
|
return false;
|
|
20342
20848
|
}
|
|
20343
20849
|
if (blocker.spanLayers.includes(sourceSegment.layer) && distancePointToSegment(
|
|
20344
20850
|
blocker.center,
|
|
20345
20851
|
sourceSegment.start,
|
|
20346
20852
|
sourceSegment.end
|
|
20347
|
-
) < blocker.diameter / 2 + sourceSegment.width / 2 + rules.clearance -
|
|
20853
|
+
) < blocker.diameter / 2 + sourceSegment.width / 2 + rules.clearance - EPSILON6) {
|
|
20348
20854
|
return false;
|
|
20349
20855
|
}
|
|
20350
20856
|
return true;
|
|
@@ -20361,7 +20867,7 @@ function getConnectionCandidates(params) {
|
|
|
20361
20867
|
const perpendicularCoordinates = direction === "left" || direction === "right" ? component.yCoordinates : component.xCoordinates;
|
|
20362
20868
|
const sourcePerpendicularCoordinate = direction === "left" || direction === "right" ? source.y : source.x;
|
|
20363
20869
|
const perpendicularGridIndex = perpendicularCoordinates.findIndex(
|
|
20364
|
-
(coordinate) => Math.abs(coordinate - sourcePerpendicularCoordinate) <=
|
|
20870
|
+
(coordinate) => Math.abs(coordinate - sourcePerpendicularCoordinate) <= EPSILON6
|
|
20365
20871
|
);
|
|
20366
20872
|
const planeCheckerboardSide = perpendicularGridIndex >= 0 ? perpendicularGridIndex % 2 === 0 ? 1 : -1 : void 0;
|
|
20367
20873
|
const preferredPerpendicularSide = connection.terminationType === "boundary" ? rules.preferredBoundaryPerpendicularSideByBusId?.get(connection.busId) : rules.preferPlaneCheckerboardSites && connection.terminationType === "plane" && (direction === "left" || direction === "right") ? planeCheckerboardSide : void 0;
|
|
@@ -20372,10 +20878,10 @@ function getConnectionCandidates(params) {
|
|
|
20372
20878
|
const getPerpendicularPreferenceRank = (candidate) => {
|
|
20373
20879
|
if (preferredPerpendicularSide === void 0) return 0;
|
|
20374
20880
|
const displacement = direction === "left" || direction === "right" ? candidate.point.y - source.y : candidate.point.x - source.x;
|
|
20375
|
-
return displacement * preferredPerpendicularSide >
|
|
20881
|
+
return displacement * preferredPerpendicularSide > EPSILON6 ? 0 : Math.abs(displacement) <= EPSILON6 ? 1 : 2;
|
|
20376
20882
|
};
|
|
20377
20883
|
return candidates.toSorted(
|
|
20378
|
-
(first, second) => (preferredViaPoint ? Number(distance9(first.point, preferredViaPoint) >
|
|
20884
|
+
(first, second) => (preferredViaPoint ? Number(distance9(first.point, preferredViaPoint) > EPSILON6) - Number(distance9(second.point, preferredViaPoint) > EPSILON6) : 0) || (preferOutward ? first.outwardRank - second.outwardRank : second.outwardRank - first.outwardRank) || getPerpendicularPreferenceRank(first) - getPerpendicularPreferenceRank(second) || distance9(source, first.point) - distance9(source, second.point) || first.point.x - second.point.x || first.point.y - second.point.y
|
|
20379
20885
|
);
|
|
20380
20886
|
}
|
|
20381
20887
|
function candidatesAreMutuallyClear(params) {
|
|
@@ -20383,7 +20889,7 @@ function candidatesAreMutuallyClear(params) {
|
|
|
20383
20889
|
const canShareCopper = rules.canShareCopper?.(first.connectionIndex, second.connectionIndex) ?? false;
|
|
20384
20890
|
const requiredHoleSeparation = rules.viaHoleDiameter ? rules.viaHoleDiameter + (rules.holeToHoleClearance ?? rules.clearance) : 0;
|
|
20385
20891
|
const requiredViaSeparation = canShareCopper ? requiredHoleSeparation : Math.max(rules.viaDiameter + rules.clearance, requiredHoleSeparation);
|
|
20386
|
-
if (distance9(first.point, second.point) < requiredViaSeparation -
|
|
20892
|
+
if (distance9(first.point, second.point) < requiredViaSeparation - EPSILON6) {
|
|
20387
20893
|
return false;
|
|
20388
20894
|
}
|
|
20389
20895
|
const requiredViaToTraceClearance = rules.viaDiameter / 2 + rules.traceWidth / 2 + rules.clearance;
|
|
@@ -20392,11 +20898,11 @@ function candidatesAreMutuallyClear(params) {
|
|
|
20392
20898
|
first.point,
|
|
20393
20899
|
second.sourceSegment.start,
|
|
20394
20900
|
second.sourceSegment.end
|
|
20395
|
-
) < requiredViaToTraceClearance -
|
|
20901
|
+
) < requiredViaToTraceClearance - EPSILON6 || distancePointToSegment(
|
|
20396
20902
|
second.point,
|
|
20397
20903
|
first.sourceSegment.start,
|
|
20398
20904
|
first.sourceSegment.end
|
|
20399
|
-
) < requiredViaToTraceClearance -
|
|
20905
|
+
) < requiredViaToTraceClearance - EPSILON6) {
|
|
20400
20906
|
return false;
|
|
20401
20907
|
}
|
|
20402
20908
|
}
|
|
@@ -20538,7 +21044,7 @@ function getComponentDogboneViaSiteCandidates(preparedBuses, rules) {
|
|
|
20538
21044
|
}
|
|
20539
21045
|
|
|
20540
21046
|
// node_modules/@tscircuit/fanout-solver/lib/route-via-minimal-winding.ts
|
|
20541
|
-
var
|
|
21047
|
+
var EPSILON7 = 1e-7;
|
|
20542
21048
|
var MAX_GRID_NODE_COUNT = 12e4;
|
|
20543
21049
|
var MAX_EXPANDED_STATE_COUNT = 24e4;
|
|
20544
21050
|
var EXPANDED_STATES_PER_STEP = 5e3;
|
|
@@ -20694,7 +21200,7 @@ function getConnectorVariants(start, end) {
|
|
|
20694
21200
|
const deltaY = end.y - start.y;
|
|
20695
21201
|
const absoluteX = Math.abs(deltaX);
|
|
20696
21202
|
const absoluteY = Math.abs(deltaY);
|
|
20697
|
-
if (absoluteX <
|
|
21203
|
+
if (absoluteX < EPSILON7 || absoluteY < EPSILON7 || Math.abs(absoluteX - absoluteY) < EPSILON7) {
|
|
20698
21204
|
return [[start, end]];
|
|
20699
21205
|
}
|
|
20700
21206
|
if (absoluteX > absoluteY) {
|
|
@@ -20729,18 +21235,18 @@ function compressPath(points) {
|
|
|
20729
21235
|
function getSegments(points, width, layer) {
|
|
20730
21236
|
return points.slice(1).flatMap((point6, index) => {
|
|
20731
21237
|
const start = points[index];
|
|
20732
|
-
return distance9(start, point6) <
|
|
21238
|
+
return distance9(start, point6) < EPSILON7 ? [] : [{ start, end: point6, width, layer }];
|
|
20733
21239
|
});
|
|
20734
21240
|
}
|
|
20735
21241
|
function segmentIsStraightOr45Degrees(segment2) {
|
|
20736
21242
|
const deltaX = Math.abs(segment2.end.x - segment2.start.x);
|
|
20737
21243
|
const deltaY = Math.abs(segment2.end.y - segment2.start.y);
|
|
20738
|
-
return deltaX <
|
|
21244
|
+
return deltaX < EPSILON7 || deltaY < EPSILON7 || Math.abs(deltaX - deltaY) < EPSILON7;
|
|
20739
21245
|
}
|
|
20740
21246
|
function pathHasNoProperSelfCrossing(segments) {
|
|
20741
21247
|
for (let firstIndex = 0; firstIndex < segments.length; firstIndex++) {
|
|
20742
21248
|
for (let secondIndex = firstIndex + 2; secondIndex < segments.length; secondIndex++) {
|
|
20743
|
-
if (firstIndex === 0 && secondIndex === segments.length - 1 && distance9(segments[firstIndex].start, segments[secondIndex].end) <
|
|
21249
|
+
if (firstIndex === 0 && secondIndex === segments.length - 1 && distance9(segments[firstIndex].start, segments[secondIndex].end) < EPSILON7) {
|
|
20744
21250
|
continue;
|
|
20745
21251
|
}
|
|
20746
21252
|
if (distanceSegmentToSegment(
|
|
@@ -20748,7 +21254,7 @@ function pathHasNoProperSelfCrossing(segments) {
|
|
|
20748
21254
|
segments[firstIndex].end,
|
|
20749
21255
|
segments[secondIndex].start,
|
|
20750
21256
|
segments[secondIndex].end
|
|
20751
|
-
) <
|
|
21257
|
+
) < EPSILON7) {
|
|
20752
21258
|
return false;
|
|
20753
21259
|
}
|
|
20754
21260
|
}
|
|
@@ -20824,7 +21330,7 @@ function buildPlan(params) {
|
|
|
20824
21330
|
width: traceWidth,
|
|
20825
21331
|
layer: connection.sourceLayer
|
|
20826
21332
|
};
|
|
20827
|
-
const hasSourceDogbone = distance9(sourcePoint, terminal.viaPoint) >
|
|
21333
|
+
const hasSourceDogbone = distance9(sourcePoint, terminal.viaPoint) > EPSILON7;
|
|
20828
21334
|
const changesLayer = connection.sourceLayer !== targetLayer;
|
|
20829
21335
|
const targetSegments = getSegments(targetLayerPoints, traceWidth, targetLayer);
|
|
20830
21336
|
const spanLayers = getViaSpanLayers2({
|
|
@@ -20948,7 +21454,8 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
20948
21454
|
preferTargetDirectedLaneBias = false,
|
|
20949
21455
|
allowSourceLayerRouting = false,
|
|
20950
21456
|
adaptiveRouteOrder = false,
|
|
20951
|
-
alignGridToPads = false
|
|
21457
|
+
alignGridToPads = false,
|
|
21458
|
+
includeReverseTargetRotation = false
|
|
20952
21459
|
} = params;
|
|
20953
21460
|
if (maximumRouteOrderAttempts !== void 0 && (!Number.isInteger(maximumRouteOrderAttempts) || maximumRouteOrderAttempts < 1)) {
|
|
20954
21461
|
throw new Error(
|
|
@@ -21009,6 +21516,18 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21009
21516
|
const margin = (segment2.width + traceWidth) / 2 + clearance;
|
|
21010
21517
|
return !(Math.max(segment2.start.x, segment2.end.x) < minX - margin || Math.min(segment2.start.x, segment2.end.x) > maxX + margin || Math.max(segment2.start.y, segment2.end.y) < minY - margin || Math.min(segment2.start.y, segment2.end.y) > maxY + margin);
|
|
21011
21518
|
});
|
|
21519
|
+
if (allowSourceLayerRouting) {
|
|
21520
|
+
blockingSegments.push(
|
|
21521
|
+
...reservedVias.flatMap(
|
|
21522
|
+
(reserved) => reserved.sourceEscapeSegment?.layer === targetLayer ? [
|
|
21523
|
+
{
|
|
21524
|
+
connectionName: reserved.connectionName,
|
|
21525
|
+
segment: reserved.sourceEscapeSegment
|
|
21526
|
+
}
|
|
21527
|
+
] : []
|
|
21528
|
+
)
|
|
21529
|
+
);
|
|
21530
|
+
}
|
|
21012
21531
|
const blockingVias = blockingCopper.vias.filter(({ via }) => {
|
|
21013
21532
|
if (!via.spanLayers.includes(targetLayer)) return false;
|
|
21014
21533
|
const margin = via.diameter / 2 + traceWidth / 2 + clearance;
|
|
@@ -21065,7 +21584,7 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21065
21584
|
if (obstacle.connectedTo.includes(connectionName) || allowSameNetMerges && obstacleSharesElectricalNet(srj, obstacle, connectionName)) {
|
|
21066
21585
|
continue;
|
|
21067
21586
|
}
|
|
21068
|
-
if (distanceSegmentToObstacle(segment2, obstacle) < requiredObstacleClearance -
|
|
21587
|
+
if (distanceSegmentToObstacle(segment2, obstacle) < requiredObstacleClearance - EPSILON7) {
|
|
21069
21588
|
return false;
|
|
21070
21589
|
}
|
|
21071
21590
|
}
|
|
@@ -21076,7 +21595,7 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21076
21595
|
segment2.end,
|
|
21077
21596
|
blocker.segment.start,
|
|
21078
21597
|
blocker.segment.end
|
|
21079
|
-
) < (segment2.width + blocker.segment.width) / 2 + clearance -
|
|
21598
|
+
) < (segment2.width + blocker.segment.width) / 2 + clearance - EPSILON7) {
|
|
21080
21599
|
return false;
|
|
21081
21600
|
}
|
|
21082
21601
|
}
|
|
@@ -21090,7 +21609,7 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21090
21609
|
segment2.end,
|
|
21091
21610
|
blocker.segment.start,
|
|
21092
21611
|
blocker.segment.end
|
|
21093
|
-
) < (segment2.width + blocker.segment.width) / 2 + clearance -
|
|
21612
|
+
) < (segment2.width + blocker.segment.width) / 2 + clearance - EPSILON7) {
|
|
21094
21613
|
return false;
|
|
21095
21614
|
}
|
|
21096
21615
|
}
|
|
@@ -21110,7 +21629,7 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21110
21629
|
if (blocker.via.center.x < segmentMinX - requiredDistance || blocker.via.center.x > segmentMaxX + requiredDistance || blocker.via.center.y < segmentMinY - requiredDistance || blocker.via.center.y > segmentMaxY + requiredDistance) {
|
|
21111
21630
|
continue;
|
|
21112
21631
|
}
|
|
21113
|
-
if (distancePointToSegment(blocker.via.center, segment2.start, segment2.end) < requiredDistance -
|
|
21632
|
+
if (distancePointToSegment(blocker.via.center, segment2.start, segment2.end) < requiredDistance - EPSILON7) {
|
|
21114
21633
|
return false;
|
|
21115
21634
|
}
|
|
21116
21635
|
}
|
|
@@ -21308,10 +21827,10 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21308
21827
|
let expandedBatchPoints = [];
|
|
21309
21828
|
while (heap.size > 0 && expandedStateCount < MAX_EXPANDED_STATE_COUNT) {
|
|
21310
21829
|
const current = heap.pop();
|
|
21311
|
-
if (current.score >= bestGoalCost -
|
|
21830
|
+
if (current.score >= bestGoalCost - EPSILON7) break;
|
|
21312
21831
|
const state = current.node * 9 + current.direction;
|
|
21313
21832
|
const currentDistance = distances[state];
|
|
21314
|
-
if (current.score > currentDistance + heuristic(nodes[current.node].point) +
|
|
21833
|
+
if (current.score > currentDistance + heuristic(nodes[current.node].point) + EPSILON7)
|
|
21315
21834
|
continue;
|
|
21316
21835
|
expandedStateCount++;
|
|
21317
21836
|
expandedStatesSinceYield++;
|
|
@@ -21339,7 +21858,7 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21339
21858
|
for (const startConnector of startConnectors) {
|
|
21340
21859
|
for (const endConnector of endConnectors) {
|
|
21341
21860
|
const candidateCost = currentDistance - shortestStartLength + startConnector.length + endConnector.length;
|
|
21342
|
-
if (candidateCost >= bestGoalCost -
|
|
21861
|
+
if (candidateCost >= bestGoalCost - EPSILON7) continue;
|
|
21343
21862
|
const points = compressPath([
|
|
21344
21863
|
...startConnector.points,
|
|
21345
21864
|
...gridPoints.slice(1),
|
|
@@ -21385,7 +21904,7 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21385
21904
|
const lanePenalty = laneBias === 0 ? 0 : laneBias > 0 ? Math.max(0, targetTrack - nextTrack) * 0.2 : Math.max(0, nextTrack - targetTrack) * 0.2;
|
|
21386
21905
|
const nextDistance = currentDistance + (deltaColumn !== 0 && deltaRow !== 0 ? gridStep * Math.SQRT2 : gridStep) + (addsTurn ? gridStep * 0.2 : 0) + lanePenalty;
|
|
21387
21906
|
const nextState = nextNode * 9 + directionIndex;
|
|
21388
|
-
if (nextDistance >= distances[nextState] -
|
|
21907
|
+
if (nextDistance >= distances[nextState] - EPSILON7) continue;
|
|
21389
21908
|
const edgeIndex = current.node * 8 + directionIndex;
|
|
21390
21909
|
if (edgeClearance[edgeIndex] === 0) {
|
|
21391
21910
|
const clear = segmentIsClear({
|
|
@@ -21448,9 +21967,9 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21448
21967
|
);
|
|
21449
21968
|
const meanViaTrack = viaTracks.reduce((sum, track) => sum + track, 0) / viaTracks.length;
|
|
21450
21969
|
const meanTargetTrack = targetTracks.reduce((sum, track) => sum + track, 0) / targetTracks.length;
|
|
21451
|
-
const viasAreBeforeTargets = Math.max(...viaTracks) < Math.min(...targetTracks) -
|
|
21452
|
-
const viasAreAfterTargets = Math.min(...viaTracks) > Math.max(...targetTracks) +
|
|
21453
|
-
const laneBiases = preferTargetDirectedLaneBias ? viasAreBeforeTargets ? [0, 1, -1] : viasAreAfterTargets ? [0, -1, 1] : bus.direction === boundaryDirection && meanTargetTrack > meanViaTrack +
|
|
21970
|
+
const viasAreBeforeTargets = Math.max(...viaTracks) < Math.min(...targetTracks) - EPSILON7;
|
|
21971
|
+
const viasAreAfterTargets = Math.min(...viaTracks) > Math.max(...targetTracks) + EPSILON7;
|
|
21972
|
+
const laneBiases = preferTargetDirectedLaneBias ? viasAreBeforeTargets ? [0, 1, -1] : viasAreAfterTargets ? [0, -1, 1] : bus.direction === boundaryDirection && meanTargetTrack > meanViaTrack + EPSILON7 ? [1, 0, -1] : bus.direction === boundaryDirection && meanTargetTrack < meanViaTrack - EPSILON7 ? [-1, 0, 1] : [0, 1, -1] : viasAreBeforeTargets ? [1, 0, -1] : viasAreAfterTargets ? [-1, 0, 1] : [0, 1, -1];
|
|
21454
21973
|
const initialRouteOrderFactories = [];
|
|
21455
21974
|
if (alignGridToPads && preferTargetDirectedLaneBias && viasAreBeforeTargets) {
|
|
21456
21975
|
initialRouteOrderFactories.push(() => targetOrderedTerminals);
|
|
@@ -21462,9 +21981,15 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21462
21981
|
]);
|
|
21463
21982
|
} else if (viasAreAfterTargets) {
|
|
21464
21983
|
initialRouteOrderFactories.push(() => [...targetOrderedTerminals].reverse());
|
|
21984
|
+
if (includeReverseTargetRotation && targetOrderedTerminals.length > 2) {
|
|
21985
|
+
initialRouteOrderFactories.push(() => [
|
|
21986
|
+
...targetOrderedTerminals.slice(0, -1).toReversed(),
|
|
21987
|
+
targetOrderedTerminals.at(-1)
|
|
21988
|
+
]);
|
|
21989
|
+
}
|
|
21465
21990
|
}
|
|
21466
21991
|
initialRouteOrderFactories.push(
|
|
21467
|
-
...preferTargetDirectedLaneBias && bus.direction === boundaryDirection && meanTargetTrack < meanViaTrack -
|
|
21992
|
+
...preferTargetDirectedLaneBias && bus.direction === boundaryDirection && meanTargetTrack < meanViaTrack - EPSILON7 && !viasAreBeforeTargets && !viasAreAfterTargets ? [
|
|
21468
21993
|
() => [...targetOrderedTerminals].reverse(),
|
|
21469
21994
|
() => targetOrderedTerminals
|
|
21470
21995
|
] : [
|
|
@@ -23037,7 +23562,7 @@ function routePlaneTerminatedBus(params) {
|
|
|
23037
23562
|
otherPlans: [...acceptedPlans, ...fixedPlans],
|
|
23038
23563
|
staticClearanceCache,
|
|
23039
23564
|
blockingBusCounts,
|
|
23040
|
-
cacheKey: `plane-fixed:${bus.busId}:${targetLayer}:${preparedConnection.connectionIndex}:${candidateIndex}`,
|
|
23565
|
+
cacheKey: `plane-fixed:${bus.busId}:${targetLayer}:${preparedConnection.connectionIndex}:${fixedViaPoint.x}:${fixedViaPoint.y}:${candidateIndex}`,
|
|
23041
23566
|
srj,
|
|
23042
23567
|
sharedBoundary: bus.sharedBoundary,
|
|
23043
23568
|
clearance,
|
|
@@ -23598,6 +24123,36 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23598
24123
|
const dogboneTerminalPatterns = acceptedBoundaryPlansExist && !allowBlindAndBuriedVias ? [...mixedDogboneTerminalPatterns, ...uniformDogboneTerminalPatterns] : [...uniformDogboneTerminalPatterns, ...mixedDogboneTerminalPatterns];
|
|
23599
24124
|
const unreservedTerminalPatterns = canUseViaInPadTerminals ? planeTerminationsAlreadyOccupyTheFanout ? [viaInPadTerminalPattern, ...dogboneTerminalPatterns] : [...dogboneTerminalPatterns, viaInPadTerminalPattern] : dogboneTerminalPatterns;
|
|
23600
24125
|
const terminalPatterns = fixedViaPointsByConnectionIndex ? fixedViaTerminalPatterns : [...fixedViaTerminalPatterns, ...unreservedTerminalPatterns];
|
|
24126
|
+
if (!fixedViaPointsByConnectionIndex && !allowBlindAndBuriedVias && bus.connections.length > 1 && bus.connections.every(
|
|
24127
|
+
(connection) => connection.sourceLayer !== targetLayer
|
|
24128
|
+
) && viaHandednesses.includes(-1) && viaHandednesses.includes(1)) {
|
|
24129
|
+
const variants = getDogboneSideVariants(bus.connections, bus.direction);
|
|
24130
|
+
for (const handedness of viaHandednesses) {
|
|
24131
|
+
for (const flippedIndices of variants) {
|
|
24132
|
+
terminalPatterns.push({
|
|
24133
|
+
label: `local-dogbone-repair-${handedness}-${flippedIndices.join("-")}`,
|
|
24134
|
+
useViaInPad: false,
|
|
24135
|
+
getViaHandedness: () => handedness,
|
|
24136
|
+
maximumRouteOrderAttempts: 6,
|
|
24137
|
+
localDogboneRepair: true,
|
|
24138
|
+
getViaPoint: (connection) => {
|
|
24139
|
+
const point6 = getInitialViaPoint({
|
|
24140
|
+
preparedConnection: connection,
|
|
24141
|
+
bus,
|
|
24142
|
+
targetLayer,
|
|
24143
|
+
traceWidth,
|
|
24144
|
+
viaDiameter,
|
|
24145
|
+
clearance,
|
|
24146
|
+
viaHandedness: handedness
|
|
24147
|
+
});
|
|
24148
|
+
if (!flippedIndices.includes(connection.connectionIndex))
|
|
24149
|
+
return point6;
|
|
24150
|
+
return bus.direction === "up" || bus.direction === "down" ? { x: point6.x, y: 2 * connection.sourcePoint.y - point6.y } : { x: 2 * connection.sourcePoint.x - point6.x, y: point6.y };
|
|
24151
|
+
}
|
|
24152
|
+
});
|
|
24153
|
+
}
|
|
24154
|
+
}
|
|
24155
|
+
}
|
|
23601
24156
|
const seenTerminalSignatures = /* @__PURE__ */ new Set();
|
|
23602
24157
|
for (const terminalPattern of terminalPatterns) {
|
|
23603
24158
|
const terminals = bus.connections.map((preparedConnection) => {
|
|
@@ -23640,12 +24195,45 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23640
24195
|
};
|
|
23641
24196
|
});
|
|
23642
24197
|
const alignGridToPads = alignWindingGridToPads || Boolean(terminalPattern.getViaPoint && !fixedViaPointsByConnectionIndex);
|
|
24198
|
+
if (terminalPattern.localDogboneRepair && !matchComponentDogboneViaSites([bus], {
|
|
24199
|
+
viaDiameter,
|
|
24200
|
+
viaHoleDiameter,
|
|
24201
|
+
traceWidth,
|
|
24202
|
+
clearance,
|
|
24203
|
+
additionalObstacles: srj.obstacles,
|
|
24204
|
+
fixedViaPointsByConnectionIndex: new Map(
|
|
24205
|
+
terminals.map((terminal) => [
|
|
24206
|
+
terminal.connection.connectionIndex,
|
|
24207
|
+
terminal.viaPoint
|
|
24208
|
+
])
|
|
24209
|
+
),
|
|
24210
|
+
blockingSegments: acceptedPlans.flatMap(
|
|
24211
|
+
(plan) => getPlanSegments(plan).map((segment2) => ({
|
|
24212
|
+
connectionIndex: plan.connectionIndex,
|
|
24213
|
+
segment: segment2
|
|
24214
|
+
}))
|
|
24215
|
+
),
|
|
24216
|
+
blockingVias: [
|
|
24217
|
+
...acceptedPlans.flatMap(
|
|
24218
|
+
(plan) => getPlanVias2(plan).map((via) => ({
|
|
24219
|
+
connectionIndex: plan.connectionIndex,
|
|
24220
|
+
center: via.center,
|
|
24221
|
+
diameter: via.diameter,
|
|
24222
|
+
spanLayers: via.spanLayers
|
|
24223
|
+
}))
|
|
24224
|
+
),
|
|
24225
|
+
// These are future connections, not members of the current bus.
|
|
24226
|
+
...reservedVias.map(({ via }) => ({ connectionIndex: -1, ...via }))
|
|
24227
|
+
]
|
|
24228
|
+
})) {
|
|
24229
|
+
continue;
|
|
24230
|
+
}
|
|
23643
24231
|
const gridStepDivisor = terminalPattern.getViaPoint && Math.min(bus.pitchX, bus.pitchY) - 2 * (viaDiameter / 2 + traceWidth / 2 + clearance) < traceWidth + clearance ? 2 : 1;
|
|
23644
24232
|
const terminalSignature = `${terminals.map(
|
|
23645
24233
|
(terminal) => `${terminal.connection.connectionIndex}:${terminal.viaPoint.x}:${terminal.viaPoint.y}:${terminal.exitPoint.x}:${terminal.exitPoint.y}`
|
|
23646
24234
|
).join(
|
|
23647
24235
|
"|"
|
|
23648
|
-
)}:${terminalPattern.maximumRouteOrderAttempts ?? "all"}:${gridStepDivisor}:${alignGridToPads}`;
|
|
24236
|
+
)}:${terminalPattern.maximumRouteOrderAttempts ?? "all"}:${gridStepDivisor}:${alignGridToPads}:${Boolean(terminalPattern.localDogboneRepair)}`;
|
|
23649
24237
|
if (seenTerminalSignatures.has(terminalSignature)) continue;
|
|
23650
24238
|
seenTerminalSignatures.add(terminalSignature);
|
|
23651
24239
|
const windingSteps = routeViaMinimalWindingAlternativesSteps(
|
|
@@ -23665,6 +24253,7 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23665
24253
|
maximumRouteOrderAttempts: terminalPattern.maximumRouteOrderAttempts,
|
|
23666
24254
|
adaptiveRouteOrder: adaptiveWindingRouteOrder,
|
|
23667
24255
|
alignGridToPads,
|
|
24256
|
+
includeReverseTargetRotation: terminalPattern.localDogboneRepair,
|
|
23668
24257
|
reservedVias,
|
|
23669
24258
|
gridStepDivisor,
|
|
23670
24259
|
preferTargetDirectedLaneBias: terminalPattern.preferTargetDirectedLaneBias
|
|
@@ -23714,41 +24303,46 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23714
24303
|
}
|
|
23715
24304
|
}
|
|
23716
24305
|
}
|
|
23717
|
-
if (alternatives.length === 0 && allowBoundarySideViaFallback && fixedViaPointsByConnectionIndex && bus.connections.length
|
|
23718
|
-
|
|
24306
|
+
if (alternatives.length === 0 && allowBoundarySideViaFallback && fixedViaPointsByConnectionIndex && bus.connections.length <= 2 && bus.exitEdge && bus.connections.every(
|
|
24307
|
+
(connection) => connection.sourceLayer === bus.connections[0].sourceLayer && connection.sourceLayer !== targetLayer
|
|
24308
|
+
)) {
|
|
24309
|
+
const sourceLayer2 = bus.connections[0].sourceLayer;
|
|
23719
24310
|
const boundaryDirection = getDirectionForExitEdge(bus.exitEdge);
|
|
23720
24311
|
const boundaryExitAxis = getExitAxis(bus, boundaryDirection);
|
|
23721
|
-
const
|
|
23722
|
-
bus
|
|
23723
|
-
|
|
23724
|
-
|
|
23725
|
-
|
|
23726
|
-
|
|
23727
|
-
|
|
23728
|
-
|
|
23729
|
-
|
|
23730
|
-
|
|
23731
|
-
|
|
23732
|
-
|
|
23733
|
-
|
|
23734
|
-
|
|
23735
|
-
|
|
23736
|
-
|
|
23737
|
-
|
|
23738
|
-
|
|
23739
|
-
boundaryDirection
|
|
24312
|
+
const finalTracks = bus.connections.map(
|
|
24313
|
+
(preparedConnection) => preferCornerBoundaryVia && getCornerSide(bus) ? getCornerTargetTrack({
|
|
24314
|
+
bus,
|
|
24315
|
+
connection: preparedConnection,
|
|
24316
|
+
cornerExitLaneOffset: cornerLaneOffsets.exit,
|
|
24317
|
+
traceWidth,
|
|
24318
|
+
viaDiameter,
|
|
24319
|
+
clearance,
|
|
24320
|
+
layerNames,
|
|
24321
|
+
targetLayer,
|
|
24322
|
+
cornerBandTargetTrackOffset
|
|
24323
|
+
}) : getBoundaryTargetTrack({
|
|
24324
|
+
bus,
|
|
24325
|
+
connection: preparedConnection,
|
|
24326
|
+
boundaryDirection
|
|
24327
|
+
})
|
|
24328
|
+
);
|
|
24329
|
+
const finalExitPoints = finalTracks.map(
|
|
24330
|
+
(track) => makePoint(boundaryExitAxis, track, boundaryDirection)
|
|
23740
24331
|
);
|
|
23741
24332
|
const sourceLayerSrj = {
|
|
23742
24333
|
...srj,
|
|
23743
|
-
obstacles: srj.obstacles.map(
|
|
23744
|
-
|
|
24334
|
+
obstacles: srj.obstacles.map((obstacle) => {
|
|
24335
|
+
const connection = bus.connections.find(
|
|
24336
|
+
(connection2) => obstacle === connection2.sourceObstacle || distance9(obstacle.center, connection2.sourcePoint) <= 1e-7 && obstacle.layers.includes(connection2.sourceLayer)
|
|
24337
|
+
);
|
|
24338
|
+
return connection ? {
|
|
23745
24339
|
...obstacle,
|
|
23746
24340
|
connectedTo: [
|
|
23747
24341
|
...obstacle.connectedTo,
|
|
23748
|
-
|
|
24342
|
+
connection.connection.name
|
|
23749
24343
|
]
|
|
23750
|
-
} : obstacle
|
|
23751
|
-
)
|
|
24344
|
+
} : obstacle;
|
|
24345
|
+
})
|
|
23752
24346
|
};
|
|
23753
24347
|
const insetStep = Math.max(
|
|
23754
24348
|
viaDiameter / 2 + clearance,
|
|
@@ -23757,34 +24351,104 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23757
24351
|
const cornerSide = preferCornerBoundaryVia ? getCornerSide(bus) : void 0;
|
|
23758
24352
|
const boundaryViaCandidates = [1, 2, 3, 4, 5].flatMap((multiple) => {
|
|
23759
24353
|
const inset = multiple * insetStep;
|
|
23760
|
-
const straight =
|
|
23761
|
-
|
|
23762
|
-
|
|
23763
|
-
|
|
24354
|
+
const straight = finalTracks.map(
|
|
24355
|
+
(finalTrack) => makePoint(
|
|
24356
|
+
boundaryExitAxis - directionSign(boundaryDirection) * inset,
|
|
24357
|
+
finalTrack,
|
|
24358
|
+
boundaryDirection
|
|
24359
|
+
)
|
|
23764
24360
|
);
|
|
23765
24361
|
if (!cornerSide) return [straight];
|
|
23766
24362
|
return [
|
|
23767
|
-
|
|
23768
|
-
|
|
23769
|
-
|
|
23770
|
-
|
|
24363
|
+
finalTracks.map(
|
|
24364
|
+
(finalTrack) => makePoint(
|
|
24365
|
+
boundaryExitAxis - directionSign(boundaryDirection) * inset,
|
|
24366
|
+
finalTrack + (cornerSide === "maximum" ? inset : -inset),
|
|
24367
|
+
boundaryDirection
|
|
24368
|
+
)
|
|
23771
24369
|
),
|
|
23772
24370
|
straight
|
|
23773
24371
|
];
|
|
23774
24372
|
});
|
|
23775
|
-
|
|
24373
|
+
const sourceCenter = {
|
|
24374
|
+
x: bus.connections.reduce(
|
|
24375
|
+
(sum, connection) => sum + connection.sourcePoint.x,
|
|
24376
|
+
0
|
|
24377
|
+
) / bus.connections.length,
|
|
24378
|
+
y: bus.connections.reduce(
|
|
24379
|
+
(sum, connection) => sum + connection.sourcePoint.y,
|
|
24380
|
+
0
|
|
24381
|
+
) / bus.connections.length
|
|
24382
|
+
};
|
|
24383
|
+
const directions = [
|
|
24384
|
+
{ x: -1, y: 0, distance: sourceCenter.x - Math.min(...bus.xCoordinates) },
|
|
24385
|
+
{ x: 1, y: 0, distance: Math.max(...bus.xCoordinates) - sourceCenter.x },
|
|
24386
|
+
{ x: 0, y: -1, distance: sourceCenter.y - Math.min(...bus.yCoordinates) },
|
|
24387
|
+
{ x: 0, y: 1, distance: Math.max(...bus.yCoordinates) - sourceCenter.y }
|
|
24388
|
+
].toSorted((first, second) => first.distance - second.distance);
|
|
24389
|
+
const displacedViaCandidates = bus.connections.length === 2 ? directions.flatMap(
|
|
24390
|
+
(direction) => [3].map(
|
|
24391
|
+
(multiple) => bus.connections.map((connection) => {
|
|
24392
|
+
const original = fixedViaPointsByConnectionIndex.get(
|
|
24393
|
+
connection.connectionIndex
|
|
24394
|
+
);
|
|
24395
|
+
return {
|
|
24396
|
+
x: original.x + direction.x * multiple * bus.pitchX,
|
|
24397
|
+
y: original.y + direction.y * multiple * bus.pitchY
|
|
24398
|
+
};
|
|
24399
|
+
})
|
|
24400
|
+
)
|
|
24401
|
+
) : [];
|
|
24402
|
+
const viaCandidates = [
|
|
24403
|
+
...displacedViaCandidates.map((points) => ({
|
|
24404
|
+
points,
|
|
24405
|
+
boundarySide: false
|
|
24406
|
+
})),
|
|
24407
|
+
...boundaryViaCandidates.map((points) => ({
|
|
24408
|
+
points,
|
|
24409
|
+
boundarySide: true
|
|
24410
|
+
}))
|
|
24411
|
+
];
|
|
24412
|
+
for (const { points: boundaryViaPoints, boundarySide } of viaCandidates) {
|
|
24413
|
+
const boundaryVias = bus.connections.map((connection, index) => ({
|
|
24414
|
+
connectionName: connection.connection.name,
|
|
24415
|
+
via: {
|
|
24416
|
+
center: boundaryViaPoints[index],
|
|
24417
|
+
diameter: viaDiameter,
|
|
24418
|
+
spanLayers: getViaSpanLayers2({
|
|
24419
|
+
fromLayer: sourceLayer2,
|
|
24420
|
+
toLayer: targetLayer,
|
|
24421
|
+
layerNames,
|
|
24422
|
+
allowBlindAndBuriedVias
|
|
24423
|
+
})
|
|
24424
|
+
}
|
|
24425
|
+
}));
|
|
24426
|
+
if (boundaryVias.some(
|
|
24427
|
+
(candidate, index) => boundaryVias.some(
|
|
24428
|
+
(other, otherIndex) => index !== otherIndex && (distance9(candidate.via.center, other.via.center) < viaDiameter + clearance - 1e-9 || boundarySide && distancePointToSegment(
|
|
24429
|
+
candidate.via.center,
|
|
24430
|
+
other.via.center,
|
|
24431
|
+
finalExitPoints[otherIndex]
|
|
24432
|
+
) < viaDiameter / 2 + traceWidth / 2 + clearance - 1e-9)
|
|
24433
|
+
)
|
|
24434
|
+
))
|
|
24435
|
+
continue;
|
|
24436
|
+
if (boundaryVias.some(
|
|
24437
|
+
({ via }) => srj.obstacles.some(
|
|
24438
|
+
(obstacle) => obstacle.layers.some((layer) => via.spanLayers.includes(layer)) && distancePointToObstacle(via.center, obstacle) < via.diameter / 2 + clearance - 1e-9
|
|
24439
|
+
)
|
|
24440
|
+
))
|
|
24441
|
+
continue;
|
|
23776
24442
|
const sourceLayerSteps = routeViaMinimalWindingAlternativesSteps(
|
|
23777
24443
|
{
|
|
23778
24444
|
srj: sourceLayerSrj,
|
|
23779
24445
|
bus,
|
|
23780
|
-
targetLayer:
|
|
23781
|
-
terminals:
|
|
23782
|
-
|
|
23783
|
-
|
|
23784
|
-
|
|
23785
|
-
|
|
23786
|
-
}
|
|
23787
|
-
],
|
|
24446
|
+
targetLayer: sourceLayer2,
|
|
24447
|
+
terminals: bus.connections.map((preparedConnection, index) => ({
|
|
24448
|
+
connection: preparedConnection,
|
|
24449
|
+
viaPoint: preparedConnection.sourcePoint,
|
|
24450
|
+
exitPoint: boundaryViaPoints[index]
|
|
24451
|
+
})),
|
|
23788
24452
|
acceptedPlans,
|
|
23789
24453
|
layerNames,
|
|
23790
24454
|
traceWidth,
|
|
@@ -23793,8 +24457,8 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23793
24457
|
clearance,
|
|
23794
24458
|
allowBlindAndBuriedVias,
|
|
23795
24459
|
allowSameNetMerges,
|
|
23796
|
-
maximumRouteOrderAttempts: 3,
|
|
23797
|
-
reservedVias,
|
|
24460
|
+
maximumRouteOrderAttempts: bus.connections.length === 1 ? 3 : 6,
|
|
24461
|
+
reservedVias: bus.connections.length > 1 ? [...reservedVias, ...boundaryVias] : reservedVias,
|
|
23798
24462
|
gridStepDivisor: 2,
|
|
23799
24463
|
allowSourceLayerRouting: true,
|
|
23800
24464
|
alignGridToPads: true
|
|
@@ -23807,72 +24471,124 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23807
24471
|
yield {
|
|
23808
24472
|
phase: "via-minimal-winding",
|
|
23809
24473
|
busId: bus.busId,
|
|
23810
|
-
targetLayer:
|
|
24474
|
+
targetLayer: sourceLayer2,
|
|
23811
24475
|
winding: sourceLayerResult.value
|
|
23812
24476
|
};
|
|
23813
24477
|
sourceLayerResult = sourceLayerSteps.next();
|
|
23814
24478
|
}
|
|
23815
|
-
const
|
|
23816
|
-
if (!
|
|
23817
|
-
|
|
23818
|
-
|
|
23819
|
-
|
|
23820
|
-
|
|
23821
|
-
|
|
23822
|
-
|
|
23823
|
-
|
|
23824
|
-
|
|
23825
|
-
|
|
23826
|
-
|
|
23827
|
-
|
|
23828
|
-
|
|
23829
|
-
|
|
23830
|
-
|
|
23831
|
-
|
|
23832
|
-
|
|
23833
|
-
|
|
23834
|
-
|
|
23835
|
-
|
|
23836
|
-
|
|
23837
|
-
|
|
23838
|
-
|
|
23839
|
-
|
|
23840
|
-
|
|
23841
|
-
|
|
23842
|
-
|
|
23843
|
-
|
|
23844
|
-
|
|
23845
|
-
|
|
23846
|
-
|
|
23847
|
-
|
|
23848
|
-
|
|
23849
|
-
|
|
23850
|
-
|
|
23851
|
-
|
|
23852
|
-
|
|
23853
|
-
|
|
23854
|
-
|
|
23855
|
-
|
|
23856
|
-
|
|
23857
|
-
|
|
23858
|
-
|
|
23859
|
-
|
|
23860
|
-
|
|
23861
|
-
|
|
23862
|
-
|
|
23863
|
-
|
|
23864
|
-
|
|
23865
|
-
|
|
23866
|
-
|
|
23867
|
-
|
|
23868
|
-
|
|
23869
|
-
|
|
23870
|
-
|
|
23871
|
-
|
|
23872
|
-
|
|
23873
|
-
|
|
24479
|
+
const sourceLayerPlans = sourceLayerResult.value[0];
|
|
24480
|
+
if (!sourceLayerPlans || sourceLayerPlans.length !== bus.connections.length)
|
|
24481
|
+
continue;
|
|
24482
|
+
let targetLayerPlans;
|
|
24483
|
+
if (!boundarySide) {
|
|
24484
|
+
const targetSteps = routeViaMinimalWindingAlternativesSteps(
|
|
24485
|
+
{
|
|
24486
|
+
srj,
|
|
24487
|
+
bus,
|
|
24488
|
+
targetLayer,
|
|
24489
|
+
terminals: bus.connections.map((connection, index) => ({
|
|
24490
|
+
connection,
|
|
24491
|
+
viaPoint: boundaryViaPoints[index],
|
|
24492
|
+
exitPoint: finalExitPoints[index]
|
|
24493
|
+
})),
|
|
24494
|
+
acceptedPlans,
|
|
24495
|
+
layerNames,
|
|
24496
|
+
traceWidth,
|
|
24497
|
+
viaDiameter,
|
|
24498
|
+
viaHoleDiameter,
|
|
24499
|
+
clearance,
|
|
24500
|
+
allowBlindAndBuriedVias,
|
|
24501
|
+
allowSameNetMerges,
|
|
24502
|
+
maximumRouteOrderAttempts: 6,
|
|
24503
|
+
reservedVias,
|
|
24504
|
+
gridStepDivisor: 2,
|
|
24505
|
+
alignGridToPads: true
|
|
24506
|
+
},
|
|
24507
|
+
1,
|
|
24508
|
+
includeVisualization
|
|
24509
|
+
);
|
|
24510
|
+
let targetResult = targetSteps.next();
|
|
24511
|
+
while (!targetResult.done) {
|
|
24512
|
+
yield {
|
|
24513
|
+
phase: "via-minimal-winding",
|
|
24514
|
+
busId: bus.busId,
|
|
24515
|
+
targetLayer,
|
|
24516
|
+
winding: targetResult.value
|
|
24517
|
+
};
|
|
24518
|
+
targetResult = targetSteps.next();
|
|
24519
|
+
}
|
|
24520
|
+
targetLayerPlans = targetResult.value[0];
|
|
24521
|
+
if (!targetLayerPlans) continue;
|
|
24522
|
+
}
|
|
24523
|
+
const plans = sourceLayerPlans.map(
|
|
24524
|
+
(sourceLayerPlan, index) => {
|
|
24525
|
+
const boundaryViaPoint = boundaryViaPoints[index];
|
|
24526
|
+
const finalExitPoint = finalExitPoints[index];
|
|
24527
|
+
const targetSegment = {
|
|
24528
|
+
start: boundaryViaPoint,
|
|
24529
|
+
end: finalExitPoint,
|
|
24530
|
+
width: traceWidth,
|
|
24531
|
+
layer: targetLayer
|
|
24532
|
+
};
|
|
24533
|
+
const targetSegments = targetLayerPlans ? targetLayerPlans[index].segments.filter(
|
|
24534
|
+
(segment2) => segment2.layer === targetLayer
|
|
24535
|
+
) : [targetSegment];
|
|
24536
|
+
const via = {
|
|
24537
|
+
center: boundaryViaPoint,
|
|
24538
|
+
diameter: viaDiameter,
|
|
24539
|
+
holeDiameter: viaHoleDiameter,
|
|
24540
|
+
fromLayer: sourceLayer2,
|
|
24541
|
+
toLayer: targetLayer,
|
|
24542
|
+
spanLayers: getViaSpanLayers2({
|
|
24543
|
+
fromLayer: sourceLayer2,
|
|
24544
|
+
toLayer: targetLayer,
|
|
24545
|
+
layerNames,
|
|
24546
|
+
allowBlindAndBuriedVias
|
|
24547
|
+
})
|
|
24548
|
+
};
|
|
24549
|
+
const sourceRoute = sourceLayerPlan.trace.route.filter(
|
|
24550
|
+
(item) => item.route_type === "wire"
|
|
24551
|
+
);
|
|
24552
|
+
return {
|
|
24553
|
+
...sourceLayerPlan,
|
|
24554
|
+
...preferCornerBoundaryVia || bus.connections.length > 1 ? { sourceEscapeSegmentCount: sourceLayerPlan.segments.length } : {},
|
|
24555
|
+
targetLayer,
|
|
24556
|
+
exitPoint: finalExitPoint,
|
|
24557
|
+
via,
|
|
24558
|
+
segments: [...sourceLayerPlan.segments, ...targetSegments],
|
|
24559
|
+
length: sourceLayerPlan.length + targetSegments.reduce(
|
|
24560
|
+
(sum, segment2) => sum + distance9(segment2.start, segment2.end),
|
|
24561
|
+
0
|
|
24562
|
+
),
|
|
24563
|
+
trace: {
|
|
24564
|
+
...sourceLayerPlan.trace,
|
|
24565
|
+
route: [
|
|
24566
|
+
...sourceRoute,
|
|
24567
|
+
{
|
|
24568
|
+
route_type: "via",
|
|
24569
|
+
...boundaryViaPoint,
|
|
24570
|
+
from_layer: sourceLayer2,
|
|
24571
|
+
to_layer: targetLayer,
|
|
24572
|
+
via_diameter: viaDiameter,
|
|
24573
|
+
via_hole_diameter: viaHoleDiameter
|
|
24574
|
+
},
|
|
24575
|
+
{
|
|
24576
|
+
route_type: "wire",
|
|
24577
|
+
...boundaryViaPoint,
|
|
24578
|
+
width: traceWidth,
|
|
24579
|
+
layer: targetLayer
|
|
24580
|
+
},
|
|
24581
|
+
...targetSegments.map((segment2) => ({
|
|
24582
|
+
route_type: "wire",
|
|
24583
|
+
...segment2.end,
|
|
24584
|
+
width: traceWidth,
|
|
24585
|
+
layer: targetLayer
|
|
24586
|
+
}))
|
|
24587
|
+
]
|
|
24588
|
+
}
|
|
24589
|
+
};
|
|
23874
24590
|
}
|
|
23875
|
-
|
|
24591
|
+
);
|
|
23876
24592
|
const boundaryViaPlansAreClear = fanoutPlansAreClear({
|
|
23877
24593
|
plans: [...acceptedPlans, ...plans],
|
|
23878
24594
|
srj,
|
|
@@ -23881,23 +24597,28 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23881
24597
|
allowBlindAndBuriedVias,
|
|
23882
24598
|
allowSameNetMerges
|
|
23883
24599
|
});
|
|
23884
|
-
const reservedViasAreClear =
|
|
23885
|
-
|
|
23886
|
-
|
|
23887
|
-
reserved.connectionName
|
|
23888
|
-
|
|
23889
|
-
|
|
23890
|
-
|
|
23891
|
-
|
|
23892
|
-
|
|
23893
|
-
|
|
23894
|
-
|
|
23895
|
-
|
|
23896
|
-
|
|
23897
|
-
|
|
23898
|
-
|
|
23899
|
-
|
|
23900
|
-
|
|
24600
|
+
const reservedViasAreClear = plans.every(
|
|
24601
|
+
(plan) => reservedVias.every((reserved) => {
|
|
24602
|
+
const via = plan.via;
|
|
24603
|
+
if (reserved.connectionName === plan.connectionName || allowSameNetMerges && connectionsShareElectricalNet(
|
|
24604
|
+
srj,
|
|
24605
|
+
reserved.connectionName,
|
|
24606
|
+
plan.connectionName
|
|
24607
|
+
))
|
|
24608
|
+
return true;
|
|
24609
|
+
if (via.spanLayers.some(
|
|
24610
|
+
(layer) => reserved.via.spanLayers.includes(layer)
|
|
24611
|
+
) && distance9(via.center, reserved.via.center) < (via.diameter + reserved.via.diameter) / 2 + clearance - 1e-9)
|
|
24612
|
+
return false;
|
|
24613
|
+
return plan.segments.every(
|
|
24614
|
+
(segment2) => !reserved.via.spanLayers.includes(segment2.layer) || distancePointToSegment(
|
|
24615
|
+
reserved.via.center,
|
|
24616
|
+
segment2.start,
|
|
24617
|
+
segment2.end
|
|
24618
|
+
) >= reserved.via.diameter / 2 + traceWidth / 2 + clearance - 1e-9
|
|
24619
|
+
);
|
|
24620
|
+
})
|
|
24621
|
+
);
|
|
23901
24622
|
if (boundaryViaPlansAreClear && reservedViasAreClear) {
|
|
23902
24623
|
addAlternative(plans);
|
|
23903
24624
|
return alternatives;
|
|
@@ -23931,326 +24652,92 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23931
24652
|
})
|
|
23932
24653
|
].filter(
|
|
23933
24654
|
(track, index, tracks) => tracks.findIndex((candidate) => Math.abs(candidate - track) < 1e-9) === index
|
|
23934
|
-
);
|
|
23935
|
-
const trackCandidates = preferredTracks.flatMap(
|
|
23936
|
-
(preferredTrack) => getTrackCandidates({
|
|
23937
|
-
bus,
|
|
23938
|
-
connection: preparedConnection,
|
|
23939
|
-
preferredTrack,
|
|
23940
|
-
traceWidth,
|
|
23941
|
-
clearance
|
|
23942
|
-
})
|
|
23943
|
-
).filter(
|
|
23944
|
-
(track, index, tracks) => tracks.findIndex(
|
|
23945
|
-
(candidate) => Math.abs(candidate.value - track.value) < 1e-9
|
|
23946
|
-
) === index
|
|
23947
|
-
);
|
|
23948
|
-
for (let trackIndex = 0; trackIndex < trackCandidates.length; trackIndex++) {
|
|
23949
|
-
const track = trackCandidates[trackIndex];
|
|
23950
|
-
const plan = buildPlan2({
|
|
23951
|
-
preparedConnection,
|
|
23952
|
-
bus,
|
|
23953
|
-
targetLayer,
|
|
23954
|
-
track: track.value,
|
|
23955
|
-
exitAxis,
|
|
23956
|
-
layerNames,
|
|
23957
|
-
traceWidth,
|
|
23958
|
-
viaDiameter,
|
|
23959
|
-
viaHoleDiameter,
|
|
23960
|
-
viaHandedness,
|
|
23961
|
-
interstitialEscape,
|
|
23962
|
-
spreadLaneIndex: Math.min(
|
|
23963
|
-
connectionRank,
|
|
23964
|
-
bus.connections.length - connectionRank - 1
|
|
23965
|
-
),
|
|
23966
|
-
cornerExitLaneOffset: cornerLaneOffsets.exit,
|
|
23967
|
-
cornerLocalChannelLaneOffset: cornerLaneOffsets.localChannel,
|
|
23968
|
-
cornerBoundaryChannelLaneOffset: cornerLaneOffsets.boundaryChannel,
|
|
23969
|
-
clearance,
|
|
23970
|
-
terminateAtVia: false,
|
|
23971
|
-
allowBlindAndBuriedVias,
|
|
23972
|
-
cornerBandTargetTrackOffset
|
|
23973
|
-
});
|
|
23974
|
-
if (!planIsClear({
|
|
23975
|
-
plan,
|
|
23976
|
-
otherPlans: [...acceptedPlans, ...candidatePlans],
|
|
23977
|
-
staticClearanceCache,
|
|
23978
|
-
blockingBusCounts,
|
|
23979
|
-
cacheKey: `boundary:${bus.busId}:${targetLayer}:${preparedConnection.connectionIndex}:${viaHandedness}:${trackIndex}:${bus.exitEdge ?? "legacy"}:${cornerLaneOffsets.exit}:${cornerLaneOffsets.localChannel}:${cornerLaneOffsets.boundaryChannel}:${cornerBandTargetTrackOffset ?? 0}`,
|
|
23980
|
-
srj,
|
|
23981
|
-
sharedBoundary: bus.sharedBoundary,
|
|
23982
|
-
clearance,
|
|
23983
|
-
allowBlindAndBuriedVias,
|
|
23984
|
-
allowSameNetMerges
|
|
23985
|
-
})) {
|
|
23986
|
-
continue;
|
|
23987
|
-
}
|
|
23988
|
-
searchConnectionOrder(
|
|
23989
|
-
connectionOrder,
|
|
23990
|
-
viaHandedness,
|
|
23991
|
-
connectionIndex + 1,
|
|
23992
|
-
[...candidatePlans, plan]
|
|
23993
|
-
);
|
|
23994
|
-
if (alternatives.length >= maxAlternatives) return;
|
|
23995
|
-
if (maxAlternatives === 1) return;
|
|
23996
|
-
}
|
|
23997
|
-
};
|
|
23998
|
-
for (const viaHandedness of viaHandednesses) {
|
|
23999
|
-
for (const connectionOrder of getConnectionOrders(bus)) {
|
|
24000
|
-
searchConnectionOrder(connectionOrder, viaHandedness, 0, []);
|
|
24001
|
-
if (alternatives.length >= maxAlternatives) return alternatives;
|
|
24002
|
-
}
|
|
24003
|
-
}
|
|
24004
|
-
return alternatives;
|
|
24005
|
-
}
|
|
24006
|
-
function routeBusAlternatives(params, maxAlternatives = 1) {
|
|
24007
|
-
const steps = routeBusAlternativesSteps(params, maxAlternatives);
|
|
24008
|
-
let result = steps.next();
|
|
24009
|
-
while (!result.done) result = steps.next();
|
|
24010
|
-
return result.value;
|
|
24011
|
-
}
|
|
24012
|
-
function routeBus(params) {
|
|
24013
|
-
return routeBusAlternatives(params, 1)[0] ?? null;
|
|
24014
|
-
}
|
|
24015
|
-
|
|
24016
|
-
// node_modules/@tscircuit/fanout-solver/lib/shorten-bus-plans.ts
|
|
24017
|
-
function shortenBusPlans(params) {
|
|
24018
|
-
const { bus } = params;
|
|
24019
|
-
let plans = [...params.plans];
|
|
24020
|
-
if (bus.maxLengthSkew === void 0) return plans;
|
|
24021
|
-
const busPlans = plans.filter((plan) => plan.busId === bus.busId).toSorted((first, second) => second.length - first.length);
|
|
24022
|
-
for (const plan of busPlans) {
|
|
24023
|
-
const minimumLength = Math.min(
|
|
24024
|
-
...plans.filter((p) => p.busId === bus.busId).map((p) => p.length)
|
|
24025
|
-
);
|
|
24026
|
-
if (plan.length - minimumLength <= bus.maxLengthSkew) continue;
|
|
24027
|
-
if (!plan.via || plan.additionalVias?.length || plan.planeEndpointVia || plan.segments.filter((segment2) => segment2.layer === plan.sourceLayer).length !== 1)
|
|
24028
|
-
continue;
|
|
24029
|
-
const connection = bus.connections.find(
|
|
24030
|
-
(c) => c.connectionIndex === plan.connectionIndex
|
|
24031
|
-
);
|
|
24032
|
-
for (const alignGridToPads of [true, false]) {
|
|
24033
|
-
const candidate = routeViaMinimalWinding({
|
|
24034
|
-
...params,
|
|
24035
|
-
bus: {
|
|
24036
|
-
...bus,
|
|
24037
|
-
connections: [connection],
|
|
24038
|
-
routableEscapeLayers: [plan.targetLayer]
|
|
24039
|
-
},
|
|
24040
|
-
terminals: [
|
|
24041
|
-
{ connection, viaPoint: plan.via.center, exitPoint: plan.exitPoint }
|
|
24042
|
-
],
|
|
24043
|
-
targetLayer: plan.targetLayer,
|
|
24044
|
-
acceptedPlans: plans.filter((p) => p !== plan),
|
|
24045
|
-
allowBlindAndBuriedVias: false,
|
|
24046
|
-
gridStepDivisor: 2,
|
|
24047
|
-
alignGridToPads,
|
|
24048
|
-
maximumRouteOrderAttempts: 1
|
|
24049
|
-
})?.[0];
|
|
24050
|
-
if (!candidate || candidate.length >= plan.length - 1e-6) continue;
|
|
24051
|
-
const nextPlans = plans.map((p) => p === plan ? candidate : p);
|
|
24052
|
-
if (!fanoutPlansAreClear({
|
|
24053
|
-
...params,
|
|
24054
|
-
plans: nextPlans,
|
|
24055
|
-
allowBlindAndBuriedVias: false
|
|
24056
|
-
}))
|
|
24057
|
-
continue;
|
|
24058
|
-
plans = nextPlans;
|
|
24059
|
-
break;
|
|
24060
|
-
}
|
|
24061
|
-
}
|
|
24062
|
-
return plans;
|
|
24063
|
-
}
|
|
24064
|
-
|
|
24065
|
-
// node_modules/@tscircuit/fanout-solver/lib/fanout-solver.ts
|
|
24066
|
-
import { mergeGraphics } from "graphics-debug";
|
|
24067
|
-
|
|
24068
|
-
// node_modules/@tscircuit/fanout-solver/lib/add-via-layer-metadata.ts
|
|
24069
|
-
function addViaLayerMetadataToTrace(params) {
|
|
24070
|
-
const { trace, layerNames, allowBlindAndBuriedVias } = params;
|
|
24071
|
-
return {
|
|
24072
|
-
...trace,
|
|
24073
|
-
route: trace.route.map(
|
|
24074
|
-
(routePoint) => routePoint.route_type === "via" && !allowBlindAndBuriedVias ? {
|
|
24075
|
-
...routePoint,
|
|
24076
|
-
layers: getViaSpanLayers2({
|
|
24077
|
-
fromLayer: routePoint.from_layer,
|
|
24078
|
-
toLayer: routePoint.to_layer,
|
|
24079
|
-
layerNames,
|
|
24080
|
-
allowBlindAndBuriedVias
|
|
24081
|
-
})
|
|
24082
|
-
} : { ...routePoint }
|
|
24083
|
-
)
|
|
24084
|
-
};
|
|
24085
|
-
}
|
|
24086
|
-
function addViaLayerMetadataToSrj(params) {
|
|
24087
|
-
const { srj, layerNames, allowBlindAndBuriedVias } = params;
|
|
24088
|
-
return {
|
|
24089
|
-
...srj,
|
|
24090
|
-
traces: (srj.traces ?? []).map(
|
|
24091
|
-
(trace) => addViaLayerMetadataToTrace({
|
|
24092
|
-
trace,
|
|
24093
|
-
layerNames,
|
|
24094
|
-
allowBlindAndBuriedVias
|
|
24095
|
-
})
|
|
24096
|
-
)
|
|
24097
|
-
};
|
|
24098
|
-
}
|
|
24099
|
-
|
|
24100
|
-
// node_modules/@tscircuit/fanout-solver/lib/build-output.ts
|
|
24101
|
-
function createViaObstacle(plan, layerNames, via, viaIndex) {
|
|
24102
|
-
const zLayers = via.spanLayers.map((layer) => {
|
|
24103
|
-
const layerIndex = layerNames.indexOf(layer);
|
|
24104
|
-
if (layerIndex < 0) {
|
|
24105
|
-
throw new Error(
|
|
24106
|
-
`FanoutSolver: via for "${plan.connectionName}" uses unknown layer "${layer}"`
|
|
24107
|
-
);
|
|
24108
|
-
}
|
|
24109
|
-
return layerIndex;
|
|
24110
|
-
});
|
|
24111
|
-
const outputIds = createFanoutOutputIds(plan);
|
|
24112
|
-
return {
|
|
24113
|
-
obstacleId: viaIndex === "endpoint" ? outputIds.planeEndpointViaObstacleId : viaIndex === 0 ? outputIds.viaObstacleId : `${outputIds.viaObstacleId}:${viaIndex}`,
|
|
24114
|
-
type: "rect",
|
|
24115
|
-
center: via.center,
|
|
24116
|
-
width: via.diameter,
|
|
24117
|
-
height: via.diameter,
|
|
24118
|
-
layers: via.spanLayers,
|
|
24119
|
-
zLayers,
|
|
24120
|
-
__zLayers: zLayers,
|
|
24121
|
-
connectedTo: [plan.connectionName, plan.trace.pcb_trace_id]
|
|
24122
|
-
};
|
|
24123
|
-
}
|
|
24124
|
-
function buildOutputSimpleRouteJson(params) {
|
|
24125
|
-
const { inputSrj, plans, layerNames } = params;
|
|
24126
|
-
const outputConnections = inputSrj.connections.map((connection) => ({
|
|
24127
|
-
...connection,
|
|
24128
|
-
pointsToConnect: connection.pointsToConnect.map((point6) => ({ ...point6 }))
|
|
24129
|
-
}));
|
|
24130
|
-
const viaObstacles = [];
|
|
24131
|
-
const planeTerminatedConnectionNames = /* @__PURE__ */ new Set();
|
|
24132
|
-
for (const plan of plans) {
|
|
24133
|
-
const connection = outputConnections[plan.connectionIndex];
|
|
24134
|
-
if (!connection) {
|
|
24135
|
-
throw new Error(
|
|
24136
|
-
`FanoutSolver: output connection index ${plan.connectionIndex} is missing`
|
|
24137
|
-
);
|
|
24138
|
-
}
|
|
24139
|
-
const outputIds = createFanoutOutputIds(plan);
|
|
24140
|
-
connection.pointsToConnect[plan.sourcePointIndex] = {
|
|
24141
|
-
x: plan.exitPoint.x,
|
|
24142
|
-
y: plan.exitPoint.y,
|
|
24143
|
-
layer: plan.targetLayer,
|
|
24144
|
-
pointId: plan.termination.type === "plane" ? outputIds.planeExitPointId : outputIds.boundaryExitPointId
|
|
24145
|
-
};
|
|
24146
|
-
if (plan.termination.type === "plane") {
|
|
24147
|
-
planeTerminatedConnectionNames.add(plan.connectionName);
|
|
24148
|
-
}
|
|
24149
|
-
if (plan.via) {
|
|
24150
|
-
const viaObstacle = createViaObstacle(plan, layerNames, plan.via, 0);
|
|
24151
|
-
if (viaObstacle) viaObstacles.push(viaObstacle);
|
|
24152
|
-
}
|
|
24153
|
-
for (const [additionalViaIndex, via] of (plan.additionalVias ?? []).entries()) {
|
|
24154
|
-
const viaObstacle = createViaObstacle(
|
|
24155
|
-
plan,
|
|
24655
|
+
);
|
|
24656
|
+
const trackCandidates = preferredTracks.flatMap(
|
|
24657
|
+
(preferredTrack) => getTrackCandidates({
|
|
24658
|
+
bus,
|
|
24659
|
+
connection: preparedConnection,
|
|
24660
|
+
preferredTrack,
|
|
24661
|
+
traceWidth,
|
|
24662
|
+
clearance
|
|
24663
|
+
})
|
|
24664
|
+
).filter(
|
|
24665
|
+
(track, index, tracks) => tracks.findIndex(
|
|
24666
|
+
(candidate) => Math.abs(candidate.value - track.value) < 1e-9
|
|
24667
|
+
) === index
|
|
24668
|
+
);
|
|
24669
|
+
for (let trackIndex = 0; trackIndex < trackCandidates.length; trackIndex++) {
|
|
24670
|
+
const track = trackCandidates[trackIndex];
|
|
24671
|
+
const plan = buildPlan2({
|
|
24672
|
+
preparedConnection,
|
|
24673
|
+
bus,
|
|
24674
|
+
targetLayer,
|
|
24675
|
+
track: track.value,
|
|
24676
|
+
exitAxis,
|
|
24156
24677
|
layerNames,
|
|
24157
|
-
|
|
24158
|
-
|
|
24159
|
-
|
|
24160
|
-
|
|
24161
|
-
|
|
24162
|
-
|
|
24163
|
-
|
|
24678
|
+
traceWidth,
|
|
24679
|
+
viaDiameter,
|
|
24680
|
+
viaHoleDiameter,
|
|
24681
|
+
viaHandedness,
|
|
24682
|
+
interstitialEscape,
|
|
24683
|
+
spreadLaneIndex: Math.min(
|
|
24684
|
+
connectionRank,
|
|
24685
|
+
bus.connections.length - connectionRank - 1
|
|
24686
|
+
),
|
|
24687
|
+
cornerExitLaneOffset: cornerLaneOffsets.exit,
|
|
24688
|
+
cornerLocalChannelLaneOffset: cornerLaneOffsets.localChannel,
|
|
24689
|
+
cornerBoundaryChannelLaneOffset: cornerLaneOffsets.boundaryChannel,
|
|
24690
|
+
clearance,
|
|
24691
|
+
terminateAtVia: false,
|
|
24692
|
+
allowBlindAndBuriedVias,
|
|
24693
|
+
cornerBandTargetTrackOffset
|
|
24694
|
+
});
|
|
24695
|
+
if (!planIsClear({
|
|
24164
24696
|
plan,
|
|
24165
|
-
|
|
24166
|
-
|
|
24167
|
-
|
|
24697
|
+
otherPlans: [...acceptedPlans, ...candidatePlans],
|
|
24698
|
+
staticClearanceCache,
|
|
24699
|
+
blockingBusCounts,
|
|
24700
|
+
cacheKey: `boundary:${bus.busId}:${targetLayer}:${preparedConnection.connectionIndex}:${viaHandedness}:${trackIndex}:${bus.exitEdge ?? "legacy"}:${cornerLaneOffsets.exit}:${cornerLaneOffsets.localChannel}:${cornerLaneOffsets.boundaryChannel}:${cornerBandTargetTrackOffset ?? 0}`,
|
|
24701
|
+
srj,
|
|
24702
|
+
sharedBoundary: bus.sharedBoundary,
|
|
24703
|
+
clearance,
|
|
24704
|
+
allowBlindAndBuriedVias,
|
|
24705
|
+
allowSameNetMerges
|
|
24706
|
+
})) {
|
|
24707
|
+
continue;
|
|
24708
|
+
}
|
|
24709
|
+
searchConnectionOrder(
|
|
24710
|
+
connectionOrder,
|
|
24711
|
+
viaHandedness,
|
|
24712
|
+
connectionIndex + 1,
|
|
24713
|
+
[...candidatePlans, plan]
|
|
24168
24714
|
);
|
|
24169
|
-
if (
|
|
24715
|
+
if (alternatives.length >= maxAlternatives) return;
|
|
24716
|
+
if (maxAlternatives === 1) return;
|
|
24170
24717
|
}
|
|
24171
|
-
}
|
|
24172
|
-
const planTraces = plans.flatMap((plan) => [
|
|
24173
|
-
plan.trace,
|
|
24174
|
-
...plan.planeEndpointTrace ? [plan.planeEndpointTrace] : []
|
|
24175
|
-
]);
|
|
24176
|
-
const coordinateRoutePoints = planTraces.flatMap(
|
|
24177
|
-
(trace) => trace.route.filter(
|
|
24178
|
-
(routePoint) => "x" in routePoint && "y" in routePoint
|
|
24179
|
-
)
|
|
24180
|
-
);
|
|
24181
|
-
const boundsMargin = Math.max(
|
|
24182
|
-
inputSrj.defaultObstacleMargin ?? 0,
|
|
24183
|
-
inputSrj.minTraceWidth
|
|
24184
|
-
);
|
|
24185
|
-
const outputBounds = coordinateRoutePoints.length === 0 ? { ...inputSrj.bounds } : {
|
|
24186
|
-
minX: Math.min(
|
|
24187
|
-
inputSrj.bounds.minX,
|
|
24188
|
-
...coordinateRoutePoints.map(
|
|
24189
|
-
(routePoint) => routePoint.x - boundsMargin
|
|
24190
|
-
)
|
|
24191
|
-
),
|
|
24192
|
-
maxX: Math.max(
|
|
24193
|
-
inputSrj.bounds.maxX,
|
|
24194
|
-
...coordinateRoutePoints.map(
|
|
24195
|
-
(routePoint) => routePoint.x + boundsMargin
|
|
24196
|
-
)
|
|
24197
|
-
),
|
|
24198
|
-
minY: Math.min(
|
|
24199
|
-
inputSrj.bounds.minY,
|
|
24200
|
-
...coordinateRoutePoints.map(
|
|
24201
|
-
(routePoint) => routePoint.y - boundsMargin
|
|
24202
|
-
)
|
|
24203
|
-
),
|
|
24204
|
-
maxY: Math.max(
|
|
24205
|
-
inputSrj.bounds.maxY,
|
|
24206
|
-
...coordinateRoutePoints.map(
|
|
24207
|
-
(routePoint) => routePoint.y + boundsMargin
|
|
24208
|
-
)
|
|
24209
|
-
)
|
|
24210
|
-
};
|
|
24211
|
-
return {
|
|
24212
|
-
...inputSrj,
|
|
24213
|
-
fanoutPlaneConnectivity: plans.flatMap(
|
|
24214
|
-
(plan) => plan.termination.type === "plane" ? [
|
|
24215
|
-
{
|
|
24216
|
-
connectionName: plan.connectionName,
|
|
24217
|
-
layer: plan.termination.layer
|
|
24218
|
-
}
|
|
24219
|
-
] : []
|
|
24220
|
-
),
|
|
24221
|
-
bounds: outputBounds,
|
|
24222
|
-
connections: outputConnections.filter(
|
|
24223
|
-
(connection) => !planeTerminatedConnectionNames.has(connection.name)
|
|
24224
|
-
),
|
|
24225
|
-
buses: inputSrj.buses?.map((bus) => ({
|
|
24226
|
-
...bus,
|
|
24227
|
-
connectionNames: bus.connectionNames.filter(
|
|
24228
|
-
(connectionName) => !planeTerminatedConnectionNames.has(connectionName)
|
|
24229
|
-
)
|
|
24230
|
-
})).filter((bus) => bus.connectionNames.length > 0),
|
|
24231
|
-
obstacles: [
|
|
24232
|
-
...inputSrj.obstacles.map((obstacle) => ({
|
|
24233
|
-
...obstacle,
|
|
24234
|
-
center: { ...obstacle.center },
|
|
24235
|
-
layers: [...obstacle.layers],
|
|
24236
|
-
connectedTo: [...obstacle.connectedTo]
|
|
24237
|
-
})),
|
|
24238
|
-
...viaObstacles
|
|
24239
|
-
],
|
|
24240
|
-
traces: [
|
|
24241
|
-
...(inputSrj.traces ?? []).map((trace) => ({
|
|
24242
|
-
...trace,
|
|
24243
|
-
route: trace.route.map((routePoint) => ({ ...routePoint }))
|
|
24244
|
-
})),
|
|
24245
|
-
...planTraces
|
|
24246
|
-
]
|
|
24247
24718
|
};
|
|
24719
|
+
for (const viaHandedness of viaHandednesses) {
|
|
24720
|
+
for (const connectionOrder of getConnectionOrders(bus)) {
|
|
24721
|
+
searchConnectionOrder(connectionOrder, viaHandedness, 0, []);
|
|
24722
|
+
if (alternatives.length >= maxAlternatives) return alternatives;
|
|
24723
|
+
}
|
|
24724
|
+
}
|
|
24725
|
+
return alternatives;
|
|
24726
|
+
}
|
|
24727
|
+
function routeBusAlternatives(params, maxAlternatives = 1) {
|
|
24728
|
+
const steps = routeBusAlternativesSteps(params, maxAlternatives);
|
|
24729
|
+
let result = steps.next();
|
|
24730
|
+
while (!result.done) result = steps.next();
|
|
24731
|
+
return result.value;
|
|
24732
|
+
}
|
|
24733
|
+
function routeBus(params) {
|
|
24734
|
+
return routeBusAlternatives(params, 1)[0] ?? null;
|
|
24248
24735
|
}
|
|
24249
24736
|
|
|
24250
24737
|
// node_modules/@tscircuit/fanout-solver/lib/match-bus-lengths.ts
|
|
24251
|
-
var
|
|
24738
|
+
var EPSILON8 = 1e-6;
|
|
24252
24739
|
function pointsMatch2(first, second) {
|
|
24253
|
-
return distance9(first, second) <=
|
|
24740
|
+
return distance9(first, second) <= EPSILON8;
|
|
24254
24741
|
}
|
|
24255
24742
|
function getPlanVias3(plan) {
|
|
24256
24743
|
return [plan.via, ...plan.additionalVias ?? []].filter(
|
|
@@ -24355,26 +24842,26 @@ function splitSegmentAtDenseBounds(params) {
|
|
|
24355
24842
|
const deltaY = segment2.end.y - segment2.start.y;
|
|
24356
24843
|
const splitParameters = [0, 1];
|
|
24357
24844
|
const addSplitParameter = (parameter) => {
|
|
24358
|
-
if (parameter <=
|
|
24845
|
+
if (parameter <= EPSILON8 || parameter >= 1 - EPSILON8) return;
|
|
24359
24846
|
const point6 = {
|
|
24360
24847
|
x: segment2.start.x + deltaX * parameter,
|
|
24361
24848
|
y: segment2.start.y + deltaY * parameter
|
|
24362
24849
|
};
|
|
24363
|
-
if (point6.x < expandedBounds.minX -
|
|
24850
|
+
if (point6.x < expandedBounds.minX - EPSILON8 || point6.x > expandedBounds.maxX + EPSILON8 || point6.y < expandedBounds.minY - EPSILON8 || point6.y > expandedBounds.maxY + EPSILON8) {
|
|
24364
24851
|
return;
|
|
24365
24852
|
}
|
|
24366
24853
|
splitParameters.push(parameter);
|
|
24367
24854
|
};
|
|
24368
|
-
if (Math.abs(deltaX) >
|
|
24855
|
+
if (Math.abs(deltaX) > EPSILON8) {
|
|
24369
24856
|
addSplitParameter((expandedBounds.minX - segment2.start.x) / deltaX);
|
|
24370
24857
|
addSplitParameter((expandedBounds.maxX - segment2.start.x) / deltaX);
|
|
24371
24858
|
}
|
|
24372
|
-
if (Math.abs(deltaY) >
|
|
24859
|
+
if (Math.abs(deltaY) > EPSILON8) {
|
|
24373
24860
|
addSplitParameter((expandedBounds.minY - segment2.start.y) / deltaY);
|
|
24374
24861
|
addSplitParameter((expandedBounds.maxY - segment2.start.y) / deltaY);
|
|
24375
24862
|
}
|
|
24376
24863
|
const parameters = splitParameters.toSorted((first, second) => first - second).filter(
|
|
24377
|
-
(parameter, index, values) => index === 0 || Math.abs(parameter - values[index - 1]) >
|
|
24864
|
+
(parameter, index, values) => index === 0 || Math.abs(parameter - values[index - 1]) > EPSILON8
|
|
24378
24865
|
);
|
|
24379
24866
|
return parameters.slice(1).map((endParameter, index) => {
|
|
24380
24867
|
const startParameter = parameters[index];
|
|
@@ -24421,7 +24908,7 @@ function hasNonAdjacentSelfIntersection(segments) {
|
|
|
24421
24908
|
first.end,
|
|
24422
24909
|
second.start,
|
|
24423
24910
|
second.end
|
|
24424
|
-
) <=
|
|
24911
|
+
) <= EPSILON8) {
|
|
24425
24912
|
return true;
|
|
24426
24913
|
}
|
|
24427
24914
|
}
|
|
@@ -24463,7 +24950,7 @@ function replacementCopperIsSelfClear(params) {
|
|
|
24463
24950
|
const replacement = segments[replacementIndex];
|
|
24464
24951
|
for (const [otherIndex, other] of segments.entries()) {
|
|
24465
24952
|
const requiredCenterlineClearance = replacement.width / 2 + other.width / 2 + clearance;
|
|
24466
|
-
if (getConnectedPathDistance(replacementIndex, otherIndex) <= requiredCenterlineClearance +
|
|
24953
|
+
if (getConnectedPathDistance(replacementIndex, otherIndex) <= requiredCenterlineClearance + EPSILON8) {
|
|
24467
24954
|
continue;
|
|
24468
24955
|
}
|
|
24469
24956
|
if (!segmentsAreClear(replacement, other, clearance)) return false;
|
|
@@ -24473,7 +24960,7 @@ function replacementCopperIsSelfClear(params) {
|
|
|
24473
24960
|
if (pointsMatch2(via.center, replacement.start) || pointsMatch2(via.center, replacement.end)) {
|
|
24474
24961
|
continue;
|
|
24475
24962
|
}
|
|
24476
|
-
if (distancePointToSegment(via.center, replacement.start, replacement.end) < via.diameter / 2 + replacement.width / 2 + clearance -
|
|
24963
|
+
if (distancePointToSegment(via.center, replacement.start, replacement.end) < via.diameter / 2 + replacement.width / 2 + clearance - EPSILON8) {
|
|
24477
24964
|
return false;
|
|
24478
24965
|
}
|
|
24479
24966
|
}
|
|
@@ -24481,7 +24968,7 @@ function replacementCopperIsSelfClear(params) {
|
|
|
24481
24968
|
return true;
|
|
24482
24969
|
}
|
|
24483
24970
|
function pointIsInsideBounds2(point6, bounds) {
|
|
24484
|
-
return point6.x >= bounds.minX -
|
|
24971
|
+
return point6.x >= bounds.minX - EPSILON8 && point6.x <= bounds.maxX + EPSILON8 && point6.y >= bounds.minY - EPSILON8 && point6.y <= bounds.maxY + EPSILON8;
|
|
24485
24972
|
}
|
|
24486
24973
|
function addScaled(point6, vector2, scale3) {
|
|
24487
24974
|
return {
|
|
@@ -24501,9 +24988,9 @@ function createMeanderPoints(params) {
|
|
|
24501
24988
|
const dx = segment2.end.x - segment2.start.x;
|
|
24502
24989
|
const dy = segment2.end.y - segment2.start.y;
|
|
24503
24990
|
const segmentLength = Math.hypot(dx, dy);
|
|
24504
|
-
if (segmentLength <=
|
|
24505
|
-
const isAxisAligned = Math.abs(dx) <=
|
|
24506
|
-
const isFortyFiveDegree = Math.abs(Math.abs(dx) - Math.abs(dy)) <=
|
|
24991
|
+
if (segmentLength <= EPSILON8) return null;
|
|
24992
|
+
const isAxisAligned = Math.abs(dx) <= EPSILON8 || Math.abs(dy) <= EPSILON8;
|
|
24993
|
+
const isFortyFiveDegree = Math.abs(Math.abs(dx) - Math.abs(dy)) <= EPSILON8;
|
|
24507
24994
|
if (!isAxisAligned && !isFortyFiveDegree) return null;
|
|
24508
24995
|
const tangent = { x: dx / segmentLength, y: dy / segmentLength };
|
|
24509
24996
|
const normal = {
|
|
@@ -24519,11 +25006,11 @@ function createMeanderPoints(params) {
|
|
|
24519
25006
|
const toothGap = pitch;
|
|
24520
25007
|
const occupiedLength = toothCount * toothSpan + Math.max(0, toothCount - 1) * toothGap;
|
|
24521
25008
|
const minimumLead = pitch / 4;
|
|
24522
|
-
if (occupiedLength + minimumLead * 2 > segmentLength +
|
|
25009
|
+
if (occupiedLength + minimumLead * 2 > segmentLength + EPSILON8) {
|
|
24523
25010
|
return null;
|
|
24524
25011
|
}
|
|
24525
25012
|
const minimumAddedLengthPerTooth = 4 * chamfer * (Math.SQRT2 - 1);
|
|
24526
|
-
if (targetAddedLength +
|
|
25013
|
+
if (targetAddedLength + EPSILON8 < minimumAddedLengthPerTooth * toothCount) {
|
|
24527
25014
|
return null;
|
|
24528
25015
|
}
|
|
24529
25016
|
const verticalRun = (targetAddedLength / toothCount - minimumAddedLengthPerTooth) / 2;
|
|
@@ -24656,10 +25143,10 @@ function* createSpreadLaneCandidates(plan, clearance) {
|
|
|
24656
25143
|
(a, b) => distance9(b.segment.start, b.segment.end) - distance9(a.segment.start, a.segment.end)
|
|
24657
25144
|
)) {
|
|
24658
25145
|
const length7 = distance9(segment2.start, segment2.end);
|
|
24659
|
-
if (length7 <=
|
|
25146
|
+
if (length7 <= EPSILON8) continue;
|
|
24660
25147
|
const dx = Math.abs(segment2.end.x - segment2.start.x);
|
|
24661
25148
|
const dy = Math.abs(segment2.end.y - segment2.start.y);
|
|
24662
|
-
if (dx >
|
|
25149
|
+
if (dx > EPSILON8 && dy > EPSILON8 && Math.abs(dx - dy) > EPSILON8) continue;
|
|
24663
25150
|
const tangent = {
|
|
24664
25151
|
x: (segment2.end.x - segment2.start.x) / length7,
|
|
24665
25152
|
y: (segment2.end.y - segment2.start.y) / length7
|
|
@@ -24729,15 +25216,15 @@ function matchBusPlanLengths(params) {
|
|
|
24729
25216
|
}
|
|
24730
25217
|
const maxLengthSkew = bus.maxLengthSkew;
|
|
24731
25218
|
const skew = getBusSkew(busPlans);
|
|
24732
|
-
if (skew <= maxLengthSkew +
|
|
25219
|
+
if (skew <= maxLengthSkew + EPSILON8) break;
|
|
24733
25220
|
const shortest = busPlans.toSorted(
|
|
24734
25221
|
(first, second) => first.length - second.length || first.connectionName.localeCompare(second.connectionName)
|
|
24735
25222
|
)[0];
|
|
24736
25223
|
const longestLength = Math.max(...busPlans.map((plan) => plan.length));
|
|
24737
25224
|
const deficit = longestLength - shortest.length;
|
|
24738
25225
|
const minimumRequiredAddition = Math.max(
|
|
24739
|
-
|
|
24740
|
-
deficit - maxLengthSkew +
|
|
25226
|
+
EPSILON8,
|
|
25227
|
+
deficit - maxLengthSkew + EPSILON8
|
|
24741
25228
|
);
|
|
24742
25229
|
const targetAddedLengths = [
|
|
24743
25230
|
minimumRequiredAddition,
|
|
@@ -24747,8 +25234,8 @@ function matchBusPlanLengths(params) {
|
|
|
24747
25234
|
deficit,
|
|
24748
25235
|
deficit + maxLengthSkew
|
|
24749
25236
|
].filter(
|
|
24750
|
-
(value, index, values) => value >
|
|
24751
|
-
(candidate) => Math.abs(candidate - value) <
|
|
25237
|
+
(value, index, values) => value > EPSILON8 && values.findIndex(
|
|
25238
|
+
(candidate) => Math.abs(candidate - value) < EPSILON8
|
|
24752
25239
|
) === index
|
|
24753
25240
|
).toSorted((first, second) => first - second);
|
|
24754
25241
|
let acceptedPlans = null;
|
|
@@ -24759,7 +25246,7 @@ function matchBusPlanLengths(params) {
|
|
|
24759
25246
|
const nextBusPlans = nextPlans.filter(
|
|
24760
25247
|
(plan) => plan.busId === bus.busId
|
|
24761
25248
|
);
|
|
24762
|
-
if (getBusSkew(nextBusPlans) > skew +
|
|
25249
|
+
if (getBusSkew(nextBusPlans) > skew + EPSILON8) return null;
|
|
24763
25250
|
if (!fanoutPlansAreClear({
|
|
24764
25251
|
plans: nextPlans,
|
|
24765
25252
|
srj: inputSrj,
|
|
@@ -24793,7 +25280,7 @@ function matchBusPlanLengths(params) {
|
|
|
24793
25280
|
const search = (currentPlan, stagesRemaining) => {
|
|
24794
25281
|
const addedLength = currentPlan.length - shortest.length;
|
|
24795
25282
|
const remainingAddition = targetAddedLength - addedLength;
|
|
24796
|
-
if (remainingAddition <=
|
|
25283
|
+
if (remainingAddition <= EPSILON8) {
|
|
24797
25284
|
return acceptCandidate(currentPlan);
|
|
24798
25285
|
}
|
|
24799
25286
|
if (stagesRemaining <= 0) return null;
|
|
@@ -24838,7 +25325,7 @@ function matchBusPlanLengths(params) {
|
|
|
24838
25325
|
if (!acceptedPlans) continue;
|
|
24839
25326
|
break;
|
|
24840
25327
|
}
|
|
24841
|
-
if (!acceptedPlans && Math.abs(targetAddedLength - minimumRequiredAddition) <=
|
|
25328
|
+
if (!acceptedPlans && Math.abs(targetAddedLength - minimumRequiredAddition) <= EPSILON8) {
|
|
24842
25329
|
acceptedPlans = findMultiSpanCandidate(targetAddedLength);
|
|
24843
25330
|
}
|
|
24844
25331
|
if (acceptedPlans) break;
|
|
@@ -24880,7 +25367,7 @@ function matchBusPlanLengths(params) {
|
|
|
24880
25367
|
const matchedBusPlans = matchedPlans.filter(
|
|
24881
25368
|
(plan) => plan.busId === bus.busId
|
|
24882
25369
|
);
|
|
24883
|
-
if (getBusSkew(matchedBusPlans) > bus.maxLengthSkew +
|
|
25370
|
+
if (getBusSkew(matchedBusPlans) > bus.maxLengthSkew + EPSILON8) {
|
|
24884
25371
|
return { plans: null, failedBus: bus };
|
|
24885
25372
|
}
|
|
24886
25373
|
}
|
|
@@ -25862,7 +26349,7 @@ function prepareFanoutBuses(srj, options) {
|
|
|
25862
26349
|
}
|
|
25863
26350
|
|
|
25864
26351
|
// node_modules/@tscircuit/fanout-solver/lib/route-single-layer-adaptive-exits.ts
|
|
25865
|
-
var
|
|
26352
|
+
var EPSILON9 = 1e-9;
|
|
25866
26353
|
var OBSTACLE_BIN_SIZE = 1;
|
|
25867
26354
|
var FANOUT_FLOW_DEBUG_ENABLED = globalThis.process?.env?.FANOUT_FLOW_DEBUG === "1";
|
|
25868
26355
|
function visualizeFlowProgress(params) {
|
|
@@ -25943,7 +26430,7 @@ function connectWith45DegreeSegments(start, end) {
|
|
|
25943
26430
|
const deltaY = end.y - start.y;
|
|
25944
26431
|
const absoluteX = Math.abs(deltaX);
|
|
25945
26432
|
const absoluteY = Math.abs(deltaY);
|
|
25946
|
-
if (absoluteX <
|
|
26433
|
+
if (absoluteX < EPSILON9 || absoluteY < EPSILON9 || Math.abs(absoluteX - absoluteY) < EPSILON9) {
|
|
25947
26434
|
return [start, end];
|
|
25948
26435
|
}
|
|
25949
26436
|
if (absoluteX > absoluteY) {
|
|
@@ -25972,7 +26459,7 @@ function enforceStraightOr45DegreeSegments(points) {
|
|
|
25972
26459
|
const start = normalized.at(-1);
|
|
25973
26460
|
const deltaX = Math.abs(end.x - start.x);
|
|
25974
26461
|
const deltaY = Math.abs(end.y - start.y);
|
|
25975
|
-
if (deltaX <
|
|
26462
|
+
if (deltaX < EPSILON9 || deltaY < EPSILON9 || Math.abs(deltaX - deltaY) < EPSILON9) {
|
|
25976
26463
|
normalized.push(end);
|
|
25977
26464
|
continue;
|
|
25978
26465
|
}
|
|
@@ -26005,7 +26492,7 @@ function chamferOrthogonalPolyline3(points, requestedChamfer) {
|
|
|
26005
26492
|
const next = points[index + 1];
|
|
26006
26493
|
const incomingLength = distance9(previous, corner);
|
|
26007
26494
|
const outgoingLength = distance9(corner, next);
|
|
26008
|
-
if (incomingLength <
|
|
26495
|
+
if (incomingLength < EPSILON9 || outgoingLength < EPSILON9) continue;
|
|
26009
26496
|
const incoming = {
|
|
26010
26497
|
x: (corner.x - previous.x) / incomingLength,
|
|
26011
26498
|
y: (corner.y - previous.y) / incomingLength
|
|
@@ -26038,7 +26525,7 @@ function chamferOrthogonalPolyline3(points, requestedChamfer) {
|
|
|
26038
26525
|
function getSegments2(points, traceWidth) {
|
|
26039
26526
|
const segments = [];
|
|
26040
26527
|
for (let index = 1; index < points.length; index++) {
|
|
26041
|
-
if (distance9(points[index - 1], points[index]) <
|
|
26528
|
+
if (distance9(points[index - 1], points[index]) < EPSILON9) continue;
|
|
26042
26529
|
segments.push({
|
|
26043
26530
|
start: points[index - 1],
|
|
26044
26531
|
end: points[index],
|
|
@@ -26189,7 +26676,7 @@ function* createFlowGridSteps(params) {
|
|
|
26189
26676
|
for (let node = 0; node < nodeCount; node++) {
|
|
26190
26677
|
const point6 = points[node];
|
|
26191
26678
|
if (getNearbyObstacles(point6).every(
|
|
26192
|
-
(obstacle) => distancePointToObstacle(point6, obstacle) >= requiredObstacleDistance -
|
|
26679
|
+
(obstacle) => distancePointToObstacle(point6, obstacle) >= requiredObstacleDistance - EPSILON9
|
|
26193
26680
|
)) {
|
|
26194
26681
|
obstacleFreeNodes[node] = 1;
|
|
26195
26682
|
}
|
|
@@ -26228,7 +26715,7 @@ function* createFlowGridSteps(params) {
|
|
|
26228
26715
|
layer: "top"
|
|
26229
26716
|
};
|
|
26230
26717
|
if (getNearbyObstacles(segment2.start, segment2.end).some(
|
|
26231
|
-
(obstacle) => distanceSegmentToObstacle(segment2, obstacle) < requiredObstacleDistance -
|
|
26718
|
+
(obstacle) => distanceSegmentToObstacle(segment2, obstacle) < requiredObstacleDistance - EPSILON9
|
|
26232
26719
|
)) {
|
|
26233
26720
|
continue;
|
|
26234
26721
|
}
|
|
@@ -26293,7 +26780,7 @@ function* routeDirectionGroupSteps(params) {
|
|
|
26293
26780
|
const segments = getSegments2(points, traceWidth);
|
|
26294
26781
|
return segments.every(
|
|
26295
26782
|
(segment2) => obstacles.every(
|
|
26296
|
-
(obstacle) => obstacleBelongsToItem(obstacle, item) || distanceSegmentToObstacle(segment2, obstacle) >= requiredObstacleDistance -
|
|
26783
|
+
(obstacle) => obstacleBelongsToItem(obstacle, item) || distanceSegmentToObstacle(segment2, obstacle) >= requiredObstacleDistance - EPSILON9
|
|
26297
26784
|
)
|
|
26298
26785
|
) && segments.every(
|
|
26299
26786
|
(segment2) => acceptedSegments.every(
|
|
@@ -26302,7 +26789,7 @@ function* routeDirectionGroupSteps(params) {
|
|
|
26302
26789
|
segment2.end,
|
|
26303
26790
|
acceptedSegment.start,
|
|
26304
26791
|
acceptedSegment.end
|
|
26305
|
-
) >= requiredRouteDistance -
|
|
26792
|
+
) >= requiredRouteDistance - EPSILON9
|
|
26306
26793
|
)
|
|
26307
26794
|
);
|
|
26308
26795
|
};
|
|
@@ -26392,7 +26879,7 @@ function* routeDirectionGroupSteps(params) {
|
|
|
26392
26879
|
segment2.end,
|
|
26393
26880
|
otherSegment.start,
|
|
26394
26881
|
otherSegment.end
|
|
26395
|
-
) >= requiredRouteDistance -
|
|
26882
|
+
) >= requiredRouteDistance - EPSILON9
|
|
26396
26883
|
)
|
|
26397
26884
|
)
|
|
26398
26885
|
);
|
|
@@ -26460,7 +26947,7 @@ function* routeDirectionGroupSteps(params) {
|
|
|
26460
26947
|
const node = row * columnCount + column;
|
|
26461
26948
|
if (!freeNodes[node] || terminalNodes.has(node)) continue;
|
|
26462
26949
|
const point6 = pointForNode(node);
|
|
26463
|
-
if (distanceSegmentToSegment(segment2.start, segment2.end, point6, point6) < requiredRouteDistance -
|
|
26950
|
+
if (distanceSegmentToSegment(segment2.start, segment2.end, point6, point6) < requiredRouteDistance - EPSILON9) {
|
|
26464
26951
|
freeNodes[node] = 0;
|
|
26465
26952
|
}
|
|
26466
26953
|
}
|
|
@@ -26680,7 +27167,7 @@ function buildPlan3(route, traceWidth) {
|
|
|
26680
27167
|
function getConnectorVariants2(start, end) {
|
|
26681
27168
|
const first = connectWith45DegreeSegments(start, end);
|
|
26682
27169
|
const second = connectWith45DegreeSegments(end, start).reverse();
|
|
26683
|
-
return first.length === second.length && first.every((point6, index) => distance9(point6, second[index]) <
|
|
27170
|
+
return first.length === second.length && first.every((point6, index) => distance9(point6, second[index]) < EPSILON9) ? [first] : [first, second];
|
|
26684
27171
|
}
|
|
26685
27172
|
function plansHaveRequiredClearance(params) {
|
|
26686
27173
|
const { plans, items, obstacles, traceWidth, clearance } = params;
|
|
@@ -26707,7 +27194,7 @@ function plansHaveRequiredClearance(params) {
|
|
|
26707
27194
|
for (const [netKey, segments] of uniqueSegmentsByNet) {
|
|
26708
27195
|
for (const segment2 of segments) {
|
|
26709
27196
|
for (const obstacle of obstacles) {
|
|
26710
|
-
if (obstacle.connectedTo.includes(netKey) || distanceSegmentToObstacle(segment2, obstacle) >= requiredObstacleDistance -
|
|
27197
|
+
if (obstacle.connectedTo.includes(netKey) || distanceSegmentToObstacle(segment2, obstacle) >= requiredObstacleDistance - EPSILON9) {
|
|
26711
27198
|
continue;
|
|
26712
27199
|
}
|
|
26713
27200
|
if (FANOUT_FLOW_DEBUG_ENABLED) {
|
|
@@ -26734,7 +27221,7 @@ function plansHaveRequiredClearance(params) {
|
|
|
26734
27221
|
first.end,
|
|
26735
27222
|
second.start,
|
|
26736
27223
|
second.end
|
|
26737
|
-
) < requiredRouteDistance -
|
|
27224
|
+
) < requiredRouteDistance - EPSILON9) {
|
|
26738
27225
|
if (FANOUT_FLOW_DEBUG_ENABLED) {
|
|
26739
27226
|
console.error("single-layer routes violate copper clearance", {
|
|
26740
27227
|
firstNetKey: entries[firstIndex][0],
|
|
@@ -26759,10 +27246,10 @@ function plansHaveRequiredClearance(params) {
|
|
|
26759
27246
|
return true;
|
|
26760
27247
|
}
|
|
26761
27248
|
function getDirectionForBoundaryPoint(point6, boundary) {
|
|
26762
|
-
if (Math.abs(point6.x - boundary.minX) <
|
|
26763
|
-
if (Math.abs(point6.x - boundary.maxX) <
|
|
26764
|
-
if (Math.abs(point6.y - boundary.minY) <
|
|
26765
|
-
if (Math.abs(point6.y - boundary.maxY) <
|
|
27249
|
+
if (Math.abs(point6.x - boundary.minX) < EPSILON9) return "left";
|
|
27250
|
+
if (Math.abs(point6.x - boundary.maxX) < EPSILON9) return "right";
|
|
27251
|
+
if (Math.abs(point6.y - boundary.minY) < EPSILON9) return "down";
|
|
27252
|
+
if (Math.abs(point6.y - boundary.maxY) < EPSILON9) return "up";
|
|
26766
27253
|
return null;
|
|
26767
27254
|
}
|
|
26768
27255
|
function* routeWithAdaptiveExitsSteps(params) {
|
|
@@ -26852,7 +27339,7 @@ function* routeWithAdaptiveExitsSteps(params) {
|
|
|
26852
27339
|
const connectorSegments = getSegments2(connectorPoints, traceWidth);
|
|
26853
27340
|
const clearsObstacles = connectorSegments.every(
|
|
26854
27341
|
(segment2) => obstacles.every(
|
|
26855
|
-
(obstacle) => obstacleBelongsToItem(obstacle, mergeItem) || distanceSegmentToObstacle(segment2, obstacle) >= requiredObstacleDistance -
|
|
27342
|
+
(obstacle) => obstacleBelongsToItem(obstacle, mergeItem) || distanceSegmentToObstacle(segment2, obstacle) >= requiredObstacleDistance - EPSILON9
|
|
26856
27343
|
)
|
|
26857
27344
|
);
|
|
26858
27345
|
const clearsRoutes = connectorSegments.every(
|
|
@@ -26863,7 +27350,7 @@ function* routeWithAdaptiveExitsSteps(params) {
|
|
|
26863
27350
|
segment2.end,
|
|
26864
27351
|
otherSegment.start,
|
|
26865
27352
|
otherSegment.end
|
|
26866
|
-
) >= requiredRouteDistance -
|
|
27353
|
+
) >= requiredRouteDistance - EPSILON9
|
|
26867
27354
|
)
|
|
26868
27355
|
)
|
|
26869
27356
|
);
|
|
@@ -27938,10 +28425,62 @@ function routeSingleLayerWithPushAndShove(params) {
|
|
|
27938
28425
|
return paths.map(buildPlan4);
|
|
27939
28426
|
}
|
|
27940
28427
|
|
|
28428
|
+
// node_modules/@tscircuit/fanout-solver/lib/runtime-process.ts
|
|
28429
|
+
var getRuntimeProcess = (runtime) => runtime.process ?? { env: {} };
|
|
28430
|
+
|
|
28431
|
+
// node_modules/@tscircuit/fanout-solver/lib/shorten-bus-plans.ts
|
|
28432
|
+
function shortenBusPlans(params) {
|
|
28433
|
+
const { bus } = params;
|
|
28434
|
+
let plans = [...params.plans];
|
|
28435
|
+
if (bus.maxLengthSkew === void 0) return plans;
|
|
28436
|
+
const busPlans = plans.filter((plan) => plan.busId === bus.busId).toSorted((first, second) => second.length - first.length);
|
|
28437
|
+
for (const plan of busPlans) {
|
|
28438
|
+
const minimumLength = Math.min(
|
|
28439
|
+
...plans.filter((p) => p.busId === bus.busId).map((p) => p.length)
|
|
28440
|
+
);
|
|
28441
|
+
if (plan.length - minimumLength <= bus.maxLengthSkew) continue;
|
|
28442
|
+
if (!plan.via || plan.additionalVias?.length || plan.planeEndpointVia || plan.segments.filter((segment2) => segment2.layer === plan.sourceLayer).length !== 1)
|
|
28443
|
+
continue;
|
|
28444
|
+
const connection = bus.connections.find(
|
|
28445
|
+
(c) => c.connectionIndex === plan.connectionIndex
|
|
28446
|
+
);
|
|
28447
|
+
for (const alignGridToPads of [true, false]) {
|
|
28448
|
+
const candidate = routeViaMinimalWinding({
|
|
28449
|
+
...params,
|
|
28450
|
+
bus: {
|
|
28451
|
+
...bus,
|
|
28452
|
+
connections: [connection],
|
|
28453
|
+
routableEscapeLayers: [plan.targetLayer]
|
|
28454
|
+
},
|
|
28455
|
+
terminals: [
|
|
28456
|
+
{ connection, viaPoint: plan.via.center, exitPoint: plan.exitPoint }
|
|
28457
|
+
],
|
|
28458
|
+
targetLayer: plan.targetLayer,
|
|
28459
|
+
acceptedPlans: plans.filter((p) => p !== plan),
|
|
28460
|
+
allowBlindAndBuriedVias: false,
|
|
28461
|
+
gridStepDivisor: 2,
|
|
28462
|
+
alignGridToPads,
|
|
28463
|
+
maximumRouteOrderAttempts: 1
|
|
28464
|
+
})?.[0];
|
|
28465
|
+
if (!candidate || candidate.length >= plan.length - 1e-6) continue;
|
|
28466
|
+
const nextPlans = plans.map((p) => p === plan ? candidate : p);
|
|
28467
|
+
if (!fanoutPlansAreClear({
|
|
28468
|
+
...params,
|
|
28469
|
+
plans: nextPlans,
|
|
28470
|
+
allowBlindAndBuriedVias: false
|
|
28471
|
+
}))
|
|
28472
|
+
continue;
|
|
28473
|
+
plans = nextPlans;
|
|
28474
|
+
break;
|
|
28475
|
+
}
|
|
28476
|
+
}
|
|
28477
|
+
return plans;
|
|
28478
|
+
}
|
|
28479
|
+
|
|
27941
28480
|
// node_modules/@tscircuit/fanout-solver/lib/validate-fanout-solution.ts
|
|
27942
|
-
var
|
|
28481
|
+
var EPSILON10 = 1e-6;
|
|
27943
28482
|
function pointsMatch3(first, second) {
|
|
27944
|
-
return distance9(first, second) <=
|
|
28483
|
+
return distance9(first, second) <= EPSILON10;
|
|
27945
28484
|
}
|
|
27946
28485
|
function getPointLayers3(point6) {
|
|
27947
28486
|
return "layer" in point6 ? [point6.layer] : point6.layers;
|
|
@@ -27960,26 +28499,26 @@ function connectionPointsMatch(first, second) {
|
|
|
27960
28499
|
return pointsMatch3(first, second) && getPointLayers3(first).join("\0") === getPointLayers3(second).join("\0") && first.pointId === second.pointId && first.pcb_port_id === second.pcb_port_id;
|
|
27961
28500
|
}
|
|
27962
28501
|
function pointIsOnBoundary(point6, boundary) {
|
|
27963
|
-
const inside = point6.x >= boundary.minX -
|
|
27964
|
-
const onEdge = Math.abs(point6.x - boundary.minX) <=
|
|
28502
|
+
const inside = point6.x >= boundary.minX - EPSILON10 && point6.x <= boundary.maxX + EPSILON10 && point6.y >= boundary.minY - EPSILON10 && point6.y <= boundary.maxY + EPSILON10;
|
|
28503
|
+
const onEdge = Math.abs(point6.x - boundary.minX) <= EPSILON10 || Math.abs(point6.x - boundary.maxX) <= EPSILON10 || Math.abs(point6.y - boundary.minY) <= EPSILON10 || Math.abs(point6.y - boundary.maxY) <= EPSILON10;
|
|
27965
28504
|
return inside && onEdge;
|
|
27966
28505
|
}
|
|
27967
28506
|
function pointIsOnBoundaryEdge(point6, edge, boundary) {
|
|
27968
|
-
const inside = point6.x >= boundary.minX -
|
|
28507
|
+
const inside = point6.x >= boundary.minX - EPSILON10 && point6.x <= boundary.maxX + EPSILON10 && point6.y >= boundary.minY - EPSILON10 && point6.y <= boundary.maxY + EPSILON10;
|
|
27969
28508
|
if (!inside) return false;
|
|
27970
28509
|
switch (edge) {
|
|
27971
28510
|
case "left":
|
|
27972
|
-
return Math.abs(point6.x - boundary.minX) <=
|
|
28511
|
+
return Math.abs(point6.x - boundary.minX) <= EPSILON10;
|
|
27973
28512
|
case "right":
|
|
27974
|
-
return Math.abs(point6.x - boundary.maxX) <=
|
|
28513
|
+
return Math.abs(point6.x - boundary.maxX) <= EPSILON10;
|
|
27975
28514
|
case "top":
|
|
27976
|
-
return Math.abs(point6.y - boundary.maxY) <=
|
|
28515
|
+
return Math.abs(point6.y - boundary.maxY) <= EPSILON10;
|
|
27977
28516
|
case "bottom":
|
|
27978
|
-
return Math.abs(point6.y - boundary.minY) <=
|
|
28517
|
+
return Math.abs(point6.y - boundary.minY) <= EPSILON10;
|
|
27979
28518
|
}
|
|
27980
28519
|
}
|
|
27981
28520
|
function pointIsInsideBounds3(point6, bounds) {
|
|
27982
|
-
return point6.x >= bounds.minX -
|
|
28521
|
+
return point6.x >= bounds.minX - EPSILON10 && point6.x <= bounds.maxX + EPSILON10 && point6.y >= bounds.minY - EPSILON10 && point6.y <= bounds.maxY + EPSILON10;
|
|
27983
28522
|
}
|
|
27984
28523
|
function addIssue2(issues, code, message, plan, otherConnectionName) {
|
|
27985
28524
|
issues.push({
|
|
@@ -28112,7 +28651,7 @@ function validatePlanStructure(params) {
|
|
|
28112
28651
|
);
|
|
28113
28652
|
}
|
|
28114
28653
|
const isAllowedViaInPadPlaneTermination = inputSrj.allowViaInPad === true && plan.termination.type === "plane" && plan.via !== void 0 && pointsMatch3(plan.via.center, plan.sourcePoint) && pointsMatch3(plan.exitPoint, plan.sourcePoint);
|
|
28115
|
-
if (!isAllowedViaInPadPlaneTermination && (plan.segments.length === 0 || plan.length <=
|
|
28654
|
+
if (!isAllowedViaInPadPlaneTermination && (plan.segments.length === 0 || plan.length <= EPSILON10)) {
|
|
28116
28655
|
addIssue2(
|
|
28117
28656
|
issues,
|
|
28118
28657
|
"not-broken-out",
|
|
@@ -28195,7 +28734,7 @@ function validatePlanStructure(params) {
|
|
|
28195
28734
|
});
|
|
28196
28735
|
if (traceSegments.length !== plan.segments.length || traceSegments.some((segment2, index) => {
|
|
28197
28736
|
const declared = plan.segments[index];
|
|
28198
|
-
return !declared || segment2.layer !== declared.layer || Math.abs(segment2.width - declared.width) >
|
|
28737
|
+
return !declared || segment2.layer !== declared.layer || Math.abs(segment2.width - declared.width) > EPSILON10 || !pointsMatch3(segment2.start, declared.start) || !pointsMatch3(segment2.end, declared.end);
|
|
28199
28738
|
})) {
|
|
28200
28739
|
addIssue2(
|
|
28201
28740
|
issues,
|
|
@@ -28220,8 +28759,8 @@ function validatePlanStructure(params) {
|
|
|
28220
28759
|
});
|
|
28221
28760
|
if (endpointSegments.length !== plan.planeEndpointSegments.length || endpointSegments.some((segment2, index) => {
|
|
28222
28761
|
const declared = plan.planeEndpointSegments?.[index];
|
|
28223
|
-
return !declared || segment2.layer !== declared.layer || Math.abs(segment2.width - declared.width) >
|
|
28224
|
-
}) || distance9(plan.planeEndpointVia.center, plan.targetPoint) <=
|
|
28762
|
+
return !declared || segment2.layer !== declared.layer || Math.abs(segment2.width - declared.width) > EPSILON10 || !pointsMatch3(segment2.start, declared.start) || !pointsMatch3(segment2.end, declared.end);
|
|
28763
|
+
}) || distance9(plan.planeEndpointVia.center, plan.targetPoint) <= EPSILON10) {
|
|
28225
28764
|
addIssue2(
|
|
28226
28765
|
issues,
|
|
28227
28766
|
"trace-plan-mismatch",
|
|
@@ -28301,7 +28840,7 @@ function plansHaveConnectedCopper(first, second) {
|
|
|
28301
28840
|
firstSegment.end,
|
|
28302
28841
|
secondSegment.start,
|
|
28303
28842
|
secondSegment.end
|
|
28304
|
-
) <= (firstSegment.width + secondSegment.width) / 2 +
|
|
28843
|
+
) <= (firstSegment.width + secondSegment.width) / 2 + EPSILON10) {
|
|
28305
28844
|
return true;
|
|
28306
28845
|
}
|
|
28307
28846
|
}
|
|
@@ -28310,7 +28849,7 @@ function plansHaveConnectedCopper(first, second) {
|
|
|
28310
28849
|
secondVia.center,
|
|
28311
28850
|
firstSegment.start,
|
|
28312
28851
|
firstSegment.end
|
|
28313
|
-
) <= secondVia.diameter / 2 + firstSegment.width / 2 +
|
|
28852
|
+
) <= secondVia.diameter / 2 + firstSegment.width / 2 + EPSILON10) {
|
|
28314
28853
|
return true;
|
|
28315
28854
|
}
|
|
28316
28855
|
}
|
|
@@ -28321,14 +28860,14 @@ function plansHaveConnectedCopper(first, second) {
|
|
|
28321
28860
|
firstVia.center,
|
|
28322
28861
|
secondSegment.start,
|
|
28323
28862
|
secondSegment.end
|
|
28324
|
-
) <= firstVia.diameter / 2 + secondSegment.width / 2 +
|
|
28863
|
+
) <= firstVia.diameter / 2 + secondSegment.width / 2 + EPSILON10) {
|
|
28325
28864
|
return true;
|
|
28326
28865
|
}
|
|
28327
28866
|
}
|
|
28328
28867
|
for (const secondVia of secondVias) {
|
|
28329
28868
|
if (firstVia.spanLayers.some(
|
|
28330
28869
|
(layer) => secondVia.spanLayers.includes(layer)
|
|
28331
|
-
) && distance9(firstVia.center, secondVia.center) <= (firstVia.diameter + secondVia.diameter) / 2 +
|
|
28870
|
+
) && distance9(firstVia.center, secondVia.center) <= (firstVia.diameter + secondVia.diameter) / 2 + EPSILON10) {
|
|
28332
28871
|
return true;
|
|
28333
28872
|
}
|
|
28334
28873
|
}
|
|
@@ -29037,6 +29576,7 @@ function visualizeSimpleRouteJson(srj) {
|
|
|
29037
29576
|
}
|
|
29038
29577
|
|
|
29039
29578
|
// node_modules/@tscircuit/fanout-solver/lib/fanout-solver.ts
|
|
29579
|
+
var process = getRuntimeProcess(globalThis);
|
|
29040
29580
|
var FanoutWorkSolver = class extends BaseSolver {
|
|
29041
29581
|
constructor(solverName, generator, getVisualization, getStats, getProgress) {
|
|
29042
29582
|
super();
|
|
@@ -29626,7 +30166,10 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
29626
30166
|
buses: this.preparedBuses,
|
|
29627
30167
|
escapeLayers: this.config.escapeLayers,
|
|
29628
30168
|
escapeLayersByBusId: this.escapeLayersByBusId,
|
|
29629
|
-
preferOrderedCoordinatedWindingLayers: this.config.densePlaneReservationBusIds.length > 0 || this.config.denseUnrestrictedPlaneRoutingBusIds.length > 0
|
|
30169
|
+
preferOrderedCoordinatedWindingLayers: this.config.densePlaneReservationBusIds.length > 0 || this.config.denseUnrestrictedPlaneRoutingBusIds.length > 0 || shouldUseAdaptiveDensePlaneRouting(
|
|
30170
|
+
this.preparedBuses,
|
|
30171
|
+
this.config.allowBlindAndBuriedVias
|
|
30172
|
+
)
|
|
29630
30173
|
}),
|
|
29631
30174
|
generatedAssignments,
|
|
29632
30175
|
maxAssignments: this.config.maxLayerCombinations
|
|
@@ -29915,6 +30458,8 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
29915
30458
|
"plane-match:incremental-failed",
|
|
29916
30459
|
"plane-route:alternate-candidate-counts",
|
|
29917
30460
|
"plane-route:alternate-search",
|
|
30461
|
+
"plane-route:csp",
|
|
30462
|
+
"plane-reservation-core",
|
|
29918
30463
|
"plane-route:promote-failed",
|
|
29919
30464
|
"plane-route:alternate-choice",
|
|
29920
30465
|
"plane-route:promote-zero-candidates",
|
|
@@ -29931,7 +30476,13 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
29931
30476
|
const unsortedBoundaryBuses = params.busesInRoutingOrder.filter(
|
|
29932
30477
|
(bus) => bus.termination.type === "boundary"
|
|
29933
30478
|
);
|
|
29934
|
-
const
|
|
30479
|
+
const configuredDensePlaneRouting = this.config.densePlaneReservationBusIds.length > 0 || this.config.denseUnrestrictedPlaneRoutingBusIds.length > 0;
|
|
30480
|
+
const useAdaptiveDensePlaneRouting = !configuredDensePlaneRouting && shouldUseAdaptiveDensePlaneRouting(
|
|
30481
|
+
this.preparedBuses,
|
|
30482
|
+
this.config.allowBlindAndBuriedVias
|
|
30483
|
+
);
|
|
30484
|
+
const useConfiguredDensePlaneRouting = configuredDensePlaneRouting || useAdaptiveDensePlaneRouting;
|
|
30485
|
+
const useAdaptiveJointPlaneSelection = useAdaptiveDensePlaneRouting && (params.planeReservationRetryCount ?? 0) === 0;
|
|
29935
30486
|
const matchLengthsAfterPlanes = useConfiguredDensePlaneRouting && params.lengthMatchingStage !== "before-planes";
|
|
29936
30487
|
const useJointBoundaryViaReservation = shouldUseJointBoundaryViaReservation(
|
|
29937
30488
|
unsortedBoundaryBuses.map((bus) => bus.connections.length)
|
|
@@ -30078,6 +30629,17 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
30078
30629
|
0,
|
|
30079
30630
|
Number.isFinite(initialPlaneReservationCount) ? initialPlaneReservationCount : 8
|
|
30080
30631
|
) : planeBuses;
|
|
30632
|
+
if (params.promotedPlaneReservationBusIds) {
|
|
30633
|
+
activeBoundaryReservationPlaneBuses = [
|
|
30634
|
+
.../* @__PURE__ */ new Set([
|
|
30635
|
+
...activeBoundaryReservationPlaneBuses,
|
|
30636
|
+
...planeBuses.filter(
|
|
30637
|
+
(bus) => params.promotedPlaneReservationBusIds.includes(bus.busId)
|
|
30638
|
+
)
|
|
30639
|
+
])
|
|
30640
|
+
];
|
|
30641
|
+
}
|
|
30642
|
+
const unroutablePlaneBusIds = /* @__PURE__ */ new Set();
|
|
30081
30643
|
debugDense(
|
|
30082
30644
|
"start",
|
|
30083
30645
|
boundaryBuses.map((bus) => `${bus.busId}:${bus.connections.length}`),
|
|
@@ -30110,7 +30672,7 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
30110
30672
|
const preferBoundaryOutwardByBusId = new Map(
|
|
30111
30673
|
boundaryBuses.map((bus) => [
|
|
30112
30674
|
bus.busId,
|
|
30113
|
-
bus.connections.length === 1 ? hasThreeWideBoundaryBuses && narrowBusIsEmbeddedInWideSourceField(bus) ? true : useGeometryAwareSingletonOutwardPreference && getCornerBandSide(bus.exitEdge, bus.preferredExit) ? getDenseSingletonBoundaryGeometry(bus).targetProjection > 0 : getExitEdgeForDirection(bus.direction) !== bus.exitEdge :
|
|
30675
|
+
bus.connections.length === 1 ? hasThreeWideBoundaryBuses && narrowBusIsEmbeddedInWideSourceField(bus) ? true : useGeometryAwareSingletonOutwardPreference && getCornerBandSide(bus.exitEdge, bus.preferredExit) ? getDenseSingletonBoundaryGeometry(bus).targetProjection > 0 : getExitEdgeForDirection(bus.direction) !== bus.exitEdge : hasThreeWideBoundaryBuses && bus.connections.length >= 8 ? false : getExitEdgeForDirection(bus.direction) !== bus.exitEdge
|
|
30114
30676
|
])
|
|
30115
30677
|
);
|
|
30116
30678
|
for (const bus of wideBoundaryBuses) {
|
|
@@ -30392,6 +30954,14 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
30392
30954
|
return [
|
|
30393
30955
|
{
|
|
30394
30956
|
connectionName: connection.connection.name,
|
|
30957
|
+
sourceEscapeSegment: matchedPlans.some(
|
|
30958
|
+
(plan) => plan.connectionIndex === connection.connectionIndex
|
|
30959
|
+
) ? void 0 : {
|
|
30960
|
+
start: connection.sourcePoint,
|
|
30961
|
+
end: center,
|
|
30962
|
+
layer: connection.sourceLayer,
|
|
30963
|
+
width: this.config.traceWidth
|
|
30964
|
+
},
|
|
30395
30965
|
via: {
|
|
30396
30966
|
center,
|
|
30397
30967
|
diameter: this.config.viaDiameter,
|
|
@@ -30446,7 +31016,7 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
30446
31016
|
fixedViaPointsByConnectionIndex,
|
|
30447
31017
|
reservedVias: getReservedVias(bus),
|
|
30448
31018
|
viaMinimalOnly: process.env.FANOUT_DEBUG_ALLOW_EXTRA_VIAS !== "1",
|
|
30449
|
-
allowBoundarySideViaFallback:
|
|
31019
|
+
allowBoundarySideViaFallback: bus.connections.length === 1,
|
|
30450
31020
|
preferCornerBoundaryVia: useConfiguredDensePlaneRouting,
|
|
30451
31021
|
adaptiveWindingRouteOrder,
|
|
30452
31022
|
alignWindingGridToPads: usePadAlignedDenseRouting && !useConfiguredDensePlaneRouting,
|
|
@@ -30557,7 +31127,21 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
30557
31127
|
))[0];
|
|
30558
31128
|
if (busPlans) break;
|
|
30559
31129
|
}
|
|
30560
|
-
if (!busPlans)
|
|
31130
|
+
if (!busPlans) {
|
|
31131
|
+
fixedViaPointsByConnectionIndex = originalPoints;
|
|
31132
|
+
if (bus.connections.length === 2) {
|
|
31133
|
+
busPlans = (yield* routeAlternatives(
|
|
31134
|
+
{
|
|
31135
|
+
...routeParams,
|
|
31136
|
+
fixedViaPointsByConnectionIndex: originalPoints,
|
|
31137
|
+
reservedVias: getReservedVias(bus),
|
|
31138
|
+
allowBoundarySideViaFallback: true,
|
|
31139
|
+
fixedViaFallbackRouteOrderAttempts: 1
|
|
31140
|
+
},
|
|
31141
|
+
1
|
|
31142
|
+
))[0];
|
|
31143
|
+
}
|
|
31144
|
+
}
|
|
30561
31145
|
}
|
|
30562
31146
|
if (busPlans && bus.maxLengthSkew !== void 0) {
|
|
30563
31147
|
const lengths = busPlans.map((plan) => plan.length);
|
|
@@ -31162,6 +31746,73 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
31162
31746
|
}))
|
|
31163
31747
|
})
|
|
31164
31748
|
);
|
|
31749
|
+
if (useAdaptiveJointPlaneSelection) {
|
|
31750
|
+
const acceptedPlans = [
|
|
31751
|
+
...candidatePlans,
|
|
31752
|
+
...feasibleAlternatePlanePlans
|
|
31753
|
+
];
|
|
31754
|
+
for (const planeBus of planeBuses) {
|
|
31755
|
+
if (matchedPlaneBuses.includes(planeBus) || candidateSets.some((set) => set.planeBus === planeBus))
|
|
31756
|
+
continue;
|
|
31757
|
+
const candidates = [];
|
|
31758
|
+
const sites = getComponentDogboneViaSiteCandidates(
|
|
31759
|
+
[planeBus],
|
|
31760
|
+
{
|
|
31761
|
+
viaDiameter: this.config.viaDiameter,
|
|
31762
|
+
viaHoleDiameter: this.config.viaHoleDiameter,
|
|
31763
|
+
traceWidth: this.config.traceWidth,
|
|
31764
|
+
clearance: this.config.clearance,
|
|
31765
|
+
blockingSegments: acceptedPlans.flatMap(
|
|
31766
|
+
(plan) => plan.segments.map((segment2) => ({
|
|
31767
|
+
connectionIndex: plan.connectionIndex,
|
|
31768
|
+
segment: segment2
|
|
31769
|
+
}))
|
|
31770
|
+
),
|
|
31771
|
+
blockingVias: acceptedPlans.flatMap(
|
|
31772
|
+
(plan) => [
|
|
31773
|
+
plan.via,
|
|
31774
|
+
...plan.additionalVias ?? [],
|
|
31775
|
+
plan.planeEndpointVia
|
|
31776
|
+
].filter((via) => via !== void 0).map((via) => ({
|
|
31777
|
+
connectionIndex: plan.connectionIndex,
|
|
31778
|
+
center: via.center,
|
|
31779
|
+
diameter: via.diameter,
|
|
31780
|
+
spanLayers: via.spanLayers
|
|
31781
|
+
}))
|
|
31782
|
+
),
|
|
31783
|
+
additionalObstacles: this.routingSrj.obstacles,
|
|
31784
|
+
preferPlaneCheckerboardSites: true,
|
|
31785
|
+
canShareCopper
|
|
31786
|
+
}
|
|
31787
|
+
);
|
|
31788
|
+
for (const site of sites) {
|
|
31789
|
+
const plans = routeBus({
|
|
31790
|
+
srj: this.routingSrj,
|
|
31791
|
+
bus: planeBus,
|
|
31792
|
+
targetLayer: params.busLayerAssignments[planeBus.busId],
|
|
31793
|
+
acceptedPlans,
|
|
31794
|
+
layerNames: this.config.layerNames,
|
|
31795
|
+
traceWidth: this.config.traceWidth,
|
|
31796
|
+
viaDiameter: this.config.viaDiameter,
|
|
31797
|
+
viaHoleDiameter: this.config.viaHoleDiameter,
|
|
31798
|
+
clearance: this.config.clearance,
|
|
31799
|
+
compactBusTracks: this.config.compactBusTracks,
|
|
31800
|
+
allowBlindAndBuriedVias: false,
|
|
31801
|
+
allowSameNetMerges: this.config.allowSameNetMerges,
|
|
31802
|
+
fixedViaPointsByConnectionIndex: /* @__PURE__ */ new Map([
|
|
31803
|
+
[site.connectionIndex, site.point]
|
|
31804
|
+
])
|
|
31805
|
+
});
|
|
31806
|
+
if (plans)
|
|
31807
|
+
candidates.push({
|
|
31808
|
+
key: `${planeBus.busId}:local:${candidates.length}`,
|
|
31809
|
+
planeBus,
|
|
31810
|
+
plans
|
|
31811
|
+
});
|
|
31812
|
+
}
|
|
31813
|
+
candidateSets.push({ planeBus, candidates });
|
|
31814
|
+
}
|
|
31815
|
+
}
|
|
31165
31816
|
debugDense(
|
|
31166
31817
|
"plane-route:alternate-candidate-counts",
|
|
31167
31818
|
candidateSets.map((candidateSet) => [
|
|
@@ -31169,6 +31820,10 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
31169
31820
|
candidateSet.candidates.length
|
|
31170
31821
|
])
|
|
31171
31822
|
);
|
|
31823
|
+
for (const candidateSet of candidateSets) {
|
|
31824
|
+
if (candidateSet.candidates.length === 0)
|
|
31825
|
+
unroutablePlaneBusIds.add(candidateSet.planeBus.busId);
|
|
31826
|
+
}
|
|
31172
31827
|
const compatibilityByCandidatePair = /* @__PURE__ */ new Map();
|
|
31173
31828
|
const candidatesAreCompatible = (first, second) => {
|
|
31174
31829
|
const cacheKey = [first.key, second.key].toSorted().join("|");
|
|
@@ -31261,10 +31916,70 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
31261
31916
|
);
|
|
31262
31917
|
return null;
|
|
31263
31918
|
};
|
|
31264
|
-
|
|
31265
|
-
|
|
31266
|
-
[
|
|
31267
|
-
|
|
31919
|
+
let selectedCandidates;
|
|
31920
|
+
if (useAdaptiveDensePlaneRouting) {
|
|
31921
|
+
const getUniqueDomains = (sets) => sets.map((set) => [
|
|
31922
|
+
...new Map(
|
|
31923
|
+
set.candidates.map((candidate) => [
|
|
31924
|
+
JSON.stringify(
|
|
31925
|
+
candidate.plans.map((plan) => [
|
|
31926
|
+
plan.segments,
|
|
31927
|
+
plan.via,
|
|
31928
|
+
plan.additionalVias,
|
|
31929
|
+
plan.planeEndpointSegments,
|
|
31930
|
+
plan.planeEndpointVia
|
|
31931
|
+
])
|
|
31932
|
+
),
|
|
31933
|
+
candidate
|
|
31934
|
+
])
|
|
31935
|
+
).values()
|
|
31936
|
+
]);
|
|
31937
|
+
const runCandidateSelection = (sets, maximumSearchStates = maximumAlternatePlaneSearchStates) => selectCompatibleCandidates({
|
|
31938
|
+
candidateSets: getUniqueDomains(sets),
|
|
31939
|
+
maximumSearchStates,
|
|
31940
|
+
areCompatible: (first, second) => fanoutPlansAreMutuallyClear({
|
|
31941
|
+
plans: [...first.plans, ...second.plans],
|
|
31942
|
+
srj: this.routingSrj,
|
|
31943
|
+
clearance: this.config.clearance,
|
|
31944
|
+
allowSameNetMerges: this.config.allowSameNetMerges
|
|
31945
|
+
})
|
|
31946
|
+
});
|
|
31947
|
+
const result = runCandidateSelection(candidateSets);
|
|
31948
|
+
debugDense(
|
|
31949
|
+
"plane-route:csp",
|
|
31950
|
+
result.selection ? "complete" : "failed",
|
|
31951
|
+
result.searchStates,
|
|
31952
|
+
result.emptyDomainIndices.map(
|
|
31953
|
+
(index) => candidateSets[index].planeBus.busId
|
|
31954
|
+
)
|
|
31955
|
+
);
|
|
31956
|
+
selectedCandidates = result.selection;
|
|
31957
|
+
alternatePlaneSearchStates = result.searchStates;
|
|
31958
|
+
if (!result.selection && useAdaptiveDensePlaneRouting) {
|
|
31959
|
+
const expanded = result.emptyDomainIndices.map((index) => candidateSets[index].planeBus.busId).filter(
|
|
31960
|
+
(id) => !orderedAlternatePlaneBuses.some(
|
|
31961
|
+
(bus) => bus.busId === id
|
|
31962
|
+
)
|
|
31963
|
+
);
|
|
31964
|
+
if (expanded.length > 0) {
|
|
31965
|
+
debugDense("plane-route:expand-domains", expanded);
|
|
31966
|
+
return matchViaPointsAroundPlans(
|
|
31967
|
+
candidatePlans,
|
|
31968
|
+
/* @__PURE__ */ new Set([...promotedAlternatePlaneBusIds, ...expanded])
|
|
31969
|
+
);
|
|
31970
|
+
}
|
|
31971
|
+
}
|
|
31972
|
+
if (!result.selection)
|
|
31973
|
+
for (const index of (params.planeReservationRetryCount ?? 0) === 0 ? result.conflictDomainIndices : result.emptyDomainIndices.slice(0, 1))
|
|
31974
|
+
unroutablePlaneBusIds.add(
|
|
31975
|
+
candidateSets[index].planeBus.busId
|
|
31976
|
+
);
|
|
31977
|
+
} else {
|
|
31978
|
+
selectedCandidates = selectCompatiblePlaneRoutes(
|
|
31979
|
+
candidateSets,
|
|
31980
|
+
[]
|
|
31981
|
+
);
|
|
31982
|
+
}
|
|
31268
31983
|
alternatePlanePlans = selectedCandidates ? [
|
|
31269
31984
|
...feasibleAlternatePlanePlans,
|
|
31270
31985
|
...selectedCandidates.flatMap(
|
|
@@ -31288,6 +32003,15 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
31288
32003
|
);
|
|
31289
32004
|
if (!alternatePlanePlans) return null;
|
|
31290
32005
|
feasibleAlternatePlanePlans = alternatePlanePlans;
|
|
32006
|
+
if (useAdaptiveJointPlaneSelection) {
|
|
32007
|
+
matchedPlaneBusesInRoutingOrder = [];
|
|
32008
|
+
return new Map([
|
|
32009
|
+
...fixedBoundaryViaPoints,
|
|
32010
|
+
...alternatePlanePlans.filter((plan) => plan.via).map(
|
|
32011
|
+
(plan) => [plan.connectionIndex, plan.via.center]
|
|
32012
|
+
)
|
|
32013
|
+
]);
|
|
32014
|
+
}
|
|
31291
32015
|
}
|
|
31292
32016
|
let planeBusIdsRoutedWithoutDogbones = /* @__PURE__ */ new Set();
|
|
31293
32017
|
let allBlockingSegments = [...blockingSegments];
|
|
@@ -31679,6 +32403,30 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
31679
32403
|
return { plans: matchedPlans, failedBusIds: [] };
|
|
31680
32404
|
}
|
|
31681
32405
|
}
|
|
32406
|
+
if (useAdaptiveDensePlaneRouting && unroutablePlaneBusIds.size > 0 && (params.planeReservationRetryCount ?? 0) < 5) {
|
|
32407
|
+
const newlyPromoted = [...unroutablePlaneBusIds].filter(
|
|
32408
|
+
(id) => !activeBoundaryReservationPlaneBuses.some((bus) => bus.busId === id)
|
|
32409
|
+
);
|
|
32410
|
+
const refinedPromotions = (params.planeReservationRetryCount ?? 0) === 0 ? refineAdaptivePlaneReservationCore({
|
|
32411
|
+
candidateBusIds: newlyPromoted,
|
|
32412
|
+
activeBusIds: new Set(
|
|
32413
|
+
activeBoundaryReservationPlaneBuses.map((bus) => bus.busId)
|
|
32414
|
+
),
|
|
32415
|
+
planeBuses
|
|
32416
|
+
}) : newlyPromoted;
|
|
32417
|
+
if (refinedPromotions.length > 0) {
|
|
32418
|
+
debugDense("plane-reservation-core", refinedPromotions);
|
|
32419
|
+
debugDense("promote-reservations", refinedPromotions);
|
|
32420
|
+
return yield* this.routeDenseThroughAllMixedTerminationSteps({
|
|
32421
|
+
...params,
|
|
32422
|
+
promotedPlaneReservationBusIds: [
|
|
32423
|
+
...params.promotedPlaneReservationBusIds ?? [],
|
|
32424
|
+
...refinedPromotions
|
|
32425
|
+
],
|
|
32426
|
+
planeReservationRetryCount: (params.planeReservationRetryCount ?? 0) + 1
|
|
32427
|
+
});
|
|
32428
|
+
}
|
|
32429
|
+
}
|
|
31682
32430
|
if (matchLengthsAfterPlanes) {
|
|
31683
32431
|
return yield* this.routeDenseThroughAllMixedTerminationSteps({
|
|
31684
32432
|
...params,
|
|
@@ -45341,7 +46089,7 @@ function Group_getRoutingPhasePlans(group) {
|
|
|
45341
46089
|
var package_default = {
|
|
45342
46090
|
name: "@tscircuit/core",
|
|
45343
46091
|
type: "module",
|
|
45344
|
-
version: "0.0.
|
|
46092
|
+
version: "0.0.1833",
|
|
45345
46093
|
types: "dist/index.d.ts",
|
|
45346
46094
|
main: "dist/index.js",
|
|
45347
46095
|
module: "dist/index.js",
|
|
@@ -45381,7 +46129,7 @@ var package_default = {
|
|
|
45381
46129
|
"@tscircuit/common": "^0.0.20",
|
|
45382
46130
|
"@tscircuit/copper-pour-solver": "^0.0.52",
|
|
45383
46131
|
"@tscircuit/create-fdm-enclosure": "0.0.4",
|
|
45384
|
-
"@tscircuit/fanout-solver": "0.0.
|
|
46132
|
+
"@tscircuit/fanout-solver": "0.0.56",
|
|
45385
46133
|
"@tscircuit/footprinter": "^0.0.426",
|
|
45386
46134
|
"@tscircuit/image-utils": "^0.0.8",
|
|
45387
46135
|
"@tscircuit/implicit-copper-pour-solver": "github:tscircuit/implicit-copper-pour-solver#d880f80e4eb7a34d5da5a521f10a63c810d4728b",
|
|
@@ -50235,15 +50983,15 @@ import {
|
|
|
50235
50983
|
} from "@tscircuit/copper-pour-solver";
|
|
50236
50984
|
|
|
50237
50985
|
// lib/components/primitive-components/CopperPour/utils/mark-trace-segments-inside-copper-pour.ts
|
|
50238
|
-
var
|
|
50986
|
+
var EPSILON11 = 1e-9;
|
|
50239
50987
|
var isWireRoutePoint = (routePoint) => routePoint.route_type === "wire";
|
|
50240
50988
|
var isPointOnSegment = (p, a, b) => {
|
|
50241
50989
|
const cross3 = (p.y - a.y) * (b.x - a.x) - (p.x - a.x) * (b.y - a.y);
|
|
50242
|
-
if (Math.abs(cross3) >
|
|
50990
|
+
if (Math.abs(cross3) > EPSILON11) return false;
|
|
50243
50991
|
const dot = (p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y);
|
|
50244
|
-
if (dot < -
|
|
50992
|
+
if (dot < -EPSILON11) return false;
|
|
50245
50993
|
const squaredLength = (b.x - a.x) ** 2 + (b.y - a.y) ** 2;
|
|
50246
|
-
if (dot - squaredLength >
|
|
50994
|
+
if (dot - squaredLength > EPSILON11) return false;
|
|
50247
50995
|
return true;
|
|
50248
50996
|
};
|
|
50249
50997
|
var isPointInRing = (point6, ring) => {
|
|
@@ -50267,7 +51015,7 @@ var isPointInRectPour = (p, pour) => {
|
|
|
50267
51015
|
const dy = p.y - center.y;
|
|
50268
51016
|
const localX = dx * cosR - dy * sinR;
|
|
50269
51017
|
const localY = dx * sinR + dy * cosR;
|
|
50270
|
-
return Math.abs(localX) <= width / 2 +
|
|
51018
|
+
return Math.abs(localX) <= width / 2 + EPSILON11 && Math.abs(localY) <= height / 2 + EPSILON11;
|
|
50271
51019
|
};
|
|
50272
51020
|
var isPointInBrepPour = (p, pour) => {
|
|
50273
51021
|
const outerRing = pour.brep_shape.outer_ring.vertices.map((v) => ({
|
|
@@ -50301,7 +51049,7 @@ var isSegmentFullyInsideCopperPour = (start, end, pour) => {
|
|
|
50301
51049
|
const dx = end.x - start.x;
|
|
50302
51050
|
const dy = end.y - start.y;
|
|
50303
51051
|
const length7 = Math.hypot(dx, dy);
|
|
50304
|
-
if (length7 <=
|
|
51052
|
+
if (length7 <= EPSILON11) return false;
|
|
50305
51053
|
const samples = [0, 0.25, 0.5, 0.75, 1];
|
|
50306
51054
|
return samples.every(
|
|
50307
51055
|
(t) => isPointInCopperPour(
|