@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.
- package/components/SolverDebugger3d.tsx +92 -162
- package/dist/index.d.ts +34 -27
- package/dist/index.js +93 -69
- package/lib/RectDiffPipeline.ts +55 -0
- package/lib/index.ts +1 -1
- package/lib/solvers/RectDiffSolver.ts +1 -34
- package/lib/solvers/rectdiff/visualization.ts +66 -0
- package/package.json +2 -2
- package/pages/board-with-cutout.page.tsx +3 -4
- package/pages/bugreport11.page.tsx +8 -3
- package/pages/example01.page.tsx +3 -4
- package/pages/keyboard-bugreport04.page.tsx +8 -4
- package/tests/board-outline.test.ts +2 -2
- package/tests/examples/example01.test.tsx +2 -2
- package/tests/incremental-solver.test.ts +10 -16
- package/tests/obstacle-extra-layers.test.ts +12 -5
- package/tests/obstacle-zlayers.test.ts +13 -5
- package/tests/rect-diff-solver.test.ts +14 -23
- package/lib/solvers/rectdiff/gapfill/detection/deduplicateGaps.ts +0 -28
- package/lib/solvers/rectdiff/gapfill/detection/findAllGaps.ts +0 -83
- package/lib/solvers/rectdiff/gapfill/detection/findGapsOnLayer.ts +0 -100
- package/lib/solvers/rectdiff/gapfill/detection/mergeUncoveredCells.ts +0 -75
- package/lib/solvers/rectdiff/gapfill/detection.ts +0 -3
- package/lib/solvers/rectdiff/gapfill/engine/addPlacement.ts +0 -27
- package/lib/solvers/rectdiff/gapfill/engine/calculateCoverage.ts +0 -44
- package/lib/solvers/rectdiff/gapfill/engine/findUncoveredPoints.ts +0 -43
- package/lib/solvers/rectdiff/gapfill/engine/getGapFillProgress.ts +0 -42
- package/lib/solvers/rectdiff/gapfill/engine/initGapFillState.ts +0 -57
- package/lib/solvers/rectdiff/gapfill/engine/stepGapFill.ts +0 -128
- package/lib/solvers/rectdiff/gapfill/engine/tryExpandGap.ts +0 -78
- package/lib/solvers/rectdiff/gapfill/engine.ts +0 -7
- package/lib/solvers/rectdiff/gapfill/types.ts +0 -60
- 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 [
|
|
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
|
-
|
|
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:
|
|
622
|
+
gap: 12,
|
|
628
623
|
alignItems: "center",
|
|
629
|
-
|
|
624
|
+
padding: "12px 16px",
|
|
625
|
+
background: "#f8fafc",
|
|
626
|
+
borderRadius: 8,
|
|
627
|
+
border: "1px solid #e2e8f0",
|
|
630
628
|
}}
|
|
631
629
|
>
|
|
632
|
-
<
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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: "
|
|
636
|
+
padding: "6px 12px",
|
|
650
637
|
borderRadius: 6,
|
|
651
638
|
border: "1px solid #cbd5e1",
|
|
652
|
-
background: "
|
|
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
|
-
|
|
659
|
-
|
|
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
|
-
{
|
|
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:
|
|
672
|
+
fontSize: 13,
|
|
744
673
|
}}
|
|
745
674
|
>
|
|
746
675
|
<input
|
|
747
676
|
type="checkbox"
|
|
748
|
-
checked={
|
|
749
|
-
onChange={(e) =>
|
|
677
|
+
checked={showRoot}
|
|
678
|
+
onChange={(e) => setShowRoot(e.target.checked)}
|
|
750
679
|
/>
|
|
751
|
-
|
|
680
|
+
Root
|
|
752
681
|
</label>
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
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
|
-
|
|
714
|
+
display: "inline-flex",
|
|
715
|
+
gap: 6,
|
|
716
|
+
alignItems: "center",
|
|
717
|
+
fontSize: 13,
|
|
798
718
|
}}
|
|
799
719
|
>
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
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
|
-
{
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 "./
|
|
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
|