@tscircuit/rectdiff 0.0.7 → 0.0.9
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 -19
- package/dist/index.js +93 -1
- package/lib/RectDiffPipeline.ts +55 -0
- package/lib/index.ts +1 -1
- package/lib/solvers/RectDiffSolver.ts +1 -1
- 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 +21 -0
- package/test-assets/bugreport04-aa1d41.json +5378 -0
- 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
|
@@ -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
|
*/
|
|
@@ -120,4 +121,18 @@ declare class RectDiffSolver extends BaseSolver {
|
|
|
120
121
|
visualize(): GraphicsObject;
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
|
|
124
|
+
interface RectDiffPipelineInput {
|
|
125
|
+
simpleRouteJson: SimpleRouteJson;
|
|
126
|
+
gridOptions?: Partial<GridFill3DOptions>;
|
|
127
|
+
}
|
|
128
|
+
declare class RectDiffPipeline extends BasePipelineSolver<RectDiffPipelineInput> {
|
|
129
|
+
rectDiffSolver?: RectDiffSolver;
|
|
130
|
+
pipelineDef: _tscircuit_solver_utils.PipelineStep<RectDiffSolver>[];
|
|
131
|
+
getConstructorParams(): RectDiffPipelineInput[];
|
|
132
|
+
getOutput(): {
|
|
133
|
+
meshNodes: CapacityMeshNode[];
|
|
134
|
+
};
|
|
135
|
+
visualize(): GraphicsObject;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
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
|
|
|
@@ -1354,6 +1357,95 @@ z:${p.zLayers.join(",")}`
|
|
|
1354
1357
|
};
|
|
1355
1358
|
}
|
|
1356
1359
|
};
|
|
1360
|
+
|
|
1361
|
+
// lib/solvers/rectdiff/visualization.ts
|
|
1362
|
+
function createBaseVisualization(srj, title = "RectDiff") {
|
|
1363
|
+
const rects = [];
|
|
1364
|
+
const lines = [];
|
|
1365
|
+
const boardBounds = {
|
|
1366
|
+
minX: srj.bounds.minX,
|
|
1367
|
+
maxX: srj.bounds.maxX,
|
|
1368
|
+
minY: srj.bounds.minY,
|
|
1369
|
+
maxY: srj.bounds.maxY
|
|
1370
|
+
};
|
|
1371
|
+
if (srj.outline && srj.outline.length > 1) {
|
|
1372
|
+
lines.push({
|
|
1373
|
+
points: [...srj.outline, srj.outline[0]],
|
|
1374
|
+
strokeColor: "#111827",
|
|
1375
|
+
strokeWidth: 0.01,
|
|
1376
|
+
label: "outline"
|
|
1377
|
+
});
|
|
1378
|
+
} else {
|
|
1379
|
+
rects.push({
|
|
1380
|
+
center: {
|
|
1381
|
+
x: (boardBounds.minX + boardBounds.maxX) / 2,
|
|
1382
|
+
y: (boardBounds.minY + boardBounds.maxY) / 2
|
|
1383
|
+
},
|
|
1384
|
+
width: boardBounds.maxX - boardBounds.minX,
|
|
1385
|
+
height: boardBounds.maxY - boardBounds.minY,
|
|
1386
|
+
fill: "none",
|
|
1387
|
+
stroke: "#111827",
|
|
1388
|
+
label: "board"
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
for (const obstacle of srj.obstacles ?? []) {
|
|
1392
|
+
if (obstacle.type === "rect" || obstacle.type === "oval") {
|
|
1393
|
+
rects.push({
|
|
1394
|
+
center: { x: obstacle.center.x, y: obstacle.center.y },
|
|
1395
|
+
width: obstacle.width,
|
|
1396
|
+
height: obstacle.height,
|
|
1397
|
+
fill: "#fee2e2",
|
|
1398
|
+
stroke: "#ef4444",
|
|
1399
|
+
layer: "obstacle",
|
|
1400
|
+
label: "obstacle"
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
return {
|
|
1405
|
+
title,
|
|
1406
|
+
coordinateSystem: "cartesian",
|
|
1407
|
+
rects,
|
|
1408
|
+
points: [],
|
|
1409
|
+
lines
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
// lib/RectDiffPipeline.ts
|
|
1414
|
+
var RectDiffPipeline = class extends BasePipelineSolver2 {
|
|
1415
|
+
rectDiffSolver;
|
|
1416
|
+
pipelineDef = [
|
|
1417
|
+
definePipelineStep(
|
|
1418
|
+
"rectDiffSolver",
|
|
1419
|
+
RectDiffSolver,
|
|
1420
|
+
(instance) => [
|
|
1421
|
+
{
|
|
1422
|
+
simpleRouteJson: instance.inputProblem.simpleRouteJson,
|
|
1423
|
+
gridOptions: instance.inputProblem.gridOptions
|
|
1424
|
+
}
|
|
1425
|
+
],
|
|
1426
|
+
{
|
|
1427
|
+
onSolved: () => {
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
)
|
|
1431
|
+
];
|
|
1432
|
+
getConstructorParams() {
|
|
1433
|
+
return [this.inputProblem];
|
|
1434
|
+
}
|
|
1435
|
+
getOutput() {
|
|
1436
|
+
return this.getSolver("rectDiffSolver").getOutput();
|
|
1437
|
+
}
|
|
1438
|
+
visualize() {
|
|
1439
|
+
const solver = this.getSolver("rectDiffSolver");
|
|
1440
|
+
if (solver) {
|
|
1441
|
+
return solver.visualize();
|
|
1442
|
+
}
|
|
1443
|
+
return createBaseVisualization(
|
|
1444
|
+
this.inputProblem.simpleRouteJson,
|
|
1445
|
+
"RectDiff Pipeline (not started)"
|
|
1446
|
+
);
|
|
1447
|
+
}
|
|
1448
|
+
};
|
|
1357
1449
|
export {
|
|
1358
|
-
|
|
1450
|
+
RectDiffPipeline
|
|
1359
1451
|
};
|
|
@@ -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"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { GraphicsObject } from "graphics-debug"
|
|
2
|
+
import type { SimpleRouteJson } from "../../types/srj-types"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create basic visualization showing board bounds/outline and obstacles.
|
|
6
|
+
* This can be used before solver initialization to show the problem space.
|
|
7
|
+
*/
|
|
8
|
+
export function createBaseVisualization(
|
|
9
|
+
srj: SimpleRouteJson,
|
|
10
|
+
title: string = "RectDiff",
|
|
11
|
+
): GraphicsObject {
|
|
12
|
+
const rects: NonNullable<GraphicsObject["rects"]> = []
|
|
13
|
+
const lines: NonNullable<GraphicsObject["lines"]> = []
|
|
14
|
+
|
|
15
|
+
const boardBounds = {
|
|
16
|
+
minX: srj.bounds.minX,
|
|
17
|
+
maxX: srj.bounds.maxX,
|
|
18
|
+
minY: srj.bounds.minY,
|
|
19
|
+
maxY: srj.bounds.maxY,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Draw board outline or bounds rectangle
|
|
23
|
+
if (srj.outline && srj.outline.length > 1) {
|
|
24
|
+
lines.push({
|
|
25
|
+
points: [...srj.outline, srj.outline[0]!],
|
|
26
|
+
strokeColor: "#111827",
|
|
27
|
+
strokeWidth: 0.01,
|
|
28
|
+
label: "outline",
|
|
29
|
+
})
|
|
30
|
+
} else {
|
|
31
|
+
rects.push({
|
|
32
|
+
center: {
|
|
33
|
+
x: (boardBounds.minX + boardBounds.maxX) / 2,
|
|
34
|
+
y: (boardBounds.minY + boardBounds.maxY) / 2,
|
|
35
|
+
},
|
|
36
|
+
width: boardBounds.maxX - boardBounds.minX,
|
|
37
|
+
height: boardBounds.maxY - boardBounds.minY,
|
|
38
|
+
fill: "none",
|
|
39
|
+
stroke: "#111827",
|
|
40
|
+
label: "board",
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Draw obstacles
|
|
45
|
+
for (const obstacle of srj.obstacles ?? []) {
|
|
46
|
+
if (obstacle.type === "rect" || obstacle.type === "oval") {
|
|
47
|
+
rects.push({
|
|
48
|
+
center: { x: obstacle.center.x, y: obstacle.center.y },
|
|
49
|
+
width: obstacle.width,
|
|
50
|
+
height: obstacle.height,
|
|
51
|
+
fill: "#fee2e2",
|
|
52
|
+
stroke: "#ef4444",
|
|
53
|
+
layer: "obstacle",
|
|
54
|
+
label: "obstacle",
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
title,
|
|
61
|
+
coordinateSystem: "cartesian",
|
|
62
|
+
rects,
|
|
63
|
+
points: [],
|
|
64
|
+
lines,
|
|
65
|
+
}
|
|
66
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/rectdiff",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"@biomejs/biome": "^2.3.5",
|
|
15
15
|
"@react-hook/resize-observer": "^2.0.2",
|
|
16
|
-
"@tscircuit/solver-utils": "^0.0.
|
|
16
|
+
"@tscircuit/solver-utils": "^0.0.7",
|
|
17
17
|
"@types/bun": "latest",
|
|
18
18
|
"@types/react": "^18",
|
|
19
19
|
"@types/react-dom": "^18",
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { GenericSolverDebugger } from "@tscircuit/solver-utils/react"
|
|
2
1
|
import simpleRouteJson from "../test-assets/board-with-cutout.json"
|
|
3
|
-
import {
|
|
2
|
+
import { RectDiffPipeline } from "../lib/RectDiffPipeline"
|
|
4
3
|
import { useMemo } from "react"
|
|
5
4
|
import { SolverDebugger3d } from "../components/SolverDebugger3d"
|
|
6
5
|
|
|
7
6
|
export default () => {
|
|
8
|
-
const solver = useMemo(() => new
|
|
7
|
+
const solver = useMemo(() => new RectDiffPipeline({ simpleRouteJson }), [])
|
|
9
8
|
|
|
10
|
-
return <SolverDebugger3d solver={solver} />
|
|
9
|
+
return <SolverDebugger3d solver={solver} simpleRouteJson={simpleRouteJson} />
|
|
11
10
|
}
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import simpleRouteJson from "../test-assets/bugreport11-b2de3c.json"
|
|
2
|
-
import {
|
|
2
|
+
import { RectDiffPipeline } from "../lib/RectDiffPipeline"
|
|
3
3
|
import { useMemo } from "react"
|
|
4
4
|
import { SolverDebugger3d } from "../components/SolverDebugger3d"
|
|
5
5
|
|
|
6
6
|
export default () => {
|
|
7
7
|
const solver = useMemo(
|
|
8
8
|
() =>
|
|
9
|
-
new
|
|
9
|
+
new RectDiffPipeline({
|
|
10
10
|
simpleRouteJson: simpleRouteJson.simple_route_json,
|
|
11
11
|
}),
|
|
12
12
|
[],
|
|
13
13
|
)
|
|
14
14
|
|
|
15
|
-
return
|
|
15
|
+
return (
|
|
16
|
+
<SolverDebugger3d
|
|
17
|
+
solver={solver}
|
|
18
|
+
simpleRouteJson={simpleRouteJson.simple_route_json}
|
|
19
|
+
/>
|
|
20
|
+
)
|
|
16
21
|
}
|