@tscircuit/rectdiff 0.0.27 → 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 (46) 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 -774
  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 -311
  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/solver/bugreport49-634662/__snapshots__/bugreport49-634662.snap.svg +0 -44
  44. package/tests/solver/bugreport49-634662/bugreport49-634662.test.ts +0 -134
  45. package/tests/solver/bugreport50-multi-support-layer-merge/__snapshots__/bugreport50-multi-support-layer-merge.snap.svg +0 -44
  46. 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,312 +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 cloneNode2 = (node) => ({
1150
- ...node,
1151
- center: { ...node.center },
1152
- availableZ: [...node.availableZ]
1153
- });
1154
- var cloneNodeWithRect2 = (node, rect, capacityMeshNodeId) => ({
1155
- ...node,
1156
- capacityMeshNodeId,
1157
- center: {
1158
- x: rect.x + rect.width / 2,
1159
- y: rect.y + rect.height / 2
1160
- },
1161
- width: rect.width,
1162
- height: rect.height,
1163
- availableZ: [...node.availableZ],
1164
- layer: `z${node.availableZ.join(",")}`
1165
- });
1166
- var isFreeNode2 = (node) => !node._containsObstacle && !node._containsTarget;
1167
- var isSingletonOuterNode = (node, outerZ) => node.availableZ.length === 1 && node.availableZ[0] === outerZ;
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) => {
1170
- let remaining = [target];
1171
- for (const cutter of cutters) {
1172
- if (remaining.length === 0) return remaining;
1173
- const nextRemaining = [];
1174
- for (const piece of remaining) {
1175
- nextRemaining.push(...subtractRect2D(piece, cutter));
1176
- }
1177
- remaining = nextRemaining;
1178
- }
1179
- return remaining;
1180
- };
1181
- var isFullyCoveredByRects2 = (target, coveringRects) => {
1182
- return subtractRects2(target, coveringRects).length === 0;
1183
- };
1184
- var OuterLayerContainmentMergeSolver = class extends BaseSolver4 {
1185
- constructor(input) {
1186
- super();
1187
- this.input = input;
1188
- }
1189
- input;
1190
- outputNodes = [];
1191
- promotedNodeIds = /* @__PURE__ */ new Set();
1192
- residualNodeIds = /* @__PURE__ */ new Set();
1193
- _setup() {
1194
- this.outputNodes = this.input.meshNodes.map(cloneNode2);
1195
- this.promotedNodeIds.clear();
1196
- this.residualNodeIds.clear();
1197
- }
1198
- _step() {
1199
- this.outputNodes = this.processOuterLayerContainmentMerges();
1200
- this.solved = true;
1201
- }
1202
- processOuterLayerContainmentMerges() {
1203
- const srj = this.input.simpleRouteJson;
1204
- const layerCount = Math.max(1, srj.layerCount || 1);
1205
- if (layerCount < 3) {
1206
- return this.input.meshNodes.map(cloneNode2);
1207
- }
1208
- const topZ = 0;
1209
- const bottomZ = layerCount - 1;
1210
- const viaMinSize = Math.max(srj.minViaDiameter ?? 0, srj.minTraceWidth || 0);
1211
- const originalNodes = this.input.meshNodes.map(cloneNode2);
1212
- const obstaclesByLayer = this.buildObstaclesByLayer(layerCount);
1213
- const mutableOuterNodes = originalNodes.filter(
1214
- (node) => isFreeNode2(node) && (isSingletonOuterNode(node, topZ) || isSingletonOuterNode(node, bottomZ))
1215
- );
1216
- const immutableNodes = originalNodes.filter(
1217
- (node) => !mutableOuterNodes.includes(node)
1218
- );
1219
- const freeSupportRectsByOuterLayer = /* @__PURE__ */ new Map();
1220
- freeSupportRectsByOuterLayer.set(
1221
- topZ,
1222
- originalNodes.filter((node) => isFreeNode2(node) && node.availableZ.includes(topZ)).map(nodeToRect2)
1223
- );
1224
- freeSupportRectsByOuterLayer.set(
1225
- bottomZ,
1226
- originalNodes.filter((node) => isFreeNode2(node) && node.availableZ.includes(bottomZ)).map(nodeToRect2)
1227
- );
1228
- const promotedNodes = [];
1229
- const promotedRects = [];
1230
- const candidateNodes = mutableOuterNodes.filter(
1231
- (node) => node.width + EPS >= viaMinSize && node.height + EPS >= viaMinSize
1232
- ).sort((a, b) => rectArea2(nodeToRect2(b)) - rectArea2(nodeToRect2(a)));
1233
- for (const candidate of candidateNodes) {
1234
- const candidateZ = candidate.availableZ[0];
1235
- const oppositeZ = candidateZ === topZ ? bottomZ : topZ;
1236
- const candidateRect = nodeToRect2(candidate);
1237
- const oppositeSupportRects = freeSupportRectsByOuterLayer.get(oppositeZ) ?? [];
1238
- if (!this.isTransitCompatibleAcrossIntermediateLayers({
1239
- rect: candidateRect,
1240
- fromZ: candidateZ,
1241
- toZ: oppositeZ,
1242
- obstaclesByLayer
1243
- })) {
1244
- continue;
1245
- }
1246
- if (!isFullyCoveredByRects2(candidateRect, oppositeSupportRects)) {
1247
- continue;
1248
- }
1249
- promotedNodes.push({
1250
- ...candidate,
1251
- availableZ: [topZ, bottomZ],
1252
- layer: `z${topZ},${bottomZ}`
1253
- });
1254
- promotedRects.push(candidateRect);
1255
- this.promotedNodeIds.add(candidate.capacityMeshNodeId);
1256
- }
1257
- let nextResidualId = 0;
1258
- const residualNodes = [];
1259
- for (const node of mutableOuterNodes) {
1260
- if (this.promotedNodeIds.has(node.capacityMeshNodeId)) {
1261
- continue;
1262
- }
1263
- const nodeRect = nodeToRect2(node);
1264
- const remainingPieces = subtractRects2(nodeRect, promotedRects);
1265
- if (remainingPieces.length === 1 && sameRect2(remainingPieces[0], nodeRect)) {
1266
- residualNodes.push(node);
1267
- continue;
1268
- }
1269
- for (const piece of remainingPieces) {
1270
- const residualNode = cloneNodeWithRect2(
1271
- node,
1272
- piece,
1273
- `${node.capacityMeshNodeId}-outer-merge-${nextResidualId++}`
1274
- );
1275
- residualNodes.push(residualNode);
1276
- this.residualNodeIds.add(residualNode.capacityMeshNodeId);
1277
- }
1278
- }
1279
- return [...immutableNodes, ...promotedNodes, ...residualNodes];
1280
- }
1281
- buildObstaclesByLayer(layerCount) {
1282
- const out = Array.from(
1283
- { length: layerCount },
1284
- () => []
1285
- );
1286
- for (const obstacle of this.input.simpleRouteJson.obstacles ?? []) {
1287
- const baseRect = obstacleToXYRect(obstacle);
1288
- if (!baseRect) continue;
1289
- const rect = padRect(baseRect, this.input.obstacleClearance ?? 0);
1290
- const zLayers = obstacleZs(obstacle, this.input.zIndexByName);
1291
- for (const z of zLayers) {
1292
- if (z < 0 || z >= layerCount) continue;
1293
- out[z].push({ obstacle, rect });
1294
- }
1295
- }
1296
- 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 });
1297
721
  }
1298
- isTransitCompatibleAcrossIntermediateLayers(params) {
1299
- const { rect, fromZ, toZ, obstaclesByLayer } = params;
1300
- const lo = Math.min(fromZ, toZ);
1301
- const hi = Math.max(fromZ, toZ);
1302
- if (hi - lo < 2) return false;
1303
- for (let z = lo + 1; z < hi; z++) {
1304
- const overlapping = (obstaclesByLayer[z] ?? []).filter(
1305
- (entry) => overlaps(entry.rect, rect)
1306
- );
1307
- if (overlapping.length === 0) return false;
1308
- const nonCopperOverlap = overlapping.some(
1309
- (entry) => !entry.obstacle.isCopperPour
1310
- );
1311
- if (nonCopperOverlap) return false;
1312
- const copperRects = overlapping.filter((entry) => entry.obstacle.isCopperPour).map((entry) => entry.rect);
1313
- if (!isFullyCoveredByRects2(rect, copperRects)) {
1314
- return false;
1315
- }
1316
- }
1317
- 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 });
1318
724
  }
1319
- getOutput() {
1320
- 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 });
1321
728
  }
1322
- visualize() {
1323
- return {
1324
- title: "OuterLayerContainmentMergeSolver",
1325
- coordinateSystem: "cartesian",
1326
- rects: this.outputNodes.map((node) => {
1327
- const colors = getColorForZLayer(node.availableZ);
1328
- const isPromoted = this.promotedNodeIds.has(node.capacityMeshNodeId);
1329
- const isResidual = this.residualNodeIds.has(node.capacityMeshNodeId);
1330
- return {
1331
- center: node.center,
1332
- width: node.width,
1333
- height: node.height,
1334
- stroke: isPromoted ? "rgba(22, 163, 74, 0.95)" : isResidual ? "rgba(37, 99, 235, 0.95)" : colors.stroke,
1335
- 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,
1336
- layer: `z${node.availableZ.join(",")}`,
1337
- label: [
1338
- `node ${node.capacityMeshNodeId}`,
1339
- `z:${node.availableZ.join(",")}`
1340
- ].join("\n")
1341
- };
1342
- }),
1343
- points: [],
1344
- lines: [],
1345
- texts: []
1346
- };
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 });
1347
731
  }
1348
- };
1349
-
1350
- // lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts
1351
- import {
1352
- BasePipelineSolver as BasePipelineSolver2,
1353
- definePipelineStep as definePipelineStep2
1354
- } from "@tscircuit/solver-utils";
1355
-
1356
- // lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts
1357
- import { BaseSolver as BaseSolver5 } from "@tscircuit/solver-utils";
732
+ return out.filter((r) => r.width > EPS4 && r.height > EPS4);
733
+ }
1358
734
 
1359
735
  // lib/solvers/RectDiffSeedingSolver/isPointInPolygon.ts
1360
736
  function isPointInPolygon(p, polygon) {
@@ -1417,7 +793,7 @@ function computeInverseRects(bounds, polygon) {
1417
793
  }
1418
794
  const finalRects = [];
1419
795
  rawRects.sort((a, b) => {
1420
- 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;
1421
797
  return a.x - b.x;
1422
798
  });
1423
799
  let current = null;
@@ -1426,9 +802,9 @@ function computeInverseRects(bounds, polygon) {
1426
802
  current = r;
1427
803
  continue;
1428
804
  }
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;
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;
1432
808
  if (sameY && sameHeight && touchesX) {
1433
809
  current.width += r.width;
1434
810
  } else {
@@ -1438,7 +814,7 @@ function computeInverseRects(bounds, polygon) {
1438
814
  }
1439
815
  if (current) finalRects.push(current);
1440
816
  finalRects.sort((a, b) => {
1441
- 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;
1442
818
  return a.y - b.y;
1443
819
  });
1444
820
  const mergedVertical = [];
@@ -1448,9 +824,9 @@ function computeInverseRects(bounds, polygon) {
1448
824
  current = r;
1449
825
  continue;
1450
826
  }
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;
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;
1454
830
  if (sameX && sameWidth && touchesY) {
1455
831
  current.height += r.height;
1456
832
  } else {
@@ -1462,6 +838,81 @@ function computeInverseRects(bounds, polygon) {
1462
838
  return mergedVertical;
1463
839
  }
1464
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
+
1465
916
  // lib/utils/isSelfRect.ts
1466
917
  var EPS5 = 1e-9;
1467
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;
@@ -1516,11 +967,11 @@ function maxExpandRight(params) {
1516
967
  const { r, bounds, blockers, maxAspect } = params;
1517
968
  let maxWidth = bounds.x + bounds.width - r.x;
1518
969
  for (const b of blockers) {
1519
- 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;
1520
971
  if (verticallyOverlaps) {
1521
972
  if (gte(b.x, r.x + r.width)) {
1522
973
  maxWidth = Math.min(maxWidth, b.x - r.x);
1523
- } 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) {
1524
975
  return 0;
1525
976
  }
1526
977
  }
@@ -1537,11 +988,11 @@ function maxExpandDown(params) {
1537
988
  const { r, bounds, blockers, maxAspect } = params;
1538
989
  let maxHeight = bounds.y + bounds.height - r.y;
1539
990
  for (const b of blockers) {
1540
- 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;
1541
992
  if (horizOverlaps) {
1542
993
  if (gte(b.y, r.y + r.height)) {
1543
994
  maxHeight = Math.min(maxHeight, b.y - r.y);
1544
- } 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) {
1545
996
  return 0;
1546
997
  }
1547
998
  }
@@ -1558,11 +1009,11 @@ function maxExpandLeft(params) {
1558
1009
  const { r, bounds, blockers, maxAspect } = params;
1559
1010
  let minX = bounds.x;
1560
1011
  for (const b of blockers) {
1561
- 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;
1562
1013
  if (verticallyOverlaps) {
1563
1014
  if (lte(b.x + b.width, r.x)) {
1564
1015
  minX = Math.max(minX, b.x + b.width);
1565
- } 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) {
1566
1017
  return 0;
1567
1018
  }
1568
1019
  }
@@ -1579,11 +1030,11 @@ function maxExpandUp(params) {
1579
1030
  const { r, bounds, blockers, maxAspect } = params;
1580
1031
  let minY = bounds.y;
1581
1032
  for (const b of blockers) {
1582
- 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;
1583
1034
  if (horizOverlaps) {
1584
1035
  if (lte(b.y + b.height, r.y)) {
1585
1036
  minY = Math.max(minY, b.y + b.height);
1586
- } 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) {
1587
1038
  return 0;
1588
1039
  }
1589
1040
  }
@@ -1608,7 +1059,7 @@ var toQueryRect = (params) => {
1608
1059
  const minY = Math.max(bounds.y, rect.y);
1609
1060
  const maxX = Math.min(bounds.x + bounds.width, rect.x + rect.width);
1610
1061
  const maxY = Math.min(bounds.y + bounds.height, rect.y + rect.height);
1611
- if (maxX <= minX + EPS || maxY <= minY + EPS) return null;
1062
+ if (maxX <= minX + EPS4 || maxY <= minY + EPS4) return null;
1612
1063
  return { minX, minY, maxX, maxY };
1613
1064
  };
1614
1065
  function expandRectFromSeed(params) {
@@ -1714,7 +1165,7 @@ function expandRectFromSeed(params) {
1714
1165
  improved = true;
1715
1166
  }
1716
1167
  }
1717
- if (r.width + EPS >= minReq.width && r.height + EPS >= minReq.height) {
1168
+ if (r.width + EPS4 >= minReq.width && r.height + EPS4 >= minReq.height) {
1718
1169
  const area = r.width * r.height;
1719
1170
  if (area > bestArea) {
1720
1171
  best = quantizeRect(r);
@@ -1808,7 +1259,7 @@ function computeCandidates3D(params) {
1808
1259
  ]);
1809
1260
  for (let x = bounds.x; x < bounds.x + bounds.width; x += gridSize) {
1810
1261
  for (let y = bounds.y; y < bounds.y + bounds.height; y += gridSize) {
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) {
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) {
1812
1263
  continue;
1813
1264
  }
1814
1265
  if (isFullyOccupiedAtPoint({
@@ -1880,7 +1331,7 @@ function computeUncoveredSegments(params) {
1880
1331
  const s = quantize3(i.start);
1881
1332
  const e = quantize3(i.end);
1882
1333
  return { start: Math.min(s, e), end: Math.max(s, e) };
1883
- }).filter((i) => i.end > i.start + EPS);
1334
+ }).filter((i) => i.end > i.start + EPS4);
1884
1335
  if (normalizedIntervals.length === 0) {
1885
1336
  const center = (lineStartQ + lineEndQ) / 2;
1886
1337
  return [{ start: lineStartQ, end: lineEndQ, center }];
@@ -1890,7 +1341,7 @@ function computeUncoveredSegments(params) {
1890
1341
  let current = { ...sorted[0] };
1891
1342
  for (let i = 1; i < sorted.length; i++) {
1892
1343
  const interval = sorted[i];
1893
- if (interval.start <= current.end + EPS) {
1344
+ if (interval.start <= current.end + EPS4) {
1894
1345
  current.end = Math.max(current.end, interval.end);
1895
1346
  } else {
1896
1347
  merged.push(current);
@@ -1899,7 +1350,7 @@ function computeUncoveredSegments(params) {
1899
1350
  }
1900
1351
  merged.push(current);
1901
1352
  const uncovered = [];
1902
- if (merged[0].start > lineStartQ + EPS) {
1353
+ if (merged[0].start > lineStartQ + EPS4) {
1903
1354
  const start = lineStartQ;
1904
1355
  const end = merged[0].start;
1905
1356
  if (end - start >= minSegmentLength) {
@@ -1913,7 +1364,7 @@ function computeUncoveredSegments(params) {
1913
1364
  uncovered.push({ start, end, center: (start + end) / 2 });
1914
1365
  }
1915
1366
  }
1916
- if (merged[merged.length - 1].end < lineEndQ - EPS) {
1367
+ if (merged[merged.length - 1].end < lineEndQ - EPS4) {
1917
1368
  const start = merged[merged.length - 1].end;
1918
1369
  const end = lineEndQ;
1919
1370
  if (end - start >= minSegmentLength) {
@@ -1932,7 +1383,7 @@ function computeEdgeCandidates3D(params) {
1932
1383
  hardPlacedByLayer
1933
1384
  } = params;
1934
1385
  const out = [];
1935
- const \u03B4 = Math.max(minSize * 0.15, EPS * 3);
1386
+ const \u03B4 = Math.max(minSize * 0.15, EPS4 * 3);
1936
1387
  const dedup = /* @__PURE__ */ new Set();
1937
1388
  const hardRectsByLayer = Array.from(
1938
1389
  { length: layerCount },
@@ -1956,7 +1407,7 @@ function computeEdgeCandidates3D(params) {
1956
1407
  const { z } = p;
1957
1408
  const x = qx;
1958
1409
  const y = qy;
1959
- 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)
1960
1411
  return;
1961
1412
  if (fullyOcc({ x, y })) return;
1962
1413
  const hard = hardRectsByLayer[z] ?? [];
@@ -2093,7 +1544,7 @@ function computeEdgeCandidates3D(params) {
2093
1544
  }
2094
1545
  for (const b of blockers) {
2095
1546
  const obLeftX = b.x - \u03B4;
2096
- if (obLeftX > bounds.x + EPS && obLeftX < bounds.x + bounds.width - EPS) {
1547
+ if (obLeftX > bounds.x + EPS4 && obLeftX < bounds.x + bounds.width - EPS4) {
2097
1548
  const obLeftCovering = blockers.filter(
2098
1549
  (bl) => bl !== b && bl.x <= obLeftX && bl.x + bl.width >= obLeftX
2099
1550
  ).map((bl) => ({
@@ -2111,7 +1562,7 @@ function computeEdgeCandidates3D(params) {
2111
1562
  }
2112
1563
  }
2113
1564
  const obRightX = b.x + b.width + \u03B4;
2114
- if (obRightX > bounds.x + EPS && obRightX < bounds.x + bounds.width - EPS) {
1565
+ if (obRightX > bounds.x + EPS4 && obRightX < bounds.x + bounds.width - EPS4) {
2115
1566
  const obRightCovering = blockers.filter(
2116
1567
  (bl) => bl !== b && bl.x <= obRightX && bl.x + bl.width >= obRightX
2117
1568
  ).map((bl) => ({
@@ -2129,7 +1580,7 @@ function computeEdgeCandidates3D(params) {
2129
1580
  }
2130
1581
  }
2131
1582
  const obTopY = b.y - \u03B4;
2132
- if (obTopY > bounds.y + EPS && obTopY < bounds.y + bounds.height - EPS) {
1583
+ if (obTopY > bounds.y + EPS4 && obTopY < bounds.y + bounds.height - EPS4) {
2133
1584
  const obTopCovering = blockers.filter(
2134
1585
  (bl) => bl !== b && bl.y <= obTopY && bl.y + bl.height >= obTopY
2135
1586
  ).map((bl) => ({
@@ -2147,7 +1598,7 @@ function computeEdgeCandidates3D(params) {
2147
1598
  }
2148
1599
  }
2149
1600
  const obBottomY = b.y + b.height + \u03B4;
2150
- if (obBottomY > bounds.y + EPS && obBottomY < bounds.y + bounds.height - EPS) {
1601
+ if (obBottomY > bounds.y + EPS4 && obBottomY < bounds.y + bounds.height - EPS4) {
2151
1602
  const obBottomCovering = blockers.filter(
2152
1603
  (bl) => bl !== b && bl.y <= obBottomY && bl.y + bl.height >= obBottomY
2153
1604
  ).map((bl) => ({
@@ -2222,12 +1673,12 @@ function resizeSoftOverlaps(params, newIndex) {
2222
1673
  params.options.minMulti.height
2223
1674
  );
2224
1675
  for (const p of parts) {
2225
- if (p.width + EPS >= minW && p.height + EPS >= minH) {
1676
+ if (p.width + EPS4 >= minW && p.height + EPS4 >= minH) {
2226
1677
  toAdd.push({ rect: p, zLayers: sharedZ.slice() });
2227
1678
  }
2228
1679
  }
2229
1680
  }
2230
- 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;
2231
1682
  removeIdx.sort((a, b) => b - a).forEach((idx) => {
2232
1683
  const rem = params.placed.splice(idx, 1)[0];
2233
1684
  if (params.placedIndexByLayer) {
@@ -2236,7 +1687,7 @@ function resizeSoftOverlaps(params, newIndex) {
2236
1687
  if (tree)
2237
1688
  tree.remove(
2238
1689
  rectToTree(rem.rect, { zLayers: rem.zLayers }),
2239
- sameRect3
1690
+ sameRect
2240
1691
  );
2241
1692
  }
2242
1693
  }
@@ -2254,9 +1705,23 @@ function resizeSoftOverlaps(params, newIndex) {
2254
1705
  }
2255
1706
  }
2256
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
+
2257
1722
  // lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts
2258
1723
  import RBush3 from "rbush";
2259
- var RectDiffSeedingSolver = class extends BaseSolver5 {
1724
+ var RectDiffSeedingSolver = class extends BaseSolver3 {
2260
1725
  constructor(input) {
2261
1726
  super();
2262
1727
  this.input = input;
@@ -2615,7 +2080,7 @@ z:${placement.zLayers.join(",")}`
2615
2080
  };
2616
2081
 
2617
2082
  // lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts
2618
- import { BaseSolver as BaseSolver6 } from "@tscircuit/solver-utils";
2083
+ import { BaseSolver as BaseSolver4 } from "@tscircuit/solver-utils";
2619
2084
 
2620
2085
  // lib/utils/finalizeRects.ts
2621
2086
  function finalizeRects(params) {
@@ -2687,7 +2152,7 @@ import RBush4 from "rbush";
2687
2152
  var sameTreeRect = (a, b) => a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY;
2688
2153
 
2689
2154
  // lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts
2690
- var RectDiffExpansionSolver = class extends BaseSolver6 {
2155
+ var RectDiffExpansionSolver = class extends BaseSolver4 {
2691
2156
  constructor(input) {
2692
2157
  super();
2693
2158
  this.input = input;
@@ -2833,6 +2298,19 @@ import "rbush";
2833
2298
 
2834
2299
  // lib/solvers/RectDiffGridSolverPipeline/buildObstacleIndexes.ts
2835
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
2836
2314
  var buildObstacleIndexesByLayer = (params) => {
2837
2315
  const { srj, boardVoidRects, obstacleClearance } = params;
2838
2316
  const { layerNames, zIndexByName } = buildZIndexMap({
@@ -3176,8 +2654,6 @@ import { mergeGraphics as mergeGraphics2 } from "graphics-debug";
3176
2654
  var RectDiffPipeline = class extends BasePipelineSolver3 {
3177
2655
  rectDiffGridSolverPipeline;
3178
2656
  gapFillSolver;
3179
- outerLayerContainmentMergeSolver;
3180
- adjacentLayerContainmentMergeSolver;
3181
2657
  boardVoidRects;
3182
2658
  zIndexByName;
3183
2659
  layerNames;
@@ -3213,28 +2689,6 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
3213
2689
  }
3214
2690
  }
3215
2691
  ]
3216
- ),
3217
- definePipelineStep3(
3218
- "outerLayerContainmentMergeSolver",
3219
- OuterLayerContainmentMergeSolver,
3220
- (rectDiffPipeline) => [
3221
- {
3222
- meshNodes: rectDiffPipeline.gapFillSolver?.getOutput().outputNodes ?? rectDiffPipeline.rectDiffGridSolverPipeline?.getOutput().meshNodes ?? [],
3223
- simpleRouteJson: rectDiffPipeline.inputProblem.simpleRouteJson,
3224
- zIndexByName: rectDiffPipeline.zIndexByName ?? /* @__PURE__ */ new Map(),
3225
- obstacleClearance: rectDiffPipeline.inputProblem.obstacleClearance
3226
- }
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
- ]
3238
2692
  )
3239
2693
  ];
3240
2694
  _setup() {
@@ -3260,14 +2714,6 @@ var RectDiffPipeline = class extends BasePipelineSolver3 {
3260
2714
  return [this.inputProblem];
3261
2715
  }
3262
2716
  getOutput() {
3263
- const adjacentLayerMergeOutput = this.adjacentLayerContainmentMergeSolver?.getOutput();
3264
- if (adjacentLayerMergeOutput) {
3265
- return { meshNodes: adjacentLayerMergeOutput.outputNodes };
3266
- }
3267
- const outerLayerMergeOutput = this.outerLayerContainmentMergeSolver?.getOutput();
3268
- if (outerLayerMergeOutput) {
3269
- return { meshNodes: outerLayerMergeOutput.outputNodes };
3270
- }
3271
2717
  const gapFillOutput = this.gapFillSolver?.getOutput();
3272
2718
  if (gapFillOutput) {
3273
2719
  return { meshNodes: gapFillOutput.outputNodes };