@tscircuit/rectdiff 0.0.18 → 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 CHANGED
@@ -278,7 +278,7 @@ declare class RectDiffSeedingSolver extends BaseSolver {
278
278
  visualize(): GraphicsObject;
279
279
  }
280
280
 
281
- type RectDiffExpansionSolverSnapshot = {
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
- private srj;
312
- private layerNames;
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
- blockers,
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
- blockers: hardBlockers,
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
- // Engine fields (same shape used by rectdiff/engine.ts)
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 tree = this.placedIndexByLayer[z];
2051
- if (tree) tree.insert(rectToTree(placement.rect));
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
- blockers: hardBlockers,
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 (!this.solved && this._meshNodes.length === 0) {
2133
- this.finalizeIfNeeded();
2134
- }
2135
- return { meshNodes: this._meshNodes };
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
- initialSnapshot: {
2249
- ...pipeline.rectDiffSeedingSolver.getOutput(),
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() {
@@ -1,8 +1,5 @@
1
1
  import RBush from "rbush"
2
- import type {
3
- RectDiffExpansionSolverInput,
4
- RectDiffExpansionSolverSnapshot,
5
- } from "../solvers/RectDiffExpansionSolver/RectDiffExpansionSolver"
2
+ import type { RectDiffExpansionSolverInput } from "../solvers/RectDiffExpansionSolver/RectDiffExpansionSolver"
6
3
  import type { SimpleRouteJson } from "../types/srj-types"
7
4
  import type { XYRect } from "../rectdiff-types"
8
5
  import type { RTreeRect } from "lib/types/capacity-mesh-types"
@@ -34,7 +31,7 @@ export const createTwoNodeExpansionInput = (): RectDiffExpansionSolverInput => {
34
31
  )
35
32
  // Start with all-empty obstacle indexes for a "clean" scenario
36
33
 
37
- const initialSnapshot: RectDiffExpansionSolverSnapshot = {
34
+ return {
38
35
  srj,
39
36
  layerNames: ["top"],
40
37
  layerCount,
@@ -57,10 +54,6 @@ export const createTwoNodeExpansionInput = (): RectDiffExpansionSolverInput => {
57
54
  edgeAnalysisDone: true,
58
55
  totalSeedsThisGrid: 0,
59
56
  consumedSeedsThisGrid: 0,
60
- }
61
-
62
- return {
63
- initialSnapshot,
64
57
  obstacleIndexByLayer,
65
58
  }
66
59
  }