@tscircuit/rectdiff 0.0.26 → 0.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +21 -0
- package/dist/index.js +487 -160
- package/lib/RectDiffPipeline.ts +23 -0
- package/lib/solvers/AdjacentLayerContainmentMergeSolver/AdjacentLayerContainmentMergeSolver.ts +456 -0
- package/package.json +1 -1
- package/pages/bugreports/bugreport50-multi-support-layer-merge.page.tsx +19 -0
- package/tests/__snapshots__/board-outline.snap.svg +2 -2
- package/tests/solver/__snapshots__/rectDiffGridSolverPipeline.snap.svg +1 -1
- package/tests/solver/both-points-equivalent/__snapshots__/both-points-equivalent.snap.svg +1 -1
- package/tests/solver/bugreport01-be84eb/__snapshots__/bugreport01-be84eb.snap.svg +1 -1
- package/tests/solver/bugreport02-bc4361/__snapshots__/bugreport02-bc4361.snap.svg +1 -1
- package/tests/solver/bugreport03-fe4a17/__snapshots__/bugreport03-fe4a17.snap.svg +1 -1
- package/tests/solver/bugreport07-d3f3be/__snapshots__/bugreport07-d3f3be.snap.svg +1 -1
- package/tests/solver/bugreport08-e3ec95/__snapshots__/bugreport08-e3ec95.snap.svg +1 -1
- package/tests/solver/bugreport09-618e09/__snapshots__/bugreport09-618e09.snap.svg +1 -1
- package/tests/solver/bugreport10-71239a/__snapshots__/bugreport10-71239a.snap.svg +1 -1
- package/tests/solver/bugreport11-b2de3c/__snapshots__/bugreport11-b2de3c.snap.svg +1 -1
- package/tests/solver/bugreport12-35ce1c/__snapshots__/bugreport12-35ce1c.snap.svg +1 -1
- package/tests/solver/bugreport13-b9a758/__snapshots__/bugreport13-b9a758.snap.svg +1 -1
- package/tests/solver/bugreport16-d95f38/__snapshots__/bugreport16-d95f38.snap.svg +1 -1
- package/tests/solver/bugreport19/__snapshots__/bugreport19.snap.svg +1 -1
- package/tests/solver/bugreport20-obstacle-clipping/__snapshots__/bugreport20-obstacle-clipping.snap.svg +1 -1
- package/tests/solver/bugreport21-board-outline/__snapshots__/bugreport21-board-outline.snap.svg +2 -2
- package/tests/solver/bugreport22-2a75ce/__snapshots__/bugreport22-2a75ce.snap.svg +1 -1
- package/tests/solver/bugreport23-LGA15x4/__snapshots__/bugreport23-LGA15x4.snap.svg +1 -1
- package/tests/solver/bugreport24-05597c/__snapshots__/bugreport24-05597c.snap.svg +1 -1
- package/tests/solver/bugreport25-4b1d55/__snapshots__/bugreport25-4b1d55.snap.svg +1 -1
- package/tests/solver/bugreport36-bf8303/__snapshots__/bugreport36-bf8303.snap.svg +1 -1
- package/tests/solver/bugreport50-multi-support-layer-merge/__snapshots__/bugreport50-multi-support-layer-merge.snap.svg +44 -0
- package/tests/solver/bugreport50-multi-support-layer-merge/bugreport50-multi-support-layer-merge.json +972 -0
- package/tests/solver/bugreport50-multi-support-layer-merge/bugreport50-multi-support-layer-merge.test.ts +125 -0
- package/tests/solver/interaction/__snapshots__/interaction.snap.svg +1 -1
- package/tests/solver/multi-point/__snapshots__/multi-point.snap.svg +1 -1
- package/tests/solver/no-better-path/__snapshots__/no-better-path.snap.svg +1 -1
- package/tests/solver/transitivity/__snapshots__/transitivity.snap.svg +2 -2
package/dist/index.js
CHANGED
|
@@ -4,6 +4,388 @@ import {
|
|
|
4
4
|
definePipelineStep as definePipelineStep3
|
|
5
5
|
} from "@tscircuit/solver-utils";
|
|
6
6
|
|
|
7
|
+
// lib/solvers/AdjacentLayerContainmentMergeSolver/AdjacentLayerContainmentMergeSolver.ts
|
|
8
|
+
import { BaseSolver } from "@tscircuit/solver-utils";
|
|
9
|
+
|
|
10
|
+
// lib/utils/getColorForZLayer.ts
|
|
11
|
+
var getColorForZLayer = (zLayers) => {
|
|
12
|
+
const minZ = Math.min(...zLayers);
|
|
13
|
+
const colors = [
|
|
14
|
+
{ fill: "#dbeafe", stroke: "#3b82f6" },
|
|
15
|
+
{ fill: "#fef3c7", stroke: "#f59e0b" },
|
|
16
|
+
{ fill: "#d1fae5", stroke: "#10b981" },
|
|
17
|
+
{ fill: "#e9d5ff", stroke: "#a855f7" },
|
|
18
|
+
{ fill: "#fed7aa", stroke: "#f97316" },
|
|
19
|
+
{ fill: "#fecaca", stroke: "#ef4444" }
|
|
20
|
+
];
|
|
21
|
+
return colors[minZ % colors.length];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// lib/utils/rectdiff-geometry.ts
|
|
25
|
+
var EPS = 1e-9;
|
|
26
|
+
var clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
27
|
+
var gt = (a, b) => a > b + EPS;
|
|
28
|
+
var gte = (a, b) => a > b - EPS;
|
|
29
|
+
var lt = (a, b) => a < b - EPS;
|
|
30
|
+
var lte = (a, b) => a < b + EPS;
|
|
31
|
+
function overlaps(a, b) {
|
|
32
|
+
return !(a.x + a.width <= b.x + EPS || b.x + b.width <= a.x + EPS || a.y + a.height <= b.y + EPS || b.y + b.height <= a.y + EPS);
|
|
33
|
+
}
|
|
34
|
+
function containsPoint(r, p) {
|
|
35
|
+
return p.x >= r.x - EPS && p.x <= r.x + r.width + EPS && p.y >= r.y - EPS && p.y <= r.y + r.height + EPS;
|
|
36
|
+
}
|
|
37
|
+
function distancePointToRectEdges(p, r) {
|
|
38
|
+
const minX = r.x;
|
|
39
|
+
const maxX = r.x + r.width;
|
|
40
|
+
const minY = r.y;
|
|
41
|
+
const maxY = r.y + r.height;
|
|
42
|
+
if (p.x >= minX && p.x <= maxX && p.y >= minY && p.y <= maxY) {
|
|
43
|
+
return Math.min(p.x - minX, maxX - p.x, p.y - minY, maxY - p.y);
|
|
44
|
+
}
|
|
45
|
+
const dx = p.x < minX ? minX - p.x : p.x > maxX ? p.x - maxX : 0;
|
|
46
|
+
const dy = p.y < minY ? minY - p.y : p.y > maxY ? p.y - maxY : 0;
|
|
47
|
+
if (dx === 0) return dy;
|
|
48
|
+
if (dy === 0) return dx;
|
|
49
|
+
return Math.hypot(dx, dy);
|
|
50
|
+
}
|
|
51
|
+
function intersect1D(r1, r2) {
|
|
52
|
+
const lo = Math.max(r1[0], r2[0]);
|
|
53
|
+
const hi = Math.min(r1[1], r2[1]);
|
|
54
|
+
return hi > lo + EPS ? [lo, hi] : null;
|
|
55
|
+
}
|
|
56
|
+
function subtractRect2D(A, B) {
|
|
57
|
+
if (!overlaps(A, B)) return [A];
|
|
58
|
+
const Xi = intersect1D([A.x, A.x + A.width], [B.x, B.x + B.width]);
|
|
59
|
+
const Yi = intersect1D([A.y, A.y + A.height], [B.y, B.y + B.height]);
|
|
60
|
+
if (!Xi || !Yi) return [A];
|
|
61
|
+
const [X0, X1] = Xi;
|
|
62
|
+
const [Y0, Y1] = Yi;
|
|
63
|
+
const out = [];
|
|
64
|
+
if (X0 > A.x + EPS) {
|
|
65
|
+
out.push({ x: A.x, y: A.y, width: X0 - A.x, height: A.height });
|
|
66
|
+
}
|
|
67
|
+
if (A.x + A.width > X1 + EPS) {
|
|
68
|
+
out.push({ x: X1, y: A.y, width: A.x + A.width - X1, height: A.height });
|
|
69
|
+
}
|
|
70
|
+
const midW = Math.max(0, X1 - X0);
|
|
71
|
+
if (midW > EPS && Y0 > A.y + EPS) {
|
|
72
|
+
out.push({ x: X0, y: A.y, width: midW, height: Y0 - A.y });
|
|
73
|
+
}
|
|
74
|
+
if (midW > EPS && A.y + A.height > Y1 + EPS) {
|
|
75
|
+
out.push({ x: X0, y: Y1, width: midW, height: A.y + A.height - Y1 });
|
|
76
|
+
}
|
|
77
|
+
return out.filter((r) => r.width > EPS && r.height > EPS);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// lib/solvers/AdjacentLayerContainmentMergeSolver/AdjacentLayerContainmentMergeSolver.ts
|
|
81
|
+
var DEFAULT_MIN_FRAGMENT_AREA = 0.2 ** 2;
|
|
82
|
+
var nodeToRect = (node) => ({
|
|
83
|
+
x: node.center.x - node.width / 2,
|
|
84
|
+
y: node.center.y - node.height / 2,
|
|
85
|
+
width: node.width,
|
|
86
|
+
height: node.height
|
|
87
|
+
});
|
|
88
|
+
var rectArea = (rect) => rect.width * rect.height;
|
|
89
|
+
var cloneNode = (node) => ({
|
|
90
|
+
...node,
|
|
91
|
+
center: { ...node.center },
|
|
92
|
+
availableZ: [...node.availableZ]
|
|
93
|
+
});
|
|
94
|
+
var cloneNodeWithRect = (node, rect, capacityMeshNodeId) => ({
|
|
95
|
+
...node,
|
|
96
|
+
capacityMeshNodeId,
|
|
97
|
+
center: {
|
|
98
|
+
x: rect.x + rect.width / 2,
|
|
99
|
+
y: rect.y + rect.height / 2
|
|
100
|
+
},
|
|
101
|
+
width: rect.width,
|
|
102
|
+
height: rect.height,
|
|
103
|
+
availableZ: [...node.availableZ],
|
|
104
|
+
layer: `z${node.availableZ.join(",")}`
|
|
105
|
+
});
|
|
106
|
+
var clonePromotedNodeWithRect = (node, rect, capacityMeshNodeId, availableZ) => ({
|
|
107
|
+
...node,
|
|
108
|
+
capacityMeshNodeId,
|
|
109
|
+
center: {
|
|
110
|
+
x: rect.x + rect.width / 2,
|
|
111
|
+
y: rect.y + rect.height / 2
|
|
112
|
+
},
|
|
113
|
+
width: rect.width,
|
|
114
|
+
height: rect.height,
|
|
115
|
+
availableZ: [...availableZ],
|
|
116
|
+
layer: `z${availableZ.join(",")}`
|
|
117
|
+
});
|
|
118
|
+
var isFreeNode = (node) => !node._containsObstacle && !node._containsTarget;
|
|
119
|
+
var isSingletonNodeOnLayer = (node, z) => node.availableZ.length === 1 && node.availableZ[0] === z;
|
|
120
|
+
var sameRect = (a, b) => Math.abs(a.x - b.x) <= EPS && Math.abs(a.y - b.y) <= EPS && Math.abs(a.width - b.width) <= EPS && Math.abs(a.height - b.height) <= EPS;
|
|
121
|
+
var subtractRects = (target, cutters) => {
|
|
122
|
+
let remaining = [target];
|
|
123
|
+
for (const cutter of cutters) {
|
|
124
|
+
if (remaining.length === 0) return remaining;
|
|
125
|
+
const nextRemaining = [];
|
|
126
|
+
for (const piece of remaining) {
|
|
127
|
+
nextRemaining.push(...subtractRect2D(piece, cutter));
|
|
128
|
+
}
|
|
129
|
+
remaining = nextRemaining;
|
|
130
|
+
}
|
|
131
|
+
return remaining;
|
|
132
|
+
};
|
|
133
|
+
var isFullyCoveredByRects = (target, coveringRects) => {
|
|
134
|
+
return subtractRects(target, coveringRects).length === 0;
|
|
135
|
+
};
|
|
136
|
+
var sortAndDedupeCuts = (values) => {
|
|
137
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
138
|
+
const out = [];
|
|
139
|
+
for (const value of sorted) {
|
|
140
|
+
const last = out[out.length - 1];
|
|
141
|
+
if (last == null || Math.abs(last - value) > EPS) {
|
|
142
|
+
out.push(value);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
};
|
|
147
|
+
var partitionRectByRects = (target, supportRects) => {
|
|
148
|
+
const xCuts = [target.x, target.x + target.width];
|
|
149
|
+
const yCuts = [target.y, target.y + target.height];
|
|
150
|
+
const targetMaxX = target.x + target.width;
|
|
151
|
+
const targetMaxY = target.y + target.height;
|
|
152
|
+
for (const rect of supportRects) {
|
|
153
|
+
const x0 = Math.max(target.x, rect.x);
|
|
154
|
+
const x1 = Math.min(targetMaxX, rect.x + rect.width);
|
|
155
|
+
const y0 = Math.max(target.y, rect.y);
|
|
156
|
+
const y1 = Math.min(targetMaxY, rect.y + rect.height);
|
|
157
|
+
if (x1 <= x0 + EPS || y1 <= y0 + EPS) continue;
|
|
158
|
+
xCuts.push(x0, x1);
|
|
159
|
+
yCuts.push(y0, y1);
|
|
160
|
+
}
|
|
161
|
+
const xs = sortAndDedupeCuts(xCuts);
|
|
162
|
+
const ys = sortAndDedupeCuts(yCuts);
|
|
163
|
+
const cells = [];
|
|
164
|
+
for (let xi = 0; xi < xs.length - 1; xi++) {
|
|
165
|
+
const x0 = xs[xi];
|
|
166
|
+
const x1 = xs[xi + 1];
|
|
167
|
+
if (x1 <= x0 + EPS) continue;
|
|
168
|
+
for (let yi = 0; yi < ys.length - 1; yi++) {
|
|
169
|
+
const y0 = ys[yi];
|
|
170
|
+
const y1 = ys[yi + 1];
|
|
171
|
+
if (y1 <= y0 + EPS) continue;
|
|
172
|
+
cells.push({
|
|
173
|
+
x: x0,
|
|
174
|
+
y: y0,
|
|
175
|
+
width: x1 - x0,
|
|
176
|
+
height: y1 - y0
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return cells;
|
|
181
|
+
};
|
|
182
|
+
var canMergeHorizontally = (a, b) => Math.abs(a.y - b.y) <= EPS && Math.abs(a.height - b.height) <= EPS && Math.abs(a.x + a.width - b.x) <= EPS;
|
|
183
|
+
var canMergeVertically = (a, b) => Math.abs(a.x - b.x) <= EPS && Math.abs(a.width - b.width) <= EPS && Math.abs(a.y + a.height - b.y) <= EPS;
|
|
184
|
+
var mergeTouchingRects = (rects) => {
|
|
185
|
+
const out = rects.map((rect) => ({ ...rect }));
|
|
186
|
+
let changed = true;
|
|
187
|
+
while (changed) {
|
|
188
|
+
changed = false;
|
|
189
|
+
outer: for (let i = 0; i < out.length; i++) {
|
|
190
|
+
for (let j = i + 1; j < out.length; j++) {
|
|
191
|
+
const a = out[i];
|
|
192
|
+
const b = out[j];
|
|
193
|
+
if (canMergeHorizontally(a, b) || canMergeHorizontally(b, a)) {
|
|
194
|
+
const merged = {
|
|
195
|
+
x: Math.min(a.x, b.x),
|
|
196
|
+
y: a.y,
|
|
197
|
+
width: a.width + b.width,
|
|
198
|
+
height: a.height
|
|
199
|
+
};
|
|
200
|
+
out.splice(j, 1);
|
|
201
|
+
out.splice(i, 1, merged);
|
|
202
|
+
changed = true;
|
|
203
|
+
break outer;
|
|
204
|
+
}
|
|
205
|
+
if (canMergeVertically(a, b) || canMergeVertically(b, a)) {
|
|
206
|
+
const merged = {
|
|
207
|
+
x: a.x,
|
|
208
|
+
y: Math.min(a.y, b.y),
|
|
209
|
+
width: a.width,
|
|
210
|
+
height: a.height + b.height
|
|
211
|
+
};
|
|
212
|
+
out.splice(j, 1);
|
|
213
|
+
out.splice(i, 1, merged);
|
|
214
|
+
changed = true;
|
|
215
|
+
break outer;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return out;
|
|
221
|
+
};
|
|
222
|
+
var isPromotableRect = (params) => {
|
|
223
|
+
const { rect, viaMinSize, minFragmentArea } = params;
|
|
224
|
+
return rect.width > EPS && rect.height > EPS && rectArea(rect) + EPS >= minFragmentArea && rect.width + EPS >= viaMinSize && rect.height + EPS >= viaMinSize;
|
|
225
|
+
};
|
|
226
|
+
var isResidualRect = (rect, minFragmentArea) => rect.width > EPS && rect.height > EPS && rectArea(rect) + EPS >= minFragmentArea;
|
|
227
|
+
var computePromotablePieces = (params) => {
|
|
228
|
+
const { target, supportRects, viaMinSize, minFragmentArea } = params;
|
|
229
|
+
const overlappingSupports = supportRects.filter(
|
|
230
|
+
(rect) => overlaps(rect, target)
|
|
231
|
+
);
|
|
232
|
+
if (overlappingSupports.length === 0) return [];
|
|
233
|
+
if (isFullyCoveredByRects(target, overlappingSupports) && isPromotableRect({ rect: target, viaMinSize, minFragmentArea })) {
|
|
234
|
+
return [target];
|
|
235
|
+
}
|
|
236
|
+
const partitioned = partitionRectByRects(target, overlappingSupports);
|
|
237
|
+
const coveredPieces = partitioned.filter(
|
|
238
|
+
(piece) => isFullyCoveredByRects(piece, overlappingSupports)
|
|
239
|
+
);
|
|
240
|
+
if (coveredPieces.length === 0) return [];
|
|
241
|
+
const mergedCoveredPieces = mergeTouchingRects(coveredPieces);
|
|
242
|
+
return mergedCoveredPieces.filter(
|
|
243
|
+
(piece) => isFullyCoveredByRects(piece, overlappingSupports) && isPromotableRect({ rect: piece, viaMinSize, minFragmentArea })
|
|
244
|
+
);
|
|
245
|
+
};
|
|
246
|
+
var AdjacentLayerContainmentMergeSolver = class extends BaseSolver {
|
|
247
|
+
constructor(input) {
|
|
248
|
+
super();
|
|
249
|
+
this.input = input;
|
|
250
|
+
}
|
|
251
|
+
input;
|
|
252
|
+
outputNodes = [];
|
|
253
|
+
promotedNodeIds = /* @__PURE__ */ new Set();
|
|
254
|
+
residualNodeIds = /* @__PURE__ */ new Set();
|
|
255
|
+
_setup() {
|
|
256
|
+
this.outputNodes = this.input.meshNodes.map(cloneNode);
|
|
257
|
+
this.promotedNodeIds.clear();
|
|
258
|
+
this.residualNodeIds.clear();
|
|
259
|
+
}
|
|
260
|
+
_step() {
|
|
261
|
+
this.outputNodes = this.processAdjacentLayerContainmentMerges();
|
|
262
|
+
this.solved = true;
|
|
263
|
+
}
|
|
264
|
+
processAdjacentLayerContainmentMerges() {
|
|
265
|
+
const srj = this.input.simpleRouteJson;
|
|
266
|
+
const layerCount = Math.max(1, srj.layerCount || 1);
|
|
267
|
+
if (layerCount < 2) {
|
|
268
|
+
return this.input.meshNodes.map(cloneNode);
|
|
269
|
+
}
|
|
270
|
+
const viaMinSize = Math.max(srj.minViaDiameter ?? 0, srj.minTraceWidth || 0);
|
|
271
|
+
const minFragmentArea = Math.max(
|
|
272
|
+
EPS,
|
|
273
|
+
this.input.minFragmentArea ?? DEFAULT_MIN_FRAGMENT_AREA
|
|
274
|
+
);
|
|
275
|
+
let workingNodes = this.input.meshNodes.map(cloneNode);
|
|
276
|
+
let nextResidualId = 0;
|
|
277
|
+
let nextPromotedId = 0;
|
|
278
|
+
for (let lowerZ = 0; lowerZ < layerCount - 1; lowerZ++) {
|
|
279
|
+
const upperZ = lowerZ + 1;
|
|
280
|
+
const mutableNodes = workingNodes.filter(
|
|
281
|
+
(node) => isFreeNode(node) && (isSingletonNodeOnLayer(node, lowerZ) || isSingletonNodeOnLayer(node, upperZ))
|
|
282
|
+
);
|
|
283
|
+
if (mutableNodes.length === 0) continue;
|
|
284
|
+
const immutableNodes = workingNodes.filter(
|
|
285
|
+
(node) => !mutableNodes.includes(node)
|
|
286
|
+
);
|
|
287
|
+
const supportRectsByLayer = /* @__PURE__ */ new Map();
|
|
288
|
+
supportRectsByLayer.set(
|
|
289
|
+
lowerZ,
|
|
290
|
+
mutableNodes.filter((node) => isSingletonNodeOnLayer(node, lowerZ)).map(nodeToRect)
|
|
291
|
+
);
|
|
292
|
+
supportRectsByLayer.set(
|
|
293
|
+
upperZ,
|
|
294
|
+
mutableNodes.filter((node) => isSingletonNodeOnLayer(node, upperZ)).map(nodeToRect)
|
|
295
|
+
);
|
|
296
|
+
const promotedRects = [];
|
|
297
|
+
const promotedNodes = [];
|
|
298
|
+
const candidateNodes = mutableNodes.filter(
|
|
299
|
+
(node) => isPromotableRect({
|
|
300
|
+
rect: nodeToRect(node),
|
|
301
|
+
viaMinSize,
|
|
302
|
+
minFragmentArea
|
|
303
|
+
})
|
|
304
|
+
).sort((a, b) => rectArea(nodeToRect(b)) - rectArea(nodeToRect(a)));
|
|
305
|
+
for (const candidate of candidateNodes) {
|
|
306
|
+
const candidateRect = nodeToRect(candidate);
|
|
307
|
+
const candidatePieces = subtractRects(
|
|
308
|
+
candidateRect,
|
|
309
|
+
promotedRects
|
|
310
|
+
).filter((piece) => isResidualRect(piece, minFragmentArea));
|
|
311
|
+
const candidateZ = candidate.availableZ[0];
|
|
312
|
+
const oppositeZ = candidateZ === lowerZ ? upperZ : lowerZ;
|
|
313
|
+
const supportRects = supportRectsByLayer.get(oppositeZ) ?? [];
|
|
314
|
+
for (const piece of candidatePieces) {
|
|
315
|
+
const promotablePieces = computePromotablePieces({
|
|
316
|
+
target: piece,
|
|
317
|
+
supportRects,
|
|
318
|
+
viaMinSize,
|
|
319
|
+
minFragmentArea
|
|
320
|
+
});
|
|
321
|
+
for (const promotablePiece of promotablePieces) {
|
|
322
|
+
promotedRects.push(promotablePiece);
|
|
323
|
+
const promotedNode = clonePromotedNodeWithRect(
|
|
324
|
+
candidate,
|
|
325
|
+
promotablePiece,
|
|
326
|
+
`${candidate.capacityMeshNodeId}-adjacent-merge-${nextPromotedId++}`,
|
|
327
|
+
[lowerZ, upperZ]
|
|
328
|
+
);
|
|
329
|
+
promotedNodes.push(promotedNode);
|
|
330
|
+
this.promotedNodeIds.add(promotedNode.capacityMeshNodeId);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const residualNodes = [];
|
|
335
|
+
for (const node of mutableNodes) {
|
|
336
|
+
const nodeRect = nodeToRect(node);
|
|
337
|
+
const remainingPieces = subtractRects(nodeRect, promotedRects).filter(
|
|
338
|
+
(piece) => isResidualRect(piece, minFragmentArea)
|
|
339
|
+
);
|
|
340
|
+
if (remainingPieces.length === 1 && sameRect(remainingPieces[0], nodeRect)) {
|
|
341
|
+
residualNodes.push(node);
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
for (const piece of remainingPieces) {
|
|
345
|
+
const residualNode = cloneNodeWithRect(
|
|
346
|
+
node,
|
|
347
|
+
piece,
|
|
348
|
+
`${node.capacityMeshNodeId}-adjacent-residual-${nextResidualId++}`
|
|
349
|
+
);
|
|
350
|
+
residualNodes.push(residualNode);
|
|
351
|
+
this.residualNodeIds.add(residualNode.capacityMeshNodeId);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
workingNodes = [...immutableNodes, ...promotedNodes, ...residualNodes];
|
|
355
|
+
}
|
|
356
|
+
return workingNodes;
|
|
357
|
+
}
|
|
358
|
+
getOutput() {
|
|
359
|
+
return { outputNodes: this.outputNodes };
|
|
360
|
+
}
|
|
361
|
+
visualize() {
|
|
362
|
+
return {
|
|
363
|
+
title: "AdjacentLayerContainmentMergeSolver",
|
|
364
|
+
coordinateSystem: "cartesian",
|
|
365
|
+
rects: this.outputNodes.map((node) => {
|
|
366
|
+
const colors = getColorForZLayer(node.availableZ);
|
|
367
|
+
const isPromoted = this.promotedNodeIds.has(node.capacityMeshNodeId);
|
|
368
|
+
const isResidual = this.residualNodeIds.has(node.capacityMeshNodeId);
|
|
369
|
+
return {
|
|
370
|
+
center: node.center,
|
|
371
|
+
width: node.width,
|
|
372
|
+
height: node.height,
|
|
373
|
+
stroke: isPromoted ? "rgba(245, 158, 11, 0.95)" : isResidual ? "rgba(37, 99, 235, 0.95)" : colors.stroke,
|
|
374
|
+
fill: node._containsObstacle ? "rgba(239, 68, 68, 0.35)" : isPromoted ? "rgba(251, 191, 36, 0.28)" : isResidual ? "rgba(59, 130, 246, 0.18)" : colors.fill,
|
|
375
|
+
layer: `z${node.availableZ.join(",")}`,
|
|
376
|
+
label: [
|
|
377
|
+
`node ${node.capacityMeshNodeId}`,
|
|
378
|
+
`z:${node.availableZ.join(",")}`
|
|
379
|
+
].join("\n")
|
|
380
|
+
};
|
|
381
|
+
}),
|
|
382
|
+
points: [],
|
|
383
|
+
lines: [],
|
|
384
|
+
texts: []
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
|
|
7
389
|
// lib/solvers/GapFillSolver/GapFillSolverPipeline.ts
|
|
8
390
|
import {
|
|
9
391
|
BasePipelineSolver,
|
|
@@ -11,14 +393,14 @@ import {
|
|
|
11
393
|
} from "@tscircuit/solver-utils";
|
|
12
394
|
|
|
13
395
|
// lib/solvers/GapFillSolver/FindSegmentsWithAdjacentEmptySpaceSolver.ts
|
|
14
|
-
import { BaseSolver } from "@tscircuit/solver-utils";
|
|
396
|
+
import { BaseSolver as BaseSolver2 } from "@tscircuit/solver-utils";
|
|
15
397
|
import Flatbush from "flatbush";
|
|
16
398
|
|
|
17
399
|
// lib/solvers/GapFillSolver/projectToUncoveredSegments.ts
|
|
18
|
-
var
|
|
400
|
+
var EPS2 = 1e-4;
|
|
19
401
|
function projectToUncoveredSegments(primaryEdge, overlappingEdges) {
|
|
20
|
-
const isHorizontal = Math.abs(primaryEdge.start.y - primaryEdge.end.y) <
|
|
21
|
-
const isVertical = Math.abs(primaryEdge.start.x - primaryEdge.end.x) <
|
|
402
|
+
const isHorizontal = Math.abs(primaryEdge.start.y - primaryEdge.end.y) < EPS2;
|
|
403
|
+
const isVertical = Math.abs(primaryEdge.start.x - primaryEdge.end.x) < EPS2;
|
|
22
404
|
if (!isHorizontal && !isVertical) return [];
|
|
23
405
|
const axis = isHorizontal ? "x" : "y";
|
|
24
406
|
const perp = isHorizontal ? "y" : "x";
|
|
@@ -31,16 +413,16 @@ function projectToUncoveredSegments(primaryEdge, overlappingEdges) {
|
|
|
31
413
|
const intervals = [];
|
|
32
414
|
for (const e of overlappingEdges) {
|
|
33
415
|
if (e === primaryEdge) continue;
|
|
34
|
-
const eIsHorizontal = Math.abs(e.start.y - e.end.y) <
|
|
35
|
-
const eIsVertical = Math.abs(e.start.x - e.end.x) <
|
|
416
|
+
const eIsHorizontal = Math.abs(e.start.y - e.end.y) < EPS2;
|
|
417
|
+
const eIsVertical = Math.abs(e.start.x - e.end.x) < EPS2;
|
|
36
418
|
if (axis === "x" && !eIsHorizontal) continue;
|
|
37
419
|
if (axis === "y" && !eIsVertical) continue;
|
|
38
|
-
if (Math.abs(e.start[perp] - lineCoord) >
|
|
420
|
+
if (Math.abs(e.start[perp] - lineCoord) > EPS2) continue;
|
|
39
421
|
const eMin = Math.min(e.start[axis], e.end[axis]);
|
|
40
422
|
const eMax = Math.max(e.start[axis], e.end[axis]);
|
|
41
423
|
const s = clamp2(eMin);
|
|
42
424
|
const t = clamp2(eMax);
|
|
43
|
-
if (t - s >
|
|
425
|
+
if (t - s > EPS2) intervals.push({ s, e: t });
|
|
44
426
|
}
|
|
45
427
|
if (intervals.length === 0) {
|
|
46
428
|
return [
|
|
@@ -55,19 +437,19 @@ function projectToUncoveredSegments(primaryEdge, overlappingEdges) {
|
|
|
55
437
|
const merged = [];
|
|
56
438
|
for (const it of intervals) {
|
|
57
439
|
const last = merged[merged.length - 1];
|
|
58
|
-
if (!last || it.s > last.e +
|
|
440
|
+
if (!last || it.s > last.e + EPS2) merged.push({ ...it });
|
|
59
441
|
else last.e = Math.max(last.e, it.e);
|
|
60
442
|
}
|
|
61
443
|
const uncovered = [];
|
|
62
444
|
let cursor = pMin;
|
|
63
445
|
for (const m of merged) {
|
|
64
|
-
if (m.s > cursor +
|
|
446
|
+
if (m.s > cursor + EPS2) uncovered.push({ s: cursor, e: m.s });
|
|
65
447
|
cursor = Math.max(cursor, m.e);
|
|
66
|
-
if (cursor >= pMax -
|
|
448
|
+
if (cursor >= pMax - EPS2) break;
|
|
67
449
|
}
|
|
68
|
-
if (pMax > cursor +
|
|
450
|
+
if (pMax > cursor + EPS2) uncovered.push({ s: cursor, e: pMax });
|
|
69
451
|
if (uncovered.length === 0) return [];
|
|
70
|
-
return uncovered.filter((u) => u.e - u.s >
|
|
452
|
+
return uncovered.filter((u) => u.e - u.s > EPS2).map((u) => {
|
|
71
453
|
const start = axis === "x" ? { x: u.s, y: lineCoord } : { x: lineCoord, y: u.s };
|
|
72
454
|
const end = axis === "x" ? { x: u.e, y: lineCoord } : { x: lineCoord, y: u.e };
|
|
73
455
|
return {
|
|
@@ -156,8 +538,8 @@ var visuallyOffsetLine = (line, options) => {
|
|
|
156
538
|
|
|
157
539
|
// lib/solvers/GapFillSolver/FindSegmentsWithAdjacentEmptySpaceSolver.ts
|
|
158
540
|
import "@tscircuit/math-utils";
|
|
159
|
-
var
|
|
160
|
-
var FindSegmentsWithAdjacentEmptySpaceSolver = class extends
|
|
541
|
+
var EPS3 = 1e-4;
|
|
542
|
+
var FindSegmentsWithAdjacentEmptySpaceSolver = class extends BaseSolver2 {
|
|
161
543
|
constructor(input) {
|
|
162
544
|
super();
|
|
163
545
|
this.input = input;
|
|
@@ -175,7 +557,7 @@ var FindSegmentsWithAdjacentEmptySpaceSolver = class extends BaseSolver {
|
|
|
175
557
|
;
|
|
176
558
|
[start, end] = [end, start];
|
|
177
559
|
}
|
|
178
|
-
if (Math.abs(start.x - end.x) <
|
|
560
|
+
if (Math.abs(start.x - end.x) < EPS3 && start.y > end.y) {
|
|
179
561
|
;
|
|
180
562
|
[start, end] = [end, start];
|
|
181
563
|
}
|
|
@@ -221,10 +603,10 @@ var FindSegmentsWithAdjacentEmptySpaceSolver = class extends BaseSolver {
|
|
|
221
603
|
const candidateEdge = this.unprocessedEdges.shift();
|
|
222
604
|
this.lastCandidateEdge = candidateEdge;
|
|
223
605
|
const nearbyEdges = this.edgeSpatialIndex.search(
|
|
224
|
-
candidateEdge.start.x -
|
|
225
|
-
candidateEdge.start.y -
|
|
226
|
-
candidateEdge.end.x +
|
|
227
|
-
candidateEdge.end.y +
|
|
606
|
+
candidateEdge.start.x - EPS3,
|
|
607
|
+
candidateEdge.start.y - EPS3,
|
|
608
|
+
candidateEdge.end.x + EPS3,
|
|
609
|
+
candidateEdge.end.y + EPS3
|
|
228
610
|
);
|
|
229
611
|
const overlappingEdges = nearbyEdges.map((i) => this.allEdges[i]).filter((e) => e.z === candidateEdge.z);
|
|
230
612
|
this.lastOverlappingEdges = overlappingEdges;
|
|
@@ -310,7 +692,7 @@ var FindSegmentsWithAdjacentEmptySpaceSolver = class extends BaseSolver {
|
|
|
310
692
|
};
|
|
311
693
|
|
|
312
694
|
// lib/solvers/GapFillSolver/ExpandEdgesToEmptySpaceSolver.ts
|
|
313
|
-
import { BaseSolver as
|
|
695
|
+
import { BaseSolver as BaseSolver3 } from "@tscircuit/solver-utils";
|
|
314
696
|
import RBush from "rbush";
|
|
315
697
|
|
|
316
698
|
// lib/solvers/GapFillSolver/getBoundsFromCorners.ts
|
|
@@ -325,8 +707,8 @@ var getBoundsFromCorners = (corners) => {
|
|
|
325
707
|
|
|
326
708
|
// lib/solvers/GapFillSolver/ExpandEdgesToEmptySpaceSolver.ts
|
|
327
709
|
import { segmentToBoxMinDistance } from "@tscircuit/math-utils";
|
|
328
|
-
var
|
|
329
|
-
var ExpandEdgesToEmptySpaceSolver = class extends
|
|
710
|
+
var EPS4 = 1e-4;
|
|
711
|
+
var ExpandEdgesToEmptySpaceSolver = class extends BaseSolver3 {
|
|
330
712
|
constructor(input) {
|
|
331
713
|
super();
|
|
332
714
|
this.input = input;
|
|
@@ -392,12 +774,12 @@ var ExpandEdgesToEmptySpaceSolver = class extends BaseSolver2 {
|
|
|
392
774
|
let collidingNodes = null;
|
|
393
775
|
let searchDistance = 1;
|
|
394
776
|
const searchCorner1 = {
|
|
395
|
-
x: segment.start.x + dx *
|
|
396
|
-
y: segment.start.y + dy *
|
|
777
|
+
x: segment.start.x + dx * EPS4 + normDeltaStartEnd.x * EPS4 * 10,
|
|
778
|
+
y: segment.start.y + dy * EPS4 + normDeltaStartEnd.y * EPS4 * 10
|
|
397
779
|
};
|
|
398
780
|
const searchCorner2 = {
|
|
399
|
-
x: segment.end.x + dx *
|
|
400
|
-
y: segment.end.y + dy *
|
|
781
|
+
x: segment.end.x + dx * EPS4 - normDeltaStartEnd.x * EPS4 * 10,
|
|
782
|
+
y: segment.end.y + dy * EPS4 - normDeltaStartEnd.y * EPS4 * 10
|
|
401
783
|
};
|
|
402
784
|
this.lastSearchCorner1 = searchCorner1;
|
|
403
785
|
this.lastSearchCorner2 = searchCorner2;
|
|
@@ -462,7 +844,7 @@ var ExpandEdgesToEmptySpaceSolver = class extends BaseSolver2 {
|
|
|
462
844
|
}
|
|
463
845
|
};
|
|
464
846
|
this.lastExpandedSegment = expandedSegment;
|
|
465
|
-
if (nodeWidth <
|
|
847
|
+
if (nodeWidth < EPS4 || nodeHeight < EPS4) {
|
|
466
848
|
return;
|
|
467
849
|
}
|
|
468
850
|
this.expandedSegments.push(expandedSegment);
|
|
@@ -668,7 +1050,7 @@ var GapFillSolverPipeline = class extends BasePipelineSolver {
|
|
|
668
1050
|
};
|
|
669
1051
|
|
|
670
1052
|
// lib/solvers/OuterLayerContainmentMergeSolver/OuterLayerContainmentMergeSolver.ts
|
|
671
|
-
import { BaseSolver as
|
|
1053
|
+
import { BaseSolver as BaseSolver4 } from "@tscircuit/solver-utils";
|
|
672
1054
|
|
|
673
1055
|
// lib/solvers/RectDiffSeedingSolver/layers.ts
|
|
674
1056
|
function layerSortKey(n) {
|
|
@@ -745,76 +1127,6 @@ function obstacleToXYRect(ob) {
|
|
|
745
1127
|
return { x: ob.center.x - w / 2, y: ob.center.y - h / 2, width: w, height: h };
|
|
746
1128
|
}
|
|
747
1129
|
|
|
748
|
-
// lib/utils/getColorForZLayer.ts
|
|
749
|
-
var getColorForZLayer = (zLayers) => {
|
|
750
|
-
const minZ = Math.min(...zLayers);
|
|
751
|
-
const colors = [
|
|
752
|
-
{ fill: "#dbeafe", stroke: "#3b82f6" },
|
|
753
|
-
{ fill: "#fef3c7", stroke: "#f59e0b" },
|
|
754
|
-
{ fill: "#d1fae5", stroke: "#10b981" },
|
|
755
|
-
{ fill: "#e9d5ff", stroke: "#a855f7" },
|
|
756
|
-
{ fill: "#fed7aa", stroke: "#f97316" },
|
|
757
|
-
{ fill: "#fecaca", stroke: "#ef4444" }
|
|
758
|
-
];
|
|
759
|
-
return colors[minZ % colors.length];
|
|
760
|
-
};
|
|
761
|
-
|
|
762
|
-
// lib/utils/rectdiff-geometry.ts
|
|
763
|
-
var EPS4 = 1e-9;
|
|
764
|
-
var clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
765
|
-
var gt = (a, b) => a > b + EPS4;
|
|
766
|
-
var gte = (a, b) => a > b - EPS4;
|
|
767
|
-
var lt = (a, b) => a < b - EPS4;
|
|
768
|
-
var lte = (a, b) => a < b + EPS4;
|
|
769
|
-
function overlaps(a, b) {
|
|
770
|
-
return !(a.x + a.width <= b.x + EPS4 || b.x + b.width <= a.x + EPS4 || a.y + a.height <= b.y + EPS4 || b.y + b.height <= a.y + EPS4);
|
|
771
|
-
}
|
|
772
|
-
function containsPoint(r, p) {
|
|
773
|
-
return p.x >= r.x - EPS4 && p.x <= r.x + r.width + EPS4 && p.y >= r.y - EPS4 && p.y <= r.y + r.height + EPS4;
|
|
774
|
-
}
|
|
775
|
-
function distancePointToRectEdges(p, r) {
|
|
776
|
-
const minX = r.x;
|
|
777
|
-
const maxX = r.x + r.width;
|
|
778
|
-
const minY = r.y;
|
|
779
|
-
const maxY = r.y + r.height;
|
|
780
|
-
if (p.x >= minX && p.x <= maxX && p.y >= minY && p.y <= maxY) {
|
|
781
|
-
return Math.min(p.x - minX, maxX - p.x, p.y - minY, maxY - p.y);
|
|
782
|
-
}
|
|
783
|
-
const dx = p.x < minX ? minX - p.x : p.x > maxX ? p.x - maxX : 0;
|
|
784
|
-
const dy = p.y < minY ? minY - p.y : p.y > maxY ? p.y - maxY : 0;
|
|
785
|
-
if (dx === 0) return dy;
|
|
786
|
-
if (dy === 0) return dx;
|
|
787
|
-
return Math.hypot(dx, dy);
|
|
788
|
-
}
|
|
789
|
-
function intersect1D(r1, r2) {
|
|
790
|
-
const lo = Math.max(r1[0], r2[0]);
|
|
791
|
-
const hi = Math.min(r1[1], r2[1]);
|
|
792
|
-
return hi > lo + EPS4 ? [lo, hi] : null;
|
|
793
|
-
}
|
|
794
|
-
function subtractRect2D(A, B) {
|
|
795
|
-
if (!overlaps(A, B)) return [A];
|
|
796
|
-
const Xi = intersect1D([A.x, A.x + A.width], [B.x, B.x + B.width]);
|
|
797
|
-
const Yi = intersect1D([A.y, A.y + A.height], [B.y, B.y + B.height]);
|
|
798
|
-
if (!Xi || !Yi) return [A];
|
|
799
|
-
const [X0, X1] = Xi;
|
|
800
|
-
const [Y0, Y1] = Yi;
|
|
801
|
-
const out = [];
|
|
802
|
-
if (X0 > A.x + EPS4) {
|
|
803
|
-
out.push({ x: A.x, y: A.y, width: X0 - A.x, height: A.height });
|
|
804
|
-
}
|
|
805
|
-
if (A.x + A.width > X1 + EPS4) {
|
|
806
|
-
out.push({ x: X1, y: A.y, width: A.x + A.width - X1, height: A.height });
|
|
807
|
-
}
|
|
808
|
-
const midW = Math.max(0, X1 - X0);
|
|
809
|
-
if (midW > EPS4 && Y0 > A.y + EPS4) {
|
|
810
|
-
out.push({ x: X0, y: A.y, width: midW, height: Y0 - A.y });
|
|
811
|
-
}
|
|
812
|
-
if (midW > EPS4 && A.y + A.height > Y1 + EPS4) {
|
|
813
|
-
out.push({ x: X0, y: Y1, width: midW, height: A.y + A.height - Y1 });
|
|
814
|
-
}
|
|
815
|
-
return out.filter((r) => r.width > EPS4 && r.height > EPS4);
|
|
816
|
-
}
|
|
817
|
-
|
|
818
1130
|
// lib/utils/padRect.ts
|
|
819
1131
|
var padRect = (rect, clearance) => {
|
|
820
1132
|
if (!clearance || clearance <= 0) return rect;
|
|
@@ -827,19 +1139,19 @@ var padRect = (rect, clearance) => {
|
|
|
827
1139
|
};
|
|
828
1140
|
|
|
829
1141
|
// lib/solvers/OuterLayerContainmentMergeSolver/OuterLayerContainmentMergeSolver.ts
|
|
830
|
-
var
|
|
1142
|
+
var nodeToRect2 = (node) => ({
|
|
831
1143
|
x: node.center.x - node.width / 2,
|
|
832
1144
|
y: node.center.y - node.height / 2,
|
|
833
1145
|
width: node.width,
|
|
834
1146
|
height: node.height
|
|
835
1147
|
});
|
|
836
|
-
var
|
|
837
|
-
var
|
|
1148
|
+
var rectArea2 = (rect) => rect.width * rect.height;
|
|
1149
|
+
var cloneNode2 = (node) => ({
|
|
838
1150
|
...node,
|
|
839
1151
|
center: { ...node.center },
|
|
840
1152
|
availableZ: [...node.availableZ]
|
|
841
1153
|
});
|
|
842
|
-
var
|
|
1154
|
+
var cloneNodeWithRect2 = (node, rect, capacityMeshNodeId) => ({
|
|
843
1155
|
...node,
|
|
844
1156
|
capacityMeshNodeId,
|
|
845
1157
|
center: {
|
|
@@ -851,10 +1163,10 @@ var cloneNodeWithRect = (node, rect, capacityMeshNodeId) => ({
|
|
|
851
1163
|
availableZ: [...node.availableZ],
|
|
852
1164
|
layer: `z${node.availableZ.join(",")}`
|
|
853
1165
|
});
|
|
854
|
-
var
|
|
1166
|
+
var isFreeNode2 = (node) => !node._containsObstacle && !node._containsTarget;
|
|
855
1167
|
var isSingletonOuterNode = (node, outerZ) => node.availableZ.length === 1 && node.availableZ[0] === outerZ;
|
|
856
|
-
var
|
|
857
|
-
var
|
|
1168
|
+
var sameRect2 = (a, b) => Math.abs(a.x - b.x) <= EPS && Math.abs(a.y - b.y) <= EPS && Math.abs(a.width - b.width) <= EPS && Math.abs(a.height - b.height) <= EPS;
|
|
1169
|
+
var subtractRects2 = (target, cutters) => {
|
|
858
1170
|
let remaining = [target];
|
|
859
1171
|
for (const cutter of cutters) {
|
|
860
1172
|
if (remaining.length === 0) return remaining;
|
|
@@ -866,10 +1178,10 @@ var subtractRects = (target, cutters) => {
|
|
|
866
1178
|
}
|
|
867
1179
|
return remaining;
|
|
868
1180
|
};
|
|
869
|
-
var
|
|
870
|
-
return
|
|
1181
|
+
var isFullyCoveredByRects2 = (target, coveringRects) => {
|
|
1182
|
+
return subtractRects2(target, coveringRects).length === 0;
|
|
871
1183
|
};
|
|
872
|
-
var OuterLayerContainmentMergeSolver = class extends
|
|
1184
|
+
var OuterLayerContainmentMergeSolver = class extends BaseSolver4 {
|
|
873
1185
|
constructor(input) {
|
|
874
1186
|
super();
|
|
875
1187
|
this.input = input;
|
|
@@ -879,7 +1191,7 @@ var OuterLayerContainmentMergeSolver = class extends BaseSolver3 {
|
|
|
879
1191
|
promotedNodeIds = /* @__PURE__ */ new Set();
|
|
880
1192
|
residualNodeIds = /* @__PURE__ */ new Set();
|
|
881
1193
|
_setup() {
|
|
882
|
-
this.outputNodes = this.input.meshNodes.map(
|
|
1194
|
+
this.outputNodes = this.input.meshNodes.map(cloneNode2);
|
|
883
1195
|
this.promotedNodeIds.clear();
|
|
884
1196
|
this.residualNodeIds.clear();
|
|
885
1197
|
}
|
|
@@ -891,15 +1203,15 @@ var OuterLayerContainmentMergeSolver = class extends BaseSolver3 {
|
|
|
891
1203
|
const srj = this.input.simpleRouteJson;
|
|
892
1204
|
const layerCount = Math.max(1, srj.layerCount || 1);
|
|
893
1205
|
if (layerCount < 3) {
|
|
894
|
-
return this.input.meshNodes.map(
|
|
1206
|
+
return this.input.meshNodes.map(cloneNode2);
|
|
895
1207
|
}
|
|
896
1208
|
const topZ = 0;
|
|
897
1209
|
const bottomZ = layerCount - 1;
|
|
898
1210
|
const viaMinSize = Math.max(srj.minViaDiameter ?? 0, srj.minTraceWidth || 0);
|
|
899
|
-
const originalNodes = this.input.meshNodes.map(
|
|
1211
|
+
const originalNodes = this.input.meshNodes.map(cloneNode2);
|
|
900
1212
|
const obstaclesByLayer = this.buildObstaclesByLayer(layerCount);
|
|
901
1213
|
const mutableOuterNodes = originalNodes.filter(
|
|
902
|
-
(node) =>
|
|
1214
|
+
(node) => isFreeNode2(node) && (isSingletonOuterNode(node, topZ) || isSingletonOuterNode(node, bottomZ))
|
|
903
1215
|
);
|
|
904
1216
|
const immutableNodes = originalNodes.filter(
|
|
905
1217
|
(node) => !mutableOuterNodes.includes(node)
|
|
@@ -907,21 +1219,21 @@ var OuterLayerContainmentMergeSolver = class extends BaseSolver3 {
|
|
|
907
1219
|
const freeSupportRectsByOuterLayer = /* @__PURE__ */ new Map();
|
|
908
1220
|
freeSupportRectsByOuterLayer.set(
|
|
909
1221
|
topZ,
|
|
910
|
-
originalNodes.filter((node) =>
|
|
1222
|
+
originalNodes.filter((node) => isFreeNode2(node) && node.availableZ.includes(topZ)).map(nodeToRect2)
|
|
911
1223
|
);
|
|
912
1224
|
freeSupportRectsByOuterLayer.set(
|
|
913
1225
|
bottomZ,
|
|
914
|
-
originalNodes.filter((node) =>
|
|
1226
|
+
originalNodes.filter((node) => isFreeNode2(node) && node.availableZ.includes(bottomZ)).map(nodeToRect2)
|
|
915
1227
|
);
|
|
916
1228
|
const promotedNodes = [];
|
|
917
1229
|
const promotedRects = [];
|
|
918
1230
|
const candidateNodes = mutableOuterNodes.filter(
|
|
919
|
-
(node) => node.width +
|
|
920
|
-
).sort((a, b) =>
|
|
1231
|
+
(node) => node.width + EPS >= viaMinSize && node.height + EPS >= viaMinSize
|
|
1232
|
+
).sort((a, b) => rectArea2(nodeToRect2(b)) - rectArea2(nodeToRect2(a)));
|
|
921
1233
|
for (const candidate of candidateNodes) {
|
|
922
1234
|
const candidateZ = candidate.availableZ[0];
|
|
923
1235
|
const oppositeZ = candidateZ === topZ ? bottomZ : topZ;
|
|
924
|
-
const candidateRect =
|
|
1236
|
+
const candidateRect = nodeToRect2(candidate);
|
|
925
1237
|
const oppositeSupportRects = freeSupportRectsByOuterLayer.get(oppositeZ) ?? [];
|
|
926
1238
|
if (!this.isTransitCompatibleAcrossIntermediateLayers({
|
|
927
1239
|
rect: candidateRect,
|
|
@@ -931,7 +1243,7 @@ var OuterLayerContainmentMergeSolver = class extends BaseSolver3 {
|
|
|
931
1243
|
})) {
|
|
932
1244
|
continue;
|
|
933
1245
|
}
|
|
934
|
-
if (!
|
|
1246
|
+
if (!isFullyCoveredByRects2(candidateRect, oppositeSupportRects)) {
|
|
935
1247
|
continue;
|
|
936
1248
|
}
|
|
937
1249
|
promotedNodes.push({
|
|
@@ -948,14 +1260,14 @@ var OuterLayerContainmentMergeSolver = class extends BaseSolver3 {
|
|
|
948
1260
|
if (this.promotedNodeIds.has(node.capacityMeshNodeId)) {
|
|
949
1261
|
continue;
|
|
950
1262
|
}
|
|
951
|
-
const nodeRect =
|
|
952
|
-
const remainingPieces =
|
|
953
|
-
if (remainingPieces.length === 1 &&
|
|
1263
|
+
const nodeRect = nodeToRect2(node);
|
|
1264
|
+
const remainingPieces = subtractRects2(nodeRect, promotedRects);
|
|
1265
|
+
if (remainingPieces.length === 1 && sameRect2(remainingPieces[0], nodeRect)) {
|
|
954
1266
|
residualNodes.push(node);
|
|
955
1267
|
continue;
|
|
956
1268
|
}
|
|
957
1269
|
for (const piece of remainingPieces) {
|
|
958
|
-
const residualNode =
|
|
1270
|
+
const residualNode = cloneNodeWithRect2(
|
|
959
1271
|
node,
|
|
960
1272
|
piece,
|
|
961
1273
|
`${node.capacityMeshNodeId}-outer-merge-${nextResidualId++}`
|
|
@@ -998,7 +1310,7 @@ var OuterLayerContainmentMergeSolver = class extends BaseSolver3 {
|
|
|
998
1310
|
);
|
|
999
1311
|
if (nonCopperOverlap) return false;
|
|
1000
1312
|
const copperRects = overlapping.filter((entry) => entry.obstacle.isCopperPour).map((entry) => entry.rect);
|
|
1001
|
-
if (!
|
|
1313
|
+
if (!isFullyCoveredByRects2(rect, copperRects)) {
|
|
1002
1314
|
return false;
|
|
1003
1315
|
}
|
|
1004
1316
|
}
|
|
@@ -1042,7 +1354,7 @@ import {
|
|
|
1042
1354
|
} from "@tscircuit/solver-utils";
|
|
1043
1355
|
|
|
1044
1356
|
// lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts
|
|
1045
|
-
import { BaseSolver as
|
|
1357
|
+
import { BaseSolver as BaseSolver5 } from "@tscircuit/solver-utils";
|
|
1046
1358
|
|
|
1047
1359
|
// lib/solvers/RectDiffSeedingSolver/isPointInPolygon.ts
|
|
1048
1360
|
function isPointInPolygon(p, polygon) {
|
|
@@ -1105,7 +1417,7 @@ function computeInverseRects(bounds, polygon) {
|
|
|
1105
1417
|
}
|
|
1106
1418
|
const finalRects = [];
|
|
1107
1419
|
rawRects.sort((a, b) => {
|
|
1108
|
-
if (Math.abs(a.y - b.y) >
|
|
1420
|
+
if (Math.abs(a.y - b.y) > EPS) return a.y - b.y;
|
|
1109
1421
|
return a.x - b.x;
|
|
1110
1422
|
});
|
|
1111
1423
|
let current = null;
|
|
@@ -1114,9 +1426,9 @@ function computeInverseRects(bounds, polygon) {
|
|
|
1114
1426
|
current = r;
|
|
1115
1427
|
continue;
|
|
1116
1428
|
}
|
|
1117
|
-
const sameY = Math.abs(current.y - r.y) <
|
|
1118
|
-
const sameHeight = Math.abs(current.height - r.height) <
|
|
1119
|
-
const touchesX = Math.abs(current.x + current.width - r.x) <
|
|
1429
|
+
const sameY = Math.abs(current.y - r.y) < EPS;
|
|
1430
|
+
const sameHeight = Math.abs(current.height - r.height) < EPS;
|
|
1431
|
+
const touchesX = Math.abs(current.x + current.width - r.x) < EPS;
|
|
1120
1432
|
if (sameY && sameHeight && touchesX) {
|
|
1121
1433
|
current.width += r.width;
|
|
1122
1434
|
} else {
|
|
@@ -1126,7 +1438,7 @@ function computeInverseRects(bounds, polygon) {
|
|
|
1126
1438
|
}
|
|
1127
1439
|
if (current) finalRects.push(current);
|
|
1128
1440
|
finalRects.sort((a, b) => {
|
|
1129
|
-
if (Math.abs(a.x - b.x) >
|
|
1441
|
+
if (Math.abs(a.x - b.x) > EPS) return a.x - b.x;
|
|
1130
1442
|
return a.y - b.y;
|
|
1131
1443
|
});
|
|
1132
1444
|
const mergedVertical = [];
|
|
@@ -1136,9 +1448,9 @@ function computeInverseRects(bounds, polygon) {
|
|
|
1136
1448
|
current = r;
|
|
1137
1449
|
continue;
|
|
1138
1450
|
}
|
|
1139
|
-
const sameX = Math.abs(current.x - r.x) <
|
|
1140
|
-
const sameWidth = Math.abs(current.width - r.width) <
|
|
1141
|
-
const touchesY = Math.abs(current.y + current.height - r.y) <
|
|
1451
|
+
const sameX = Math.abs(current.x - r.x) < EPS;
|
|
1452
|
+
const sameWidth = Math.abs(current.width - r.width) < EPS;
|
|
1453
|
+
const touchesY = Math.abs(current.y + current.height - r.y) < EPS;
|
|
1142
1454
|
if (sameX && sameWidth && touchesY) {
|
|
1143
1455
|
current.height += r.height;
|
|
1144
1456
|
} else {
|
|
@@ -1204,11 +1516,11 @@ function maxExpandRight(params) {
|
|
|
1204
1516
|
const { r, bounds, blockers, maxAspect } = params;
|
|
1205
1517
|
let maxWidth = bounds.x + bounds.width - r.x;
|
|
1206
1518
|
for (const b of blockers) {
|
|
1207
|
-
const verticallyOverlaps = r.y + r.height > b.y +
|
|
1519
|
+
const verticallyOverlaps = r.y + r.height > b.y + EPS && b.y + b.height > r.y + EPS;
|
|
1208
1520
|
if (verticallyOverlaps) {
|
|
1209
1521
|
if (gte(b.x, r.x + r.width)) {
|
|
1210
1522
|
maxWidth = Math.min(maxWidth, b.x - r.x);
|
|
1211
|
-
} else if (b.x + b.width > r.x + r.width -
|
|
1523
|
+
} else if (b.x + b.width > r.x + r.width - EPS && b.x < r.x + r.width + EPS) {
|
|
1212
1524
|
return 0;
|
|
1213
1525
|
}
|
|
1214
1526
|
}
|
|
@@ -1225,11 +1537,11 @@ function maxExpandDown(params) {
|
|
|
1225
1537
|
const { r, bounds, blockers, maxAspect } = params;
|
|
1226
1538
|
let maxHeight = bounds.y + bounds.height - r.y;
|
|
1227
1539
|
for (const b of blockers) {
|
|
1228
|
-
const horizOverlaps = r.x + r.width > b.x +
|
|
1540
|
+
const horizOverlaps = r.x + r.width > b.x + EPS && b.x + b.width > r.x + EPS;
|
|
1229
1541
|
if (horizOverlaps) {
|
|
1230
1542
|
if (gte(b.y, r.y + r.height)) {
|
|
1231
1543
|
maxHeight = Math.min(maxHeight, b.y - r.y);
|
|
1232
|
-
} else if (b.y + b.height > r.y + r.height -
|
|
1544
|
+
} else if (b.y + b.height > r.y + r.height - EPS && b.y < r.y + r.height + EPS) {
|
|
1233
1545
|
return 0;
|
|
1234
1546
|
}
|
|
1235
1547
|
}
|
|
@@ -1246,11 +1558,11 @@ function maxExpandLeft(params) {
|
|
|
1246
1558
|
const { r, bounds, blockers, maxAspect } = params;
|
|
1247
1559
|
let minX = bounds.x;
|
|
1248
1560
|
for (const b of blockers) {
|
|
1249
|
-
const verticallyOverlaps = r.y + r.height > b.y +
|
|
1561
|
+
const verticallyOverlaps = r.y + r.height > b.y + EPS && b.y + b.height > r.y + EPS;
|
|
1250
1562
|
if (verticallyOverlaps) {
|
|
1251
1563
|
if (lte(b.x + b.width, r.x)) {
|
|
1252
1564
|
minX = Math.max(minX, b.x + b.width);
|
|
1253
|
-
} else if (b.x < r.x +
|
|
1565
|
+
} else if (b.x < r.x + EPS && b.x + b.width > r.x - EPS) {
|
|
1254
1566
|
return 0;
|
|
1255
1567
|
}
|
|
1256
1568
|
}
|
|
@@ -1267,11 +1579,11 @@ function maxExpandUp(params) {
|
|
|
1267
1579
|
const { r, bounds, blockers, maxAspect } = params;
|
|
1268
1580
|
let minY = bounds.y;
|
|
1269
1581
|
for (const b of blockers) {
|
|
1270
|
-
const horizOverlaps = r.x + r.width > b.x +
|
|
1582
|
+
const horizOverlaps = r.x + r.width > b.x + EPS && b.x + b.width > r.x + EPS;
|
|
1271
1583
|
if (horizOverlaps) {
|
|
1272
1584
|
if (lte(b.y + b.height, r.y)) {
|
|
1273
1585
|
minY = Math.max(minY, b.y + b.height);
|
|
1274
|
-
} else if (b.y < r.y +
|
|
1586
|
+
} else if (b.y < r.y + EPS && b.y + b.height > r.y - EPS) {
|
|
1275
1587
|
return 0;
|
|
1276
1588
|
}
|
|
1277
1589
|
}
|
|
@@ -1296,7 +1608,7 @@ var toQueryRect = (params) => {
|
|
|
1296
1608
|
const minY = Math.max(bounds.y, rect.y);
|
|
1297
1609
|
const maxX = Math.min(bounds.x + bounds.width, rect.x + rect.width);
|
|
1298
1610
|
const maxY = Math.min(bounds.y + bounds.height, rect.y + rect.height);
|
|
1299
|
-
if (maxX <= minX +
|
|
1611
|
+
if (maxX <= minX + EPS || maxY <= minY + EPS) return null;
|
|
1300
1612
|
return { minX, minY, maxX, maxY };
|
|
1301
1613
|
};
|
|
1302
1614
|
function expandRectFromSeed(params) {
|
|
@@ -1402,7 +1714,7 @@ function expandRectFromSeed(params) {
|
|
|
1402
1714
|
improved = true;
|
|
1403
1715
|
}
|
|
1404
1716
|
}
|
|
1405
|
-
if (r.width +
|
|
1717
|
+
if (r.width + EPS >= minReq.width && r.height + EPS >= minReq.height) {
|
|
1406
1718
|
const area = r.width * r.height;
|
|
1407
1719
|
if (area > bestArea) {
|
|
1408
1720
|
best = quantizeRect(r);
|
|
@@ -1496,7 +1808,7 @@ function computeCandidates3D(params) {
|
|
|
1496
1808
|
]);
|
|
1497
1809
|
for (let x = bounds.x; x < bounds.x + bounds.width; x += gridSize) {
|
|
1498
1810
|
for (let y = bounds.y; y < bounds.y + bounds.height; y += gridSize) {
|
|
1499
|
-
if (Math.abs(x - bounds.x) <
|
|
1811
|
+
if (Math.abs(x - bounds.x) < EPS || Math.abs(y - bounds.y) < EPS || x > bounds.x + bounds.width - gridSize - EPS || y > bounds.y + bounds.height - gridSize - EPS) {
|
|
1500
1812
|
continue;
|
|
1501
1813
|
}
|
|
1502
1814
|
if (isFullyOccupiedAtPoint({
|
|
@@ -1568,7 +1880,7 @@ function computeUncoveredSegments(params) {
|
|
|
1568
1880
|
const s = quantize3(i.start);
|
|
1569
1881
|
const e = quantize3(i.end);
|
|
1570
1882
|
return { start: Math.min(s, e), end: Math.max(s, e) };
|
|
1571
|
-
}).filter((i) => i.end > i.start +
|
|
1883
|
+
}).filter((i) => i.end > i.start + EPS);
|
|
1572
1884
|
if (normalizedIntervals.length === 0) {
|
|
1573
1885
|
const center = (lineStartQ + lineEndQ) / 2;
|
|
1574
1886
|
return [{ start: lineStartQ, end: lineEndQ, center }];
|
|
@@ -1578,7 +1890,7 @@ function computeUncoveredSegments(params) {
|
|
|
1578
1890
|
let current = { ...sorted[0] };
|
|
1579
1891
|
for (let i = 1; i < sorted.length; i++) {
|
|
1580
1892
|
const interval = sorted[i];
|
|
1581
|
-
if (interval.start <= current.end +
|
|
1893
|
+
if (interval.start <= current.end + EPS) {
|
|
1582
1894
|
current.end = Math.max(current.end, interval.end);
|
|
1583
1895
|
} else {
|
|
1584
1896
|
merged.push(current);
|
|
@@ -1587,7 +1899,7 @@ function computeUncoveredSegments(params) {
|
|
|
1587
1899
|
}
|
|
1588
1900
|
merged.push(current);
|
|
1589
1901
|
const uncovered = [];
|
|
1590
|
-
if (merged[0].start > lineStartQ +
|
|
1902
|
+
if (merged[0].start > lineStartQ + EPS) {
|
|
1591
1903
|
const start = lineStartQ;
|
|
1592
1904
|
const end = merged[0].start;
|
|
1593
1905
|
if (end - start >= minSegmentLength) {
|
|
@@ -1601,7 +1913,7 @@ function computeUncoveredSegments(params) {
|
|
|
1601
1913
|
uncovered.push({ start, end, center: (start + end) / 2 });
|
|
1602
1914
|
}
|
|
1603
1915
|
}
|
|
1604
|
-
if (merged[merged.length - 1].end < lineEndQ -
|
|
1916
|
+
if (merged[merged.length - 1].end < lineEndQ - EPS) {
|
|
1605
1917
|
const start = merged[merged.length - 1].end;
|
|
1606
1918
|
const end = lineEndQ;
|
|
1607
1919
|
if (end - start >= minSegmentLength) {
|
|
@@ -1620,7 +1932,7 @@ function computeEdgeCandidates3D(params) {
|
|
|
1620
1932
|
hardPlacedByLayer
|
|
1621
1933
|
} = params;
|
|
1622
1934
|
const out = [];
|
|
1623
|
-
const \u03B4 = Math.max(minSize * 0.15,
|
|
1935
|
+
const \u03B4 = Math.max(minSize * 0.15, EPS * 3);
|
|
1624
1936
|
const dedup = /* @__PURE__ */ new Set();
|
|
1625
1937
|
const hardRectsByLayer = Array.from(
|
|
1626
1938
|
{ length: layerCount },
|
|
@@ -1644,7 +1956,7 @@ function computeEdgeCandidates3D(params) {
|
|
|
1644
1956
|
const { z } = p;
|
|
1645
1957
|
const x = qx;
|
|
1646
1958
|
const y = qy;
|
|
1647
|
-
if (x < bounds.x +
|
|
1959
|
+
if (x < bounds.x + EPS || y < bounds.y + EPS || x > bounds.x + bounds.width - EPS || y > bounds.y + bounds.height - EPS)
|
|
1648
1960
|
return;
|
|
1649
1961
|
if (fullyOcc({ x, y })) return;
|
|
1650
1962
|
const hard = hardRectsByLayer[z] ?? [];
|
|
@@ -1781,7 +2093,7 @@ function computeEdgeCandidates3D(params) {
|
|
|
1781
2093
|
}
|
|
1782
2094
|
for (const b of blockers) {
|
|
1783
2095
|
const obLeftX = b.x - \u03B4;
|
|
1784
|
-
if (obLeftX > bounds.x +
|
|
2096
|
+
if (obLeftX > bounds.x + EPS && obLeftX < bounds.x + bounds.width - EPS) {
|
|
1785
2097
|
const obLeftCovering = blockers.filter(
|
|
1786
2098
|
(bl) => bl !== b && bl.x <= obLeftX && bl.x + bl.width >= obLeftX
|
|
1787
2099
|
).map((bl) => ({
|
|
@@ -1799,7 +2111,7 @@ function computeEdgeCandidates3D(params) {
|
|
|
1799
2111
|
}
|
|
1800
2112
|
}
|
|
1801
2113
|
const obRightX = b.x + b.width + \u03B4;
|
|
1802
|
-
if (obRightX > bounds.x +
|
|
2114
|
+
if (obRightX > bounds.x + EPS && obRightX < bounds.x + bounds.width - EPS) {
|
|
1803
2115
|
const obRightCovering = blockers.filter(
|
|
1804
2116
|
(bl) => bl !== b && bl.x <= obRightX && bl.x + bl.width >= obRightX
|
|
1805
2117
|
).map((bl) => ({
|
|
@@ -1817,7 +2129,7 @@ function computeEdgeCandidates3D(params) {
|
|
|
1817
2129
|
}
|
|
1818
2130
|
}
|
|
1819
2131
|
const obTopY = b.y - \u03B4;
|
|
1820
|
-
if (obTopY > bounds.y +
|
|
2132
|
+
if (obTopY > bounds.y + EPS && obTopY < bounds.y + bounds.height - EPS) {
|
|
1821
2133
|
const obTopCovering = blockers.filter(
|
|
1822
2134
|
(bl) => bl !== b && bl.y <= obTopY && bl.y + bl.height >= obTopY
|
|
1823
2135
|
).map((bl) => ({
|
|
@@ -1835,7 +2147,7 @@ function computeEdgeCandidates3D(params) {
|
|
|
1835
2147
|
}
|
|
1836
2148
|
}
|
|
1837
2149
|
const obBottomY = b.y + b.height + \u03B4;
|
|
1838
|
-
if (obBottomY > bounds.y +
|
|
2150
|
+
if (obBottomY > bounds.y + EPS && obBottomY < bounds.y + bounds.height - EPS) {
|
|
1839
2151
|
const obBottomCovering = blockers.filter(
|
|
1840
2152
|
(bl) => bl !== b && bl.y <= obBottomY && bl.y + bl.height >= obBottomY
|
|
1841
2153
|
).map((bl) => ({
|
|
@@ -1910,12 +2222,12 @@ function resizeSoftOverlaps(params, newIndex) {
|
|
|
1910
2222
|
params.options.minMulti.height
|
|
1911
2223
|
);
|
|
1912
2224
|
for (const p of parts) {
|
|
1913
|
-
if (p.width +
|
|
2225
|
+
if (p.width + EPS >= minW && p.height + EPS >= minH) {
|
|
1914
2226
|
toAdd.push({ rect: p, zLayers: sharedZ.slice() });
|
|
1915
2227
|
}
|
|
1916
2228
|
}
|
|
1917
2229
|
}
|
|
1918
|
-
const
|
|
2230
|
+
const sameRect3 = (a, b) => a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY;
|
|
1919
2231
|
removeIdx.sort((a, b) => b - a).forEach((idx) => {
|
|
1920
2232
|
const rem = params.placed.splice(idx, 1)[0];
|
|
1921
2233
|
if (params.placedIndexByLayer) {
|
|
@@ -1924,7 +2236,7 @@ function resizeSoftOverlaps(params, newIndex) {
|
|
|
1924
2236
|
if (tree)
|
|
1925
2237
|
tree.remove(
|
|
1926
2238
|
rectToTree(rem.rect, { zLayers: rem.zLayers }),
|
|
1927
|
-
|
|
2239
|
+
sameRect3
|
|
1928
2240
|
);
|
|
1929
2241
|
}
|
|
1930
2242
|
}
|
|
@@ -1944,7 +2256,7 @@ function resizeSoftOverlaps(params, newIndex) {
|
|
|
1944
2256
|
|
|
1945
2257
|
// lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts
|
|
1946
2258
|
import RBush3 from "rbush";
|
|
1947
|
-
var RectDiffSeedingSolver = class extends
|
|
2259
|
+
var RectDiffSeedingSolver = class extends BaseSolver5 {
|
|
1948
2260
|
constructor(input) {
|
|
1949
2261
|
super();
|
|
1950
2262
|
this.input = input;
|
|
@@ -2303,7 +2615,7 @@ z:${placement.zLayers.join(",")}`
|
|
|
2303
2615
|
};
|
|
2304
2616
|
|
|
2305
2617
|
// lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts
|
|
2306
|
-
import { BaseSolver as
|
|
2618
|
+
import { BaseSolver as BaseSolver6 } from "@tscircuit/solver-utils";
|
|
2307
2619
|
|
|
2308
2620
|
// lib/utils/finalizeRects.ts
|
|
2309
2621
|
function finalizeRects(params) {
|
|
@@ -2375,7 +2687,7 @@ import RBush4 from "rbush";
|
|
|
2375
2687
|
var sameTreeRect = (a, b) => a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY;
|
|
2376
2688
|
|
|
2377
2689
|
// lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts
|
|
2378
|
-
var RectDiffExpansionSolver = class extends
|
|
2690
|
+
var RectDiffExpansionSolver = class extends BaseSolver6 {
|
|
2379
2691
|
constructor(input) {
|
|
2380
2692
|
super();
|
|
2381
2693
|
this.input = input;
|
|
@@ -2865,6 +3177,7 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
|
|
|
2865
3177
|
rectDiffGridSolverPipeline;
|
|
2866
3178
|
gapFillSolver;
|
|
2867
3179
|
outerLayerContainmentMergeSolver;
|
|
3180
|
+
adjacentLayerContainmentMergeSolver;
|
|
2868
3181
|
boardVoidRects;
|
|
2869
3182
|
zIndexByName;
|
|
2870
3183
|
layerNames;
|
|
@@ -2912,6 +3225,16 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
|
|
|
2912
3225
|
obstacleClearance: rectDiffPipeline.inputProblem.obstacleClearance
|
|
2913
3226
|
}
|
|
2914
3227
|
]
|
|
3228
|
+
),
|
|
3229
|
+
definePipelineStep3(
|
|
3230
|
+
"adjacentLayerContainmentMergeSolver",
|
|
3231
|
+
AdjacentLayerContainmentMergeSolver,
|
|
3232
|
+
(rectDiffPipeline) => [
|
|
3233
|
+
{
|
|
3234
|
+
meshNodes: rectDiffPipeline.outerLayerContainmentMergeSolver?.getOutput().outputNodes ?? rectDiffPipeline.gapFillSolver?.getOutput().outputNodes ?? rectDiffPipeline.rectDiffGridSolverPipeline?.getOutput().meshNodes ?? [],
|
|
3235
|
+
simpleRouteJson: rectDiffPipeline.inputProblem.simpleRouteJson
|
|
3236
|
+
}
|
|
3237
|
+
]
|
|
2915
3238
|
)
|
|
2916
3239
|
];
|
|
2917
3240
|
_setup() {
|
|
@@ -2937,6 +3260,10 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
|
|
|
2937
3260
|
return [this.inputProblem];
|
|
2938
3261
|
}
|
|
2939
3262
|
getOutput() {
|
|
3263
|
+
const adjacentLayerMergeOutput = this.adjacentLayerContainmentMergeSolver?.getOutput();
|
|
3264
|
+
if (adjacentLayerMergeOutput) {
|
|
3265
|
+
return { meshNodes: adjacentLayerMergeOutput.outputNodes };
|
|
3266
|
+
}
|
|
2940
3267
|
const outerLayerMergeOutput = this.outerLayerContainmentMergeSolver?.getOutput();
|
|
2941
3268
|
if (outerLayerMergeOutput) {
|
|
2942
3269
|
return { meshNodes: outerLayerMergeOutput.outputNodes };
|