@tscircuit/rectdiff 0.0.20 → 0.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/.github/workflows/bun-test.yml +1 -1
  2. package/dist/index.js +30 -6
  3. package/lib/solvers/RectDiffSeedingSolver/computeInverseRects.ts +37 -1
  4. package/lib/utils/expandRectFromSeed.ts +11 -5
  5. package/package.json +1 -1
  6. package/test-assets/bugreport-c7537683-stalling.json +1107 -0
  7. package/tests/bugreport-stalling.test.ts +102 -0
  8. package/tests/solver/__snapshots__/rectDiffGridSolverPipeline.snap.svg +2 -2
  9. package/tests/solver/both-points-equivalent/__snapshots__/both-points-equivalent.snap.svg +44 -0
  10. package/tests/solver/both-points-equivalent/both-points-equivalent.srj.json +64 -0
  11. package/tests/solver/both-points-equivalent/both-points-equivalent.test.ts +92 -0
  12. package/tests/solver/bugreport01-be84eb/__snapshots__/bugreport01-be84eb.snap.svg +44 -0
  13. package/tests/solver/bugreport01-be84eb/bugreport01-be84eb.json +929 -0
  14. package/tests/solver/bugreport01-be84eb/bugreport01-be84eb.test.ts +93 -0
  15. package/tests/solver/bugreport02-bc4361/__snapshots__/bugreport02-bc4361.snap.svg +44 -0
  16. package/tests/solver/bugreport02-bc4361/bugreport02-bc4361.json +1276 -0
  17. package/tests/solver/bugreport02-bc4361/bugreport02-bc4361.test.ts +93 -0
  18. package/tests/solver/bugreport03-fe4a17/__snapshots__/bugreport03-fe4a17.snap.svg +44 -0
  19. package/tests/solver/bugreport03-fe4a17/bugreport03-fe4a17.json +986 -0
  20. package/tests/solver/bugreport03-fe4a17/bugreport03-fe4a17.test.ts +93 -0
  21. package/tests/solver/bugreport07-d3f3be/__snapshots__/bugreport07-d3f3be.snap.svg +44 -0
  22. package/tests/solver/bugreport07-d3f3be/bugreport07-d3f3be.json +1211 -0
  23. package/tests/solver/bugreport07-d3f3be/bugreport07-d3f3be.test.ts +93 -0
  24. package/tests/solver/bugreport08-e3ec95/__snapshots__/bugreport08-e3ec95.snap.svg +44 -0
  25. package/tests/solver/bugreport08-e3ec95/bugreport08-e3ec95.json +1511 -0
  26. package/tests/solver/bugreport08-e3ec95/bugreport08-e3ec95.test.ts +93 -0
  27. package/tests/solver/bugreport09-618e09/__snapshots__/bugreport09-618e09.snap.svg +44 -0
  28. package/tests/solver/bugreport09-618e09/bugreport09-618e09.json +2827 -0
  29. package/tests/solver/bugreport09-618e09/bugreport09-618e09.test.ts +93 -0
  30. package/tests/solver/bugreport10-71239a/__snapshots__/bugreport10-71239a.snap.svg +44 -0
  31. package/tests/solver/bugreport10-71239a/bugreport10-71239a.json +890 -0
  32. package/tests/solver/bugreport10-71239a/bugreport10-71239a.test.ts +93 -0
  33. package/tests/solver/bugreport11-b2de3c/__snapshots__/bugreport11-b2de3c.snap.svg +44 -0
  34. package/tests/solver/bugreport11-b2de3c/bugreport11-b2de3c.json +4315 -0
  35. package/tests/solver/bugreport11-b2de3c/bugreport11-b2de3c.test.ts +93 -0
  36. package/tests/solver/bugreport12-35ce1c/__snapshots__/bugreport12-35ce1c.snap.svg +44 -0
  37. package/tests/solver/bugreport12-35ce1c/bugreport12-35ce1c.json +475 -0
  38. package/tests/solver/bugreport12-35ce1c/bugreport12-35ce1c.test.ts +93 -0
  39. package/tests/solver/bugreport13-b9a758/__snapshots__/bugreport13-b9a758.snap.svg +44 -0
  40. package/tests/solver/bugreport13-b9a758/bugreport13-b9a758.json +15815 -0
  41. package/tests/solver/bugreport13-b9a758/bugreport13-b9a758.test.ts +93 -0
  42. package/tests/solver/bugreport16-d95f38/__snapshots__/bugreport16-d95f38.snap.svg +44 -0
  43. package/tests/solver/bugreport16-d95f38/bugreport16-d95f38.json +623 -0
  44. package/tests/solver/bugreport16-d95f38/bugreport16-d95f38.test.ts +93 -0
  45. package/tests/solver/bugreport18-1b2d06/__snapshots__/bugreport18-1b2d06.snap.svg +44 -0
  46. package/tests/solver/bugreport18-1b2d06/bugreport18-1b2d06.json +543 -0
  47. package/tests/solver/bugreport18-1b2d06/bugreport18-1b2d06.test.ts +93 -0
  48. package/tests/solver/bugreport19/__snapshots__/bugreport19.snap.svg +44 -0
  49. package/tests/solver/bugreport19/bugreport19.json +678 -0
  50. package/tests/solver/bugreport19/bugreport19.test.ts +93 -0
  51. package/tests/solver/bugreport20-obstacle-clipping/__snapshots__/bugreport20-obstacle-clipping.snap.svg +44 -0
  52. package/tests/solver/bugreport20-obstacle-clipping/bugreport20-obstacle-clipping.json +109 -0
  53. package/tests/solver/bugreport20-obstacle-clipping/bugreport20-obstacle-clipping.test.ts +93 -0
  54. package/tests/solver/bugreport21-board-outline/__snapshots__/bugreport21-board-outline.snap.svg +44 -0
  55. package/tests/solver/bugreport21-board-outline/bugreport21-board-outline.json +148 -0
  56. package/tests/solver/bugreport21-board-outline/bugreport21-board-outline.test.ts +93 -0
  57. package/tests/solver/bugreport22-2a75ce/__snapshots__/bugreport22-2a75ce.snap.svg +44 -0
  58. package/tests/solver/bugreport22-2a75ce/bugreport22-2a75ce.json +1550 -0
  59. package/tests/solver/bugreport22-2a75ce/bugreport22-2a75ce.test.ts +93 -0
  60. package/tests/solver/bugreport23-LGA15x4/__snapshots__/bugreport23-LGA15x4.snap.svg +44 -0
  61. package/tests/solver/bugreport23-LGA15x4/bugreport23-LGA15x4.srj.json +2401 -0
  62. package/tests/solver/bugreport23-LGA15x4/bugreport23-LGA15x4.test.ts +92 -0
  63. package/tests/solver/bugreport24-05597c/__snapshots__/bugreport24-05597c.snap.svg +44 -0
  64. package/tests/solver/bugreport24-05597c/bugreport24-05597c.json +8427 -0
  65. package/tests/solver/bugreport24-05597c/bugreport24-05597c.test.ts +93 -0
  66. package/tests/solver/bugreport25-4b1d55/__snapshots__/bugreport25-4b1d55.snap.svg +44 -0
  67. package/tests/solver/bugreport25-4b1d55/bugreport25-4b1d55.json +119 -0
  68. package/tests/solver/bugreport25-4b1d55/bugreport25-4b1d55.test.ts +93 -0
  69. package/tests/solver/bugreport26-66b0b2/__snapshots__/bugreport26-66b0b2.snap.svg +44 -0
  70. package/tests/solver/bugreport26-66b0b2/bugreport26-66b0b2.json +1651 -0
  71. package/tests/solver/bugreport26-66b0b2/bugreport26-66b0b2.test.ts +93 -0
  72. package/tests/solver/bugreport27-dd3734/__snapshots__/bugreport27-dd3734.snap.svg +44 -0
  73. package/tests/solver/bugreport27-dd3734/bugreport27-dd3734.json +4116 -0
  74. package/tests/solver/bugreport27-dd3734/bugreport27-dd3734.test.ts +93 -0
  75. package/tests/solver/bugreport28-18a9ef/__snapshots__/bugreport28-18a9ef.snap.svg +44 -0
  76. package/tests/solver/bugreport28-18a9ef/bugreport28-18a9ef.json +4116 -0
  77. package/tests/solver/bugreport28-18a9ef/bugreport28-18a9ef.test.ts +93 -0
  78. package/tests/solver/bugreport29-7deae8/__snapshots__/bugreport29-7deae8.snap.svg +44 -0
  79. package/tests/solver/bugreport29-7deae8/bugreport29-7deae8.json +4118 -0
  80. package/tests/solver/bugreport29-7deae8/bugreport29-7deae8.test.ts +93 -0
  81. package/tests/solver/bugreport30-2174c8/__snapshots__/bugreport30-2174c8.snap.svg +44 -0
  82. package/tests/solver/bugreport30-2174c8/bugreport30-2174c8.json +4007 -0
  83. package/tests/solver/bugreport30-2174c8/bugreport30-2174c8.test.ts +93 -0
  84. package/tests/solver/bugreport33-213d45/__snapshots__/bugreport33-213d45.snap.svg +44 -0
  85. package/tests/solver/bugreport33-213d45/bugreport33-213d45.json +5089 -0
  86. package/tests/solver/bugreport33-213d45/bugreport33-213d45.test.ts +93 -0
  87. package/tests/solver/bugreport34-e9dea2/__snapshots__/bugreport34-e9dea2.snap.svg +44 -0
  88. package/tests/solver/bugreport34-e9dea2/bugreport34-e9dea2.json +3924 -0
  89. package/tests/solver/bugreport34-e9dea2/bugreport34-e9dea2.test.ts +93 -0
  90. package/tests/solver/bugreport35-191db9/__snapshots__/bugreport35-191db9.snap.svg +44 -0
  91. package/tests/solver/bugreport35-191db9/bugreport35-191db9.json +4237 -0
  92. package/tests/solver/bugreport35-191db9/bugreport35-191db9.test.ts +93 -0
  93. package/tests/solver/bugreport36-bf8303/__snapshots__/bugreport36-bf8303.snap.svg +44 -0
  94. package/tests/solver/bugreport36-bf8303/bugreport36-bf8303.json +29660 -0
  95. package/tests/solver/bugreport36-bf8303/bugreport36-bf8303.test.ts +93 -0
  96. package/tests/solver/interaction/__snapshots__/interaction.snap.svg +44 -0
  97. package/tests/solver/interaction/interaction.srj.json +58 -0
  98. package/tests/solver/interaction/interaction.test.ts +92 -0
  99. package/tests/solver/multi-point/__snapshots__/multi-point.snap.svg +44 -0
  100. package/tests/solver/multi-point/multi-point.srj.json +57 -0
  101. package/tests/solver/multi-point/multi-point.test.ts +92 -0
  102. package/tests/solver/no-better-path/__snapshots__/no-better-path.snap.svg +44 -0
  103. package/tests/solver/no-better-path/no-better-path.srj.json +48 -0
  104. package/tests/solver/no-better-path/no-better-path.test.ts +92 -0
  105. package/tests/solver/offboardconnects01/__snapshots__/offboardconnects01.snap.svg +44 -0
  106. package/tests/solver/offboardconnects01/offboardconnects01.srj.json +53 -0
  107. package/tests/solver/offboardconnects01/offboardconnects01.test.ts +92 -0
  108. package/tests/solver/pcb_trace_id-should-return-root-connection-name/__snapshots__/pcb_trace_id-should-return-root-connection-name.snap.svg +44 -0
  109. package/tests/solver/pcb_trace_id-should-return-root-connection-name/pcb_trace_id-should-return-root-connection-name.srj.json +129 -0
  110. package/tests/solver/pcb_trace_id-should-return-root-connection-name/pcb_trace_id-should-return-root-connection-name.test.ts +92 -0
  111. package/tests/solver/transitivity/__snapshots__/transitivity.snap.svg +44 -0
  112. package/tests/solver/transitivity/transitivity.srj.json +64 -0
  113. package/tests/solver/transitivity/transitivity.test.ts +92 -0
@@ -28,4 +28,4 @@ jobs:
28
28
  run: bun install
29
29
 
30
30
  - name: Run tests
31
- run: bun test
31
+ run: bun test --timeout 9999
package/dist/index.js CHANGED
@@ -749,11 +749,31 @@ function isPointInPolygon(p, polygon) {
749
749
  }
750
750
 
751
751
  // lib/solvers/RectDiffSeedingSolver/computeInverseRects.ts
752
+ function simplifyPolygon(polygon, precision) {
753
+ const round = (v) => Math.round(v / precision) * precision;
754
+ const seen = /* @__PURE__ */ new Set();
755
+ const result = [];
756
+ for (const p of polygon) {
757
+ const rx = round(p.x);
758
+ const ry = round(p.y);
759
+ const key = `${rx},${ry}`;
760
+ if (!seen.has(key)) {
761
+ seen.add(key);
762
+ result.push({ x: rx, y: ry });
763
+ }
764
+ }
765
+ return result;
766
+ }
752
767
  function computeInverseRects(bounds, polygon) {
753
768
  if (!polygon || polygon.length < 3) return [];
769
+ const MAX_POLYGON_POINTS = 100;
770
+ const workingPolygon = polygon.length > MAX_POLYGON_POINTS ? simplifyPolygon(
771
+ polygon,
772
+ Math.max(bounds.width, bounds.height) / MAX_POLYGON_POINTS
773
+ ) : polygon;
754
774
  const xs = /* @__PURE__ */ new Set([bounds.x, bounds.x + bounds.width]);
755
775
  const ys = /* @__PURE__ */ new Set([bounds.y, bounds.y + bounds.height]);
756
- for (const p of polygon) {
776
+ for (const p of workingPolygon) {
757
777
  xs.add(p.x);
758
778
  ys.add(p.y);
759
779
  }
@@ -1113,34 +1133,38 @@ function expandRectFromSeed(params) {
1113
1133
  continue;
1114
1134
  }
1115
1135
  for (const b of blockers) if (overlaps(r, b)) continue STRATS;
1136
+ const MIN_EXPANSION = 1e-6;
1137
+ const MAX_ITERATIONS = 1e3;
1116
1138
  let improved = true;
1117
- while (improved) {
1139
+ let iterations = 0;
1140
+ while (improved && iterations < MAX_ITERATIONS) {
1141
+ iterations++;
1118
1142
  improved = false;
1119
1143
  const commonParams = { bounds, blockers, maxAspect: maxAspectRatio };
1120
1144
  collectBlockers(searchStripRight({ rect: r, bounds }));
1121
1145
  const eR = maxExpandRight({ ...commonParams, r });
1122
- if (eR > 0) {
1146
+ if (eR > MIN_EXPANSION) {
1123
1147
  r = { ...r, width: r.width + eR };
1124
1148
  collectBlockers(r);
1125
1149
  improved = true;
1126
1150
  }
1127
1151
  collectBlockers(searchStripDown({ rect: r, bounds }));
1128
1152
  const eD = maxExpandDown({ ...commonParams, r });
1129
- if (eD > 0) {
1153
+ if (eD > MIN_EXPANSION) {
1130
1154
  r = { ...r, height: r.height + eD };
1131
1155
  collectBlockers(r);
1132
1156
  improved = true;
1133
1157
  }
1134
1158
  collectBlockers(searchStripLeft({ rect: r, bounds }));
1135
1159
  const eL = maxExpandLeft({ ...commonParams, r });
1136
- if (eL > 0) {
1160
+ if (eL > MIN_EXPANSION) {
1137
1161
  r = { x: r.x - eL, y: r.y, width: r.width + eL, height: r.height };
1138
1162
  collectBlockers(r);
1139
1163
  improved = true;
1140
1164
  }
1141
1165
  collectBlockers(searchStripUp({ rect: r, bounds }));
1142
1166
  const eU = maxExpandUp({ ...commonParams, r });
1143
- if (eU > 0) {
1167
+ if (eU > MIN_EXPANSION) {
1144
1168
  r = { x: r.x, y: r.y - eU, width: r.width, height: r.height + eU };
1145
1169
  collectBlockers(r);
1146
1170
  improved = true;
@@ -6,6 +6,31 @@ import {
6
6
  } from "../../utils/rectdiff-geometry"
7
7
  import { isPointInPolygon } from "./isPointInPolygon"
8
8
 
9
+ /**
10
+ * Simplify a polygon by reducing coordinate precision to avoid excessive grid cells.
11
+ * This rounds coordinates to a grid and removes duplicates.
12
+ */
13
+ function simplifyPolygon(
14
+ polygon: Array<{ x: number; y: number }>,
15
+ precision: number,
16
+ ): Array<{ x: number; y: number }> {
17
+ const round = (v: number) => Math.round(v / precision) * precision
18
+ const seen = new Set<string>()
19
+ const result: Array<{ x: number; y: number }> = []
20
+
21
+ for (const p of polygon) {
22
+ const rx = round(p.x)
23
+ const ry = round(p.y)
24
+ const key = `${rx},${ry}`
25
+ if (!seen.has(key)) {
26
+ seen.add(key)
27
+ result.push({ x: rx, y: ry })
28
+ }
29
+ }
30
+
31
+ return result
32
+ }
33
+
9
34
  /**
10
35
  * Decompose the empty space inside 'bounds' but outside 'polygon' into rectangles.
11
36
  * This uses a coordinate grid approach, ideal for rectilinear polygons.
@@ -16,10 +41,21 @@ export function computeInverseRects(
16
41
  ): XYRect[] {
17
42
  if (!polygon || polygon.length < 3) return []
18
43
 
44
+ // Simplify polygon if it has too many points to avoid O(n^2) performance issues
45
+ // A polygon with 350+ points (like rounded corners) creates too many grid cells
46
+ const MAX_POLYGON_POINTS = 100
47
+ const workingPolygon =
48
+ polygon.length > MAX_POLYGON_POINTS
49
+ ? simplifyPolygon(
50
+ polygon,
51
+ Math.max(bounds.width, bounds.height) / MAX_POLYGON_POINTS,
52
+ )
53
+ : polygon
54
+
19
55
  // 1. Collect unique sorted X and Y coordinates
20
56
  const xs = new Set<number>([bounds.x, bounds.x + bounds.width])
21
57
  const ys = new Set<number>([bounds.y, bounds.y + bounds.height])
22
- for (const p of polygon) {
58
+ for (const p of workingPolygon) {
23
59
  xs.add(p.x)
24
60
  ys.add(p.y)
25
61
  }
@@ -286,14 +286,20 @@ export function expandRectFromSeed(params: {
286
286
  for (const b of blockers) if (overlaps(r, b)) continue STRATS
287
287
 
288
288
  // greedy expansions in 4 directions
289
+ // Use a minimum expansion threshold to avoid infinitesimal improvements
290
+ // that can occur with mixed-precision floating point coordinates
291
+ const MIN_EXPANSION = 1e-6
292
+ const MAX_ITERATIONS = 1000
289
293
  let improved = true
290
- while (improved) {
294
+ let iterations = 0
295
+ while (improved && iterations < MAX_ITERATIONS) {
296
+ iterations++
291
297
  improved = false
292
298
  const commonParams = { bounds, blockers, maxAspect: maxAspectRatio }
293
299
 
294
300
  collectBlockers(searchStripRight({ rect: r, bounds }))
295
301
  const eR = maxExpandRight({ ...commonParams, r })
296
- if (eR > 0) {
302
+ if (eR > MIN_EXPANSION) {
297
303
  r = { ...r, width: r.width + eR }
298
304
  collectBlockers(r)
299
305
  improved = true
@@ -301,7 +307,7 @@ export function expandRectFromSeed(params: {
301
307
 
302
308
  collectBlockers(searchStripDown({ rect: r, bounds }))
303
309
  const eD = maxExpandDown({ ...commonParams, r })
304
- if (eD > 0) {
310
+ if (eD > MIN_EXPANSION) {
305
311
  r = { ...r, height: r.height + eD }
306
312
  collectBlockers(r)
307
313
  improved = true
@@ -309,7 +315,7 @@ export function expandRectFromSeed(params: {
309
315
 
310
316
  collectBlockers(searchStripLeft({ rect: r, bounds }))
311
317
  const eL = maxExpandLeft({ ...commonParams, r })
312
- if (eL > 0) {
318
+ if (eL > MIN_EXPANSION) {
313
319
  r = { x: r.x - eL, y: r.y, width: r.width + eL, height: r.height }
314
320
  collectBlockers(r)
315
321
  improved = true
@@ -317,7 +323,7 @@ export function expandRectFromSeed(params: {
317
323
 
318
324
  collectBlockers(searchStripUp({ rect: r, bounds }))
319
325
  const eU = maxExpandUp({ ...commonParams, r })
320
- if (eU > 0) {
326
+ if (eU > MIN_EXPANSION) {
321
327
  r = { x: r.x, y: r.y - eU, width: r.width, height: r.height + eU }
322
328
  collectBlockers(r)
323
329
  improved = true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/rectdiff",
3
- "version": "0.0.20",
3
+ "version": "0.0.22",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {