@tscircuit/rectdiff 0.0.28 → 0.0.29

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.
Files changed (47) hide show
  1. package/.github/workflows/bun-pver-release.yml +24 -45
  2. package/dist/index.d.ts +0 -46
  3. package/dist/index.js +220 -775
  4. package/lib/RectDiffPipeline.ts +0 -46
  5. package/lib/types/srj-types.ts +0 -1
  6. package/package.json +1 -2
  7. package/pages/repro/merge-single-layer-node.page.tsx +17 -0
  8. package/tests/__snapshots__/board-outline.snap.svg +2 -2
  9. package/tests/solver/__snapshots__/rectDiffGridSolverPipeline.snap.svg +1 -1
  10. package/tests/solver/both-points-equivalent/__snapshots__/both-points-equivalent.snap.svg +1 -1
  11. package/tests/solver/bugreport01-be84eb/__snapshots__/bugreport01-be84eb.snap.svg +1 -1
  12. package/tests/solver/bugreport02-bc4361/__snapshots__/bugreport02-bc4361.snap.svg +1 -1
  13. package/tests/solver/bugreport03-fe4a17/__snapshots__/bugreport03-fe4a17.snap.svg +1 -1
  14. package/tests/solver/bugreport07-d3f3be/__snapshots__/bugreport07-d3f3be.snap.svg +1 -1
  15. package/tests/solver/bugreport08-e3ec95/__snapshots__/bugreport08-e3ec95.snap.svg +1 -1
  16. package/tests/solver/bugreport09-618e09/__snapshots__/bugreport09-618e09.snap.svg +1 -1
  17. package/tests/solver/bugreport10-71239a/__snapshots__/bugreport10-71239a.snap.svg +1 -1
  18. package/tests/solver/bugreport11-b2de3c/__snapshots__/bugreport11-b2de3c.snap.svg +1 -1
  19. package/tests/solver/bugreport12-35ce1c/__snapshots__/bugreport12-35ce1c.snap.svg +1 -1
  20. package/tests/solver/bugreport13-b9a758/__snapshots__/bugreport13-b9a758.snap.svg +1 -1
  21. package/tests/solver/bugreport16-d95f38/__snapshots__/bugreport16-d95f38.snap.svg +1 -1
  22. package/tests/solver/bugreport19/__snapshots__/bugreport19.snap.svg +1 -1
  23. package/tests/solver/bugreport20-obstacle-clipping/__snapshots__/bugreport20-obstacle-clipping.snap.svg +1 -1
  24. package/tests/solver/bugreport21-board-outline/__snapshots__/bugreport21-board-outline.snap.svg +2 -2
  25. package/tests/solver/bugreport22-2a75ce/__snapshots__/bugreport22-2a75ce.snap.svg +1 -1
  26. package/tests/solver/bugreport23-LGA15x4/__snapshots__/bugreport23-LGA15x4.snap.svg +1 -1
  27. package/tests/solver/bugreport24-05597c/__snapshots__/bugreport24-05597c.snap.svg +1 -1
  28. package/tests/solver/bugreport25-4b1d55/__snapshots__/bugreport25-4b1d55.snap.svg +1 -1
  29. package/tests/solver/bugreport36-bf8303/__snapshots__/bugreport36-bf8303.snap.svg +1 -1
  30. package/tests/solver/interaction/__snapshots__/interaction.snap.svg +1 -1
  31. package/tests/solver/multi-point/__snapshots__/multi-point.snap.svg +1 -1
  32. package/tests/solver/no-better-path/__snapshots__/no-better-path.snap.svg +1 -1
  33. package/tests/solver/repros/merge-single-layer-node/merge-single-layer-node.json +861 -0
  34. package/tests/solver/{bugreport50-multi-support-layer-merge/bugreport50-multi-support-layer-merge.test.ts → repros/merge-single-layer-node/merge-single-layer-node.test.ts} +7 -42
  35. package/tests/solver/transitivity/__snapshots__/transitivity.snap.svg +2 -2
  36. package/tsconfig.json +5 -1
  37. package/vite.config.ts +4 -0
  38. package/lib/solvers/AdjacentLayerContainmentMergeSolver/AdjacentLayerContainmentMergeSolver.ts +0 -456
  39. package/lib/solvers/OuterLayerContainmentMergeSolver/OuterLayerContainmentMergeSolver.ts +0 -314
  40. package/pages/bugreports/bugreport50-multi-support-layer-merge.page.tsx +0 -19
  41. package/pages/pour.page.tsx +0 -18
  42. package/test-assets/bugreport49-634662.json +0 -412
  43. package/tests/outer-layer-containment-merge-solver.test.ts +0 -73
  44. package/tests/solver/bugreport49-634662/__snapshots__/bugreport49-634662.snap.svg +0 -44
  45. package/tests/solver/bugreport49-634662/bugreport49-634662.test.ts +0 -130
  46. package/tests/solver/bugreport50-multi-support-layer-merge/__snapshots__/bugreport50-multi-support-layer-merge.snap.svg +0 -44
  47. package/tests/solver/bugreport50-multi-support-layer-merge/bugreport50-multi-support-layer-merge.json +0 -972
package/dist/index.js CHANGED
@@ -4,388 +4,6 @@ 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
-
389
7
  // lib/solvers/GapFillSolver/GapFillSolverPipeline.ts
390
8
  import {
391
9
  BasePipelineSolver,
@@ -393,14 +11,14 @@ import {
393
11
  } from "@tscircuit/solver-utils";
394
12
 
395
13
  // lib/solvers/GapFillSolver/FindSegmentsWithAdjacentEmptySpaceSolver.ts
396
- import { BaseSolver as BaseSolver2 } from "@tscircuit/solver-utils";
14
+ import { BaseSolver } from "@tscircuit/solver-utils";
397
15
  import Flatbush from "flatbush";
398
16
 
399
17
  // lib/solvers/GapFillSolver/projectToUncoveredSegments.ts
400
- var EPS2 = 1e-4;
18
+ var EPS = 1e-4;
401
19
  function projectToUncoveredSegments(primaryEdge, overlappingEdges) {
402
- const isHorizontal = Math.abs(primaryEdge.start.y - primaryEdge.end.y) < EPS2;
403
- const isVertical = Math.abs(primaryEdge.start.x - primaryEdge.end.x) < EPS2;
20
+ const isHorizontal = Math.abs(primaryEdge.start.y - primaryEdge.end.y) < EPS;
21
+ const isVertical = Math.abs(primaryEdge.start.x - primaryEdge.end.x) < EPS;
404
22
  if (!isHorizontal && !isVertical) return [];
405
23
  const axis = isHorizontal ? "x" : "y";
406
24
  const perp = isHorizontal ? "y" : "x";
@@ -413,16 +31,16 @@ function projectToUncoveredSegments(primaryEdge, overlappingEdges) {
413
31
  const intervals = [];
414
32
  for (const e of overlappingEdges) {
415
33
  if (e === primaryEdge) continue;
416
- const eIsHorizontal = Math.abs(e.start.y - e.end.y) < EPS2;
417
- const eIsVertical = Math.abs(e.start.x - e.end.x) < EPS2;
34
+ const eIsHorizontal = Math.abs(e.start.y - e.end.y) < EPS;
35
+ const eIsVertical = Math.abs(e.start.x - e.end.x) < EPS;
418
36
  if (axis === "x" && !eIsHorizontal) continue;
419
37
  if (axis === "y" && !eIsVertical) continue;
420
- if (Math.abs(e.start[perp] - lineCoord) > EPS2) continue;
38
+ if (Math.abs(e.start[perp] - lineCoord) > EPS) continue;
421
39
  const eMin = Math.min(e.start[axis], e.end[axis]);
422
40
  const eMax = Math.max(e.start[axis], e.end[axis]);
423
41
  const s = clamp2(eMin);
424
42
  const t = clamp2(eMax);
425
- if (t - s > EPS2) intervals.push({ s, e: t });
43
+ if (t - s > EPS) intervals.push({ s, e: t });
426
44
  }
427
45
  if (intervals.length === 0) {
428
46
  return [
@@ -437,19 +55,19 @@ function projectToUncoveredSegments(primaryEdge, overlappingEdges) {
437
55
  const merged = [];
438
56
  for (const it of intervals) {
439
57
  const last = merged[merged.length - 1];
440
- if (!last || it.s > last.e + EPS2) merged.push({ ...it });
58
+ if (!last || it.s > last.e + EPS) merged.push({ ...it });
441
59
  else last.e = Math.max(last.e, it.e);
442
60
  }
443
61
  const uncovered = [];
444
62
  let cursor = pMin;
445
63
  for (const m of merged) {
446
- if (m.s > cursor + EPS2) uncovered.push({ s: cursor, e: m.s });
64
+ if (m.s > cursor + EPS) uncovered.push({ s: cursor, e: m.s });
447
65
  cursor = Math.max(cursor, m.e);
448
- if (cursor >= pMax - EPS2) break;
66
+ if (cursor >= pMax - EPS) break;
449
67
  }
450
- if (pMax > cursor + EPS2) uncovered.push({ s: cursor, e: pMax });
68
+ if (pMax > cursor + EPS) uncovered.push({ s: cursor, e: pMax });
451
69
  if (uncovered.length === 0) return [];
452
- return uncovered.filter((u) => u.e - u.s > EPS2).map((u) => {
70
+ return uncovered.filter((u) => u.e - u.s > EPS).map((u) => {
453
71
  const start = axis === "x" ? { x: u.s, y: lineCoord } : { x: lineCoord, y: u.s };
454
72
  const end = axis === "x" ? { x: u.e, y: lineCoord } : { x: lineCoord, y: u.e };
455
73
  return {
@@ -538,8 +156,8 @@ var visuallyOffsetLine = (line, options) => {
538
156
 
539
157
  // lib/solvers/GapFillSolver/FindSegmentsWithAdjacentEmptySpaceSolver.ts
540
158
  import "@tscircuit/math-utils";
541
- var EPS3 = 1e-4;
542
- var FindSegmentsWithAdjacentEmptySpaceSolver = class extends BaseSolver2 {
159
+ var EPS2 = 1e-4;
160
+ var FindSegmentsWithAdjacentEmptySpaceSolver = class extends BaseSolver {
543
161
  constructor(input) {
544
162
  super();
545
163
  this.input = input;
@@ -557,7 +175,7 @@ var FindSegmentsWithAdjacentEmptySpaceSolver = class extends BaseSolver2 {
557
175
  ;
558
176
  [start, end] = [end, start];
559
177
  }
560
- if (Math.abs(start.x - end.x) < EPS3 && start.y > end.y) {
178
+ if (Math.abs(start.x - end.x) < EPS2 && start.y > end.y) {
561
179
  ;
562
180
  [start, end] = [end, start];
563
181
  }
@@ -603,10 +221,10 @@ var FindSegmentsWithAdjacentEmptySpaceSolver = class extends BaseSolver2 {
603
221
  const candidateEdge = this.unprocessedEdges.shift();
604
222
  this.lastCandidateEdge = candidateEdge;
605
223
  const nearbyEdges = this.edgeSpatialIndex.search(
606
- candidateEdge.start.x - EPS3,
607
- candidateEdge.start.y - EPS3,
608
- candidateEdge.end.x + EPS3,
609
- candidateEdge.end.y + EPS3
224
+ candidateEdge.start.x - EPS2,
225
+ candidateEdge.start.y - EPS2,
226
+ candidateEdge.end.x + EPS2,
227
+ candidateEdge.end.y + EPS2
610
228
  );
611
229
  const overlappingEdges = nearbyEdges.map((i) => this.allEdges[i]).filter((e) => e.z === candidateEdge.z);
612
230
  this.lastOverlappingEdges = overlappingEdges;
@@ -692,7 +310,7 @@ var FindSegmentsWithAdjacentEmptySpaceSolver = class extends BaseSolver2 {
692
310
  };
693
311
 
694
312
  // lib/solvers/GapFillSolver/ExpandEdgesToEmptySpaceSolver.ts
695
- import { BaseSolver as BaseSolver3 } from "@tscircuit/solver-utils";
313
+ import { BaseSolver as BaseSolver2 } from "@tscircuit/solver-utils";
696
314
  import RBush from "rbush";
697
315
 
698
316
  // lib/solvers/GapFillSolver/getBoundsFromCorners.ts
@@ -707,8 +325,8 @@ var getBoundsFromCorners = (corners) => {
707
325
 
708
326
  // lib/solvers/GapFillSolver/ExpandEdgesToEmptySpaceSolver.ts
709
327
  import { segmentToBoxMinDistance } from "@tscircuit/math-utils";
710
- var EPS4 = 1e-4;
711
- var ExpandEdgesToEmptySpaceSolver = class extends BaseSolver3 {
328
+ var EPS3 = 1e-4;
329
+ var ExpandEdgesToEmptySpaceSolver = class extends BaseSolver2 {
712
330
  constructor(input) {
713
331
  super();
714
332
  this.input = input;
@@ -774,12 +392,12 @@ var ExpandEdgesToEmptySpaceSolver = class extends BaseSolver3 {
774
392
  let collidingNodes = null;
775
393
  let searchDistance = 1;
776
394
  const searchCorner1 = {
777
- x: segment.start.x + dx * EPS4 + normDeltaStartEnd.x * EPS4 * 10,
778
- y: segment.start.y + dy * EPS4 + normDeltaStartEnd.y * EPS4 * 10
395
+ x: segment.start.x + dx * EPS3 + normDeltaStartEnd.x * EPS3 * 10,
396
+ y: segment.start.y + dy * EPS3 + normDeltaStartEnd.y * EPS3 * 10
779
397
  };
780
398
  const searchCorner2 = {
781
- x: segment.end.x + dx * EPS4 - normDeltaStartEnd.x * EPS4 * 10,
782
- y: segment.end.y + dy * EPS4 - normDeltaStartEnd.y * EPS4 * 10
399
+ x: segment.end.x + dx * EPS3 - normDeltaStartEnd.x * EPS3 * 10,
400
+ y: segment.end.y + dy * EPS3 - normDeltaStartEnd.y * EPS3 * 10
783
401
  };
784
402
  this.lastSearchCorner1 = searchCorner1;
785
403
  this.lastSearchCorner2 = searchCorner2;
@@ -844,7 +462,7 @@ var ExpandEdgesToEmptySpaceSolver = class extends BaseSolver3 {
844
462
  }
845
463
  };
846
464
  this.lastExpandedSegment = expandedSegment;
847
- if (nodeWidth < EPS4 || nodeHeight < EPS4) {
465
+ if (nodeWidth < EPS3 || nodeHeight < EPS3) {
848
466
  return;
849
467
  }
850
468
  this.expandedSegments.push(expandedSegment);
@@ -1049,313 +667,70 @@ var GapFillSolverPipeline = class extends BasePipelineSolver {
1049
667
  }
1050
668
  };
1051
669
 
1052
- // lib/solvers/OuterLayerContainmentMergeSolver/OuterLayerContainmentMergeSolver.ts
1053
- import { BaseSolver as BaseSolver4 } from "@tscircuit/solver-utils";
670
+ // lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts
671
+ import {
672
+ BasePipelineSolver as BasePipelineSolver2,
673
+ definePipelineStep as definePipelineStep2
674
+ } from "@tscircuit/solver-utils";
1054
675
 
1055
- // lib/solvers/RectDiffSeedingSolver/layers.ts
1056
- function layerSortKey(n) {
1057
- const L = n.toLowerCase();
1058
- if (L === "top") return -1e6;
1059
- if (L === "bottom") return 1e6;
1060
- const m = /^inner(\d+)$/i.exec(L);
1061
- if (m) return parseInt(m[1], 10) || 0;
1062
- return 100 + L.charCodeAt(0);
1063
- }
1064
- function canonicalizeLayerOrder(names) {
1065
- return Array.from(new Set(names)).sort((a, b) => {
1066
- const ka = layerSortKey(a);
1067
- const kb = layerSortKey(b);
1068
- if (ka !== kb) return ka - kb;
1069
- return a.localeCompare(b);
1070
- });
676
+ // lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts
677
+ import { BaseSolver as BaseSolver3 } from "@tscircuit/solver-utils";
678
+
679
+ // lib/utils/rectdiff-geometry.ts
680
+ var EPS4 = 1e-9;
681
+ var clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
682
+ var gt = (a, b) => a > b + EPS4;
683
+ var gte = (a, b) => a > b - EPS4;
684
+ var lt = (a, b) => a < b - EPS4;
685
+ var lte = (a, b) => a < b + EPS4;
686
+ function overlaps(a, b) {
687
+ 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);
1071
688
  }
1072
- function buildZIndexMap(params) {
1073
- const names = canonicalizeLayerOrder(
1074
- (params.obstacles ?? []).flatMap((o) => o.layers ?? [])
1075
- );
1076
- const declaredLayerCount = Math.max(1, params.layerCount || names.length || 1);
1077
- const fallback = Array.from(
1078
- { length: declaredLayerCount },
1079
- (_, i) => i === 0 ? "top" : i === declaredLayerCount - 1 ? "bottom" : `inner${i}`
1080
- );
1081
- const ordered = [];
1082
- const seen = /* @__PURE__ */ new Set();
1083
- const push = (n) => {
1084
- const key = n.toLowerCase();
1085
- if (seen.has(key)) return;
1086
- seen.add(key);
1087
- ordered.push(n);
1088
- };
1089
- fallback.forEach(push);
1090
- names.forEach(push);
1091
- const layerNames = ordered.slice(0, declaredLayerCount);
1092
- const clampIndex = (nameLower) => {
1093
- if (layerNames.length <= 1) return 0;
1094
- if (nameLower === "top") return 0;
1095
- if (nameLower === "bottom") return layerNames.length - 1;
1096
- const m = /^inner(\d+)$/i.exec(nameLower);
1097
- if (m) {
1098
- if (layerNames.length <= 2) return layerNames.length - 1;
1099
- const parsed = parseInt(m[1], 10);
1100
- const maxInner = layerNames.length - 2;
1101
- const clampedInner = Math.min(
1102
- maxInner,
1103
- Math.max(1, Number.isFinite(parsed) ? parsed : 1)
1104
- );
1105
- return clampedInner;
1106
- }
1107
- return 0;
1108
- };
1109
- const map = /* @__PURE__ */ new Map();
1110
- layerNames.forEach((n, i) => map.set(n.toLowerCase(), i));
1111
- ordered.slice(layerNames.length).forEach((n) => {
1112
- const key = n.toLowerCase();
1113
- map.set(key, clampIndex(key));
1114
- });
1115
- return { layerNames, zIndexByName: map };
689
+ function containsPoint(r, p) {
690
+ 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;
1116
691
  }
1117
- function obstacleZs(ob, zIndexByName) {
1118
- if (ob.zLayers?.length)
1119
- return Array.from(new Set(ob.zLayers)).sort((a, b) => a - b);
1120
- const fromNames = (ob.layers ?? []).map((n) => zIndexByName.get(n.toLowerCase())).filter((v) => typeof v === "number");
1121
- return Array.from(new Set(fromNames)).sort((a, b) => a - b);
692
+ function distancePointToRectEdges(p, r) {
693
+ const minX = r.x;
694
+ const maxX = r.x + r.width;
695
+ const minY = r.y;
696
+ const maxY = r.y + r.height;
697
+ if (p.x >= minX && p.x <= maxX && p.y >= minY && p.y <= maxY) {
698
+ return Math.min(p.x - minX, maxX - p.x, p.y - minY, maxY - p.y);
699
+ }
700
+ const dx = p.x < minX ? minX - p.x : p.x > maxX ? p.x - maxX : 0;
701
+ const dy = p.y < minY ? minY - p.y : p.y > maxY ? p.y - maxY : 0;
702
+ if (dx === 0) return dy;
703
+ if (dy === 0) return dx;
704
+ return Math.hypot(dx, dy);
1122
705
  }
1123
- function obstacleToXYRect(ob) {
1124
- const w = ob.width;
1125
- const h = ob.height;
1126
- if (typeof w !== "number" || typeof h !== "number") return null;
1127
- return { x: ob.center.x - w / 2, y: ob.center.y - h / 2, width: w, height: h };
706
+ function intersect1D(r1, r2) {
707
+ const lo = Math.max(r1[0], r2[0]);
708
+ const hi = Math.min(r1[1], r2[1]);
709
+ return hi > lo + EPS4 ? [lo, hi] : null;
1128
710
  }
1129
-
1130
- // lib/utils/padRect.ts
1131
- var padRect = (rect, clearance) => {
1132
- if (!clearance || clearance <= 0) return rect;
1133
- return {
1134
- x: rect.x - clearance,
1135
- y: rect.y - clearance,
1136
- width: rect.width + 2 * clearance,
1137
- height: rect.height + 2 * clearance
1138
- };
1139
- };
1140
-
1141
- // lib/solvers/OuterLayerContainmentMergeSolver/OuterLayerContainmentMergeSolver.ts
1142
- var nodeToRect2 = (node) => ({
1143
- x: node.center.x - node.width / 2,
1144
- y: node.center.y - node.height / 2,
1145
- width: node.width,
1146
- height: node.height
1147
- });
1148
- var rectArea2 = (rect) => rect.width * rect.height;
1149
- var MIN_OUTER_LAYER_MERGE_AREA_MM2 = 1;
1150
- var cloneNode2 = (node) => ({
1151
- ...node,
1152
- center: { ...node.center },
1153
- availableZ: [...node.availableZ]
1154
- });
1155
- var cloneNodeWithRect2 = (node, rect, capacityMeshNodeId) => ({
1156
- ...node,
1157
- capacityMeshNodeId,
1158
- center: {
1159
- x: rect.x + rect.width / 2,
1160
- y: rect.y + rect.height / 2
1161
- },
1162
- width: rect.width,
1163
- height: rect.height,
1164
- availableZ: [...node.availableZ],
1165
- layer: `z${node.availableZ.join(",")}`
1166
- });
1167
- var isFreeNode2 = (node) => !node._containsObstacle && !node._containsTarget;
1168
- var isSingletonOuterNode = (node, outerZ) => node.availableZ.length === 1 && node.availableZ[0] === outerZ;
1169
- 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;
1170
- var subtractRects2 = (target, cutters) => {
1171
- let remaining = [target];
1172
- for (const cutter of cutters) {
1173
- if (remaining.length === 0) return remaining;
1174
- const nextRemaining = [];
1175
- for (const piece of remaining) {
1176
- nextRemaining.push(...subtractRect2D(piece, cutter));
1177
- }
1178
- remaining = nextRemaining;
1179
- }
1180
- return remaining;
1181
- };
1182
- var isFullyCoveredByRects2 = (target, coveringRects) => {
1183
- return subtractRects2(target, coveringRects).length === 0;
1184
- };
1185
- var OuterLayerContainmentMergeSolver = class extends BaseSolver4 {
1186
- constructor(input) {
1187
- super();
1188
- this.input = input;
1189
- }
1190
- input;
1191
- outputNodes = [];
1192
- promotedNodeIds = /* @__PURE__ */ new Set();
1193
- residualNodeIds = /* @__PURE__ */ new Set();
1194
- _setup() {
1195
- this.outputNodes = this.input.meshNodes.map(cloneNode2);
1196
- this.promotedNodeIds.clear();
1197
- this.residualNodeIds.clear();
1198
- }
1199
- _step() {
1200
- this.outputNodes = this.processOuterLayerContainmentMerges();
1201
- this.solved = true;
1202
- }
1203
- processOuterLayerContainmentMerges() {
1204
- const srj = this.input.simpleRouteJson;
1205
- const layerCount = Math.max(1, srj.layerCount || 1);
1206
- if (layerCount < 3) {
1207
- return this.input.meshNodes.map(cloneNode2);
1208
- }
1209
- const topZ = 0;
1210
- const bottomZ = layerCount - 1;
1211
- const viaMinSize = Math.max(srj.minViaDiameter ?? 0, srj.minTraceWidth || 0);
1212
- const originalNodes = this.input.meshNodes.map(cloneNode2);
1213
- const obstaclesByLayer = this.buildObstaclesByLayer(layerCount);
1214
- const mutableOuterNodes = originalNodes.filter(
1215
- (node) => isFreeNode2(node) && (isSingletonOuterNode(node, topZ) || isSingletonOuterNode(node, bottomZ))
1216
- );
1217
- const immutableNodes = originalNodes.filter(
1218
- (node) => !mutableOuterNodes.includes(node)
1219
- );
1220
- const freeSupportRectsByOuterLayer = /* @__PURE__ */ new Map();
1221
- freeSupportRectsByOuterLayer.set(
1222
- topZ,
1223
- originalNodes.filter((node) => isFreeNode2(node) && node.availableZ.includes(topZ)).map(nodeToRect2)
1224
- );
1225
- freeSupportRectsByOuterLayer.set(
1226
- bottomZ,
1227
- originalNodes.filter((node) => isFreeNode2(node) && node.availableZ.includes(bottomZ)).map(nodeToRect2)
1228
- );
1229
- const promotedNodes = [];
1230
- const promotedRects = [];
1231
- const candidateNodes = mutableOuterNodes.filter(
1232
- (node) => node.width + EPS >= viaMinSize && node.height + EPS >= viaMinSize && rectArea2(nodeToRect2(node)) > MIN_OUTER_LAYER_MERGE_AREA_MM2 + EPS
1233
- ).sort((a, b) => rectArea2(nodeToRect2(b)) - rectArea2(nodeToRect2(a)));
1234
- for (const candidate of candidateNodes) {
1235
- const candidateZ = candidate.availableZ[0];
1236
- const oppositeZ = candidateZ === topZ ? bottomZ : topZ;
1237
- const candidateRect = nodeToRect2(candidate);
1238
- const oppositeSupportRects = freeSupportRectsByOuterLayer.get(oppositeZ) ?? [];
1239
- if (!this.isTransitCompatibleAcrossIntermediateLayers({
1240
- rect: candidateRect,
1241
- fromZ: candidateZ,
1242
- toZ: oppositeZ,
1243
- obstaclesByLayer
1244
- })) {
1245
- continue;
1246
- }
1247
- if (!isFullyCoveredByRects2(candidateRect, oppositeSupportRects)) {
1248
- continue;
1249
- }
1250
- promotedNodes.push({
1251
- ...candidate,
1252
- availableZ: [topZ, bottomZ],
1253
- layer: `z${topZ},${bottomZ}`
1254
- });
1255
- promotedRects.push(candidateRect);
1256
- this.promotedNodeIds.add(candidate.capacityMeshNodeId);
1257
- }
1258
- let nextResidualId = 0;
1259
- const residualNodes = [];
1260
- for (const node of mutableOuterNodes) {
1261
- if (this.promotedNodeIds.has(node.capacityMeshNodeId)) {
1262
- continue;
1263
- }
1264
- const nodeRect = nodeToRect2(node);
1265
- const remainingPieces = subtractRects2(nodeRect, promotedRects);
1266
- if (remainingPieces.length === 1 && sameRect2(remainingPieces[0], nodeRect)) {
1267
- residualNodes.push(node);
1268
- continue;
1269
- }
1270
- for (const piece of remainingPieces) {
1271
- const residualNode = cloneNodeWithRect2(
1272
- node,
1273
- piece,
1274
- `${node.capacityMeshNodeId}-outer-merge-${nextResidualId++}`
1275
- );
1276
- residualNodes.push(residualNode);
1277
- this.residualNodeIds.add(residualNode.capacityMeshNodeId);
1278
- }
1279
- }
1280
- return [...immutableNodes, ...promotedNodes, ...residualNodes];
1281
- }
1282
- buildObstaclesByLayer(layerCount) {
1283
- const out = Array.from(
1284
- { length: layerCount },
1285
- () => []
1286
- );
1287
- for (const obstacle of this.input.simpleRouteJson.obstacles ?? []) {
1288
- const baseRect = obstacleToXYRect(obstacle);
1289
- if (!baseRect) continue;
1290
- const rect = padRect(baseRect, this.input.obstacleClearance ?? 0);
1291
- const zLayers = obstacleZs(obstacle, this.input.zIndexByName);
1292
- for (const z of zLayers) {
1293
- if (z < 0 || z >= layerCount) continue;
1294
- out[z].push({ obstacle, rect });
1295
- }
1296
- }
1297
- return out;
711
+ function subtractRect2D(A, B) {
712
+ if (!overlaps(A, B)) return [A];
713
+ const Xi = intersect1D([A.x, A.x + A.width], [B.x, B.x + B.width]);
714
+ const Yi = intersect1D([A.y, A.y + A.height], [B.y, B.y + B.height]);
715
+ if (!Xi || !Yi) return [A];
716
+ const [X0, X1] = Xi;
717
+ const [Y0, Y1] = Yi;
718
+ const out = [];
719
+ if (X0 > A.x + EPS4) {
720
+ out.push({ x: A.x, y: A.y, width: X0 - A.x, height: A.height });
1298
721
  }
1299
- isTransitCompatibleAcrossIntermediateLayers(params) {
1300
- const { rect, fromZ, toZ, obstaclesByLayer } = params;
1301
- const lo = Math.min(fromZ, toZ);
1302
- const hi = Math.max(fromZ, toZ);
1303
- if (hi - lo < 2) return false;
1304
- for (let z = lo + 1; z < hi; z++) {
1305
- const overlapping = (obstaclesByLayer[z] ?? []).filter(
1306
- (entry) => overlaps(entry.rect, rect)
1307
- );
1308
- if (overlapping.length === 0) return false;
1309
- const nonCopperOverlap = overlapping.some(
1310
- (entry) => !entry.obstacle.isCopperPour
1311
- );
1312
- if (nonCopperOverlap) return false;
1313
- const copperRects = overlapping.filter((entry) => entry.obstacle.isCopperPour).map((entry) => entry.rect);
1314
- if (!isFullyCoveredByRects2(rect, copperRects)) {
1315
- return false;
1316
- }
1317
- }
1318
- return true;
722
+ if (A.x + A.width > X1 + EPS4) {
723
+ out.push({ x: X1, y: A.y, width: A.x + A.width - X1, height: A.height });
1319
724
  }
1320
- getOutput() {
1321
- return { outputNodes: this.outputNodes };
725
+ const midW = Math.max(0, X1 - X0);
726
+ if (midW > EPS4 && Y0 > A.y + EPS4) {
727
+ out.push({ x: X0, y: A.y, width: midW, height: Y0 - A.y });
1322
728
  }
1323
- visualize() {
1324
- return {
1325
- title: "OuterLayerContainmentMergeSolver",
1326
- coordinateSystem: "cartesian",
1327
- rects: this.outputNodes.map((node) => {
1328
- const colors = getColorForZLayer(node.availableZ);
1329
- const isPromoted = this.promotedNodeIds.has(node.capacityMeshNodeId);
1330
- const isResidual = this.residualNodeIds.has(node.capacityMeshNodeId);
1331
- return {
1332
- center: node.center,
1333
- width: node.width,
1334
- height: node.height,
1335
- stroke: isPromoted ? "rgba(22, 163, 74, 0.95)" : isResidual ? "rgba(37, 99, 235, 0.95)" : colors.stroke,
1336
- fill: node._containsObstacle ? "rgba(239, 68, 68, 0.35)" : isPromoted ? "rgba(34, 197, 94, 0.28)" : isResidual ? "rgba(59, 130, 246, 0.18)" : colors.fill,
1337
- layer: `z${node.availableZ.join(",")}`,
1338
- label: [
1339
- `node ${node.capacityMeshNodeId}`,
1340
- `z:${node.availableZ.join(",")}`
1341
- ].join("\n")
1342
- };
1343
- }),
1344
- points: [],
1345
- lines: [],
1346
- texts: []
1347
- };
729
+ if (midW > EPS4 && A.y + A.height > Y1 + EPS4) {
730
+ out.push({ x: X0, y: Y1, width: midW, height: A.y + A.height - Y1 });
1348
731
  }
1349
- };
1350
-
1351
- // lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts
1352
- import {
1353
- BasePipelineSolver as BasePipelineSolver2,
1354
- definePipelineStep as definePipelineStep2
1355
- } from "@tscircuit/solver-utils";
1356
-
1357
- // lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts
1358
- import { BaseSolver as BaseSolver5 } from "@tscircuit/solver-utils";
732
+ return out.filter((r) => r.width > EPS4 && r.height > EPS4);
733
+ }
1359
734
 
1360
735
  // lib/solvers/RectDiffSeedingSolver/isPointInPolygon.ts
1361
736
  function isPointInPolygon(p, polygon) {
@@ -1418,7 +793,7 @@ function computeInverseRects(bounds, polygon) {
1418
793
  }
1419
794
  const finalRects = [];
1420
795
  rawRects.sort((a, b) => {
1421
- if (Math.abs(a.y - b.y) > EPS) return a.y - b.y;
796
+ if (Math.abs(a.y - b.y) > EPS4) return a.y - b.y;
1422
797
  return a.x - b.x;
1423
798
  });
1424
799
  let current = null;
@@ -1427,9 +802,9 @@ function computeInverseRects(bounds, polygon) {
1427
802
  current = r;
1428
803
  continue;
1429
804
  }
1430
- const sameY = Math.abs(current.y - r.y) < EPS;
1431
- const sameHeight = Math.abs(current.height - r.height) < EPS;
1432
- const touchesX = Math.abs(current.x + current.width - r.x) < EPS;
805
+ const sameY = Math.abs(current.y - r.y) < EPS4;
806
+ const sameHeight = Math.abs(current.height - r.height) < EPS4;
807
+ const touchesX = Math.abs(current.x + current.width - r.x) < EPS4;
1433
808
  if (sameY && sameHeight && touchesX) {
1434
809
  current.width += r.width;
1435
810
  } else {
@@ -1439,7 +814,7 @@ function computeInverseRects(bounds, polygon) {
1439
814
  }
1440
815
  if (current) finalRects.push(current);
1441
816
  finalRects.sort((a, b) => {
1442
- if (Math.abs(a.x - b.x) > EPS) return a.x - b.x;
817
+ if (Math.abs(a.x - b.x) > EPS4) return a.x - b.x;
1443
818
  return a.y - b.y;
1444
819
  });
1445
820
  const mergedVertical = [];
@@ -1449,9 +824,9 @@ function computeInverseRects(bounds, polygon) {
1449
824
  current = r;
1450
825
  continue;
1451
826
  }
1452
- const sameX = Math.abs(current.x - r.x) < EPS;
1453
- const sameWidth = Math.abs(current.width - r.width) < EPS;
1454
- const touchesY = Math.abs(current.y + current.height - r.y) < EPS;
827
+ const sameX = Math.abs(current.x - r.x) < EPS4;
828
+ const sameWidth = Math.abs(current.width - r.width) < EPS4;
829
+ const touchesY = Math.abs(current.y + current.height - r.y) < EPS4;
1455
830
  if (sameX && sameWidth && touchesY) {
1456
831
  current.height += r.height;
1457
832
  } else {
@@ -1463,6 +838,81 @@ function computeInverseRects(bounds, polygon) {
1463
838
  return mergedVertical;
1464
839
  }
1465
840
 
841
+ // lib/solvers/RectDiffSeedingSolver/layers.ts
842
+ function layerSortKey(n) {
843
+ const L = n.toLowerCase();
844
+ if (L === "top") return -1e6;
845
+ if (L === "bottom") return 1e6;
846
+ const m = /^inner(\d+)$/i.exec(L);
847
+ if (m) return parseInt(m[1], 10) || 0;
848
+ return 100 + L.charCodeAt(0);
849
+ }
850
+ function canonicalizeLayerOrder(names) {
851
+ return Array.from(new Set(names)).sort((a, b) => {
852
+ const ka = layerSortKey(a);
853
+ const kb = layerSortKey(b);
854
+ if (ka !== kb) return ka - kb;
855
+ return a.localeCompare(b);
856
+ });
857
+ }
858
+ function buildZIndexMap(params) {
859
+ const names = canonicalizeLayerOrder(
860
+ (params.obstacles ?? []).flatMap((o) => o.layers ?? [])
861
+ );
862
+ const declaredLayerCount = Math.max(1, params.layerCount || names.length || 1);
863
+ const fallback = Array.from(
864
+ { length: declaredLayerCount },
865
+ (_, i) => i === 0 ? "top" : i === declaredLayerCount - 1 ? "bottom" : `inner${i}`
866
+ );
867
+ const ordered = [];
868
+ const seen = /* @__PURE__ */ new Set();
869
+ const push = (n) => {
870
+ const key = n.toLowerCase();
871
+ if (seen.has(key)) return;
872
+ seen.add(key);
873
+ ordered.push(n);
874
+ };
875
+ fallback.forEach(push);
876
+ names.forEach(push);
877
+ const layerNames = ordered.slice(0, declaredLayerCount);
878
+ const clampIndex = (nameLower) => {
879
+ if (layerNames.length <= 1) return 0;
880
+ if (nameLower === "top") return 0;
881
+ if (nameLower === "bottom") return layerNames.length - 1;
882
+ const m = /^inner(\d+)$/i.exec(nameLower);
883
+ if (m) {
884
+ if (layerNames.length <= 2) return layerNames.length - 1;
885
+ const parsed = parseInt(m[1], 10);
886
+ const maxInner = layerNames.length - 2;
887
+ const clampedInner = Math.min(
888
+ maxInner,
889
+ Math.max(1, Number.isFinite(parsed) ? parsed : 1)
890
+ );
891
+ return clampedInner;
892
+ }
893
+ return 0;
894
+ };
895
+ const map = /* @__PURE__ */ new Map();
896
+ layerNames.forEach((n, i) => map.set(n.toLowerCase(), i));
897
+ ordered.slice(layerNames.length).forEach((n) => {
898
+ const key = n.toLowerCase();
899
+ map.set(key, clampIndex(key));
900
+ });
901
+ return { layerNames, zIndexByName: map };
902
+ }
903
+ function obstacleZs(ob, zIndexByName) {
904
+ if (ob.zLayers?.length)
905
+ return Array.from(new Set(ob.zLayers)).sort((a, b) => a - b);
906
+ const fromNames = (ob.layers ?? []).map((n) => zIndexByName.get(n.toLowerCase())).filter((v) => typeof v === "number");
907
+ return Array.from(new Set(fromNames)).sort((a, b) => a - b);
908
+ }
909
+ function obstacleToXYRect(ob) {
910
+ const w = ob.width;
911
+ const h = ob.height;
912
+ if (typeof w !== "number" || typeof h !== "number") return null;
913
+ return { x: ob.center.x - w / 2, y: ob.center.y - h / 2, width: w, height: h };
914
+ }
915
+
1466
916
  // lib/utils/isSelfRect.ts
1467
917
  var EPS5 = 1e-9;
1468
918
  var isSelfRect = (params) => Math.abs(params.rect.x + params.rect.width / 2 - params.startX) < EPS5 && Math.abs(params.rect.y + params.rect.height / 2 - params.startY) < EPS5 && Math.abs(params.rect.width - params.initialW) < EPS5 && Math.abs(params.rect.height - params.initialH) < EPS5;
@@ -1517,11 +967,11 @@ function maxExpandRight(params) {
1517
967
  const { r, bounds, blockers, maxAspect } = params;
1518
968
  let maxWidth = bounds.x + bounds.width - r.x;
1519
969
  for (const b of blockers) {
1520
- const verticallyOverlaps = r.y + r.height > b.y + EPS && b.y + b.height > r.y + EPS;
970
+ const verticallyOverlaps = r.y + r.height > b.y + EPS4 && b.y + b.height > r.y + EPS4;
1521
971
  if (verticallyOverlaps) {
1522
972
  if (gte(b.x, r.x + r.width)) {
1523
973
  maxWidth = Math.min(maxWidth, b.x - r.x);
1524
- } else if (b.x + b.width > r.x + r.width - EPS && b.x < r.x + r.width + EPS) {
974
+ } else if (b.x + b.width > r.x + r.width - EPS4 && b.x < r.x + r.width + EPS4) {
1525
975
  return 0;
1526
976
  }
1527
977
  }
@@ -1538,11 +988,11 @@ function maxExpandDown(params) {
1538
988
  const { r, bounds, blockers, maxAspect } = params;
1539
989
  let maxHeight = bounds.y + bounds.height - r.y;
1540
990
  for (const b of blockers) {
1541
- const horizOverlaps = r.x + r.width > b.x + EPS && b.x + b.width > r.x + EPS;
991
+ const horizOverlaps = r.x + r.width > b.x + EPS4 && b.x + b.width > r.x + EPS4;
1542
992
  if (horizOverlaps) {
1543
993
  if (gte(b.y, r.y + r.height)) {
1544
994
  maxHeight = Math.min(maxHeight, b.y - r.y);
1545
- } else if (b.y + b.height > r.y + r.height - EPS && b.y < r.y + r.height + EPS) {
995
+ } else if (b.y + b.height > r.y + r.height - EPS4 && b.y < r.y + r.height + EPS4) {
1546
996
  return 0;
1547
997
  }
1548
998
  }
@@ -1559,11 +1009,11 @@ function maxExpandLeft(params) {
1559
1009
  const { r, bounds, blockers, maxAspect } = params;
1560
1010
  let minX = bounds.x;
1561
1011
  for (const b of blockers) {
1562
- const verticallyOverlaps = r.y + r.height > b.y + EPS && b.y + b.height > r.y + EPS;
1012
+ const verticallyOverlaps = r.y + r.height > b.y + EPS4 && b.y + b.height > r.y + EPS4;
1563
1013
  if (verticallyOverlaps) {
1564
1014
  if (lte(b.x + b.width, r.x)) {
1565
1015
  minX = Math.max(minX, b.x + b.width);
1566
- } else if (b.x < r.x + EPS && b.x + b.width > r.x - EPS) {
1016
+ } else if (b.x < r.x + EPS4 && b.x + b.width > r.x - EPS4) {
1567
1017
  return 0;
1568
1018
  }
1569
1019
  }
@@ -1580,11 +1030,11 @@ function maxExpandUp(params) {
1580
1030
  const { r, bounds, blockers, maxAspect } = params;
1581
1031
  let minY = bounds.y;
1582
1032
  for (const b of blockers) {
1583
- const horizOverlaps = r.x + r.width > b.x + EPS && b.x + b.width > r.x + EPS;
1033
+ const horizOverlaps = r.x + r.width > b.x + EPS4 && b.x + b.width > r.x + EPS4;
1584
1034
  if (horizOverlaps) {
1585
1035
  if (lte(b.y + b.height, r.y)) {
1586
1036
  minY = Math.max(minY, b.y + b.height);
1587
- } else if (b.y < r.y + EPS && b.y + b.height > r.y - EPS) {
1037
+ } else if (b.y < r.y + EPS4 && b.y + b.height > r.y - EPS4) {
1588
1038
  return 0;
1589
1039
  }
1590
1040
  }
@@ -1609,7 +1059,7 @@ var toQueryRect = (params) => {
1609
1059
  const minY = Math.max(bounds.y, rect.y);
1610
1060
  const maxX = Math.min(bounds.x + bounds.width, rect.x + rect.width);
1611
1061
  const maxY = Math.min(bounds.y + bounds.height, rect.y + rect.height);
1612
- if (maxX <= minX + EPS || maxY <= minY + EPS) return null;
1062
+ if (maxX <= minX + EPS4 || maxY <= minY + EPS4) return null;
1613
1063
  return { minX, minY, maxX, maxY };
1614
1064
  };
1615
1065
  function expandRectFromSeed(params) {
@@ -1715,7 +1165,7 @@ function expandRectFromSeed(params) {
1715
1165
  improved = true;
1716
1166
  }
1717
1167
  }
1718
- if (r.width + EPS >= minReq.width && r.height + EPS >= minReq.height) {
1168
+ if (r.width + EPS4 >= minReq.width && r.height + EPS4 >= minReq.height) {
1719
1169
  const area = r.width * r.height;
1720
1170
  if (area > bestArea) {
1721
1171
  best = quantizeRect(r);
@@ -1809,7 +1259,7 @@ function computeCandidates3D(params) {
1809
1259
  ]);
1810
1260
  for (let x = bounds.x; x < bounds.x + bounds.width; x += gridSize) {
1811
1261
  for (let y = bounds.y; y < bounds.y + bounds.height; y += gridSize) {
1812
- 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) {
1262
+ if (Math.abs(x - bounds.x) < EPS4 || Math.abs(y - bounds.y) < EPS4 || x > bounds.x + bounds.width - gridSize - EPS4 || y > bounds.y + bounds.height - gridSize - EPS4) {
1813
1263
  continue;
1814
1264
  }
1815
1265
  if (isFullyOccupiedAtPoint({
@@ -1881,7 +1331,7 @@ function computeUncoveredSegments(params) {
1881
1331
  const s = quantize3(i.start);
1882
1332
  const e = quantize3(i.end);
1883
1333
  return { start: Math.min(s, e), end: Math.max(s, e) };
1884
- }).filter((i) => i.end > i.start + EPS);
1334
+ }).filter((i) => i.end > i.start + EPS4);
1885
1335
  if (normalizedIntervals.length === 0) {
1886
1336
  const center = (lineStartQ + lineEndQ) / 2;
1887
1337
  return [{ start: lineStartQ, end: lineEndQ, center }];
@@ -1891,7 +1341,7 @@ function computeUncoveredSegments(params) {
1891
1341
  let current = { ...sorted[0] };
1892
1342
  for (let i = 1; i < sorted.length; i++) {
1893
1343
  const interval = sorted[i];
1894
- if (interval.start <= current.end + EPS) {
1344
+ if (interval.start <= current.end + EPS4) {
1895
1345
  current.end = Math.max(current.end, interval.end);
1896
1346
  } else {
1897
1347
  merged.push(current);
@@ -1900,7 +1350,7 @@ function computeUncoveredSegments(params) {
1900
1350
  }
1901
1351
  merged.push(current);
1902
1352
  const uncovered = [];
1903
- if (merged[0].start > lineStartQ + EPS) {
1353
+ if (merged[0].start > lineStartQ + EPS4) {
1904
1354
  const start = lineStartQ;
1905
1355
  const end = merged[0].start;
1906
1356
  if (end - start >= minSegmentLength) {
@@ -1914,7 +1364,7 @@ function computeUncoveredSegments(params) {
1914
1364
  uncovered.push({ start, end, center: (start + end) / 2 });
1915
1365
  }
1916
1366
  }
1917
- if (merged[merged.length - 1].end < lineEndQ - EPS) {
1367
+ if (merged[merged.length - 1].end < lineEndQ - EPS4) {
1918
1368
  const start = merged[merged.length - 1].end;
1919
1369
  const end = lineEndQ;
1920
1370
  if (end - start >= minSegmentLength) {
@@ -1933,7 +1383,7 @@ function computeEdgeCandidates3D(params) {
1933
1383
  hardPlacedByLayer
1934
1384
  } = params;
1935
1385
  const out = [];
1936
- const \u03B4 = Math.max(minSize * 0.15, EPS * 3);
1386
+ const \u03B4 = Math.max(minSize * 0.15, EPS4 * 3);
1937
1387
  const dedup = /* @__PURE__ */ new Set();
1938
1388
  const hardRectsByLayer = Array.from(
1939
1389
  { length: layerCount },
@@ -1957,7 +1407,7 @@ function computeEdgeCandidates3D(params) {
1957
1407
  const { z } = p;
1958
1408
  const x = qx;
1959
1409
  const y = qy;
1960
- if (x < bounds.x + EPS || y < bounds.y + EPS || x > bounds.x + bounds.width - EPS || y > bounds.y + bounds.height - EPS)
1410
+ if (x < bounds.x + EPS4 || y < bounds.y + EPS4 || x > bounds.x + bounds.width - EPS4 || y > bounds.y + bounds.height - EPS4)
1961
1411
  return;
1962
1412
  if (fullyOcc({ x, y })) return;
1963
1413
  const hard = hardRectsByLayer[z] ?? [];
@@ -2094,7 +1544,7 @@ function computeEdgeCandidates3D(params) {
2094
1544
  }
2095
1545
  for (const b of blockers) {
2096
1546
  const obLeftX = b.x - \u03B4;
2097
- if (obLeftX > bounds.x + EPS && obLeftX < bounds.x + bounds.width - EPS) {
1547
+ if (obLeftX > bounds.x + EPS4 && obLeftX < bounds.x + bounds.width - EPS4) {
2098
1548
  const obLeftCovering = blockers.filter(
2099
1549
  (bl) => bl !== b && bl.x <= obLeftX && bl.x + bl.width >= obLeftX
2100
1550
  ).map((bl) => ({
@@ -2112,7 +1562,7 @@ function computeEdgeCandidates3D(params) {
2112
1562
  }
2113
1563
  }
2114
1564
  const obRightX = b.x + b.width + \u03B4;
2115
- if (obRightX > bounds.x + EPS && obRightX < bounds.x + bounds.width - EPS) {
1565
+ if (obRightX > bounds.x + EPS4 && obRightX < bounds.x + bounds.width - EPS4) {
2116
1566
  const obRightCovering = blockers.filter(
2117
1567
  (bl) => bl !== b && bl.x <= obRightX && bl.x + bl.width >= obRightX
2118
1568
  ).map((bl) => ({
@@ -2130,7 +1580,7 @@ function computeEdgeCandidates3D(params) {
2130
1580
  }
2131
1581
  }
2132
1582
  const obTopY = b.y - \u03B4;
2133
- if (obTopY > bounds.y + EPS && obTopY < bounds.y + bounds.height - EPS) {
1583
+ if (obTopY > bounds.y + EPS4 && obTopY < bounds.y + bounds.height - EPS4) {
2134
1584
  const obTopCovering = blockers.filter(
2135
1585
  (bl) => bl !== b && bl.y <= obTopY && bl.y + bl.height >= obTopY
2136
1586
  ).map((bl) => ({
@@ -2148,7 +1598,7 @@ function computeEdgeCandidates3D(params) {
2148
1598
  }
2149
1599
  }
2150
1600
  const obBottomY = b.y + b.height + \u03B4;
2151
- if (obBottomY > bounds.y + EPS && obBottomY < bounds.y + bounds.height - EPS) {
1601
+ if (obBottomY > bounds.y + EPS4 && obBottomY < bounds.y + bounds.height - EPS4) {
2152
1602
  const obBottomCovering = blockers.filter(
2153
1603
  (bl) => bl !== b && bl.y <= obBottomY && bl.y + bl.height >= obBottomY
2154
1604
  ).map((bl) => ({
@@ -2223,12 +1673,12 @@ function resizeSoftOverlaps(params, newIndex) {
2223
1673
  params.options.minMulti.height
2224
1674
  );
2225
1675
  for (const p of parts) {
2226
- if (p.width + EPS >= minW && p.height + EPS >= minH) {
1676
+ if (p.width + EPS4 >= minW && p.height + EPS4 >= minH) {
2227
1677
  toAdd.push({ rect: p, zLayers: sharedZ.slice() });
2228
1678
  }
2229
1679
  }
2230
1680
  }
2231
- const sameRect3 = (a, b) => a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY;
1681
+ const sameRect = (a, b) => a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY;
2232
1682
  removeIdx.sort((a, b) => b - a).forEach((idx) => {
2233
1683
  const rem = params.placed.splice(idx, 1)[0];
2234
1684
  if (params.placedIndexByLayer) {
@@ -2237,7 +1687,7 @@ function resizeSoftOverlaps(params, newIndex) {
2237
1687
  if (tree)
2238
1688
  tree.remove(
2239
1689
  rectToTree(rem.rect, { zLayers: rem.zLayers }),
2240
- sameRect3
1690
+ sameRect
2241
1691
  );
2242
1692
  }
2243
1693
  }
@@ -2255,9 +1705,23 @@ function resizeSoftOverlaps(params, newIndex) {
2255
1705
  }
2256
1706
  }
2257
1707
 
1708
+ // lib/utils/getColorForZLayer.ts
1709
+ var getColorForZLayer = (zLayers) => {
1710
+ const minZ = Math.min(...zLayers);
1711
+ const colors = [
1712
+ { fill: "#dbeafe", stroke: "#3b82f6" },
1713
+ { fill: "#fef3c7", stroke: "#f59e0b" },
1714
+ { fill: "#d1fae5", stroke: "#10b981" },
1715
+ { fill: "#e9d5ff", stroke: "#a855f7" },
1716
+ { fill: "#fed7aa", stroke: "#f97316" },
1717
+ { fill: "#fecaca", stroke: "#ef4444" }
1718
+ ];
1719
+ return colors[minZ % colors.length];
1720
+ };
1721
+
2258
1722
  // lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts
2259
1723
  import RBush3 from "rbush";
2260
- var RectDiffSeedingSolver = class extends BaseSolver5 {
1724
+ var RectDiffSeedingSolver = class extends BaseSolver3 {
2261
1725
  constructor(input) {
2262
1726
  super();
2263
1727
  this.input = input;
@@ -2616,7 +2080,7 @@ z:${placement.zLayers.join(",")}`
2616
2080
  };
2617
2081
 
2618
2082
  // lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts
2619
- import { BaseSolver as BaseSolver6 } from "@tscircuit/solver-utils";
2083
+ import { BaseSolver as BaseSolver4 } from "@tscircuit/solver-utils";
2620
2084
 
2621
2085
  // lib/utils/finalizeRects.ts
2622
2086
  function finalizeRects(params) {
@@ -2688,7 +2152,7 @@ import RBush4 from "rbush";
2688
2152
  var sameTreeRect = (a, b) => a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY;
2689
2153
 
2690
2154
  // lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts
2691
- var RectDiffExpansionSolver = class extends BaseSolver6 {
2155
+ var RectDiffExpansionSolver = class extends BaseSolver4 {
2692
2156
  constructor(input) {
2693
2157
  super();
2694
2158
  this.input = input;
@@ -2834,6 +2298,19 @@ import "rbush";
2834
2298
 
2835
2299
  // lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts
2836
2300
  import RBush5 from "rbush";
2301
+
2302
+ // lib/utils/padRect.ts
2303
+ var padRect = (rect, clearance) => {
2304
+ if (!clearance || clearance <= 0) return rect;
2305
+ return {
2306
+ x: rect.x - clearance,
2307
+ y: rect.y - clearance,
2308
+ width: rect.width + 2 * clearance,
2309
+ height: rect.height + 2 * clearance
2310
+ };
2311
+ };
2312
+
2313
+ // lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts
2837
2314
  var buildObstacleIndexesByLayer = (params) => {
2838
2315
  const { srj, boardVoidRects, obstacleClearance } = params;
2839
2316
  const { layerNames, zIndexByName } = buildZIndexMap({
@@ -3177,8 +2654,6 @@ import { mergeGraphics as mergeGraphics2 } from "graphics-debug";
3177
2654
  var RectDiffPipeline = class extends BasePipelineSolver3 {
3178
2655
  rectDiffGridSolverPipeline;
3179
2656
  gapFillSolver;
3180
- outerLayerContainmentMergeSolver;
3181
- adjacentLayerContainmentMergeSolver;
3182
2657
  boardVoidRects;
3183
2658
  zIndexByName;
3184
2659
  layerNames;
@@ -3214,28 +2689,6 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
3214
2689
  }
3215
2690
  }
3216
2691
  ]
3217
- ),
3218
- definePipelineStep3(
3219
- "outerLayerContainmentMergeSolver",
3220
- OuterLayerContainmentMergeSolver,
3221
- (rectDiffPipeline) => [
3222
- {
3223
- meshNodes: rectDiffPipeline.gapFillSolver?.getOutput().outputNodes ?? rectDiffPipeline.rectDiffGridSolverPipeline?.getOutput().meshNodes ?? [],
3224
- simpleRouteJson: rectDiffPipeline.inputProblem.simpleRouteJson,
3225
- zIndexByName: rectDiffPipeline.zIndexByName ?? /* @__PURE__ */ new Map(),
3226
- obstacleClearance: rectDiffPipeline.inputProblem.obstacleClearance
3227
- }
3228
- ]
3229
- ),
3230
- definePipelineStep3(
3231
- "adjacentLayerContainmentMergeSolver",
3232
- AdjacentLayerContainmentMergeSolver,
3233
- (rectDiffPipeline) => [
3234
- {
3235
- meshNodes: rectDiffPipeline.outerLayerContainmentMergeSolver?.getOutput().outputNodes ?? rectDiffPipeline.gapFillSolver?.getOutput().outputNodes ?? rectDiffPipeline.rectDiffGridSolverPipeline?.getOutput().meshNodes ?? [],
3236
- simpleRouteJson: rectDiffPipeline.inputProblem.simpleRouteJson
3237
- }
3238
- ]
3239
2692
  )
3240
2693
  ];
3241
2694
  _setup() {
@@ -3261,14 +2714,6 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
3261
2714
  return [this.inputProblem];
3262
2715
  }
3263
2716
  getOutput() {
3264
- const adjacentLayerMergeOutput = this.adjacentLayerContainmentMergeSolver?.getOutput();
3265
- if (adjacentLayerMergeOutput) {
3266
- return { meshNodes: adjacentLayerMergeOutput.outputNodes };
3267
- }
3268
- const outerLayerMergeOutput = this.outerLayerContainmentMergeSolver?.getOutput();
3269
- if (outerLayerMergeOutput) {
3270
- return { meshNodes: outerLayerMergeOutput.outputNodes };
3271
- }
3272
2717
  const gapFillOutput = this.gapFillSolver?.getOutput();
3273
2718
  if (gapFillOutput) {
3274
2719
  return { meshNodes: gapFillOutput.outputNodes };