@tscircuit/rectdiff 0.0.8 → 0.0.10

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 (33) hide show
  1. package/components/SolverDebugger3d.tsx +92 -162
  2. package/dist/index.d.ts +34 -27
  3. package/dist/index.js +93 -69
  4. package/lib/RectDiffPipeline.ts +55 -0
  5. package/lib/index.ts +1 -1
  6. package/lib/solvers/RectDiffSolver.ts +1 -34
  7. package/lib/solvers/rectdiff/visualization.ts +66 -0
  8. package/package.json +2 -2
  9. package/pages/board-with-cutout.page.tsx +3 -4
  10. package/pages/bugreport11.page.tsx +8 -3
  11. package/pages/example01.page.tsx +3 -4
  12. package/pages/keyboard-bugreport04.page.tsx +8 -4
  13. package/tests/board-outline.test.ts +2 -2
  14. package/tests/examples/example01.test.tsx +2 -2
  15. package/tests/incremental-solver.test.ts +10 -16
  16. package/tests/obstacle-extra-layers.test.ts +12 -5
  17. package/tests/obstacle-zlayers.test.ts +13 -5
  18. package/tests/rect-diff-solver.test.ts +14 -23
  19. package/lib/solvers/rectdiff/gapfill/detection/deduplicateGaps.ts +0 -28
  20. package/lib/solvers/rectdiff/gapfill/detection/findAllGaps.ts +0 -83
  21. package/lib/solvers/rectdiff/gapfill/detection/findGapsOnLayer.ts +0 -100
  22. package/lib/solvers/rectdiff/gapfill/detection/mergeUncoveredCells.ts +0 -75
  23. package/lib/solvers/rectdiff/gapfill/detection.ts +0 -3
  24. package/lib/solvers/rectdiff/gapfill/engine/addPlacement.ts +0 -27
  25. package/lib/solvers/rectdiff/gapfill/engine/calculateCoverage.ts +0 -44
  26. package/lib/solvers/rectdiff/gapfill/engine/findUncoveredPoints.ts +0 -43
  27. package/lib/solvers/rectdiff/gapfill/engine/getGapFillProgress.ts +0 -42
  28. package/lib/solvers/rectdiff/gapfill/engine/initGapFillState.ts +0 -57
  29. package/lib/solvers/rectdiff/gapfill/engine/stepGapFill.ts +0 -128
  30. package/lib/solvers/rectdiff/gapfill/engine/tryExpandGap.ts +0 -78
  31. package/lib/solvers/rectdiff/gapfill/engine.ts +0 -7
  32. package/lib/solvers/rectdiff/gapfill/types.ts +0 -60
  33. package/lib/solvers/rectdiff/subsolvers/GapFillSubSolver.ts +0 -253
@@ -560,7 +560,7 @@ export const SolverDebugger3d: React.FC<SolverDebugger3dProps> = ({
560
560
  defaultWireframeOutput = false,
561
561
  style,
562
562
  }) => {
563
- const [show3d, setShow3d] = useState(false)
563
+ const [renderMode, setRenderMode] = useState<"2d" | "3d">("2d")
564
564
  const [rebuildKey, setRebuildKey] = useState(0)
565
565
 
566
566
  const [showRoot, setShowRoot] = useState(defaultShowRoot)
@@ -610,204 +610,134 @@ export const SolverDebugger3d: React.FC<SolverDebugger3dProps> = ({
610
610
  return () => clearInterval(interval)
611
611
  }, [updateMeshNodes, solver])
612
612
 
613
- const toggle3d = useCallback(() => setShow3d((s) => !s), [])
614
613
  const rebuild = useCallback(() => setRebuildKey((k) => k + 1), [])
615
614
 
616
615
  return (
617
616
  <>
618
617
  <div style={{ display: "grid", gap: 12, ...style }}>
619
- <GenericSolverDebugger
620
- solver={solver as any}
621
- onSolverCompleted={handleSolverCompleted}
622
- />
623
-
618
+ {/* Topbar with Render dropdown */}
624
619
  <div
625
620
  style={{
626
621
  display: "flex",
627
- gap: 8,
622
+ gap: 12,
628
623
  alignItems: "center",
629
- flexWrap: "wrap",
624
+ padding: "12px 16px",
625
+ background: "#f8fafc",
626
+ borderRadius: 8,
627
+ border: "1px solid #e2e8f0",
630
628
  }}
631
629
  >
632
- <button
633
- onClick={toggle3d}
634
- style={{
635
- padding: "8px 10px",
636
- borderRadius: 6,
637
- border: "1px solid #cbd5e1",
638
- background: show3d ? "#1e293b" : "#2563eb",
639
- color: "white",
640
- cursor: "pointer",
641
- }}
642
- >
643
- {show3d ? "Hide 3D" : "Show 3D"}
644
- </button>
645
- {show3d && (
646
- <button
647
- onClick={rebuild}
630
+ <label style={{ display: "flex", gap: 8, alignItems: "center" }}>
631
+ <span style={{ fontWeight: 600, fontSize: 14 }}>Render:</span>
632
+ <select
633
+ value={renderMode}
634
+ onChange={(e) => setRenderMode(e.target.value as "2d" | "3d")}
648
635
  style={{
649
- padding: "8px 10px",
636
+ padding: "6px 12px",
650
637
  borderRadius: 6,
651
638
  border: "1px solid #cbd5e1",
652
- background: "#0f766e",
653
- color: "white",
639
+ background: "white",
654
640
  cursor: "pointer",
641
+ fontSize: 14,
655
642
  }}
656
- title="Rebuild 3D scene (use after changing solver params)"
657
643
  >
658
- Rebuild 3D
659
- </button>
660
- )}
661
-
662
- {/* experiment-like toggles */}
663
- <label
664
- style={{
665
- display: "inline-flex",
666
- gap: 6,
667
- alignItems: "center",
668
- marginLeft: 8,
669
- }}
670
- >
671
- <input
672
- type="checkbox"
673
- checked={showRoot}
674
- onChange={(e) => setShowRoot(e.target.checked)}
675
- />
676
- Root
677
- </label>
678
- <label
679
- style={{ display: "inline-flex", gap: 6, alignItems: "center" }}
680
- >
681
- <input
682
- type="checkbox"
683
- checked={showObstacles}
684
- onChange={(e) => setShowObstacles(e.target.checked)}
685
- />
686
- Obstacles
644
+ <option value="2d">2D (Normal)</option>
645
+ <option value="3d">3D View</option>
646
+ </select>
687
647
  </label>
688
- <label
689
- style={{ display: "inline-flex", gap: 6, alignItems: "center" }}
690
- >
691
- <input
692
- type="checkbox"
693
- checked={showOutput}
694
- onChange={(e) => setShowOutput(e.target.checked)}
695
- />
696
- Output
697
- </label>
698
- <label
699
- style={{ display: "inline-flex", gap: 6, alignItems: "center" }}
700
- >
701
- <input
702
- type="checkbox"
703
- checked={wireframeOutput}
704
- onChange={(e) => setWireframeOutput(e.target.checked)}
705
- />
706
- Wireframe Output
707
- </label>
708
-
709
- {/* Mesh opacity slider */}
710
- {show3d && (
711
- <label
712
- style={{
713
- display: "inline-flex",
714
- alignItems: "center",
715
- gap: 6,
716
- marginLeft: 8,
717
- fontSize: 12,
718
- }}
719
- >
720
- Opacity
721
- <input
722
- type="range"
723
- min={0}
724
- max={1}
725
- step={0.05}
726
- value={meshOpacity}
727
- onChange={(e) => setMeshOpacity(parseFloat(e.target.value))}
728
- />
729
- <span style={{ width: 32, textAlign: "right" }}>
730
- {meshOpacity.toFixed(2)}
731
- </span>
732
- </label>
733
- )}
734
648
 
735
- {/* Shrink boxes option */}
736
- {show3d && (
649
+ {renderMode === "3d" && (
737
650
  <>
651
+ <button
652
+ onClick={rebuild}
653
+ style={{
654
+ padding: "6px 12px",
655
+ borderRadius: 6,
656
+ border: "1px solid #cbd5e1",
657
+ background: "#0f766e",
658
+ color: "white",
659
+ cursor: "pointer",
660
+ fontSize: 14,
661
+ }}
662
+ title="Rebuild 3D scene"
663
+ >
664
+ Rebuild
665
+ </button>
666
+
738
667
  <label
739
668
  style={{
740
669
  display: "inline-flex",
741
670
  gap: 6,
742
671
  alignItems: "center",
743
- fontSize: 12,
672
+ fontSize: 13,
744
673
  }}
745
674
  >
746
675
  <input
747
676
  type="checkbox"
748
- checked={shrinkBoxes}
749
- onChange={(e) => setShrinkBoxes(e.target.checked)}
677
+ checked={showRoot}
678
+ onChange={(e) => setShowRoot(e.target.checked)}
750
679
  />
751
- Shrink boxes
680
+ Root
752
681
  </label>
753
- {shrinkBoxes && (
754
- <label
755
- style={{
756
- display: "inline-flex",
757
- gap: 4,
758
- alignItems: "center",
759
- fontSize: 12,
760
- }}
761
- >
762
- amt
763
- <input
764
- type="number"
765
- value={boxShrinkAmount}
766
- step={0.05}
767
- style={{ width: 60 }}
768
- onChange={(e) => {
769
- const v = parseFloat(e.target.value)
770
- if (Number.isNaN(v)) return
771
- setBoxShrinkAmount(Math.max(0, v))
772
- }}
773
- />
774
- </label>
775
- )}
776
- </>
777
- )}
778
-
779
- {/* Show borders option */}
780
- {show3d && (
781
- <label
782
- style={{
783
- display: "inline-flex",
784
- gap: 6,
785
- alignItems: "center",
786
- fontSize: 12,
787
- }}
788
- >
789
- <input
790
- type="checkbox"
791
- checked={showBorders}
792
- disabled={wireframeOutput}
793
- onChange={(e) => setShowBorders(e.target.checked)}
794
- />
795
- <span
682
+ <label
683
+ style={{
684
+ display: "inline-flex",
685
+ gap: 6,
686
+ alignItems: "center",
687
+ fontSize: 13,
688
+ }}
689
+ >
690
+ <input
691
+ type="checkbox"
692
+ checked={showObstacles}
693
+ onChange={(e) => setShowObstacles(e.target.checked)}
694
+ />
695
+ Obstacles
696
+ </label>
697
+ <label
698
+ style={{
699
+ display: "inline-flex",
700
+ gap: 6,
701
+ alignItems: "center",
702
+ fontSize: 13,
703
+ }}
704
+ >
705
+ <input
706
+ type="checkbox"
707
+ checked={showOutput}
708
+ onChange={(e) => setShowOutput(e.target.checked)}
709
+ />
710
+ Output
711
+ </label>
712
+ <label
796
713
  style={{
797
- opacity: wireframeOutput ? 0.5 : 1,
714
+ display: "inline-flex",
715
+ gap: 6,
716
+ alignItems: "center",
717
+ fontSize: 13,
798
718
  }}
799
719
  >
800
- Show borders
801
- </span>
802
- </label>
720
+ <input
721
+ type="checkbox"
722
+ checked={wireframeOutput}
723
+ onChange={(e) => setWireframeOutput(e.target.checked)}
724
+ />
725
+ Wireframe
726
+ </label>
727
+ </>
803
728
  )}
804
-
805
- <div style={{ fontSize: 12, color: "#334155", marginLeft: 6 }}>
806
- Drag to orbit · Wheel to zoom · Right-drag to pan
807
- </div>
808
729
  </div>
809
730
 
810
- {show3d && (
731
+ {/* Render 2D view */}
732
+ {renderMode === "2d" && (
733
+ <GenericSolverDebugger
734
+ solver={solver as any}
735
+ onSolverCompleted={handleSolverCompleted}
736
+ />
737
+ )}
738
+
739
+ {/* Render 3D view */}
740
+ {renderMode === "3d" && (
811
741
  <ThreeBoardView
812
742
  key={rebuildKey}
813
743
  nodes={meshNodes}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { BaseSolver } from '@tscircuit/solver-utils';
1
+ import * as _tscircuit_solver_utils from '@tscircuit/solver-utils';
2
+ import { BaseSolver, BasePipelineSolver } from '@tscircuit/solver-utils';
2
3
  import { GraphicsObject } from 'graphics-debug';
3
4
 
4
5
  type TraceId = string;
@@ -47,6 +48,23 @@ interface SimpleRouteConnection {
47
48
  externallyConnectedPointIds?: string[][];
48
49
  }
49
50
 
51
+ type GridFill3DOptions = {
52
+ gridSizes?: number[];
53
+ initialCellRatio?: number;
54
+ maxAspectRatio?: number | null;
55
+ minSingle: {
56
+ width: number;
57
+ height: number;
58
+ };
59
+ minMulti: {
60
+ width: number;
61
+ height: number;
62
+ minLayers: number;
63
+ };
64
+ preferMultiLayer?: boolean;
65
+ maxMultiLayerSpan?: number;
66
+ };
67
+
50
68
  type CapacityMeshNodeId = string;
51
69
  interface CapacityMeshNode {
52
70
  capacityMeshNodeId: string;
@@ -69,23 +87,6 @@ interface CapacityMeshNode {
69
87
  _parent?: CapacityMeshNode;
70
88
  }
71
89
 
72
- type GridFill3DOptions = {
73
- gridSizes?: number[];
74
- initialCellRatio?: number;
75
- maxAspectRatio?: number | null;
76
- minSingle: {
77
- width: number;
78
- height: number;
79
- };
80
- minMulti: {
81
- width: number;
82
- height: number;
83
- minLayers: number;
84
- };
85
- preferMultiLayer?: boolean;
86
- maxMultiLayerSpan?: number;
87
- };
88
-
89
90
  /**
90
91
  * A streaming, one-step-per-iteration solver for capacity mesh generation.
91
92
  */
@@ -106,18 +107,24 @@ declare class RectDiffSolver extends BaseSolver {
106
107
  getOutput(): {
107
108
  meshNodes: CapacityMeshNode[];
108
109
  };
109
- /** Get coverage percentage (0-1). */
110
- getCoverage(sampleResolution?: number): number;
111
- /** Find uncovered points for debugging gaps. */
112
- getUncoveredPoints(sampleResolution?: number): Array<{
113
- x: number;
114
- y: number;
115
- z: number;
116
- }>;
117
110
  /** Get color based on z layer for visualization. */
118
111
  private getColorForZLayer;
119
112
  /** Streaming visualization: board + obstacles + current placements. */
120
113
  visualize(): GraphicsObject;
121
114
  }
122
115
 
123
- export { type GridFill3DOptions, RectDiffSolver };
116
+ interface RectDiffPipelineInput {
117
+ simpleRouteJson: SimpleRouteJson;
118
+ gridOptions?: Partial<GridFill3DOptions>;
119
+ }
120
+ declare class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput> {
121
+ rectDiffSolver?: RectDiffSolver;
122
+ pipelineDef: _tscircuit_solver_utils.PipelineStep<RectDiffSolver>[];
123
+ getConstructorParams(): RectDiffPipelineInput[];
124
+ getOutput(): {
125
+ meshNodes: CapacityMeshNode[];
126
+ };
127
+ visualize(): GraphicsObject;
128
+ }
129
+
130
+ export { RectDiffPipeline, type RectDiffPipelineInput };
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ // lib/RectDiffPipeline.ts
2
+ import { BasePipelineSolver as BasePipelineSolver2, definePipelineStep } from "@tscircuit/solver-utils";
3
+
1
4
  // lib/solvers/RectDiffSolver.ts
2
5
  import { BaseSolver } from "@tscircuit/solver-utils";
3
6
 
@@ -1114,50 +1117,6 @@ function rectsToMeshNodes(rects) {
1114
1117
  return out;
1115
1118
  }
1116
1119
 
1117
- // lib/solvers/rectdiff/gapfill/engine/calculateCoverage.ts
1118
- function calculateCoverage({ sampleResolution = 0.1 }, ctx) {
1119
- const { bounds, layerCount, obstaclesByLayer, placedByLayer } = ctx;
1120
- let totalPoints = 0;
1121
- let coveredPoints = 0;
1122
- for (let z = 0; z < layerCount; z++) {
1123
- const obstacles = obstaclesByLayer[z] ?? [];
1124
- const placed = placedByLayer[z] ?? [];
1125
- const allRects = [...obstacles, ...placed];
1126
- for (let x = bounds.x; x <= bounds.x + bounds.width; x += sampleResolution) {
1127
- for (let y = bounds.y; y <= bounds.y + bounds.height; y += sampleResolution) {
1128
- totalPoints++;
1129
- const isCovered = allRects.some(
1130
- (r) => x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height
1131
- );
1132
- if (isCovered) coveredPoints++;
1133
- }
1134
- }
1135
- }
1136
- return totalPoints > 0 ? coveredPoints / totalPoints : 1;
1137
- }
1138
-
1139
- // lib/solvers/rectdiff/gapfill/engine/findUncoveredPoints.ts
1140
- function findUncoveredPoints({ sampleResolution = 0.05 }, ctx) {
1141
- const { bounds, layerCount, obstaclesByLayer, placedByLayer } = ctx;
1142
- const uncovered = [];
1143
- for (let z = 0; z < layerCount; z++) {
1144
- const obstacles = obstaclesByLayer[z] ?? [];
1145
- const placed = placedByLayer[z] ?? [];
1146
- const allRects = [...obstacles, ...placed];
1147
- for (let x = bounds.x; x <= bounds.x + bounds.width; x += sampleResolution) {
1148
- for (let y = bounds.y; y <= bounds.y + bounds.height; y += sampleResolution) {
1149
- const isCovered = allRects.some(
1150
- (r) => x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height
1151
- );
1152
- if (!isCovered) {
1153
- uncovered.push({ x, y, z });
1154
- }
1155
- }
1156
- }
1157
- }
1158
- return uncovered;
1159
- }
1160
-
1161
1120
  // lib/solvers/RectDiffSolver.ts
1162
1121
  var RectDiffSolver = class extends BaseSolver {
1163
1122
  srj;
@@ -1206,30 +1165,6 @@ var RectDiffSolver = class extends BaseSolver {
1206
1165
  getOutput() {
1207
1166
  return { meshNodes: this._meshNodes };
1208
1167
  }
1209
- /** Get coverage percentage (0-1). */
1210
- getCoverage(sampleResolution = 0.05) {
1211
- return calculateCoverage(
1212
- { sampleResolution },
1213
- {
1214
- bounds: this.state.bounds,
1215
- layerCount: this.state.layerCount,
1216
- obstaclesByLayer: this.state.obstaclesByLayer,
1217
- placedByLayer: this.state.placedByLayer
1218
- }
1219
- );
1220
- }
1221
- /** Find uncovered points for debugging gaps. */
1222
- getUncoveredPoints(sampleResolution = 0.05) {
1223
- return findUncoveredPoints(
1224
- { sampleResolution },
1225
- {
1226
- bounds: this.state.bounds,
1227
- layerCount: this.state.layerCount,
1228
- obstaclesByLayer: this.state.obstaclesByLayer,
1229
- placedByLayer: this.state.placedByLayer
1230
- }
1231
- );
1232
- }
1233
1168
  /** Get color based on z layer for visualization. */
1234
1169
  getColorForZLayer(zLayers) {
1235
1170
  const minZ = Math.min(...zLayers);
@@ -1354,6 +1289,95 @@ z:${p.zLayers.join(",")}`
1354
1289
  };
1355
1290
  }
1356
1291
  };
1292
+
1293
+ // lib/solvers/rectdiff/visualization.ts
1294
+ function createBaseVisualization(srj, title = "RectDiff") {
1295
+ const rects = [];
1296
+ const lines = [];
1297
+ const boardBounds = {
1298
+ minX: srj.bounds.minX,
1299
+ maxX: srj.bounds.maxX,
1300
+ minY: srj.bounds.minY,
1301
+ maxY: srj.bounds.maxY
1302
+ };
1303
+ if (srj.outline && srj.outline.length > 1) {
1304
+ lines.push({
1305
+ points: [...srj.outline, srj.outline[0]],
1306
+ strokeColor: "#111827",
1307
+ strokeWidth: 0.01,
1308
+ label: "outline"
1309
+ });
1310
+ } else {
1311
+ rects.push({
1312
+ center: {
1313
+ x: (boardBounds.minX + boardBounds.maxX) / 2,
1314
+ y: (boardBounds.minY + boardBounds.maxY) / 2
1315
+ },
1316
+ width: boardBounds.maxX - boardBounds.minX,
1317
+ height: boardBounds.maxY - boardBounds.minY,
1318
+ fill: "none",
1319
+ stroke: "#111827",
1320
+ label: "board"
1321
+ });
1322
+ }
1323
+ for (const obstacle of srj.obstacles ?? []) {
1324
+ if (obstacle.type === "rect" || obstacle.type === "oval") {
1325
+ rects.push({
1326
+ center: { x: obstacle.center.x, y: obstacle.center.y },
1327
+ width: obstacle.width,
1328
+ height: obstacle.height,
1329
+ fill: "#fee2e2",
1330
+ stroke: "#ef4444",
1331
+ layer: "obstacle",
1332
+ label: "obstacle"
1333
+ });
1334
+ }
1335
+ }
1336
+ return {
1337
+ title,
1338
+ coordinateSystem: "cartesian",
1339
+ rects,
1340
+ points: [],
1341
+ lines
1342
+ };
1343
+ }
1344
+
1345
+ // lib/RectDiffPipeline.ts
1346
+ var RectDiffPipeline = class extends BasePipelineSolver2 {
1347
+ rectDiffSolver;
1348
+ pipelineDef = [
1349
+ definePipelineStep(
1350
+ "rectDiffSolver",
1351
+ RectDiffSolver,
1352
+ (instance) => [
1353
+ {
1354
+ simpleRouteJson: instance.inputProblem.simpleRouteJson,
1355
+ gridOptions: instance.inputProblem.gridOptions
1356
+ }
1357
+ ],
1358
+ {
1359
+ onSolved: () => {
1360
+ }
1361
+ }
1362
+ )
1363
+ ];
1364
+ getConstructorParams() {
1365
+ return [this.inputProblem];
1366
+ }
1367
+ getOutput() {
1368
+ return this.getSolver("rectDiffSolver").getOutput();
1369
+ }
1370
+ visualize() {
1371
+ const solver = this.getSolver("rectDiffSolver");
1372
+ if (solver) {
1373
+ return solver.visualize();
1374
+ }
1375
+ return createBaseVisualization(
1376
+ this.inputProblem.simpleRouteJson,
1377
+ "RectDiff Pipeline (not started)"
1378
+ );
1379
+ }
1380
+ };
1357
1381
  export {
1358
- RectDiffSolver
1382
+ RectDiffPipeline
1359
1383
  };
@@ -0,0 +1,55 @@
1
+ import { BasePipelineSolver, definePipelineStep } from "@tscircuit/solver-utils"
2
+ import type { SimpleRouteJson } from "./types/srj-types"
3
+ import type { GridFill3DOptions } from "./solvers/rectdiff/types"
4
+ import { RectDiffSolver } from "./solvers/RectDiffSolver"
5
+ import type { CapacityMeshNode } from "./types/capacity-mesh-types"
6
+ import type { GraphicsObject } from "graphics-debug"
7
+ import { createBaseVisualization } from "./solvers/rectdiff/visualization"
8
+
9
+ export interface RectDiffPipelineInput {
10
+ simpleRouteJson: SimpleRouteJson
11
+ gridOptions?: Partial<GridFill3DOptions>
12
+ }
13
+
14
+ export class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput> {
15
+ rectDiffSolver?: RectDiffSolver
16
+
17
+ override pipelineDef = [
18
+ definePipelineStep(
19
+ "rectDiffSolver",
20
+ RectDiffSolver,
21
+ (instance) => [
22
+ {
23
+ simpleRouteJson: instance.inputProblem.simpleRouteJson,
24
+ gridOptions: instance.inputProblem.gridOptions,
25
+ },
26
+ ],
27
+ {
28
+ onSolved: () => {
29
+ // RectDiff mesh generation completed
30
+ },
31
+ },
32
+ ),
33
+ ]
34
+
35
+ override getConstructorParams() {
36
+ return [this.inputProblem]
37
+ }
38
+
39
+ override getOutput(): { meshNodes: CapacityMeshNode[] } {
40
+ return this.getSolver<RectDiffSolver>("rectDiffSolver")!.getOutput()
41
+ }
42
+
43
+ override visualize(): GraphicsObject {
44
+ const solver = this.getSolver<RectDiffSolver>("rectDiffSolver")
45
+ if (solver) {
46
+ return solver.visualize()
47
+ }
48
+
49
+ // Show board and obstacles even before solver is initialized
50
+ return createBaseVisualization(
51
+ this.inputProblem.simpleRouteJson,
52
+ "RectDiff Pipeline (not started)",
53
+ )
54
+ }
55
+ }
package/lib/index.ts CHANGED
@@ -1 +1 @@
1
- export * from "./solvers/RectDiffSolver"
1
+ export * from "./RectDiffPipeline"
@@ -1,5 +1,5 @@
1
1
  // lib/solvers/RectDiffSolver.ts
2
- import { BaseSolver } from "@tscircuit/solver-utils"
2
+ import { BaseSolver, BasePipelineSolver } from "@tscircuit/solver-utils"
3
3
  import type { SimpleRouteJson } from "../types/srj-types"
4
4
  import type { GraphicsObject } from "graphics-debug"
5
5
  import type { CapacityMeshNode } from "../types/capacity-mesh-types"
@@ -14,11 +14,6 @@ import {
14
14
  } from "./rectdiff/engine"
15
15
  import { rectsToMeshNodes } from "./rectdiff/rectsToMeshNodes"
16
16
  import { overlaps } from "./rectdiff/geometry"
17
- import type { GapFillOptions } from "./rectdiff/gapfill/types"
18
- import {
19
- findUncoveredPoints,
20
- calculateCoverage,
21
- } from "./rectdiff/gapfill/engine"
22
17
 
23
18
  /**
24
19
  * A streaming, one-step-per-iteration solver for capacity mesh generation.
@@ -82,34 +77,6 @@ export class RectDiffSolver extends BaseSolver {
82
77
  return { meshNodes: this._meshNodes }
83
78
  }
84
79
 
85
- /** Get coverage percentage (0-1). */
86
- getCoverage(sampleResolution: number = 0.05): number {
87
- return calculateCoverage(
88
- { sampleResolution },
89
- {
90
- bounds: this.state.bounds,
91
- layerCount: this.state.layerCount,
92
- obstaclesByLayer: this.state.obstaclesByLayer,
93
- placedByLayer: this.state.placedByLayer,
94
- },
95
- )
96
- }
97
-
98
- /** Find uncovered points for debugging gaps. */
99
- getUncoveredPoints(
100
- sampleResolution: number = 0.05,
101
- ): Array<{ x: number; y: number; z: number }> {
102
- return findUncoveredPoints(
103
- { sampleResolution },
104
- {
105
- bounds: this.state.bounds,
106
- layerCount: this.state.layerCount,
107
- obstaclesByLayer: this.state.obstaclesByLayer,
108
- placedByLayer: this.state.placedByLayer,
109
- },
110
- )
111
- }
112
-
113
80
  /** Get color based on z layer for visualization. */
114
81
  private getColorForZLayer(zLayers: number[]): {
115
82
  fill: string