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