@tscircuit/rectdiff 0.0.17 → 0.0.19
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 +3 -19
- package/dist/index.js +175 -97
- package/lib/fixtures/twoNodeExpansionFixture.ts +59 -0
- package/lib/solvers/RectDiffExpansionSolver/RectDiffExpansionSolver.ts +50 -106
- package/lib/solvers/RectDiffGridSolverPipeline/RectDiffGridSolverPipeline.ts +22 -7
- package/lib/solvers/RectDiffSeedingSolver/RectDiffSeedingSolver.ts +3 -9
- package/lib/utils/expandRectFromSeed.ts +86 -2
- package/lib/utils/isSelfRect.ts +15 -0
- package/lib/utils/searchStrip.ts +50 -0
- package/package.json +1 -1
- package/pages/features/should-expand-node.page.tsx +13 -0
- package/tests/__snapshots__/should-expand-node.snap.svg +2 -2
- package/tests/fixtures/makeSimpleRouteOutlineGraphics.ts +41 -0
- package/tests/incremental-solver.test.ts +1 -1
- package/tests/should-expand-node.test.ts +12 -18
- package/tests/solver/__snapshots__/rectDiffGridSolverPipeline.snap.svg +3 -3
- package/tests/solver/rectDiffGridSolverPipeline.test.ts +5 -1
- package/pages/gap-fill-h-shape-should-expand-node.page.tsx +0 -23
package/dist/index.d.ts
CHANGED
|
@@ -278,7 +278,7 @@ declare class RectDiffSeedingSolver extends BaseSolver {
|
|
|
278
278
|
visualize(): GraphicsObject;
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
type
|
|
281
|
+
type RectDiffExpansionSolverInput = {
|
|
282
282
|
srj: SimpleRouteJson;
|
|
283
283
|
layerNames: string[];
|
|
284
284
|
layerCount: number;
|
|
@@ -295,9 +295,6 @@ type RectDiffExpansionSolverSnapshot = {
|
|
|
295
295
|
edgeAnalysisDone: boolean;
|
|
296
296
|
totalSeedsThisGrid: number;
|
|
297
297
|
consumedSeedsThisGrid: number;
|
|
298
|
-
};
|
|
299
|
-
type RectDiffExpansionSolverInput = {
|
|
300
|
-
initialSnapshot: RectDiffExpansionSolverSnapshot;
|
|
301
298
|
obstacleIndexByLayer: Array<RBush<RTreeRect>>;
|
|
302
299
|
};
|
|
303
300
|
/**
|
|
@@ -308,21 +305,8 @@ type RectDiffExpansionSolverInput = {
|
|
|
308
305
|
*/
|
|
309
306
|
declare class RectDiffExpansionSolver extends BaseSolver {
|
|
310
307
|
private input;
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
private layerCount;
|
|
314
|
-
private bounds;
|
|
315
|
-
private options;
|
|
316
|
-
private boardVoidRects;
|
|
317
|
-
private gridIndex;
|
|
318
|
-
private candidates;
|
|
319
|
-
private placed;
|
|
320
|
-
private placedIndexByLayer;
|
|
321
|
-
private expansionIndex;
|
|
322
|
-
private edgeAnalysisDone;
|
|
323
|
-
private totalSeedsThisGrid;
|
|
324
|
-
private consumedSeedsThisGrid;
|
|
325
|
-
private _meshNodes;
|
|
308
|
+
placedIndexByLayer: Array<RBush<RTreeRect>>;
|
|
309
|
+
_meshNodes: CapacityMeshNode[];
|
|
326
310
|
constructor(input: RectDiffExpansionSolverInput);
|
|
327
311
|
_setup(): void;
|
|
328
312
|
_step(): void;
|
package/dist/index.js
CHANGED
|
@@ -897,6 +897,48 @@ function obstacleToXYRect(ob) {
|
|
|
897
897
|
return { x: ob.center.x - w / 2, y: ob.center.y - h / 2, width: w, height: h };
|
|
898
898
|
}
|
|
899
899
|
|
|
900
|
+
// lib/utils/isSelfRect.ts
|
|
901
|
+
var EPS5 = 1e-9;
|
|
902
|
+
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;
|
|
903
|
+
|
|
904
|
+
// lib/utils/searchStrip.ts
|
|
905
|
+
var searchStripRight = ({
|
|
906
|
+
rect,
|
|
907
|
+
bounds
|
|
908
|
+
}) => ({
|
|
909
|
+
x: rect.x,
|
|
910
|
+
y: rect.y,
|
|
911
|
+
width: bounds.x + bounds.width - rect.x,
|
|
912
|
+
height: rect.height
|
|
913
|
+
});
|
|
914
|
+
var searchStripDown = ({
|
|
915
|
+
rect,
|
|
916
|
+
bounds
|
|
917
|
+
}) => ({
|
|
918
|
+
x: rect.x,
|
|
919
|
+
y: rect.y,
|
|
920
|
+
width: rect.width,
|
|
921
|
+
height: bounds.y + bounds.height - rect.y
|
|
922
|
+
});
|
|
923
|
+
var searchStripLeft = ({
|
|
924
|
+
rect,
|
|
925
|
+
bounds
|
|
926
|
+
}) => ({
|
|
927
|
+
x: bounds.x,
|
|
928
|
+
y: rect.y,
|
|
929
|
+
width: rect.x - bounds.x,
|
|
930
|
+
height: rect.height
|
|
931
|
+
});
|
|
932
|
+
var searchStripUp = ({
|
|
933
|
+
rect,
|
|
934
|
+
bounds
|
|
935
|
+
}) => ({
|
|
936
|
+
x: rect.x,
|
|
937
|
+
y: bounds.y,
|
|
938
|
+
width: rect.width,
|
|
939
|
+
height: rect.y - bounds.y
|
|
940
|
+
});
|
|
941
|
+
|
|
900
942
|
// lib/utils/expandRectFromSeed.ts
|
|
901
943
|
function maxExpandRight(params) {
|
|
902
944
|
const { r, bounds, blockers, maxAspect } = params;
|
|
@@ -982,13 +1024,36 @@ function maxExpandUp(params) {
|
|
|
982
1024
|
}
|
|
983
1025
|
return Math.max(0, e);
|
|
984
1026
|
}
|
|
1027
|
+
var toRect = (tree) => ({
|
|
1028
|
+
x: tree.minX,
|
|
1029
|
+
y: tree.minY,
|
|
1030
|
+
width: tree.maxX - tree.minX,
|
|
1031
|
+
height: tree.maxY - tree.minY
|
|
1032
|
+
});
|
|
1033
|
+
var addBlocker = (params) => {
|
|
1034
|
+
const { rect, seen, blockers } = params;
|
|
1035
|
+
const key = `${rect.x}|${rect.y}|${rect.width}|${rect.height}`;
|
|
1036
|
+
if (seen.has(key)) return;
|
|
1037
|
+
seen.add(key);
|
|
1038
|
+
blockers.push(rect);
|
|
1039
|
+
};
|
|
1040
|
+
var toQueryRect = (params) => {
|
|
1041
|
+
const { bounds, rect } = params;
|
|
1042
|
+
const minX = Math.max(bounds.x, rect.x);
|
|
1043
|
+
const minY = Math.max(bounds.y, rect.y);
|
|
1044
|
+
const maxX = Math.min(bounds.x + bounds.width, rect.x + rect.width);
|
|
1045
|
+
const maxY = Math.min(bounds.y + bounds.height, rect.y + rect.height);
|
|
1046
|
+
if (maxX <= minX + EPS4 || maxY <= minY + EPS4) return null;
|
|
1047
|
+
return { minX, minY, maxX, maxY };
|
|
1048
|
+
};
|
|
985
1049
|
function expandRectFromSeed(params) {
|
|
986
1050
|
const {
|
|
987
1051
|
startX,
|
|
988
1052
|
startY,
|
|
989
1053
|
gridSize,
|
|
990
1054
|
bounds,
|
|
991
|
-
|
|
1055
|
+
obsticalIndexByLayer,
|
|
1056
|
+
placedIndexByLayer,
|
|
992
1057
|
initialCellRatio,
|
|
993
1058
|
maxAspectRatio,
|
|
994
1059
|
minReq
|
|
@@ -996,6 +1061,34 @@ function expandRectFromSeed(params) {
|
|
|
996
1061
|
const minSide = Math.max(1e-9, gridSize * initialCellRatio);
|
|
997
1062
|
const initialW = Math.max(minSide, minReq.width);
|
|
998
1063
|
const initialH = Math.max(minSide, minReq.height);
|
|
1064
|
+
const blockers = [];
|
|
1065
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1066
|
+
const collectBlockers = (searchRect) => {
|
|
1067
|
+
const query = toQueryRect({ bounds, rect: searchRect });
|
|
1068
|
+
if (!query) return;
|
|
1069
|
+
for (const z of params.zLayers) {
|
|
1070
|
+
const blockersIndex = obsticalIndexByLayer[z];
|
|
1071
|
+
if (blockersIndex) {
|
|
1072
|
+
for (const entry of blockersIndex.search(query))
|
|
1073
|
+
addBlocker({ rect: toRect(entry), seen, blockers });
|
|
1074
|
+
}
|
|
1075
|
+
const placedLayer = placedIndexByLayer[z];
|
|
1076
|
+
if (placedLayer) {
|
|
1077
|
+
for (const entry of placedLayer.search(query)) {
|
|
1078
|
+
const rect = toRect(entry);
|
|
1079
|
+
if (isSelfRect({
|
|
1080
|
+
rect,
|
|
1081
|
+
startX,
|
|
1082
|
+
startY,
|
|
1083
|
+
initialW,
|
|
1084
|
+
initialH
|
|
1085
|
+
}))
|
|
1086
|
+
continue;
|
|
1087
|
+
addBlocker({ rect, seen, blockers });
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
999
1092
|
const strategies = [
|
|
1000
1093
|
{ ox: 0, oy: 0 },
|
|
1001
1094
|
{ ox: -initialW, oy: 0 },
|
|
@@ -1012,6 +1105,7 @@ function expandRectFromSeed(params) {
|
|
|
1012
1105
|
width: initialW,
|
|
1013
1106
|
height: initialH
|
|
1014
1107
|
};
|
|
1108
|
+
collectBlockers(r);
|
|
1015
1109
|
if (lt(r.x, bounds.x) || lt(r.y, bounds.y) || gt(r.x + r.width, bounds.x + bounds.width) || gt(r.y + r.height, bounds.y + bounds.height)) {
|
|
1016
1110
|
continue;
|
|
1017
1111
|
}
|
|
@@ -1020,24 +1114,32 @@ function expandRectFromSeed(params) {
|
|
|
1020
1114
|
while (improved) {
|
|
1021
1115
|
improved = false;
|
|
1022
1116
|
const commonParams = { bounds, blockers, maxAspect: maxAspectRatio };
|
|
1117
|
+
collectBlockers(searchStripRight({ rect: r, bounds }));
|
|
1023
1118
|
const eR = maxExpandRight({ ...commonParams, r });
|
|
1024
1119
|
if (eR > 0) {
|
|
1025
1120
|
r = { ...r, width: r.width + eR };
|
|
1121
|
+
collectBlockers(r);
|
|
1026
1122
|
improved = true;
|
|
1027
1123
|
}
|
|
1124
|
+
collectBlockers(searchStripDown({ rect: r, bounds }));
|
|
1028
1125
|
const eD = maxExpandDown({ ...commonParams, r });
|
|
1029
1126
|
if (eD > 0) {
|
|
1030
1127
|
r = { ...r, height: r.height + eD };
|
|
1128
|
+
collectBlockers(r);
|
|
1031
1129
|
improved = true;
|
|
1032
1130
|
}
|
|
1131
|
+
collectBlockers(searchStripLeft({ rect: r, bounds }));
|
|
1033
1132
|
const eL = maxExpandLeft({ ...commonParams, r });
|
|
1034
1133
|
if (eL > 0) {
|
|
1035
1134
|
r = { x: r.x - eL, y: r.y, width: r.width + eL, height: r.height };
|
|
1135
|
+
collectBlockers(r);
|
|
1036
1136
|
improved = true;
|
|
1037
1137
|
}
|
|
1138
|
+
collectBlockers(searchStripUp({ rect: r, bounds }));
|
|
1038
1139
|
const eU = maxExpandUp({ ...commonParams, r });
|
|
1039
1140
|
if (eU > 0) {
|
|
1040
1141
|
r = { x: r.x, y: r.y - eU, width: r.width, height: r.height + eU };
|
|
1142
|
+
collectBlockers(r);
|
|
1041
1143
|
improved = true;
|
|
1042
1144
|
}
|
|
1043
1145
|
}
|
|
@@ -1719,21 +1821,17 @@ var RectDiffSeedingSolver = class extends BaseSolver3 {
|
|
|
1719
1821
|
});
|
|
1720
1822
|
const ordered = preferMultiLayer ? attempts : attempts.reverse();
|
|
1721
1823
|
for (const attempt of ordered) {
|
|
1722
|
-
const hardBlockers = [];
|
|
1723
|
-
for (const z of attempt.layers) {
|
|
1724
|
-
const obstacleLayer = this.input.obstacleIndexByLayer[z];
|
|
1725
|
-
if (obstacleLayer) hardBlockers.push(...obstacleLayer.all());
|
|
1726
|
-
if (hardPlacedByLayer[z]) hardBlockers.push(...hardPlacedByLayer[z]);
|
|
1727
|
-
}
|
|
1728
1824
|
const rect = expandRectFromSeed({
|
|
1729
1825
|
startX: cand.x,
|
|
1730
1826
|
startY: cand.y,
|
|
1731
1827
|
gridSize: grid,
|
|
1732
1828
|
bounds: this.bounds,
|
|
1733
|
-
|
|
1829
|
+
obsticalIndexByLayer: this.input.obstacleIndexByLayer,
|
|
1830
|
+
placedIndexByLayer: this.placedIndexByLayer,
|
|
1734
1831
|
initialCellRatio,
|
|
1735
1832
|
maxAspectRatio,
|
|
1736
|
-
minReq: attempt.minReq
|
|
1833
|
+
minReq: attempt.minReq,
|
|
1834
|
+
zLayers: attempt.layers
|
|
1737
1835
|
});
|
|
1738
1836
|
if (!rect) continue;
|
|
1739
1837
|
const placed = { rect, zLayers: [...attempt.layers] };
|
|
@@ -1996,101 +2094,55 @@ var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
|
1996
2094
|
constructor(input) {
|
|
1997
2095
|
super();
|
|
1998
2096
|
this.input = input;
|
|
1999
|
-
Object.assign(this, this.input.initialSnapshot);
|
|
2000
2097
|
}
|
|
2001
|
-
|
|
2002
|
-
srj;
|
|
2003
|
-
layerNames;
|
|
2004
|
-
layerCount;
|
|
2005
|
-
bounds;
|
|
2006
|
-
options;
|
|
2007
|
-
boardVoidRects;
|
|
2008
|
-
gridIndex;
|
|
2009
|
-
candidates;
|
|
2010
|
-
placed;
|
|
2011
|
-
placedIndexByLayer;
|
|
2012
|
-
expansionIndex;
|
|
2013
|
-
edgeAnalysisDone;
|
|
2014
|
-
totalSeedsThisGrid;
|
|
2015
|
-
consumedSeedsThisGrid;
|
|
2098
|
+
placedIndexByLayer = [];
|
|
2016
2099
|
_meshNodes = [];
|
|
2017
2100
|
_setup() {
|
|
2018
2101
|
this.stats = {
|
|
2019
|
-
gridIndex: this.gridIndex
|
|
2102
|
+
gridIndex: this.input.gridIndex
|
|
2020
2103
|
};
|
|
2021
|
-
if (this.input.obstacleIndexByLayer) {
|
|
2022
|
-
} else {
|
|
2023
|
-
const { zIndexByName } = buildZIndexMap(this.srj);
|
|
2024
|
-
this.input.obstacleIndexByLayer = Array.from(
|
|
2025
|
-
{ length: this.layerCount },
|
|
2026
|
-
() => new RBush4()
|
|
2027
|
-
);
|
|
2028
|
-
const insertObstacle = (rect, z) => {
|
|
2029
|
-
const tree = this.input.obstacleIndexByLayer[z];
|
|
2030
|
-
if (tree) tree.insert(rectToTree(rect));
|
|
2031
|
-
};
|
|
2032
|
-
for (const voidRect of this.boardVoidRects ?? []) {
|
|
2033
|
-
for (let z = 0; z < this.layerCount; z++) insertObstacle(voidRect, z);
|
|
2034
|
-
}
|
|
2035
|
-
for (const obstacle of this.srj.obstacles ?? []) {
|
|
2036
|
-
const rect = obstacleToXYRect(obstacle);
|
|
2037
|
-
if (!rect) continue;
|
|
2038
|
-
const zLayers = obstacle.zLayers?.length && obstacle.zLayers.length > 0 ? obstacle.zLayers : obstacleZs(obstacle, zIndexByName);
|
|
2039
|
-
zLayers.forEach((z) => {
|
|
2040
|
-
if (z >= 0 && z < this.layerCount) insertObstacle(rect, z);
|
|
2041
|
-
});
|
|
2042
|
-
}
|
|
2043
|
-
}
|
|
2044
2104
|
this.placedIndexByLayer = Array.from(
|
|
2045
|
-
{ length: this.layerCount },
|
|
2105
|
+
{ length: this.input.layerCount },
|
|
2046
2106
|
() => new RBush4()
|
|
2047
2107
|
);
|
|
2048
|
-
for (const placement of this.placed
|
|
2108
|
+
for (const placement of this.input.placed) {
|
|
2049
2109
|
for (const z of placement.zLayers) {
|
|
2050
|
-
const
|
|
2051
|
-
if (
|
|
2110
|
+
const placedIndex = this.placedIndexByLayer[z];
|
|
2111
|
+
if (placedIndex) placedIndex.insert(rectToTree(placement.rect));
|
|
2052
2112
|
}
|
|
2053
2113
|
}
|
|
2054
2114
|
}
|
|
2055
2115
|
_step() {
|
|
2056
2116
|
if (this.solved) return;
|
|
2057
2117
|
this._stepExpansion();
|
|
2058
|
-
this.stats.gridIndex = this.gridIndex;
|
|
2059
|
-
this.stats.placed = this.placed.length;
|
|
2060
|
-
if (this.expansionIndex >= this.placed.length) {
|
|
2118
|
+
this.stats.gridIndex = this.input.gridIndex;
|
|
2119
|
+
this.stats.placed = this.input.placed.length;
|
|
2120
|
+
if (this.input.expansionIndex >= this.input.placed.length) {
|
|
2061
2121
|
this.finalizeIfNeeded();
|
|
2062
2122
|
}
|
|
2063
2123
|
}
|
|
2064
2124
|
_stepExpansion() {
|
|
2065
|
-
if (this.expansionIndex >= this.placed.length) {
|
|
2125
|
+
if (this.input.expansionIndex >= this.input.placed.length) {
|
|
2066
2126
|
return;
|
|
2067
2127
|
}
|
|
2068
|
-
const idx = this.expansionIndex;
|
|
2069
|
-
const p = this.placed[idx];
|
|
2070
|
-
const lastGrid = this.options.gridSizes[this.options.gridSizes.length - 1];
|
|
2071
|
-
const hardPlacedByLayer = allLayerNode({
|
|
2072
|
-
layerCount: this.layerCount,
|
|
2073
|
-
placed: this.placed
|
|
2074
|
-
});
|
|
2075
|
-
const hardBlockers = [];
|
|
2076
|
-
for (const z of p.zLayers) {
|
|
2077
|
-
const obstacleTree = this.input.obstacleIndexByLayer[z];
|
|
2078
|
-
if (obstacleTree) hardBlockers.push(...obstacleTree.all());
|
|
2079
|
-
hardBlockers.push(...hardPlacedByLayer[z] ?? []);
|
|
2080
|
-
}
|
|
2128
|
+
const idx = this.input.expansionIndex;
|
|
2129
|
+
const p = this.input.placed[idx];
|
|
2130
|
+
const lastGrid = this.input.options.gridSizes[this.input.options.gridSizes.length - 1];
|
|
2081
2131
|
const oldRect = p.rect;
|
|
2082
2132
|
const expanded = expandRectFromSeed({
|
|
2083
2133
|
startX: p.rect.x + p.rect.width / 2,
|
|
2084
2134
|
startY: p.rect.y + p.rect.height / 2,
|
|
2085
2135
|
gridSize: lastGrid,
|
|
2086
|
-
bounds: this.bounds,
|
|
2087
|
-
|
|
2136
|
+
bounds: this.input.bounds,
|
|
2137
|
+
obsticalIndexByLayer: this.input.obstacleIndexByLayer,
|
|
2138
|
+
placedIndexByLayer: this.placedIndexByLayer,
|
|
2088
2139
|
initialCellRatio: 0,
|
|
2089
2140
|
maxAspectRatio: null,
|
|
2090
|
-
minReq: { width: p.rect.width, height: p.rect.height }
|
|
2141
|
+
minReq: { width: p.rect.width, height: p.rect.height },
|
|
2142
|
+
zLayers: p.zLayers
|
|
2091
2143
|
});
|
|
2092
2144
|
if (expanded) {
|
|
2093
|
-
this.placed[idx] = { rect: expanded, zLayers: p.zLayers };
|
|
2145
|
+
this.input.placed[idx] = { rect: expanded, zLayers: p.zLayers };
|
|
2094
2146
|
for (const z of p.zLayers) {
|
|
2095
2147
|
const tree = this.placedIndexByLayer[z];
|
|
2096
2148
|
if (tree) {
|
|
@@ -2100,44 +2152,55 @@ var RectDiffExpansionSolver = class extends BaseSolver4 {
|
|
|
2100
2152
|
}
|
|
2101
2153
|
resizeSoftOverlaps(
|
|
2102
2154
|
{
|
|
2103
|
-
layerCount: this.layerCount,
|
|
2104
|
-
placed: this.placed,
|
|
2105
|
-
options: this.options,
|
|
2155
|
+
layerCount: this.input.layerCount,
|
|
2156
|
+
placed: this.input.placed,
|
|
2157
|
+
options: this.input.options,
|
|
2106
2158
|
placedIndexByLayer: this.placedIndexByLayer
|
|
2107
2159
|
},
|
|
2108
2160
|
idx
|
|
2109
2161
|
);
|
|
2110
2162
|
}
|
|
2111
|
-
this.expansionIndex += 1;
|
|
2163
|
+
this.input.expansionIndex += 1;
|
|
2112
2164
|
}
|
|
2113
2165
|
finalizeIfNeeded() {
|
|
2114
2166
|
if (this.solved) return;
|
|
2115
2167
|
const rects = finalizeRects({
|
|
2116
|
-
placed: this.placed,
|
|
2117
|
-
srj: this.srj,
|
|
2118
|
-
boardVoidRects: this.boardVoidRects
|
|
2168
|
+
placed: this.input.placed,
|
|
2169
|
+
srj: this.input.srj,
|
|
2170
|
+
boardVoidRects: this.input.boardVoidRects
|
|
2119
2171
|
});
|
|
2120
2172
|
this._meshNodes = rectsToMeshNodes(rects);
|
|
2121
2173
|
this.solved = true;
|
|
2122
2174
|
}
|
|
2123
2175
|
computeProgress() {
|
|
2124
2176
|
if (this.solved) return 1;
|
|
2125
|
-
const grids = this.options.gridSizes.length;
|
|
2177
|
+
const grids = this.input.options.gridSizes.length;
|
|
2126
2178
|
const base = grids / (grids + 1);
|
|
2127
|
-
const denom = Math.max(1, this.placed.length);
|
|
2128
|
-
const frac = denom ? this.expansionIndex / denom : 1;
|
|
2179
|
+
const denom = Math.max(1, this.input.placed.length);
|
|
2180
|
+
const frac = denom ? this.input.expansionIndex / denom : 1;
|
|
2129
2181
|
return Math.min(0.999, base + frac * (1 / (grids + 1)));
|
|
2130
2182
|
}
|
|
2131
2183
|
getOutput() {
|
|
2132
|
-
if (
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2184
|
+
if (this.solved) return { meshNodes: this._meshNodes };
|
|
2185
|
+
const previewNodes = this.input.placed.map(
|
|
2186
|
+
(placement, idx) => ({
|
|
2187
|
+
capacityMeshNodeId: `expand-preview-${idx}`,
|
|
2188
|
+
center: {
|
|
2189
|
+
x: placement.rect.x + placement.rect.width / 2,
|
|
2190
|
+
y: placement.rect.y + placement.rect.height / 2
|
|
2191
|
+
},
|
|
2192
|
+
width: placement.rect.width,
|
|
2193
|
+
height: placement.rect.height,
|
|
2194
|
+
availableZ: placement.zLayers.slice(),
|
|
2195
|
+
layer: `z${placement.zLayers.join(",")}`
|
|
2196
|
+
})
|
|
2197
|
+
);
|
|
2198
|
+
return { meshNodes: previewNodes };
|
|
2136
2199
|
}
|
|
2137
2200
|
/** Simple visualization of expanded placements. */
|
|
2138
2201
|
visualize() {
|
|
2139
2202
|
const rects = [];
|
|
2140
|
-
for (const placement of this.placed ?? []) {
|
|
2203
|
+
for (const placement of this.input.placed ?? []) {
|
|
2141
2204
|
rects.push({
|
|
2142
2205
|
center: {
|
|
2143
2206
|
x: placement.rect.x + placement.rect.width / 2,
|
|
@@ -2243,15 +2306,30 @@ var RectDiffGridSolverPipeline = class extends BasePipelineSolver2 {
|
|
|
2243
2306
|
definePipelineStep2(
|
|
2244
2307
|
"rectDiffExpansionSolver",
|
|
2245
2308
|
RectDiffExpansionSolver,
|
|
2246
|
-
(pipeline) =>
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
boardVoidRects: pipeline.inputProblem.boardVoidRects ?? []
|
|
2251
|
-
},
|
|
2252
|
-
obstacleIndexByLayer: pipeline.obstacleIndexByLayer
|
|
2309
|
+
(pipeline) => {
|
|
2310
|
+
const output = pipeline.rectDiffSeedingSolver?.getOutput();
|
|
2311
|
+
if (!output) {
|
|
2312
|
+
throw new Error("RectDiffSeedingSolver did not produce output");
|
|
2253
2313
|
}
|
|
2254
|
-
|
|
2314
|
+
return [
|
|
2315
|
+
{
|
|
2316
|
+
srj: pipeline.inputProblem.simpleRouteJson,
|
|
2317
|
+
layerNames: output.layerNames ?? [],
|
|
2318
|
+
boardVoidRects: pipeline.inputProblem.boardVoidRects ?? [],
|
|
2319
|
+
layerCount: pipeline.inputProblem.simpleRouteJson.layerCount,
|
|
2320
|
+
bounds: output.bounds,
|
|
2321
|
+
candidates: output.candidates,
|
|
2322
|
+
consumedSeedsThisGrid: output.placed.length,
|
|
2323
|
+
totalSeedsThisGrid: output.candidates.length,
|
|
2324
|
+
placed: output.placed,
|
|
2325
|
+
edgeAnalysisDone: output.edgeAnalysisDone,
|
|
2326
|
+
gridIndex: output.gridIndex,
|
|
2327
|
+
expansionIndex: output.expansionIndex,
|
|
2328
|
+
obstacleIndexByLayer: pipeline.obstacleIndexByLayer,
|
|
2329
|
+
options: output.options
|
|
2330
|
+
}
|
|
2331
|
+
];
|
|
2332
|
+
}
|
|
2255
2333
|
)
|
|
2256
2334
|
];
|
|
2257
2335
|
getConstructorParams() {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import RBush from "rbush"
|
|
2
|
+
import type { RectDiffExpansionSolverInput } from "../solvers/RectDiffExpansionSolver/RectDiffExpansionSolver"
|
|
3
|
+
import type { SimpleRouteJson } from "../types/srj-types"
|
|
4
|
+
import type { XYRect } from "../rectdiff-types"
|
|
5
|
+
import type { RTreeRect } from "lib/types/capacity-mesh-types"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Builds a minimal RectDiffExpansionSolver snapshot with exactly two nodes
|
|
9
|
+
* separated by empty space. This keeps the data close to the solver’s real
|
|
10
|
+
* input shape so we can reuse it for demos/tests that reproduce the gap issue.
|
|
11
|
+
*/
|
|
12
|
+
export const createTwoNodeExpansionInput = (): RectDiffExpansionSolverInput => {
|
|
13
|
+
const srj: SimpleRouteJson = {
|
|
14
|
+
bounds: { minX: 0, maxX: 12, minY: 0, maxY: 4 },
|
|
15
|
+
layerCount: 1,
|
|
16
|
+
minTraceWidth: 0.2,
|
|
17
|
+
obstacles: [],
|
|
18
|
+
connections: [],
|
|
19
|
+
}
|
|
20
|
+
const layerCount = srj.layerCount ?? 1
|
|
21
|
+
const bounds: XYRect = {
|
|
22
|
+
x: srj.bounds.minX,
|
|
23
|
+
y: srj.bounds.minY,
|
|
24
|
+
width: srj.bounds.maxX - srj.bounds.minX,
|
|
25
|
+
height: srj.bounds.maxY - srj.bounds.minY,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const obstacleIndexByLayer = Array.from(
|
|
29
|
+
{ length: layerCount },
|
|
30
|
+
() => new RBush<RTreeRect>(),
|
|
31
|
+
)
|
|
32
|
+
// Start with all-empty obstacle indexes for a "clean" scenario
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
srj,
|
|
36
|
+
layerNames: ["top"],
|
|
37
|
+
layerCount,
|
|
38
|
+
bounds,
|
|
39
|
+
options: { gridSizes: [1] },
|
|
40
|
+
boardVoidRects: [],
|
|
41
|
+
gridIndex: 0,
|
|
42
|
+
candidates: [],
|
|
43
|
+
placed: [
|
|
44
|
+
{
|
|
45
|
+
rect: { x: 0.5, y: 0.5, width: 2, height: 3 },
|
|
46
|
+
zLayers: [0],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
rect: { x: 9.5, y: 0.5, width: 2, height: 3 },
|
|
50
|
+
zLayers: [0],
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
expansionIndex: 0,
|
|
54
|
+
edgeAnalysisDone: true,
|
|
55
|
+
totalSeedsThisGrid: 0,
|
|
56
|
+
consumedSeedsThisGrid: 0,
|
|
57
|
+
obstacleIndexByLayer,
|
|
58
|
+
}
|
|
59
|
+
}
|