@tscircuit/core 0.0.1833 → 0.0.1835
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 +994 -248
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -19989,6 +19989,312 @@ function getFanoutExitPositionConfig(exitPosition) {
|
|
|
19989
19989
|
|
|
19990
19990
|
// node_modules/@tscircuit/fanout-solver/lib/fanout-solver.ts
|
|
19991
19991
|
import { BaseSolver } from "@tscircuit/solver-utils";
|
|
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
|
|
19992
20298
|
import { mergeGraphics } from "graphics-debug";
|
|
19993
20299
|
|
|
19994
20300
|
// node_modules/@tscircuit/fanout-solver/lib/add-via-layer-metadata.ts
|
|
@@ -20259,8 +20565,25 @@ function getAllRoutedTraceCopper(srj, allowBlindAndBuriedVias = true) {
|
|
|
20259
20565
|
);
|
|
20260
20566
|
}
|
|
20261
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
|
+
|
|
20262
20585
|
// node_modules/@tscircuit/fanout-solver/lib/match-component-dogbone-via-sites.ts
|
|
20263
|
-
var
|
|
20586
|
+
var EPSILON6 = 1e-9;
|
|
20264
20587
|
var DEFAULT_MAXIMUM_SEARCH_STATES = 1e5;
|
|
20265
20588
|
function assertGeometryRules(rules) {
|
|
20266
20589
|
for (const [name, value] of [
|
|
@@ -20305,7 +20628,7 @@ function uniqueSortedCoordinates(values) {
|
|
|
20305
20628
|
const result = [];
|
|
20306
20629
|
for (const value of values.toSorted((first, second) => first - second)) {
|
|
20307
20630
|
if (!Number.isFinite(value)) continue;
|
|
20308
|
-
if (result.length === 0 || Math.abs(result.at(-1) - value) >
|
|
20631
|
+
if (result.length === 0 || Math.abs(result.at(-1) - value) > EPSILON6) {
|
|
20309
20632
|
result.push(value);
|
|
20310
20633
|
}
|
|
20311
20634
|
}
|
|
@@ -20332,10 +20655,10 @@ function getComponentMatchingInputs(preparedBuses) {
|
|
|
20332
20655
|
}
|
|
20333
20656
|
component.xCoordinates.push(...bus.xCoordinates);
|
|
20334
20657
|
component.yCoordinates.push(...bus.yCoordinates);
|
|
20335
|
-
if (Number.isFinite(bus.pitchX) && bus.pitchX >
|
|
20658
|
+
if (Number.isFinite(bus.pitchX) && bus.pitchX > EPSILON6) {
|
|
20336
20659
|
component.pitchX = Math.min(component.pitchX, bus.pitchX);
|
|
20337
20660
|
}
|
|
20338
|
-
if (Number.isFinite(bus.pitchY) && bus.pitchY >
|
|
20661
|
+
if (Number.isFinite(bus.pitchY) && bus.pitchY > EPSILON6) {
|
|
20339
20662
|
component.pitchY = Math.min(component.pitchY, bus.pitchY);
|
|
20340
20663
|
}
|
|
20341
20664
|
const obstacleSet = obstacleSetByComponentId.get(bus.componentId);
|
|
@@ -20382,7 +20705,7 @@ function getComponentMatchingInputs(preparedBuses) {
|
|
|
20382
20705
|
}
|
|
20383
20706
|
function getInterstitialCoordinates(params) {
|
|
20384
20707
|
const { coordinates, pitch } = params;
|
|
20385
|
-
if (coordinates.length === 0 || !Number.isFinite(pitch) || pitch <=
|
|
20708
|
+
if (coordinates.length === 0 || !Number.isFinite(pitch) || pitch <= EPSILON6) {
|
|
20386
20709
|
return [];
|
|
20387
20710
|
}
|
|
20388
20711
|
const interstitialCoordinates = [coordinates[0] - pitch / 2];
|
|
@@ -20400,9 +20723,9 @@ function getAdjacentInterstitialCoordinates(params) {
|
|
|
20400
20723
|
coordinates,
|
|
20401
20724
|
pitch
|
|
20402
20725
|
});
|
|
20403
|
-
const before = interstitialCoordinates.filter((coordinate) => coordinate < sourceCoordinate -
|
|
20726
|
+
const before = interstitialCoordinates.filter((coordinate) => coordinate < sourceCoordinate - EPSILON6).at(-1);
|
|
20404
20727
|
const after = interstitialCoordinates.find(
|
|
20405
|
-
(coordinate) => coordinate > sourceCoordinate +
|
|
20728
|
+
(coordinate) => coordinate > sourceCoordinate + EPSILON6
|
|
20406
20729
|
);
|
|
20407
20730
|
return [before, after].filter(
|
|
20408
20731
|
(coordinate) => coordinate !== void 0
|
|
@@ -20411,25 +20734,25 @@ function getAdjacentInterstitialCoordinates(params) {
|
|
|
20411
20734
|
function directSegmentIsStraightOr45(start, end) {
|
|
20412
20735
|
const absoluteX = Math.abs(end.x - start.x);
|
|
20413
20736
|
const absoluteY = Math.abs(end.y - start.y);
|
|
20414
|
-
return absoluteX <=
|
|
20737
|
+
return absoluteX <= EPSILON6 || absoluteY <= EPSILON6 || Math.abs(absoluteX - absoluteY) <= EPSILON6;
|
|
20415
20738
|
}
|
|
20416
20739
|
function getOutwardRank(params) {
|
|
20417
20740
|
const { source, site, direction } = params;
|
|
20418
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;
|
|
20419
|
-
return outwardDisplacement >
|
|
20742
|
+
return outwardDisplacement > EPSILON6 ? 0 : Math.abs(outwardDisplacement) <= EPSILON6 ? 1 : 2;
|
|
20420
20743
|
}
|
|
20421
20744
|
function viaSiteClearsObstacles(params) {
|
|
20422
20745
|
const { point: point6, obstacles, viaDiameter, clearance } = params;
|
|
20423
20746
|
const requiredClearance = viaDiameter / 2 + clearance;
|
|
20424
20747
|
return obstacles.every(
|
|
20425
|
-
(obstacle) => distancePointToObstacle(point6, obstacle) >= requiredClearance -
|
|
20748
|
+
(obstacle) => distancePointToObstacle(point6, obstacle) >= requiredClearance - EPSILON6
|
|
20426
20749
|
);
|
|
20427
20750
|
}
|
|
20428
20751
|
function sourceSegmentClearsOtherObstacles(params) {
|
|
20429
20752
|
const { segment: segment2, sourceObstacle, obstacles, clearance } = params;
|
|
20430
20753
|
const requiredClearance = segment2.width / 2 + clearance;
|
|
20431
20754
|
return obstacles.every(
|
|
20432
|
-
(obstacle) => obstacle === sourceObstacle || distanceSegmentToObstacle(segment2, obstacle) >= requiredClearance -
|
|
20755
|
+
(obstacle) => obstacle === sourceObstacle || distanceSegmentToObstacle(segment2, obstacle) >= requiredClearance - EPSILON6
|
|
20433
20756
|
);
|
|
20434
20757
|
}
|
|
20435
20758
|
function getConnectionCandidates(params) {
|
|
@@ -20457,7 +20780,7 @@ function getConnectionCandidates(params) {
|
|
|
20457
20780
|
];
|
|
20458
20781
|
const uniquePoints2 = [];
|
|
20459
20782
|
for (const point6 of rawPoints) {
|
|
20460
|
-
if (!uniquePoints2.some((candidate) => distance9(candidate, point6) <=
|
|
20783
|
+
if (!uniquePoints2.some((candidate) => distance9(candidate, point6) <= EPSILON6)) {
|
|
20461
20784
|
uniquePoints2.push(point6);
|
|
20462
20785
|
}
|
|
20463
20786
|
}
|
|
@@ -20508,7 +20831,7 @@ function getConnectionCandidates(params) {
|
|
|
20508
20831
|
point6,
|
|
20509
20832
|
blocker.segment.start,
|
|
20510
20833
|
blocker.segment.end
|
|
20511
|
-
) < viaToTraceClearance -
|
|
20834
|
+
) < viaToTraceClearance - EPSILON6) {
|
|
20512
20835
|
return false;
|
|
20513
20836
|
}
|
|
20514
20837
|
return blocker.segment.layer !== sourceSegment.layer || segmentsAreClear(sourceSegment, blocker.segment, rules.clearance);
|
|
@@ -20520,14 +20843,14 @@ function getConnectionCandidates(params) {
|
|
|
20520
20843
|
if (blocker.connectionIndex === preparedConnection.connectionIndex) {
|
|
20521
20844
|
return true;
|
|
20522
20845
|
}
|
|
20523
|
-
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) {
|
|
20524
20847
|
return false;
|
|
20525
20848
|
}
|
|
20526
20849
|
if (blocker.spanLayers.includes(sourceSegment.layer) && distancePointToSegment(
|
|
20527
20850
|
blocker.center,
|
|
20528
20851
|
sourceSegment.start,
|
|
20529
20852
|
sourceSegment.end
|
|
20530
|
-
) < blocker.diameter / 2 + sourceSegment.width / 2 + rules.clearance -
|
|
20853
|
+
) < blocker.diameter / 2 + sourceSegment.width / 2 + rules.clearance - EPSILON6) {
|
|
20531
20854
|
return false;
|
|
20532
20855
|
}
|
|
20533
20856
|
return true;
|
|
@@ -20544,7 +20867,7 @@ function getConnectionCandidates(params) {
|
|
|
20544
20867
|
const perpendicularCoordinates = direction === "left" || direction === "right" ? component.yCoordinates : component.xCoordinates;
|
|
20545
20868
|
const sourcePerpendicularCoordinate = direction === "left" || direction === "right" ? source.y : source.x;
|
|
20546
20869
|
const perpendicularGridIndex = perpendicularCoordinates.findIndex(
|
|
20547
|
-
(coordinate) => Math.abs(coordinate - sourcePerpendicularCoordinate) <=
|
|
20870
|
+
(coordinate) => Math.abs(coordinate - sourcePerpendicularCoordinate) <= EPSILON6
|
|
20548
20871
|
);
|
|
20549
20872
|
const planeCheckerboardSide = perpendicularGridIndex >= 0 ? perpendicularGridIndex % 2 === 0 ? 1 : -1 : void 0;
|
|
20550
20873
|
const preferredPerpendicularSide = connection.terminationType === "boundary" ? rules.preferredBoundaryPerpendicularSideByBusId?.get(connection.busId) : rules.preferPlaneCheckerboardSites && connection.terminationType === "plane" && (direction === "left" || direction === "right") ? planeCheckerboardSide : void 0;
|
|
@@ -20555,10 +20878,10 @@ function getConnectionCandidates(params) {
|
|
|
20555
20878
|
const getPerpendicularPreferenceRank = (candidate) => {
|
|
20556
20879
|
if (preferredPerpendicularSide === void 0) return 0;
|
|
20557
20880
|
const displacement = direction === "left" || direction === "right" ? candidate.point.y - source.y : candidate.point.x - source.x;
|
|
20558
|
-
return displacement * preferredPerpendicularSide >
|
|
20881
|
+
return displacement * preferredPerpendicularSide > EPSILON6 ? 0 : Math.abs(displacement) <= EPSILON6 ? 1 : 2;
|
|
20559
20882
|
};
|
|
20560
20883
|
return candidates.toSorted(
|
|
20561
|
-
(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
|
|
20562
20885
|
);
|
|
20563
20886
|
}
|
|
20564
20887
|
function candidatesAreMutuallyClear(params) {
|
|
@@ -20566,7 +20889,7 @@ function candidatesAreMutuallyClear(params) {
|
|
|
20566
20889
|
const canShareCopper = rules.canShareCopper?.(first.connectionIndex, second.connectionIndex) ?? false;
|
|
20567
20890
|
const requiredHoleSeparation = rules.viaHoleDiameter ? rules.viaHoleDiameter + (rules.holeToHoleClearance ?? rules.clearance) : 0;
|
|
20568
20891
|
const requiredViaSeparation = canShareCopper ? requiredHoleSeparation : Math.max(rules.viaDiameter + rules.clearance, requiredHoleSeparation);
|
|
20569
|
-
if (distance9(first.point, second.point) < requiredViaSeparation -
|
|
20892
|
+
if (distance9(first.point, second.point) < requiredViaSeparation - EPSILON6) {
|
|
20570
20893
|
return false;
|
|
20571
20894
|
}
|
|
20572
20895
|
const requiredViaToTraceClearance = rules.viaDiameter / 2 + rules.traceWidth / 2 + rules.clearance;
|
|
@@ -20575,11 +20898,11 @@ function candidatesAreMutuallyClear(params) {
|
|
|
20575
20898
|
first.point,
|
|
20576
20899
|
second.sourceSegment.start,
|
|
20577
20900
|
second.sourceSegment.end
|
|
20578
|
-
) < requiredViaToTraceClearance -
|
|
20901
|
+
) < requiredViaToTraceClearance - EPSILON6 || distancePointToSegment(
|
|
20579
20902
|
second.point,
|
|
20580
20903
|
first.sourceSegment.start,
|
|
20581
20904
|
first.sourceSegment.end
|
|
20582
|
-
) < requiredViaToTraceClearance -
|
|
20905
|
+
) < requiredViaToTraceClearance - EPSILON6) {
|
|
20583
20906
|
return false;
|
|
20584
20907
|
}
|
|
20585
20908
|
}
|
|
@@ -20721,7 +21044,7 @@ function getComponentDogboneViaSiteCandidates(preparedBuses, rules) {
|
|
|
20721
21044
|
}
|
|
20722
21045
|
|
|
20723
21046
|
// node_modules/@tscircuit/fanout-solver/lib/route-via-minimal-winding.ts
|
|
20724
|
-
var
|
|
21047
|
+
var EPSILON7 = 1e-7;
|
|
20725
21048
|
var MAX_GRID_NODE_COUNT = 12e4;
|
|
20726
21049
|
var MAX_EXPANDED_STATE_COUNT = 24e4;
|
|
20727
21050
|
var EXPANDED_STATES_PER_STEP = 5e3;
|
|
@@ -20877,7 +21200,7 @@ function getConnectorVariants(start, end) {
|
|
|
20877
21200
|
const deltaY = end.y - start.y;
|
|
20878
21201
|
const absoluteX = Math.abs(deltaX);
|
|
20879
21202
|
const absoluteY = Math.abs(deltaY);
|
|
20880
|
-
if (absoluteX <
|
|
21203
|
+
if (absoluteX < EPSILON7 || absoluteY < EPSILON7 || Math.abs(absoluteX - absoluteY) < EPSILON7) {
|
|
20881
21204
|
return [[start, end]];
|
|
20882
21205
|
}
|
|
20883
21206
|
if (absoluteX > absoluteY) {
|
|
@@ -20912,18 +21235,18 @@ function compressPath(points) {
|
|
|
20912
21235
|
function getSegments(points, width, layer) {
|
|
20913
21236
|
return points.slice(1).flatMap((point6, index) => {
|
|
20914
21237
|
const start = points[index];
|
|
20915
|
-
return distance9(start, point6) <
|
|
21238
|
+
return distance9(start, point6) < EPSILON7 ? [] : [{ start, end: point6, width, layer }];
|
|
20916
21239
|
});
|
|
20917
21240
|
}
|
|
20918
21241
|
function segmentIsStraightOr45Degrees(segment2) {
|
|
20919
21242
|
const deltaX = Math.abs(segment2.end.x - segment2.start.x);
|
|
20920
21243
|
const deltaY = Math.abs(segment2.end.y - segment2.start.y);
|
|
20921
|
-
return deltaX <
|
|
21244
|
+
return deltaX < EPSILON7 || deltaY < EPSILON7 || Math.abs(deltaX - deltaY) < EPSILON7;
|
|
20922
21245
|
}
|
|
20923
21246
|
function pathHasNoProperSelfCrossing(segments) {
|
|
20924
21247
|
for (let firstIndex = 0; firstIndex < segments.length; firstIndex++) {
|
|
20925
21248
|
for (let secondIndex = firstIndex + 2; secondIndex < segments.length; secondIndex++) {
|
|
20926
|
-
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) {
|
|
20927
21250
|
continue;
|
|
20928
21251
|
}
|
|
20929
21252
|
if (distanceSegmentToSegment(
|
|
@@ -20931,7 +21254,7 @@ function pathHasNoProperSelfCrossing(segments) {
|
|
|
20931
21254
|
segments[firstIndex].end,
|
|
20932
21255
|
segments[secondIndex].start,
|
|
20933
21256
|
segments[secondIndex].end
|
|
20934
|
-
) <
|
|
21257
|
+
) < EPSILON7) {
|
|
20935
21258
|
return false;
|
|
20936
21259
|
}
|
|
20937
21260
|
}
|
|
@@ -21007,7 +21330,7 @@ function buildPlan(params) {
|
|
|
21007
21330
|
width: traceWidth,
|
|
21008
21331
|
layer: connection.sourceLayer
|
|
21009
21332
|
};
|
|
21010
|
-
const hasSourceDogbone = distance9(sourcePoint, terminal.viaPoint) >
|
|
21333
|
+
const hasSourceDogbone = distance9(sourcePoint, terminal.viaPoint) > EPSILON7;
|
|
21011
21334
|
const changesLayer = connection.sourceLayer !== targetLayer;
|
|
21012
21335
|
const targetSegments = getSegments(targetLayerPoints, traceWidth, targetLayer);
|
|
21013
21336
|
const spanLayers = getViaSpanLayers2({
|
|
@@ -21131,7 +21454,8 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21131
21454
|
preferTargetDirectedLaneBias = false,
|
|
21132
21455
|
allowSourceLayerRouting = false,
|
|
21133
21456
|
adaptiveRouteOrder = false,
|
|
21134
|
-
alignGridToPads = false
|
|
21457
|
+
alignGridToPads = false,
|
|
21458
|
+
includeReverseTargetRotation = false
|
|
21135
21459
|
} = params;
|
|
21136
21460
|
if (maximumRouteOrderAttempts !== void 0 && (!Number.isInteger(maximumRouteOrderAttempts) || maximumRouteOrderAttempts < 1)) {
|
|
21137
21461
|
throw new Error(
|
|
@@ -21192,6 +21516,18 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21192
21516
|
const margin = (segment2.width + traceWidth) / 2 + clearance;
|
|
21193
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);
|
|
21194
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
|
+
}
|
|
21195
21531
|
const blockingVias = blockingCopper.vias.filter(({ via }) => {
|
|
21196
21532
|
if (!via.spanLayers.includes(targetLayer)) return false;
|
|
21197
21533
|
const margin = via.diameter / 2 + traceWidth / 2 + clearance;
|
|
@@ -21248,7 +21584,7 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21248
21584
|
if (obstacle.connectedTo.includes(connectionName) || allowSameNetMerges && obstacleSharesElectricalNet(srj, obstacle, connectionName)) {
|
|
21249
21585
|
continue;
|
|
21250
21586
|
}
|
|
21251
|
-
if (distanceSegmentToObstacle(segment2, obstacle) < requiredObstacleClearance -
|
|
21587
|
+
if (distanceSegmentToObstacle(segment2, obstacle) < requiredObstacleClearance - EPSILON7) {
|
|
21252
21588
|
return false;
|
|
21253
21589
|
}
|
|
21254
21590
|
}
|
|
@@ -21259,7 +21595,7 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21259
21595
|
segment2.end,
|
|
21260
21596
|
blocker.segment.start,
|
|
21261
21597
|
blocker.segment.end
|
|
21262
|
-
) < (segment2.width + blocker.segment.width) / 2 + clearance -
|
|
21598
|
+
) < (segment2.width + blocker.segment.width) / 2 + clearance - EPSILON7) {
|
|
21263
21599
|
return false;
|
|
21264
21600
|
}
|
|
21265
21601
|
}
|
|
@@ -21273,7 +21609,7 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21273
21609
|
segment2.end,
|
|
21274
21610
|
blocker.segment.start,
|
|
21275
21611
|
blocker.segment.end
|
|
21276
|
-
) < (segment2.width + blocker.segment.width) / 2 + clearance -
|
|
21612
|
+
) < (segment2.width + blocker.segment.width) / 2 + clearance - EPSILON7) {
|
|
21277
21613
|
return false;
|
|
21278
21614
|
}
|
|
21279
21615
|
}
|
|
@@ -21293,7 +21629,7 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21293
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) {
|
|
21294
21630
|
continue;
|
|
21295
21631
|
}
|
|
21296
|
-
if (distancePointToSegment(blocker.via.center, segment2.start, segment2.end) < requiredDistance -
|
|
21632
|
+
if (distancePointToSegment(blocker.via.center, segment2.start, segment2.end) < requiredDistance - EPSILON7) {
|
|
21297
21633
|
return false;
|
|
21298
21634
|
}
|
|
21299
21635
|
}
|
|
@@ -21491,10 +21827,10 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21491
21827
|
let expandedBatchPoints = [];
|
|
21492
21828
|
while (heap.size > 0 && expandedStateCount < MAX_EXPANDED_STATE_COUNT) {
|
|
21493
21829
|
const current = heap.pop();
|
|
21494
|
-
if (current.score >= bestGoalCost -
|
|
21830
|
+
if (current.score >= bestGoalCost - EPSILON7) break;
|
|
21495
21831
|
const state = current.node * 9 + current.direction;
|
|
21496
21832
|
const currentDistance = distances[state];
|
|
21497
|
-
if (current.score > currentDistance + heuristic(nodes[current.node].point) +
|
|
21833
|
+
if (current.score > currentDistance + heuristic(nodes[current.node].point) + EPSILON7)
|
|
21498
21834
|
continue;
|
|
21499
21835
|
expandedStateCount++;
|
|
21500
21836
|
expandedStatesSinceYield++;
|
|
@@ -21522,7 +21858,7 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21522
21858
|
for (const startConnector of startConnectors) {
|
|
21523
21859
|
for (const endConnector of endConnectors) {
|
|
21524
21860
|
const candidateCost = currentDistance - shortestStartLength + startConnector.length + endConnector.length;
|
|
21525
|
-
if (candidateCost >= bestGoalCost -
|
|
21861
|
+
if (candidateCost >= bestGoalCost - EPSILON7) continue;
|
|
21526
21862
|
const points = compressPath([
|
|
21527
21863
|
...startConnector.points,
|
|
21528
21864
|
...gridPoints.slice(1),
|
|
@@ -21568,7 +21904,7 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21568
21904
|
const lanePenalty = laneBias === 0 ? 0 : laneBias > 0 ? Math.max(0, targetTrack - nextTrack) * 0.2 : Math.max(0, nextTrack - targetTrack) * 0.2;
|
|
21569
21905
|
const nextDistance = currentDistance + (deltaColumn !== 0 && deltaRow !== 0 ? gridStep * Math.SQRT2 : gridStep) + (addsTurn ? gridStep * 0.2 : 0) + lanePenalty;
|
|
21570
21906
|
const nextState = nextNode * 9 + directionIndex;
|
|
21571
|
-
if (nextDistance >= distances[nextState] -
|
|
21907
|
+
if (nextDistance >= distances[nextState] - EPSILON7) continue;
|
|
21572
21908
|
const edgeIndex = current.node * 8 + directionIndex;
|
|
21573
21909
|
if (edgeClearance[edgeIndex] === 0) {
|
|
21574
21910
|
const clear = segmentIsClear({
|
|
@@ -21631,9 +21967,9 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21631
21967
|
);
|
|
21632
21968
|
const meanViaTrack = viaTracks.reduce((sum, track) => sum + track, 0) / viaTracks.length;
|
|
21633
21969
|
const meanTargetTrack = targetTracks.reduce((sum, track) => sum + track, 0) / targetTracks.length;
|
|
21634
|
-
const viasAreBeforeTargets = Math.max(...viaTracks) < Math.min(...targetTracks) -
|
|
21635
|
-
const viasAreAfterTargets = Math.min(...viaTracks) > Math.max(...targetTracks) +
|
|
21636
|
-
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];
|
|
21637
21973
|
const initialRouteOrderFactories = [];
|
|
21638
21974
|
if (alignGridToPads && preferTargetDirectedLaneBias && viasAreBeforeTargets) {
|
|
21639
21975
|
initialRouteOrderFactories.push(() => targetOrderedTerminals);
|
|
@@ -21645,9 +21981,15 @@ function* routeViaMinimalWindingAlternativesSteps(params, maximumAlternatives =
|
|
|
21645
21981
|
]);
|
|
21646
21982
|
} else if (viasAreAfterTargets) {
|
|
21647
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
|
+
}
|
|
21648
21990
|
}
|
|
21649
21991
|
initialRouteOrderFactories.push(
|
|
21650
|
-
...preferTargetDirectedLaneBias && bus.direction === boundaryDirection && meanTargetTrack < meanViaTrack -
|
|
21992
|
+
...preferTargetDirectedLaneBias && bus.direction === boundaryDirection && meanTargetTrack < meanViaTrack - EPSILON7 && !viasAreBeforeTargets && !viasAreAfterTargets ? [
|
|
21651
21993
|
() => [...targetOrderedTerminals].reverse(),
|
|
21652
21994
|
() => targetOrderedTerminals
|
|
21653
21995
|
] : [
|
|
@@ -23220,7 +23562,7 @@ function routePlaneTerminatedBus(params) {
|
|
|
23220
23562
|
otherPlans: [...acceptedPlans, ...fixedPlans],
|
|
23221
23563
|
staticClearanceCache,
|
|
23222
23564
|
blockingBusCounts,
|
|
23223
|
-
cacheKey: `plane-fixed:${bus.busId}:${targetLayer}:${preparedConnection.connectionIndex}:${candidateIndex}`,
|
|
23565
|
+
cacheKey: `plane-fixed:${bus.busId}:${targetLayer}:${preparedConnection.connectionIndex}:${fixedViaPoint.x}:${fixedViaPoint.y}:${candidateIndex}`,
|
|
23224
23566
|
srj,
|
|
23225
23567
|
sharedBoundary: bus.sharedBoundary,
|
|
23226
23568
|
clearance,
|
|
@@ -23781,6 +24123,36 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23781
24123
|
const dogboneTerminalPatterns = acceptedBoundaryPlansExist && !allowBlindAndBuriedVias ? [...mixedDogboneTerminalPatterns, ...uniformDogboneTerminalPatterns] : [...uniformDogboneTerminalPatterns, ...mixedDogboneTerminalPatterns];
|
|
23782
24124
|
const unreservedTerminalPatterns = canUseViaInPadTerminals ? planeTerminationsAlreadyOccupyTheFanout ? [viaInPadTerminalPattern, ...dogboneTerminalPatterns] : [...dogboneTerminalPatterns, viaInPadTerminalPattern] : dogboneTerminalPatterns;
|
|
23783
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
|
+
}
|
|
23784
24156
|
const seenTerminalSignatures = /* @__PURE__ */ new Set();
|
|
23785
24157
|
for (const terminalPattern of terminalPatterns) {
|
|
23786
24158
|
const terminals = bus.connections.map((preparedConnection) => {
|
|
@@ -23823,12 +24195,45 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23823
24195
|
};
|
|
23824
24196
|
});
|
|
23825
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
|
+
}
|
|
23826
24231
|
const gridStepDivisor = terminalPattern.getViaPoint && Math.min(bus.pitchX, bus.pitchY) - 2 * (viaDiameter / 2 + traceWidth / 2 + clearance) < traceWidth + clearance ? 2 : 1;
|
|
23827
24232
|
const terminalSignature = `${terminals.map(
|
|
23828
24233
|
(terminal) => `${terminal.connection.connectionIndex}:${terminal.viaPoint.x}:${terminal.viaPoint.y}:${terminal.exitPoint.x}:${terminal.exitPoint.y}`
|
|
23829
24234
|
).join(
|
|
23830
24235
|
"|"
|
|
23831
|
-
)}:${terminalPattern.maximumRouteOrderAttempts ?? "all"}:${gridStepDivisor}:${alignGridToPads}`;
|
|
24236
|
+
)}:${terminalPattern.maximumRouteOrderAttempts ?? "all"}:${gridStepDivisor}:${alignGridToPads}:${Boolean(terminalPattern.localDogboneRepair)}`;
|
|
23832
24237
|
if (seenTerminalSignatures.has(terminalSignature)) continue;
|
|
23833
24238
|
seenTerminalSignatures.add(terminalSignature);
|
|
23834
24239
|
const windingSteps = routeViaMinimalWindingAlternativesSteps(
|
|
@@ -23848,6 +24253,7 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23848
24253
|
maximumRouteOrderAttempts: terminalPattern.maximumRouteOrderAttempts,
|
|
23849
24254
|
adaptiveRouteOrder: adaptiveWindingRouteOrder,
|
|
23850
24255
|
alignGridToPads,
|
|
24256
|
+
includeReverseTargetRotation: terminalPattern.localDogboneRepair,
|
|
23851
24257
|
reservedVias,
|
|
23852
24258
|
gridStepDivisor,
|
|
23853
24259
|
preferTargetDirectedLaneBias: terminalPattern.preferTargetDirectedLaneBias
|
|
@@ -23897,41 +24303,46 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23897
24303
|
}
|
|
23898
24304
|
}
|
|
23899
24305
|
}
|
|
23900
|
-
if (alternatives.length === 0 && allowBoundarySideViaFallback && fixedViaPointsByConnectionIndex && bus.connections.length
|
|
23901
|
-
|
|
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;
|
|
23902
24310
|
const boundaryDirection = getDirectionForExitEdge(bus.exitEdge);
|
|
23903
24311
|
const boundaryExitAxis = getExitAxis(bus, boundaryDirection);
|
|
23904
|
-
const
|
|
23905
|
-
bus
|
|
23906
|
-
|
|
23907
|
-
|
|
23908
|
-
|
|
23909
|
-
|
|
23910
|
-
|
|
23911
|
-
|
|
23912
|
-
|
|
23913
|
-
|
|
23914
|
-
|
|
23915
|
-
|
|
23916
|
-
|
|
23917
|
-
|
|
23918
|
-
|
|
23919
|
-
|
|
23920
|
-
|
|
23921
|
-
|
|
23922
|
-
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)
|
|
23923
24331
|
);
|
|
23924
24332
|
const sourceLayerSrj = {
|
|
23925
24333
|
...srj,
|
|
23926
|
-
obstacles: srj.obstacles.map(
|
|
23927
|
-
|
|
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 ? {
|
|
23928
24339
|
...obstacle,
|
|
23929
24340
|
connectedTo: [
|
|
23930
24341
|
...obstacle.connectedTo,
|
|
23931
|
-
|
|
24342
|
+
connection.connection.name
|
|
23932
24343
|
]
|
|
23933
|
-
} : obstacle
|
|
23934
|
-
)
|
|
24344
|
+
} : obstacle;
|
|
24345
|
+
})
|
|
23935
24346
|
};
|
|
23936
24347
|
const insetStep = Math.max(
|
|
23937
24348
|
viaDiameter / 2 + clearance,
|
|
@@ -23940,34 +24351,104 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23940
24351
|
const cornerSide = preferCornerBoundaryVia ? getCornerSide(bus) : void 0;
|
|
23941
24352
|
const boundaryViaCandidates = [1, 2, 3, 4, 5].flatMap((multiple) => {
|
|
23942
24353
|
const inset = multiple * insetStep;
|
|
23943
|
-
const straight =
|
|
23944
|
-
|
|
23945
|
-
|
|
23946
|
-
|
|
24354
|
+
const straight = finalTracks.map(
|
|
24355
|
+
(finalTrack) => makePoint(
|
|
24356
|
+
boundaryExitAxis - directionSign(boundaryDirection) * inset,
|
|
24357
|
+
finalTrack,
|
|
24358
|
+
boundaryDirection
|
|
24359
|
+
)
|
|
23947
24360
|
);
|
|
23948
24361
|
if (!cornerSide) return [straight];
|
|
23949
24362
|
return [
|
|
23950
|
-
|
|
23951
|
-
|
|
23952
|
-
|
|
23953
|
-
|
|
24363
|
+
finalTracks.map(
|
|
24364
|
+
(finalTrack) => makePoint(
|
|
24365
|
+
boundaryExitAxis - directionSign(boundaryDirection) * inset,
|
|
24366
|
+
finalTrack + (cornerSide === "maximum" ? inset : -inset),
|
|
24367
|
+
boundaryDirection
|
|
24368
|
+
)
|
|
23954
24369
|
),
|
|
23955
24370
|
straight
|
|
23956
24371
|
];
|
|
23957
24372
|
});
|
|
23958
|
-
|
|
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;
|
|
23959
24442
|
const sourceLayerSteps = routeViaMinimalWindingAlternativesSteps(
|
|
23960
24443
|
{
|
|
23961
24444
|
srj: sourceLayerSrj,
|
|
23962
24445
|
bus,
|
|
23963
|
-
targetLayer:
|
|
23964
|
-
terminals:
|
|
23965
|
-
|
|
23966
|
-
|
|
23967
|
-
|
|
23968
|
-
|
|
23969
|
-
}
|
|
23970
|
-
],
|
|
24446
|
+
targetLayer: sourceLayer2,
|
|
24447
|
+
terminals: bus.connections.map((preparedConnection, index) => ({
|
|
24448
|
+
connection: preparedConnection,
|
|
24449
|
+
viaPoint: preparedConnection.sourcePoint,
|
|
24450
|
+
exitPoint: boundaryViaPoints[index]
|
|
24451
|
+
})),
|
|
23971
24452
|
acceptedPlans,
|
|
23972
24453
|
layerNames,
|
|
23973
24454
|
traceWidth,
|
|
@@ -23976,8 +24457,8 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23976
24457
|
clearance,
|
|
23977
24458
|
allowBlindAndBuriedVias,
|
|
23978
24459
|
allowSameNetMerges,
|
|
23979
|
-
maximumRouteOrderAttempts: 3,
|
|
23980
|
-
reservedVias,
|
|
24460
|
+
maximumRouteOrderAttempts: bus.connections.length === 1 ? 3 : 6,
|
|
24461
|
+
reservedVias: bus.connections.length > 1 ? [...reservedVias, ...boundaryVias] : reservedVias,
|
|
23981
24462
|
gridStepDivisor: 2,
|
|
23982
24463
|
allowSourceLayerRouting: true,
|
|
23983
24464
|
alignGridToPads: true
|
|
@@ -23990,72 +24471,124 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
23990
24471
|
yield {
|
|
23991
24472
|
phase: "via-minimal-winding",
|
|
23992
24473
|
busId: bus.busId,
|
|
23993
|
-
targetLayer:
|
|
24474
|
+
targetLayer: sourceLayer2,
|
|
23994
24475
|
winding: sourceLayerResult.value
|
|
23995
24476
|
};
|
|
23996
24477
|
sourceLayerResult = sourceLayerSteps.next();
|
|
23997
24478
|
}
|
|
23998
|
-
const
|
|
23999
|
-
if (!
|
|
24000
|
-
|
|
24001
|
-
|
|
24002
|
-
|
|
24003
|
-
|
|
24004
|
-
|
|
24005
|
-
|
|
24006
|
-
|
|
24007
|
-
|
|
24008
|
-
|
|
24009
|
-
|
|
24010
|
-
|
|
24011
|
-
|
|
24012
|
-
|
|
24013
|
-
|
|
24014
|
-
|
|
24015
|
-
|
|
24016
|
-
|
|
24017
|
-
|
|
24018
|
-
|
|
24019
|
-
|
|
24020
|
-
|
|
24021
|
-
|
|
24022
|
-
|
|
24023
|
-
|
|
24024
|
-
|
|
24025
|
-
|
|
24026
|
-
|
|
24027
|
-
|
|
24028
|
-
|
|
24029
|
-
|
|
24030
|
-
|
|
24031
|
-
|
|
24032
|
-
|
|
24033
|
-
|
|
24034
|
-
|
|
24035
|
-
|
|
24036
|
-
|
|
24037
|
-
|
|
24038
|
-
|
|
24039
|
-
|
|
24040
|
-
|
|
24041
|
-
|
|
24042
|
-
|
|
24043
|
-
|
|
24044
|
-
|
|
24045
|
-
|
|
24046
|
-
|
|
24047
|
-
|
|
24048
|
-
|
|
24049
|
-
|
|
24050
|
-
|
|
24051
|
-
|
|
24052
|
-
|
|
24053
|
-
|
|
24054
|
-
|
|
24055
|
-
|
|
24056
|
-
|
|
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
|
+
};
|
|
24057
24590
|
}
|
|
24058
|
-
|
|
24591
|
+
);
|
|
24059
24592
|
const boundaryViaPlansAreClear = fanoutPlansAreClear({
|
|
24060
24593
|
plans: [...acceptedPlans, ...plans],
|
|
24061
24594
|
srj,
|
|
@@ -24064,23 +24597,28 @@ function* routeBusAlternativesSteps(params, maxAlternatives = 1, includeVisualiz
|
|
|
24064
24597
|
allowBlindAndBuriedVias,
|
|
24065
24598
|
allowSameNetMerges
|
|
24066
24599
|
});
|
|
24067
|
-
const reservedViasAreClear =
|
|
24068
|
-
|
|
24069
|
-
|
|
24070
|
-
reserved.connectionName
|
|
24071
|
-
|
|
24072
|
-
|
|
24073
|
-
|
|
24074
|
-
|
|
24075
|
-
|
|
24076
|
-
|
|
24077
|
-
|
|
24078
|
-
|
|
24079
|
-
|
|
24080
|
-
|
|
24081
|
-
|
|
24082
|
-
|
|
24083
|
-
|
|
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
|
+
);
|
|
24084
24622
|
if (boundaryViaPlansAreClear && reservedViasAreClear) {
|
|
24085
24623
|
addAlternative(plans);
|
|
24086
24624
|
return alternatives;
|
|
@@ -24197,9 +24735,9 @@ function routeBus(params) {
|
|
|
24197
24735
|
}
|
|
24198
24736
|
|
|
24199
24737
|
// node_modules/@tscircuit/fanout-solver/lib/match-bus-lengths.ts
|
|
24200
|
-
var
|
|
24738
|
+
var EPSILON8 = 1e-6;
|
|
24201
24739
|
function pointsMatch2(first, second) {
|
|
24202
|
-
return distance9(first, second) <=
|
|
24740
|
+
return distance9(first, second) <= EPSILON8;
|
|
24203
24741
|
}
|
|
24204
24742
|
function getPlanVias3(plan) {
|
|
24205
24743
|
return [plan.via, ...plan.additionalVias ?? []].filter(
|
|
@@ -24304,26 +24842,26 @@ function splitSegmentAtDenseBounds(params) {
|
|
|
24304
24842
|
const deltaY = segment2.end.y - segment2.start.y;
|
|
24305
24843
|
const splitParameters = [0, 1];
|
|
24306
24844
|
const addSplitParameter = (parameter) => {
|
|
24307
|
-
if (parameter <=
|
|
24845
|
+
if (parameter <= EPSILON8 || parameter >= 1 - EPSILON8) return;
|
|
24308
24846
|
const point6 = {
|
|
24309
24847
|
x: segment2.start.x + deltaX * parameter,
|
|
24310
24848
|
y: segment2.start.y + deltaY * parameter
|
|
24311
24849
|
};
|
|
24312
|
-
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) {
|
|
24313
24851
|
return;
|
|
24314
24852
|
}
|
|
24315
24853
|
splitParameters.push(parameter);
|
|
24316
24854
|
};
|
|
24317
|
-
if (Math.abs(deltaX) >
|
|
24855
|
+
if (Math.abs(deltaX) > EPSILON8) {
|
|
24318
24856
|
addSplitParameter((expandedBounds.minX - segment2.start.x) / deltaX);
|
|
24319
24857
|
addSplitParameter((expandedBounds.maxX - segment2.start.x) / deltaX);
|
|
24320
24858
|
}
|
|
24321
|
-
if (Math.abs(deltaY) >
|
|
24859
|
+
if (Math.abs(deltaY) > EPSILON8) {
|
|
24322
24860
|
addSplitParameter((expandedBounds.minY - segment2.start.y) / deltaY);
|
|
24323
24861
|
addSplitParameter((expandedBounds.maxY - segment2.start.y) / deltaY);
|
|
24324
24862
|
}
|
|
24325
24863
|
const parameters = splitParameters.toSorted((first, second) => first - second).filter(
|
|
24326
|
-
(parameter, index, values) => index === 0 || Math.abs(parameter - values[index - 1]) >
|
|
24864
|
+
(parameter, index, values) => index === 0 || Math.abs(parameter - values[index - 1]) > EPSILON8
|
|
24327
24865
|
);
|
|
24328
24866
|
return parameters.slice(1).map((endParameter, index) => {
|
|
24329
24867
|
const startParameter = parameters[index];
|
|
@@ -24370,7 +24908,7 @@ function hasNonAdjacentSelfIntersection(segments) {
|
|
|
24370
24908
|
first.end,
|
|
24371
24909
|
second.start,
|
|
24372
24910
|
second.end
|
|
24373
|
-
) <=
|
|
24911
|
+
) <= EPSILON8) {
|
|
24374
24912
|
return true;
|
|
24375
24913
|
}
|
|
24376
24914
|
}
|
|
@@ -24412,7 +24950,7 @@ function replacementCopperIsSelfClear(params) {
|
|
|
24412
24950
|
const replacement = segments[replacementIndex];
|
|
24413
24951
|
for (const [otherIndex, other] of segments.entries()) {
|
|
24414
24952
|
const requiredCenterlineClearance = replacement.width / 2 + other.width / 2 + clearance;
|
|
24415
|
-
if (getConnectedPathDistance(replacementIndex, otherIndex) <= requiredCenterlineClearance +
|
|
24953
|
+
if (getConnectedPathDistance(replacementIndex, otherIndex) <= requiredCenterlineClearance + EPSILON8) {
|
|
24416
24954
|
continue;
|
|
24417
24955
|
}
|
|
24418
24956
|
if (!segmentsAreClear(replacement, other, clearance)) return false;
|
|
@@ -24422,7 +24960,7 @@ function replacementCopperIsSelfClear(params) {
|
|
|
24422
24960
|
if (pointsMatch2(via.center, replacement.start) || pointsMatch2(via.center, replacement.end)) {
|
|
24423
24961
|
continue;
|
|
24424
24962
|
}
|
|
24425
|
-
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) {
|
|
24426
24964
|
return false;
|
|
24427
24965
|
}
|
|
24428
24966
|
}
|
|
@@ -24430,7 +24968,7 @@ function replacementCopperIsSelfClear(params) {
|
|
|
24430
24968
|
return true;
|
|
24431
24969
|
}
|
|
24432
24970
|
function pointIsInsideBounds2(point6, bounds) {
|
|
24433
|
-
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;
|
|
24434
24972
|
}
|
|
24435
24973
|
function addScaled(point6, vector2, scale3) {
|
|
24436
24974
|
return {
|
|
@@ -24450,9 +24988,9 @@ function createMeanderPoints(params) {
|
|
|
24450
24988
|
const dx = segment2.end.x - segment2.start.x;
|
|
24451
24989
|
const dy = segment2.end.y - segment2.start.y;
|
|
24452
24990
|
const segmentLength = Math.hypot(dx, dy);
|
|
24453
|
-
if (segmentLength <=
|
|
24454
|
-
const isAxisAligned = Math.abs(dx) <=
|
|
24455
|
-
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;
|
|
24456
24994
|
if (!isAxisAligned && !isFortyFiveDegree) return null;
|
|
24457
24995
|
const tangent = { x: dx / segmentLength, y: dy / segmentLength };
|
|
24458
24996
|
const normal = {
|
|
@@ -24468,11 +25006,11 @@ function createMeanderPoints(params) {
|
|
|
24468
25006
|
const toothGap = pitch;
|
|
24469
25007
|
const occupiedLength = toothCount * toothSpan + Math.max(0, toothCount - 1) * toothGap;
|
|
24470
25008
|
const minimumLead = pitch / 4;
|
|
24471
|
-
if (occupiedLength + minimumLead * 2 > segmentLength +
|
|
25009
|
+
if (occupiedLength + minimumLead * 2 > segmentLength + EPSILON8) {
|
|
24472
25010
|
return null;
|
|
24473
25011
|
}
|
|
24474
25012
|
const minimumAddedLengthPerTooth = 4 * chamfer * (Math.SQRT2 - 1);
|
|
24475
|
-
if (targetAddedLength +
|
|
25013
|
+
if (targetAddedLength + EPSILON8 < minimumAddedLengthPerTooth * toothCount) {
|
|
24476
25014
|
return null;
|
|
24477
25015
|
}
|
|
24478
25016
|
const verticalRun = (targetAddedLength / toothCount - minimumAddedLengthPerTooth) / 2;
|
|
@@ -24605,10 +25143,10 @@ function* createSpreadLaneCandidates(plan, clearance) {
|
|
|
24605
25143
|
(a, b) => distance9(b.segment.start, b.segment.end) - distance9(a.segment.start, a.segment.end)
|
|
24606
25144
|
)) {
|
|
24607
25145
|
const length7 = distance9(segment2.start, segment2.end);
|
|
24608
|
-
if (length7 <=
|
|
25146
|
+
if (length7 <= EPSILON8) continue;
|
|
24609
25147
|
const dx = Math.abs(segment2.end.x - segment2.start.x);
|
|
24610
25148
|
const dy = Math.abs(segment2.end.y - segment2.start.y);
|
|
24611
|
-
if (dx >
|
|
25149
|
+
if (dx > EPSILON8 && dy > EPSILON8 && Math.abs(dx - dy) > EPSILON8) continue;
|
|
24612
25150
|
const tangent = {
|
|
24613
25151
|
x: (segment2.end.x - segment2.start.x) / length7,
|
|
24614
25152
|
y: (segment2.end.y - segment2.start.y) / length7
|
|
@@ -24678,15 +25216,15 @@ function matchBusPlanLengths(params) {
|
|
|
24678
25216
|
}
|
|
24679
25217
|
const maxLengthSkew = bus.maxLengthSkew;
|
|
24680
25218
|
const skew = getBusSkew(busPlans);
|
|
24681
|
-
if (skew <= maxLengthSkew +
|
|
25219
|
+
if (skew <= maxLengthSkew + EPSILON8) break;
|
|
24682
25220
|
const shortest = busPlans.toSorted(
|
|
24683
25221
|
(first, second) => first.length - second.length || first.connectionName.localeCompare(second.connectionName)
|
|
24684
25222
|
)[0];
|
|
24685
25223
|
const longestLength = Math.max(...busPlans.map((plan) => plan.length));
|
|
24686
25224
|
const deficit = longestLength - shortest.length;
|
|
24687
25225
|
const minimumRequiredAddition = Math.max(
|
|
24688
|
-
|
|
24689
|
-
deficit - maxLengthSkew +
|
|
25226
|
+
EPSILON8,
|
|
25227
|
+
deficit - maxLengthSkew + EPSILON8
|
|
24690
25228
|
);
|
|
24691
25229
|
const targetAddedLengths = [
|
|
24692
25230
|
minimumRequiredAddition,
|
|
@@ -24696,8 +25234,8 @@ function matchBusPlanLengths(params) {
|
|
|
24696
25234
|
deficit,
|
|
24697
25235
|
deficit + maxLengthSkew
|
|
24698
25236
|
].filter(
|
|
24699
|
-
(value, index, values) => value >
|
|
24700
|
-
(candidate) => Math.abs(candidate - value) <
|
|
25237
|
+
(value, index, values) => value > EPSILON8 && values.findIndex(
|
|
25238
|
+
(candidate) => Math.abs(candidate - value) < EPSILON8
|
|
24701
25239
|
) === index
|
|
24702
25240
|
).toSorted((first, second) => first - second);
|
|
24703
25241
|
let acceptedPlans = null;
|
|
@@ -24708,7 +25246,7 @@ function matchBusPlanLengths(params) {
|
|
|
24708
25246
|
const nextBusPlans = nextPlans.filter(
|
|
24709
25247
|
(plan) => plan.busId === bus.busId
|
|
24710
25248
|
);
|
|
24711
|
-
if (getBusSkew(nextBusPlans) > skew +
|
|
25249
|
+
if (getBusSkew(nextBusPlans) > skew + EPSILON8) return null;
|
|
24712
25250
|
if (!fanoutPlansAreClear({
|
|
24713
25251
|
plans: nextPlans,
|
|
24714
25252
|
srj: inputSrj,
|
|
@@ -24742,7 +25280,7 @@ function matchBusPlanLengths(params) {
|
|
|
24742
25280
|
const search = (currentPlan, stagesRemaining) => {
|
|
24743
25281
|
const addedLength = currentPlan.length - shortest.length;
|
|
24744
25282
|
const remainingAddition = targetAddedLength - addedLength;
|
|
24745
|
-
if (remainingAddition <=
|
|
25283
|
+
if (remainingAddition <= EPSILON8) {
|
|
24746
25284
|
return acceptCandidate(currentPlan);
|
|
24747
25285
|
}
|
|
24748
25286
|
if (stagesRemaining <= 0) return null;
|
|
@@ -24787,7 +25325,7 @@ function matchBusPlanLengths(params) {
|
|
|
24787
25325
|
if (!acceptedPlans) continue;
|
|
24788
25326
|
break;
|
|
24789
25327
|
}
|
|
24790
|
-
if (!acceptedPlans && Math.abs(targetAddedLength - minimumRequiredAddition) <=
|
|
25328
|
+
if (!acceptedPlans && Math.abs(targetAddedLength - minimumRequiredAddition) <= EPSILON8) {
|
|
24791
25329
|
acceptedPlans = findMultiSpanCandidate(targetAddedLength);
|
|
24792
25330
|
}
|
|
24793
25331
|
if (acceptedPlans) break;
|
|
@@ -24829,7 +25367,7 @@ function matchBusPlanLengths(params) {
|
|
|
24829
25367
|
const matchedBusPlans = matchedPlans.filter(
|
|
24830
25368
|
(plan) => plan.busId === bus.busId
|
|
24831
25369
|
);
|
|
24832
|
-
if (getBusSkew(matchedBusPlans) > bus.maxLengthSkew +
|
|
25370
|
+
if (getBusSkew(matchedBusPlans) > bus.maxLengthSkew + EPSILON8) {
|
|
24833
25371
|
return { plans: null, failedBus: bus };
|
|
24834
25372
|
}
|
|
24835
25373
|
}
|
|
@@ -25811,7 +26349,7 @@ function prepareFanoutBuses(srj, options) {
|
|
|
25811
26349
|
}
|
|
25812
26350
|
|
|
25813
26351
|
// node_modules/@tscircuit/fanout-solver/lib/route-single-layer-adaptive-exits.ts
|
|
25814
|
-
var
|
|
26352
|
+
var EPSILON9 = 1e-9;
|
|
25815
26353
|
var OBSTACLE_BIN_SIZE = 1;
|
|
25816
26354
|
var FANOUT_FLOW_DEBUG_ENABLED = globalThis.process?.env?.FANOUT_FLOW_DEBUG === "1";
|
|
25817
26355
|
function visualizeFlowProgress(params) {
|
|
@@ -25892,7 +26430,7 @@ function connectWith45DegreeSegments(start, end) {
|
|
|
25892
26430
|
const deltaY = end.y - start.y;
|
|
25893
26431
|
const absoluteX = Math.abs(deltaX);
|
|
25894
26432
|
const absoluteY = Math.abs(deltaY);
|
|
25895
|
-
if (absoluteX <
|
|
26433
|
+
if (absoluteX < EPSILON9 || absoluteY < EPSILON9 || Math.abs(absoluteX - absoluteY) < EPSILON9) {
|
|
25896
26434
|
return [start, end];
|
|
25897
26435
|
}
|
|
25898
26436
|
if (absoluteX > absoluteY) {
|
|
@@ -25921,7 +26459,7 @@ function enforceStraightOr45DegreeSegments(points) {
|
|
|
25921
26459
|
const start = normalized.at(-1);
|
|
25922
26460
|
const deltaX = Math.abs(end.x - start.x);
|
|
25923
26461
|
const deltaY = Math.abs(end.y - start.y);
|
|
25924
|
-
if (deltaX <
|
|
26462
|
+
if (deltaX < EPSILON9 || deltaY < EPSILON9 || Math.abs(deltaX - deltaY) < EPSILON9) {
|
|
25925
26463
|
normalized.push(end);
|
|
25926
26464
|
continue;
|
|
25927
26465
|
}
|
|
@@ -25954,7 +26492,7 @@ function chamferOrthogonalPolyline3(points, requestedChamfer) {
|
|
|
25954
26492
|
const next = points[index + 1];
|
|
25955
26493
|
const incomingLength = distance9(previous, corner);
|
|
25956
26494
|
const outgoingLength = distance9(corner, next);
|
|
25957
|
-
if (incomingLength <
|
|
26495
|
+
if (incomingLength < EPSILON9 || outgoingLength < EPSILON9) continue;
|
|
25958
26496
|
const incoming = {
|
|
25959
26497
|
x: (corner.x - previous.x) / incomingLength,
|
|
25960
26498
|
y: (corner.y - previous.y) / incomingLength
|
|
@@ -25987,7 +26525,7 @@ function chamferOrthogonalPolyline3(points, requestedChamfer) {
|
|
|
25987
26525
|
function getSegments2(points, traceWidth) {
|
|
25988
26526
|
const segments = [];
|
|
25989
26527
|
for (let index = 1; index < points.length; index++) {
|
|
25990
|
-
if (distance9(points[index - 1], points[index]) <
|
|
26528
|
+
if (distance9(points[index - 1], points[index]) < EPSILON9) continue;
|
|
25991
26529
|
segments.push({
|
|
25992
26530
|
start: points[index - 1],
|
|
25993
26531
|
end: points[index],
|
|
@@ -26138,7 +26676,7 @@ function* createFlowGridSteps(params) {
|
|
|
26138
26676
|
for (let node = 0; node < nodeCount; node++) {
|
|
26139
26677
|
const point6 = points[node];
|
|
26140
26678
|
if (getNearbyObstacles(point6).every(
|
|
26141
|
-
(obstacle) => distancePointToObstacle(point6, obstacle) >= requiredObstacleDistance -
|
|
26679
|
+
(obstacle) => distancePointToObstacle(point6, obstacle) >= requiredObstacleDistance - EPSILON9
|
|
26142
26680
|
)) {
|
|
26143
26681
|
obstacleFreeNodes[node] = 1;
|
|
26144
26682
|
}
|
|
@@ -26177,7 +26715,7 @@ function* createFlowGridSteps(params) {
|
|
|
26177
26715
|
layer: "top"
|
|
26178
26716
|
};
|
|
26179
26717
|
if (getNearbyObstacles(segment2.start, segment2.end).some(
|
|
26180
|
-
(obstacle) => distanceSegmentToObstacle(segment2, obstacle) < requiredObstacleDistance -
|
|
26718
|
+
(obstacle) => distanceSegmentToObstacle(segment2, obstacle) < requiredObstacleDistance - EPSILON9
|
|
26181
26719
|
)) {
|
|
26182
26720
|
continue;
|
|
26183
26721
|
}
|
|
@@ -26242,7 +26780,7 @@ function* routeDirectionGroupSteps(params) {
|
|
|
26242
26780
|
const segments = getSegments2(points, traceWidth);
|
|
26243
26781
|
return segments.every(
|
|
26244
26782
|
(segment2) => obstacles.every(
|
|
26245
|
-
(obstacle) => obstacleBelongsToItem(obstacle, item) || distanceSegmentToObstacle(segment2, obstacle) >= requiredObstacleDistance -
|
|
26783
|
+
(obstacle) => obstacleBelongsToItem(obstacle, item) || distanceSegmentToObstacle(segment2, obstacle) >= requiredObstacleDistance - EPSILON9
|
|
26246
26784
|
)
|
|
26247
26785
|
) && segments.every(
|
|
26248
26786
|
(segment2) => acceptedSegments.every(
|
|
@@ -26251,7 +26789,7 @@ function* routeDirectionGroupSteps(params) {
|
|
|
26251
26789
|
segment2.end,
|
|
26252
26790
|
acceptedSegment.start,
|
|
26253
26791
|
acceptedSegment.end
|
|
26254
|
-
) >= requiredRouteDistance -
|
|
26792
|
+
) >= requiredRouteDistance - EPSILON9
|
|
26255
26793
|
)
|
|
26256
26794
|
);
|
|
26257
26795
|
};
|
|
@@ -26341,7 +26879,7 @@ function* routeDirectionGroupSteps(params) {
|
|
|
26341
26879
|
segment2.end,
|
|
26342
26880
|
otherSegment.start,
|
|
26343
26881
|
otherSegment.end
|
|
26344
|
-
) >= requiredRouteDistance -
|
|
26882
|
+
) >= requiredRouteDistance - EPSILON9
|
|
26345
26883
|
)
|
|
26346
26884
|
)
|
|
26347
26885
|
);
|
|
@@ -26409,7 +26947,7 @@ function* routeDirectionGroupSteps(params) {
|
|
|
26409
26947
|
const node = row * columnCount + column;
|
|
26410
26948
|
if (!freeNodes[node] || terminalNodes.has(node)) continue;
|
|
26411
26949
|
const point6 = pointForNode(node);
|
|
26412
|
-
if (distanceSegmentToSegment(segment2.start, segment2.end, point6, point6) < requiredRouteDistance -
|
|
26950
|
+
if (distanceSegmentToSegment(segment2.start, segment2.end, point6, point6) < requiredRouteDistance - EPSILON9) {
|
|
26413
26951
|
freeNodes[node] = 0;
|
|
26414
26952
|
}
|
|
26415
26953
|
}
|
|
@@ -26629,7 +27167,7 @@ function buildPlan3(route, traceWidth) {
|
|
|
26629
27167
|
function getConnectorVariants2(start, end) {
|
|
26630
27168
|
const first = connectWith45DegreeSegments(start, end);
|
|
26631
27169
|
const second = connectWith45DegreeSegments(end, start).reverse();
|
|
26632
|
-
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];
|
|
26633
27171
|
}
|
|
26634
27172
|
function plansHaveRequiredClearance(params) {
|
|
26635
27173
|
const { plans, items, obstacles, traceWidth, clearance } = params;
|
|
@@ -26656,7 +27194,7 @@ function plansHaveRequiredClearance(params) {
|
|
|
26656
27194
|
for (const [netKey, segments] of uniqueSegmentsByNet) {
|
|
26657
27195
|
for (const segment2 of segments) {
|
|
26658
27196
|
for (const obstacle of obstacles) {
|
|
26659
|
-
if (obstacle.connectedTo.includes(netKey) || distanceSegmentToObstacle(segment2, obstacle) >= requiredObstacleDistance -
|
|
27197
|
+
if (obstacle.connectedTo.includes(netKey) || distanceSegmentToObstacle(segment2, obstacle) >= requiredObstacleDistance - EPSILON9) {
|
|
26660
27198
|
continue;
|
|
26661
27199
|
}
|
|
26662
27200
|
if (FANOUT_FLOW_DEBUG_ENABLED) {
|
|
@@ -26683,7 +27221,7 @@ function plansHaveRequiredClearance(params) {
|
|
|
26683
27221
|
first.end,
|
|
26684
27222
|
second.start,
|
|
26685
27223
|
second.end
|
|
26686
|
-
) < requiredRouteDistance -
|
|
27224
|
+
) < requiredRouteDistance - EPSILON9) {
|
|
26687
27225
|
if (FANOUT_FLOW_DEBUG_ENABLED) {
|
|
26688
27226
|
console.error("single-layer routes violate copper clearance", {
|
|
26689
27227
|
firstNetKey: entries[firstIndex][0],
|
|
@@ -26708,10 +27246,10 @@ function plansHaveRequiredClearance(params) {
|
|
|
26708
27246
|
return true;
|
|
26709
27247
|
}
|
|
26710
27248
|
function getDirectionForBoundaryPoint(point6, boundary) {
|
|
26711
|
-
if (Math.abs(point6.x - boundary.minX) <
|
|
26712
|
-
if (Math.abs(point6.x - boundary.maxX) <
|
|
26713
|
-
if (Math.abs(point6.y - boundary.minY) <
|
|
26714
|
-
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";
|
|
26715
27253
|
return null;
|
|
26716
27254
|
}
|
|
26717
27255
|
function* routeWithAdaptiveExitsSteps(params) {
|
|
@@ -26801,7 +27339,7 @@ function* routeWithAdaptiveExitsSteps(params) {
|
|
|
26801
27339
|
const connectorSegments = getSegments2(connectorPoints, traceWidth);
|
|
26802
27340
|
const clearsObstacles = connectorSegments.every(
|
|
26803
27341
|
(segment2) => obstacles.every(
|
|
26804
|
-
(obstacle) => obstacleBelongsToItem(obstacle, mergeItem) || distanceSegmentToObstacle(segment2, obstacle) >= requiredObstacleDistance -
|
|
27342
|
+
(obstacle) => obstacleBelongsToItem(obstacle, mergeItem) || distanceSegmentToObstacle(segment2, obstacle) >= requiredObstacleDistance - EPSILON9
|
|
26805
27343
|
)
|
|
26806
27344
|
);
|
|
26807
27345
|
const clearsRoutes = connectorSegments.every(
|
|
@@ -26812,7 +27350,7 @@ function* routeWithAdaptiveExitsSteps(params) {
|
|
|
26812
27350
|
segment2.end,
|
|
26813
27351
|
otherSegment.start,
|
|
26814
27352
|
otherSegment.end
|
|
26815
|
-
) >= requiredRouteDistance -
|
|
27353
|
+
) >= requiredRouteDistance - EPSILON9
|
|
26816
27354
|
)
|
|
26817
27355
|
)
|
|
26818
27356
|
);
|
|
@@ -27940,9 +28478,9 @@ function shortenBusPlans(params) {
|
|
|
27940
28478
|
}
|
|
27941
28479
|
|
|
27942
28480
|
// node_modules/@tscircuit/fanout-solver/lib/validate-fanout-solution.ts
|
|
27943
|
-
var
|
|
28481
|
+
var EPSILON10 = 1e-6;
|
|
27944
28482
|
function pointsMatch3(first, second) {
|
|
27945
|
-
return distance9(first, second) <=
|
|
28483
|
+
return distance9(first, second) <= EPSILON10;
|
|
27946
28484
|
}
|
|
27947
28485
|
function getPointLayers3(point6) {
|
|
27948
28486
|
return "layer" in point6 ? [point6.layer] : point6.layers;
|
|
@@ -27961,26 +28499,26 @@ function connectionPointsMatch(first, second) {
|
|
|
27961
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;
|
|
27962
28500
|
}
|
|
27963
28501
|
function pointIsOnBoundary(point6, boundary) {
|
|
27964
|
-
const inside = point6.x >= boundary.minX -
|
|
27965
|
-
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;
|
|
27966
28504
|
return inside && onEdge;
|
|
27967
28505
|
}
|
|
27968
28506
|
function pointIsOnBoundaryEdge(point6, edge, boundary) {
|
|
27969
|
-
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;
|
|
27970
28508
|
if (!inside) return false;
|
|
27971
28509
|
switch (edge) {
|
|
27972
28510
|
case "left":
|
|
27973
|
-
return Math.abs(point6.x - boundary.minX) <=
|
|
28511
|
+
return Math.abs(point6.x - boundary.minX) <= EPSILON10;
|
|
27974
28512
|
case "right":
|
|
27975
|
-
return Math.abs(point6.x - boundary.maxX) <=
|
|
28513
|
+
return Math.abs(point6.x - boundary.maxX) <= EPSILON10;
|
|
27976
28514
|
case "top":
|
|
27977
|
-
return Math.abs(point6.y - boundary.maxY) <=
|
|
28515
|
+
return Math.abs(point6.y - boundary.maxY) <= EPSILON10;
|
|
27978
28516
|
case "bottom":
|
|
27979
|
-
return Math.abs(point6.y - boundary.minY) <=
|
|
28517
|
+
return Math.abs(point6.y - boundary.minY) <= EPSILON10;
|
|
27980
28518
|
}
|
|
27981
28519
|
}
|
|
27982
28520
|
function pointIsInsideBounds3(point6, bounds) {
|
|
27983
|
-
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;
|
|
27984
28522
|
}
|
|
27985
28523
|
function addIssue2(issues, code, message, plan, otherConnectionName) {
|
|
27986
28524
|
issues.push({
|
|
@@ -28113,7 +28651,7 @@ function validatePlanStructure(params) {
|
|
|
28113
28651
|
);
|
|
28114
28652
|
}
|
|
28115
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);
|
|
28116
|
-
if (!isAllowedViaInPadPlaneTermination && (plan.segments.length === 0 || plan.length <=
|
|
28654
|
+
if (!isAllowedViaInPadPlaneTermination && (plan.segments.length === 0 || plan.length <= EPSILON10)) {
|
|
28117
28655
|
addIssue2(
|
|
28118
28656
|
issues,
|
|
28119
28657
|
"not-broken-out",
|
|
@@ -28196,7 +28734,7 @@ function validatePlanStructure(params) {
|
|
|
28196
28734
|
});
|
|
28197
28735
|
if (traceSegments.length !== plan.segments.length || traceSegments.some((segment2, index) => {
|
|
28198
28736
|
const declared = plan.segments[index];
|
|
28199
|
-
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);
|
|
28200
28738
|
})) {
|
|
28201
28739
|
addIssue2(
|
|
28202
28740
|
issues,
|
|
@@ -28221,8 +28759,8 @@ function validatePlanStructure(params) {
|
|
|
28221
28759
|
});
|
|
28222
28760
|
if (endpointSegments.length !== plan.planeEndpointSegments.length || endpointSegments.some((segment2, index) => {
|
|
28223
28761
|
const declared = plan.planeEndpointSegments?.[index];
|
|
28224
|
-
return !declared || segment2.layer !== declared.layer || Math.abs(segment2.width - declared.width) >
|
|
28225
|
-
}) || 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) {
|
|
28226
28764
|
addIssue2(
|
|
28227
28765
|
issues,
|
|
28228
28766
|
"trace-plan-mismatch",
|
|
@@ -28302,7 +28840,7 @@ function plansHaveConnectedCopper(first, second) {
|
|
|
28302
28840
|
firstSegment.end,
|
|
28303
28841
|
secondSegment.start,
|
|
28304
28842
|
secondSegment.end
|
|
28305
|
-
) <= (firstSegment.width + secondSegment.width) / 2 +
|
|
28843
|
+
) <= (firstSegment.width + secondSegment.width) / 2 + EPSILON10) {
|
|
28306
28844
|
return true;
|
|
28307
28845
|
}
|
|
28308
28846
|
}
|
|
@@ -28311,7 +28849,7 @@ function plansHaveConnectedCopper(first, second) {
|
|
|
28311
28849
|
secondVia.center,
|
|
28312
28850
|
firstSegment.start,
|
|
28313
28851
|
firstSegment.end
|
|
28314
|
-
) <= secondVia.diameter / 2 + firstSegment.width / 2 +
|
|
28852
|
+
) <= secondVia.diameter / 2 + firstSegment.width / 2 + EPSILON10) {
|
|
28315
28853
|
return true;
|
|
28316
28854
|
}
|
|
28317
28855
|
}
|
|
@@ -28322,14 +28860,14 @@ function plansHaveConnectedCopper(first, second) {
|
|
|
28322
28860
|
firstVia.center,
|
|
28323
28861
|
secondSegment.start,
|
|
28324
28862
|
secondSegment.end
|
|
28325
|
-
) <= firstVia.diameter / 2 + secondSegment.width / 2 +
|
|
28863
|
+
) <= firstVia.diameter / 2 + secondSegment.width / 2 + EPSILON10) {
|
|
28326
28864
|
return true;
|
|
28327
28865
|
}
|
|
28328
28866
|
}
|
|
28329
28867
|
for (const secondVia of secondVias) {
|
|
28330
28868
|
if (firstVia.spanLayers.some(
|
|
28331
28869
|
(layer) => secondVia.spanLayers.includes(layer)
|
|
28332
|
-
) && distance9(firstVia.center, secondVia.center) <= (firstVia.diameter + secondVia.diameter) / 2 +
|
|
28870
|
+
) && distance9(firstVia.center, secondVia.center) <= (firstVia.diameter + secondVia.diameter) / 2 + EPSILON10) {
|
|
28333
28871
|
return true;
|
|
28334
28872
|
}
|
|
28335
28873
|
}
|
|
@@ -29628,7 +30166,10 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
29628
30166
|
buses: this.preparedBuses,
|
|
29629
30167
|
escapeLayers: this.config.escapeLayers,
|
|
29630
30168
|
escapeLayersByBusId: this.escapeLayersByBusId,
|
|
29631
|
-
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
|
+
)
|
|
29632
30173
|
}),
|
|
29633
30174
|
generatedAssignments,
|
|
29634
30175
|
maxAssignments: this.config.maxLayerCombinations
|
|
@@ -29917,6 +30458,8 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
29917
30458
|
"plane-match:incremental-failed",
|
|
29918
30459
|
"plane-route:alternate-candidate-counts",
|
|
29919
30460
|
"plane-route:alternate-search",
|
|
30461
|
+
"plane-route:csp",
|
|
30462
|
+
"plane-reservation-core",
|
|
29920
30463
|
"plane-route:promote-failed",
|
|
29921
30464
|
"plane-route:alternate-choice",
|
|
29922
30465
|
"plane-route:promote-zero-candidates",
|
|
@@ -29933,7 +30476,13 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
29933
30476
|
const unsortedBoundaryBuses = params.busesInRoutingOrder.filter(
|
|
29934
30477
|
(bus) => bus.termination.type === "boundary"
|
|
29935
30478
|
);
|
|
29936
|
-
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;
|
|
29937
30486
|
const matchLengthsAfterPlanes = useConfiguredDensePlaneRouting && params.lengthMatchingStage !== "before-planes";
|
|
29938
30487
|
const useJointBoundaryViaReservation = shouldUseJointBoundaryViaReservation(
|
|
29939
30488
|
unsortedBoundaryBuses.map((bus) => bus.connections.length)
|
|
@@ -30080,6 +30629,17 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
30080
30629
|
0,
|
|
30081
30630
|
Number.isFinite(initialPlaneReservationCount) ? initialPlaneReservationCount : 8
|
|
30082
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();
|
|
30083
30643
|
debugDense(
|
|
30084
30644
|
"start",
|
|
30085
30645
|
boundaryBuses.map((bus) => `${bus.busId}:${bus.connections.length}`),
|
|
@@ -30112,7 +30672,7 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
30112
30672
|
const preferBoundaryOutwardByBusId = new Map(
|
|
30113
30673
|
boundaryBuses.map((bus) => [
|
|
30114
30674
|
bus.busId,
|
|
30115
|
-
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
|
|
30116
30676
|
])
|
|
30117
30677
|
);
|
|
30118
30678
|
for (const bus of wideBoundaryBuses) {
|
|
@@ -30394,6 +30954,14 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
30394
30954
|
return [
|
|
30395
30955
|
{
|
|
30396
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
|
+
},
|
|
30397
30965
|
via: {
|
|
30398
30966
|
center,
|
|
30399
30967
|
diameter: this.config.viaDiameter,
|
|
@@ -30448,7 +31016,7 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
30448
31016
|
fixedViaPointsByConnectionIndex,
|
|
30449
31017
|
reservedVias: getReservedVias(bus),
|
|
30450
31018
|
viaMinimalOnly: process.env.FANOUT_DEBUG_ALLOW_EXTRA_VIAS !== "1",
|
|
30451
|
-
allowBoundarySideViaFallback:
|
|
31019
|
+
allowBoundarySideViaFallback: bus.connections.length === 1,
|
|
30452
31020
|
preferCornerBoundaryVia: useConfiguredDensePlaneRouting,
|
|
30453
31021
|
adaptiveWindingRouteOrder,
|
|
30454
31022
|
alignWindingGridToPads: usePadAlignedDenseRouting && !useConfiguredDensePlaneRouting,
|
|
@@ -30559,7 +31127,21 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
30559
31127
|
))[0];
|
|
30560
31128
|
if (busPlans) break;
|
|
30561
31129
|
}
|
|
30562
|
-
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
|
+
}
|
|
30563
31145
|
}
|
|
30564
31146
|
if (busPlans && bus.maxLengthSkew !== void 0) {
|
|
30565
31147
|
const lengths = busPlans.map((plan) => plan.length);
|
|
@@ -31164,6 +31746,73 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
31164
31746
|
}))
|
|
31165
31747
|
})
|
|
31166
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
|
+
}
|
|
31167
31816
|
debugDense(
|
|
31168
31817
|
"plane-route:alternate-candidate-counts",
|
|
31169
31818
|
candidateSets.map((candidateSet) => [
|
|
@@ -31171,6 +31820,10 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
31171
31820
|
candidateSet.candidates.length
|
|
31172
31821
|
])
|
|
31173
31822
|
);
|
|
31823
|
+
for (const candidateSet of candidateSets) {
|
|
31824
|
+
if (candidateSet.candidates.length === 0)
|
|
31825
|
+
unroutablePlaneBusIds.add(candidateSet.planeBus.busId);
|
|
31826
|
+
}
|
|
31174
31827
|
const compatibilityByCandidatePair = /* @__PURE__ */ new Map();
|
|
31175
31828
|
const candidatesAreCompatible = (first, second) => {
|
|
31176
31829
|
const cacheKey = [first.key, second.key].toSorted().join("|");
|
|
@@ -31263,10 +31916,70 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
31263
31916
|
);
|
|
31264
31917
|
return null;
|
|
31265
31918
|
};
|
|
31266
|
-
|
|
31267
|
-
|
|
31268
|
-
[
|
|
31269
|
-
|
|
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
|
+
}
|
|
31270
31983
|
alternatePlanePlans = selectedCandidates ? [
|
|
31271
31984
|
...feasibleAlternatePlanePlans,
|
|
31272
31985
|
...selectedCandidates.flatMap(
|
|
@@ -31290,6 +32003,15 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
31290
32003
|
);
|
|
31291
32004
|
if (!alternatePlanePlans) return null;
|
|
31292
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
|
+
}
|
|
31293
32015
|
}
|
|
31294
32016
|
let planeBusIdsRoutedWithoutDogbones = /* @__PURE__ */ new Set();
|
|
31295
32017
|
let allBlockingSegments = [...blockingSegments];
|
|
@@ -31681,6 +32403,30 @@ var FanoutSolver = class extends BaseSolver {
|
|
|
31681
32403
|
return { plans: matchedPlans, failedBusIds: [] };
|
|
31682
32404
|
}
|
|
31683
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
|
+
}
|
|
31684
32430
|
if (matchLengthsAfterPlanes) {
|
|
31685
32431
|
return yield* this.routeDenseThroughAllMixedTerminationSteps({
|
|
31686
32432
|
...params,
|
|
@@ -45343,7 +46089,7 @@ function Group_getRoutingPhasePlans(group) {
|
|
|
45343
46089
|
var package_default = {
|
|
45344
46090
|
name: "@tscircuit/core",
|
|
45345
46091
|
type: "module",
|
|
45346
|
-
version: "0.0.
|
|
46092
|
+
version: "0.0.1834",
|
|
45347
46093
|
types: "dist/index.d.ts",
|
|
45348
46094
|
main: "dist/index.js",
|
|
45349
46095
|
module: "dist/index.js",
|
|
@@ -45383,7 +46129,7 @@ var package_default = {
|
|
|
45383
46129
|
"@tscircuit/common": "^0.0.20",
|
|
45384
46130
|
"@tscircuit/copper-pour-solver": "^0.0.52",
|
|
45385
46131
|
"@tscircuit/create-fdm-enclosure": "0.0.4",
|
|
45386
|
-
"@tscircuit/fanout-solver": "0.0.
|
|
46132
|
+
"@tscircuit/fanout-solver": "0.0.57",
|
|
45387
46133
|
"@tscircuit/footprinter": "^0.0.426",
|
|
45388
46134
|
"@tscircuit/image-utils": "^0.0.8",
|
|
45389
46135
|
"@tscircuit/implicit-copper-pour-solver": "github:tscircuit/implicit-copper-pour-solver#d880f80e4eb7a34d5da5a521f10a63c810d4728b",
|
|
@@ -50237,15 +50983,15 @@ import {
|
|
|
50237
50983
|
} from "@tscircuit/copper-pour-solver";
|
|
50238
50984
|
|
|
50239
50985
|
// lib/components/primitive-components/CopperPour/utils/mark-trace-segments-inside-copper-pour.ts
|
|
50240
|
-
var
|
|
50986
|
+
var EPSILON11 = 1e-9;
|
|
50241
50987
|
var isWireRoutePoint = (routePoint) => routePoint.route_type === "wire";
|
|
50242
50988
|
var isPointOnSegment = (p, a, b) => {
|
|
50243
50989
|
const cross3 = (p.y - a.y) * (b.x - a.x) - (p.x - a.x) * (b.y - a.y);
|
|
50244
|
-
if (Math.abs(cross3) >
|
|
50990
|
+
if (Math.abs(cross3) > EPSILON11) return false;
|
|
50245
50991
|
const dot = (p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y);
|
|
50246
|
-
if (dot < -
|
|
50992
|
+
if (dot < -EPSILON11) return false;
|
|
50247
50993
|
const squaredLength = (b.x - a.x) ** 2 + (b.y - a.y) ** 2;
|
|
50248
|
-
if (dot - squaredLength >
|
|
50994
|
+
if (dot - squaredLength > EPSILON11) return false;
|
|
50249
50995
|
return true;
|
|
50250
50996
|
};
|
|
50251
50997
|
var isPointInRing = (point6, ring) => {
|
|
@@ -50269,7 +51015,7 @@ var isPointInRectPour = (p, pour) => {
|
|
|
50269
51015
|
const dy = p.y - center.y;
|
|
50270
51016
|
const localX = dx * cosR - dy * sinR;
|
|
50271
51017
|
const localY = dx * sinR + dy * cosR;
|
|
50272
|
-
return Math.abs(localX) <= width / 2 +
|
|
51018
|
+
return Math.abs(localX) <= width / 2 + EPSILON11 && Math.abs(localY) <= height / 2 + EPSILON11;
|
|
50273
51019
|
};
|
|
50274
51020
|
var isPointInBrepPour = (p, pour) => {
|
|
50275
51021
|
const outerRing = pour.brep_shape.outer_ring.vertices.map((v) => ({
|
|
@@ -50303,7 +51049,7 @@ var isSegmentFullyInsideCopperPour = (start, end, pour) => {
|
|
|
50303
51049
|
const dx = end.x - start.x;
|
|
50304
51050
|
const dy = end.y - start.y;
|
|
50305
51051
|
const length7 = Math.hypot(dx, dy);
|
|
50306
|
-
if (length7 <=
|
|
51052
|
+
if (length7 <= EPSILON11) return false;
|
|
50307
51053
|
const samples = [0, 0.25, 0.5, 0.75, 1];
|
|
50308
51054
|
return samples.every(
|
|
50309
51055
|
(t) => isPointInCopperPour(
|