@tscircuit/rectdiff 0.0.1
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/.claude/settings.local.json +9 -0
- package/.github/workflows/bun-formatcheck.yml +26 -0
- package/.github/workflows/bun-pver-release.yml +71 -0
- package/.github/workflows/bun-test.yml +31 -0
- package/.github/workflows/bun-typecheck.yml +26 -0
- package/CLAUDE.md +23 -0
- package/README.md +5 -0
- package/biome.json +93 -0
- package/bun.lock +29 -0
- package/bunfig.toml +5 -0
- package/components/SolverDebugger3d.tsx +833 -0
- package/cosmos.config.json +6 -0
- package/cosmos.decorator.tsx +21 -0
- package/dist/index.d.ts +111 -0
- package/dist/index.js +921 -0
- package/experiments/rect-fill-2d.tsx +983 -0
- package/experiments/rect3d_visualizer.html +640 -0
- package/global.d.ts +4 -0
- package/index.html +12 -0
- package/lib/index.ts +1 -0
- package/lib/solvers/RectDiffSolver.ts +158 -0
- package/lib/solvers/rectdiff/candidates.ts +397 -0
- package/lib/solvers/rectdiff/engine.ts +355 -0
- package/lib/solvers/rectdiff/geometry.ts +284 -0
- package/lib/solvers/rectdiff/layers.ts +48 -0
- package/lib/solvers/rectdiff/rectsToMeshNodes.ts +22 -0
- package/lib/solvers/rectdiff/types.ts +63 -0
- package/lib/types/capacity-mesh-types.ts +33 -0
- package/lib/types/srj-types.ts +37 -0
- package/package.json +33 -0
- package/pages/example01.page.tsx +11 -0
- package/test-assets/example01.json +933 -0
- package/tests/__snapshots__/svg.snap.svg +3 -0
- package/tests/examples/__snapshots__/example01.snap.svg +121 -0
- package/tests/examples/example01.test.tsx +65 -0
- package/tests/fixtures/preload.ts +1 -0
- package/tests/incremental-solver.test.ts +100 -0
- package/tests/rect-diff-solver.test.ts +154 -0
- package/tests/svg.test.ts +12 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<svg width="640" height="640" viewBox="0 0 640 640" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="white"/><g><rect data-type="rect" data-label="board" data-x="0" data-y="0" x="86.66666666666666" y="40" width="466.66666666666674" height="560" fill="none" stroke="#111827" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-5.856893060790263" data-y="-8.275436101920713" x="205.07132953191507" y="472.9348072358533" width="11.200000000000017" height="3.079999999999984" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-5.856893060790263" data-y="-8.425436101920713" x="205.07132953191507" y="475.73480723585334" width="11.200000000000017" height="3.079999999999984" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-5.856893060790263" data-y="-8.575436101920712" x="205.07132953191507" y="478.5348072358533" width="11.200000000000017" height="3.079999999999984" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-5.856893060790263" data-y="-8.725436101920712" x="205.07132953191507" y="481.3348072358533" width="11.200000000000017" height="3.079999999999984" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-6.856893060790263" data-y="-8.275436101920713" x="186.4046628652484" y="472.9348072358533" width="11.200000000000017" height="3.079999999999984" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-6.856893060790263" data-y="-8.425436101920713" x="186.4046628652484" y="475.73480723585334" width="11.200000000000017" height="3.079999999999984" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-6.856893060790263" data-y="-8.575436101920712" x="186.4046628652484" y="478.5348072358533" width="11.200000000000017" height="3.079999999999984" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-6.856893060790263" data-y="-8.725436101920712" x="186.4046628652484" y="481.3348072358533" width="11.200000000000017" height="3.079999999999984" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-6.040227999999999" data-y="12.073" x="181.58241066666668" y="75.97066666666663" width="51.33333333333334" height="37.33333333333334" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-6.040227999999999" data-y="9.533000000000001" x="181.58241066666668" y="123.38399999999996" width="51.33333333333334" height="37.33333333333334" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-6.040227999999999" data-y="6.993" x="181.58241066666668" y="170.7973333333333" width="51.33333333333334" height="37.33333333333334" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-6.040227999999999" data-y="4.453" x="181.58241066666668" y="218.21066666666667" width="51.33333333333334" height="37.333333333333314" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-6.040227999999999" data-y="1.9129999999999998" x="181.58241066666668" y="265.624" width="51.33333333333334" height="37.333333333333314" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-6.040227999999999" data-y="-0.6269999999999998" x="181.58241066666668" y="313.0373333333333" width="51.33333333333334" height="37.33333333333337" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-6.040227999999999" data-y="-3.167" x="181.58241066666668" y="360.4506666666667" width="51.33333333333334" height="37.333333333333314" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="10.124772" data-y="-3.167" x="483.3290773333333" y="360.4506666666667" width="51.33333333333337" height="37.333333333333314" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="10.124772" data-y="-0.6269999999999998" x="483.3290773333333" y="313.0373333333333" width="51.33333333333337" height="37.33333333333337" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="10.124772" data-y="1.9129999999999998" x="483.3290773333333" y="265.624" width="51.33333333333337" height="37.333333333333314" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="10.124772" data-y="4.453" x="483.3290773333333" y="218.21066666666667" width="51.33333333333337" height="37.333333333333314" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="10.124772" data-y="6.993" x="483.3290773333333" y="170.7973333333333" width="51.33333333333337" height="37.33333333333334" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="10.124772" data-y="9.533000000000001" x="483.3290773333333" y="123.38399999999996" width="51.33333333333337" height="37.33333333333334" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="10.124772" data-y="12.073" x="483.3290773333333" y="75.97066666666663" width="51.33333333333337" height="37.33333333333334" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-2.4302279999999996" data-y="4.835" x="251.30241066666667" y="219.48000000000002" width="46.666666666666686" height="20.533333333333303" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-2.4302279999999996" data-y="6.74" x="251.30241066666667" y="183.92" width="46.666666666666686" height="20.53333333333333" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="0.7447720000000002" data-y="13.085" x="318.035744" y="59.879999999999995" width="31.73333333333335" height="31.73333333333329" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="3.2847720000000002" data-y="13.085" x="365.4490773333333" y="59.879999999999995" width="31.73333333333335" height="31.73333333333329" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="0.7447720000000002" data-y="10.545" x="318.035744" y="107.29333333333332" width="31.73333333333335" height="31.73333333333332" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="3.2847720000000002" data-y="10.545" x="365.4490773333333" y="107.29333333333332" width="31.73333333333335" height="31.73333333333332" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="0.7447720000000002" data-y="8.004999999999999" x="318.035744" y="154.70666666666668" width="31.73333333333335" height="31.73333333333332" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="3.2847720000000002" data-y="8.004999999999999" x="365.4490773333333" y="154.70666666666668" width="31.73333333333335" height="31.73333333333332" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="0.7447720000000002" data-y="5.465" x="318.035744" y="202.12" width="31.73333333333335" height="31.73333333333332" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="3.2847720000000002" data-y="5.465" x="365.4490773333333" y="202.12" width="31.73333333333335" height="31.73333333333332" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="3.6885631738662035" data-y="-10.96420520580684" x="374.8531792455025" y="510.6651638417277" width="28" height="28" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="3.6885631738662035" data-y="-8.96420520580684" x="374.8531792455025" y="473.3318305083943" width="28" height="28" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-10.727385294299685" data-y="-11.579453643315949" x="105.75547450640587" y="522.1498013418977" width="28" height="28" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-10.727385294299685" data-y="-8.079453643315949" x="105.75547450640587" y="456.81646800856436" width="28" height="28" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-10.727385294299685" data-y="-4.579453643315949" x="105.75547450640587" y="391.48313467523104" width="28" height="28" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="obstacle" data-x="-10.727385294299685" data-y="-1.079453643315949" x="105.75547450640587" y="326.1498013418977" width="28" height="28" fill="#fee2e2" stroke="#ef4444" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
2
|
+
z:0,1,2,3" data-x="3.7847720000000002" data-y="-1.7996026029034198" x="297.96907733333336" y="233.85333333333332" width="185.35999999999996" height="239.478497175061" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
3
|
+
z:0,1,2,3" data-x="-2.9227279999999993" data-y="-5.3575" x="232.91574400000002" y="240.01333333333332" width="65.05333333333334" height="359.9866666666667" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
4
|
+
z:0,1,2,3" data-x="-2.3852279999999992" data-y="11.145" x="232.91574400000002" y="40" width="85.12" height="143.92" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
5
|
+
z:0,1,2,3" data-x="6.442272" data-y="9.807500000000001" x="397.18241066666667" y="40" width="86.14666666666665" height="193.8533333333333" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
6
|
+
z:0,1,2,3" data-x="-7.321306647149842" data-y="-6.179968050960356" x="133.75547450640587" y="397.784" width="99.16026949359414" height="75.15080723585334" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
7
|
+
z:0,1,2,3" data-x="-9.957614" data-y="7.372841999999999" x="86.66666666666666" y="40" width="94.91574400000002" height="284.74723200000005" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
8
|
+
z:0,1,2,3" data-x="-7.321306647149842" data-y="-11.903968050960355" x="133.75547450640587" y="484.41480723585323" width="99.16026949359414" height="115.58519276414677" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
9
|
+
z:0,1,2,3" data-x="8.469281586933102" data-y="-11.60710260290342" x="402.8531792455025" y="473.3318305083944" width="150.4801540878309" height="126.66816949160562" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
10
|
+
z:0,1,2,3" data-x="-8.696306647149843" data-y="-2.2106580000000013" x="133.75547450640585" y="324.74723200000005" width="47.8269361602608" height="73.03676799999994" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
11
|
+
z:0,1,2,3" data-x="0.8791675869331019" data-y="-11.60710260290342" x="297.96907733333336" y="473.3318305083944" width="76.88410191216911" height="126.66816949160562" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
12
|
+
z:0,1,2,3" data-x="2.8747719999999974" data-y="9.274999999999999" x="350.1424106666666" y="139.02666666666664" width="47.04000000000008" height="15.680000000000064" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
13
|
+
z:0,1,2,3" data-x="10.624886" data-y="-6.19060260290342" x="483.3290773333333" y="397.784" width="70.00425600000005" height="75.54783050839433" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
14
|
+
z:0,1,2,3" data-x="0.7547719999999978" data-y="9.274999999999999" x="318.035744" y="139.02666666666664" width="32.10666666666657" height="15.680000000000064" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
15
|
+
z:0,1,2,3" data-x="-11.238692647149843" data-y="-13.664726821657975" x="86.66666666666666" y="550.1498013418977" width="47.08880783973919" height="49.85019865810227" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
16
|
+
z:0,1,2,3" data-x="-11.238692647149843" data-y="-9.829453643315949" x="86.66666666666666" y="484.81646800856436" width="47.08880783973919" height="37.33333333333337" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
17
|
+
z:0,1,2,3" data-x="-11.238692647149843" data-y="-6.329453643315949" x="86.66666666666666" y="419.48313467523104" width="47.08880783973919" height="37.333333333333314" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
18
|
+
z:0,1,2,3" data-x="-11.238692647149843" data-y="-2.829453643315949" x="86.66666666666666" y="354.1498013418977" width="47.08880783973919" height="37.333333333333314" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
19
|
+
z:0,1,2,3" data-x="3.6885631738662035" data-y="-13.35710260290342" x="374.8531792455025" y="538.6651638417277" width="28" height="61.33483615827231" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
20
|
+
z:0,1,2,3" data-x="2.6799999999999997" data-y="11.815000000000001" x="346.5066666666666" y="91.61333333333326" width="47.040000000000134" height="15.680000000000064" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
21
|
+
z:0,1,2,3" data-x="10.624886" data-y="14.0365" x="483.3290773333333" y="40" width="70.00425600000005" height="35.97066666666663" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
22
|
+
z:0,1,2,3" data-x="-4.172727999999999" data-y="5.812500000000002" x="232.91574400000002" y="183.92" width="18.386666666666656" height="55.15999999999994" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
23
|
+
z:1,3" data-x="-2.4302279999999996" data-y="5.7875000000000005" x="251.30241066666667" y="183.92" width="46.666666666666686" height="56.093333333333334" fill="#fef3c7" stroke="#f59e0b" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
24
|
+
z:0,1,2,3" data-x="-6.040227999999999" data-y="14.0365" x="181.58241066666668" y="40" width="51.33333333333334" height="35.97066666666663" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
25
|
+
z:2" data-x="-2.4302279999999996" data-y="5.7875000000000005" x="251.30241066666667" y="183.92" width="46.666666666666686" height="56.093333333333334" fill="#d1fae5" stroke="#10b981" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
26
|
+
z:0,1,2,3" data-x="-0.6427279999999997" data-y="5.952500000000001" x="297.96907733333336" y="183.92" width="20.066666666666663" height="49.93333333333334" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
27
|
+
z:0,1,2,3" data-x="0.6573859999999989" data-y="11.815000000000001" x="318.035744" y="91.61333333333326" width="28.47092266666658" height="15.680000000000064" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
28
|
+
z:2" data-x="0.32477200000000095" data-y="13.6175" x="318.035744" y="40" width="16.05333333333334" height="51.613333333333316" fill="#d1fae5" stroke="#10b981" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
29
|
+
z:0,1,2,3" data-x="-11.988692647149843" data-y="-11.579453643315949" x="86.66666666666666" y="522.1498013418977" width="19.08880783973919" height="28" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
30
|
+
z:0,1,2,3" data-x="-11.988692647149843" data-y="-8.079453643315949" x="86.66666666666666" y="456.81646800856436" width="19.08880783973919" height="28" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
31
|
+
z:0,1,2,3" data-x="-11.988692647149843" data-y="-4.579453643315949" x="86.66666666666666" y="391.48313467523104" width="19.08880783973919" height="28" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
32
|
+
z:0,1,2,3" data-x="-11.988692647149843" data-y="-1.041884821657976" x="86.66666666666666" y="324.74723200000005" width="19.08880783973919" height="29.402569341897674" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
33
|
+
z:0,1,2,3" data-x="2.0147720000000002" data-y="7.494999999999999" x="349.76907733333337" y="154.70666666666668" width="15.67999999999995" height="50.77333333333331" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
34
|
+
z:1,3" data-x="3.2847720000000002" data-y="6.734999999999999" x="365.4490773333333" y="154.70666666666668" width="31.73333333333335" height="79.14666666666665" fill="#fef3c7" stroke="#f59e0b" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
35
|
+
z:2" data-x="3.2847720000000002" data-y="6.734999999999999" x="365.4490773333333" y="154.70666666666668" width="31.73333333333335" height="79.14666666666665" fill="#d1fae5" stroke="#10b981" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
36
|
+
z:1" data-x="0.7447720000000004" data-y="6.734999999999999" x="318.035744" y="154.70666666666668" width="31.73333333333335" height="79.14666666666665" fill="#fef3c7" stroke="#f59e0b" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
37
|
+
z:2" data-x="0.7447720000000004" data-y="6.734999999999999" x="318.035744" y="154.70666666666668" width="31.73333333333335" height="79.14666666666665" fill="#d1fae5" stroke="#10b981" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
38
|
+
z:3" data-x="0.7447720000000004" data-y="6.734999999999999" x="318.035744" y="154.70666666666668" width="31.73333333333335" height="79.14666666666665" fill="#e9d5ff" stroke="#a855f7" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
39
|
+
z:0,1,2,3" data-x="2.0147720000000002" data-y="10.545" x="349.76907733333337" y="107.29333333333332" width="15.67999999999995" height="31.73333333333332" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
40
|
+
z:0,1,2,3" data-x="2.3522720000000006" data-y="14.467500000000001" x="334.08907733333336" y="40" width="59.639999999999986" height="19.87999999999994" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
41
|
+
z:2" data-x="1.0873859999999993" data-y="13.085" x="334.08907733333336" y="59.879999999999995" width="12.41758933333324" height="31.73333333333329" fill="#d1fae5" stroke="#10b981" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
42
|
+
z:0,1,2,3" data-x="2.0147720000000002" data-y="13.085" x="349.76907733333337" y="59.879999999999995" width="15.67999999999995" height="31.73333333333329" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
43
|
+
z:0" data-x="-6.040227999999999" data-y="0.643" x="181.58241066666668" y="302.95733333333334" width="51.33333333333334" height="10.079999999999984" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
44
|
+
z:0" data-x="0.324772000000001" data-y="14.467500000000001" x="318.035744" y="39.99999999999994" width="16.05333333333334" height="19.880000000000052" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
45
|
+
z:0" data-x="0.7447720000000002" data-y="6.734999999999999" x="318.035744" y="186.44" width="31.73333333333335" height="15.680000000000007" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
46
|
+
z:0,1,2,3" data-x="11.999886" data-y="8.463000000000001" x="534.6624106666667" y="75.97066666666663" width="18.670922666666684" height="172.10666666666668" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
47
|
+
z:0,1,2,3" data-x="11.999886" data-y="-0.09699999999999953" x="534.6624106666667" y="248.0773333333333" width="18.670922666666684" height="147.46666666666667" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
48
|
+
z:0" data-x="10.124772" data-y="0.643" x="483.3290773333333" y="302.95733333333334" width="51.33333333333337" height="10.079999999999984" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
49
|
+
z:0" data-x="-6.356893060790263" data-y="-8.500436101920712" x="197.60466286524843" y="472.9348072358533" width="7.46666666666664" height="11.480000000000018" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
50
|
+
z:0" data-x="-6.040227999999999" data-y="10.803" x="181.58241066666668" y="113.30399999999997" width="51.33333333333334" height="10.079999999999984" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
51
|
+
z:0" data-x="-6.040227999999999" data-y="8.263000000000002" x="181.58241066666668" y="160.71733333333327" width="51.33333333333334" height="10.080000000000041" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
52
|
+
z:0" data-x="-6.040227999999999" data-y="5.723000000000001" x="181.58241066666668" y="208.13066666666663" width="51.33333333333334" height="10.080000000000041" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
53
|
+
z:0" data-x="-6.040227999999999" data-y="3.183" x="181.58241066666668" y="255.54399999999998" width="51.33333333333334" height="10.080000000000041" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
54
|
+
z:0" data-x="-6.040227999999999" data-y="-1.8969999999999998" x="181.58241066666668" y="350.3706666666667" width="51.33333333333334" height="10.079999999999984" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
55
|
+
z:0" data-x="10.124772" data-y="-1.8969999999999998" x="483.3290773333333" y="350.3706666666667" width="51.33333333333337" height="10.079999999999984" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
56
|
+
z:0" data-x="10.124772" data-y="3.183" x="483.3290773333333" y="255.54399999999998" width="51.33333333333337" height="10.080000000000041" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
57
|
+
z:0" data-x="10.124772" data-y="5.723000000000001" x="483.3290773333333" y="208.13066666666663" width="51.33333333333337" height="10.080000000000041" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
58
|
+
z:0" data-x="10.124772" data-y="8.263000000000002" x="483.3290773333333" y="160.71733333333327" width="51.33333333333337" height="10.080000000000041" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
59
|
+
z:0" data-x="10.124772" data-y="10.803" x="483.3290773333333" y="113.30399999999997" width="51.33333333333337" height="10.079999999999984" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
60
|
+
z:0,1,2,3" data-x="2.0147720000000002" data-y="5.375" x="349.76907733333337" y="205.48" width="15.67999999999995" height="28.373333333333335" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
61
|
+
z:0" data-x="3.6885631738662035" data-y="-9.96420520580684" x="374.8531792455025" y="501.3318305083943" width="28" height="9.333333333333371" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
62
|
+
z:0" data-x="-2.4302279999999996" data-y="5.7875" x="251.30241066666667" y="204.45333333333332" width="46.666666666666686" height="15.0266666666667" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
63
|
+
z:0" data-x="3.2847720000000002" data-y="6.734999999999999" x="365.4490773333333" y="186.44" width="31.73333333333335" height="15.680000000000007" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
64
|
+
z:0" data-x="-8.567139177544973" data-y="-8.500436101920712" x="133.75547450640587" y="472.9348072358533" width="52.649188358842565" height="11.480000000000018" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
65
|
+
z:0" data-x="-5.111060530395131" data-y="-8.500436101920712" x="216.27132953191511" y="472.9348072358533" width="16.644414468084904" height="11.480000000000018" fill="#dbeafe" stroke="#3b82f6" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
66
|
+
z:1" data-x="3.6885631738662035" data-y="-9.96420520580684" x="374.8531792455025" y="501.3318305083943" width="28" height="9.333333333333371" fill="#fef3c7" stroke="#f59e0b" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
67
|
+
z:1" data-x="-7.321306647149842" data-y="-8.500436101920712" x="133.75547450640587" y="472.9348072358533" width="99.16026949359414" height="11.480000000000018" fill="#fef3c7" stroke="#f59e0b" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
68
|
+
z:2" data-x="3.6885631738662035" data-y="-9.96420520580684" x="374.8531792455025" y="501.3318305083943" width="28" height="9.333333333333371" fill="#d1fae5" stroke="#10b981" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
69
|
+
z:2" data-x="-7.321306647149842" data-y="-8.500436101920712" x="133.75547450640587" y="472.9348072358533" width="99.16026949359414" height="11.480000000000018" fill="#d1fae5" stroke="#10b981" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
70
|
+
z:3" data-x="3.6885631738662035" data-y="-9.96420520580684" x="374.8531792455025" y="501.3318305083943" width="28" height="9.333333333333371" fill="#e9d5ff" stroke="#a855f7" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
71
|
+
z:3" data-x="-7.321306647149842" data-y="-8.500436101920712" x="133.75547450640587" y="472.9348072358533" width="99.16026949359414" height="11.480000000000018" fill="#e9d5ff" stroke="#a855f7" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
72
|
+
z:1,2,3" data-x="0.7447720000000004" data-y="10.545" x="318.035744" y="107.29333333333332" width="31.73333333333335" height="31.73333333333332" fill="#fef3c7" stroke="#f59e0b" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
73
|
+
z:1,2,3" data-x="3.2847720000000002" data-y="10.545" x="365.4490773333333" y="107.29333333333332" width="31.73333333333335" height="31.73333333333332" fill="#fef3c7" stroke="#f59e0b" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
74
|
+
z:1,2,3" data-x="3.2847720000000002" data-y="13.085" x="365.4490773333333" y="59.879999999999995" width="31.73333333333335" height="31.73333333333329" fill="#fef3c7" stroke="#f59e0b" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
75
|
+
z:1,2,3" data-x="-6.040227999999999" data-y="4.453000000000001" x="181.58241066666668" y="75.9706666666666" width="51.33333333333334" height="321.8133333333334" fill="#fef3c7" stroke="#f59e0b" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
76
|
+
z:1,2,3" data-x="10.124772" data-y="4.453000000000001" x="483.3290773333333" y="75.9706666666666" width="51.33333333333337" height="321.8133333333334" fill="#fef3c7" stroke="#f59e0b" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
77
|
+
z:1,3" data-x="0.32477200000000095" data-y="13.617500000000001" x="318.035744" y="39.99999999999994" width="16.05333333333334" height="51.613333333333344" fill="#fef3c7" stroke="#f59e0b" stroke-width="0.05357142857142857"/></g><g><rect data-type="rect" data-label="free
|
|
78
|
+
z:1,3" data-x="1.1747720000000008" data-y="13.085" x="334.08907733333336" y="59.879999999999995" width="15.680000000000007" height="31.73333333333329" fill="#fef3c7" stroke="#f59e0b" stroke-width="0.05357142857142857"/></g><g id="crosshair" style="display: none"><line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5"/><line id="crosshair-v" x1="0" x2="640" stroke="#666" stroke-width="0.5"/><text id="coordinates" font-family="monospace" font-size="12" fill="#666"></text></g><script><![CDATA[
|
|
79
|
+
document.currentScript.parentElement.addEventListener('mousemove', (e) => {
|
|
80
|
+
const svg = e.currentTarget;
|
|
81
|
+
const rect = svg.getBoundingClientRect();
|
|
82
|
+
const x = e.clientX - rect.left;
|
|
83
|
+
const y = e.clientY - rect.top;
|
|
84
|
+
const crosshair = svg.getElementById('crosshair');
|
|
85
|
+
const h = svg.getElementById('crosshair-h');
|
|
86
|
+
const v = svg.getElementById('crosshair-v');
|
|
87
|
+
const coords = svg.getElementById('coordinates');
|
|
88
|
+
|
|
89
|
+
crosshair.style.display = 'block';
|
|
90
|
+
h.setAttribute('x1', '0');
|
|
91
|
+
h.setAttribute('x2', '640');
|
|
92
|
+
h.setAttribute('y1', y);
|
|
93
|
+
h.setAttribute('y2', y);
|
|
94
|
+
v.setAttribute('x1', x);
|
|
95
|
+
v.setAttribute('x2', x);
|
|
96
|
+
v.setAttribute('y1', '0');
|
|
97
|
+
v.setAttribute('y2', '640');
|
|
98
|
+
|
|
99
|
+
// Calculate real coordinates using inverse transformation
|
|
100
|
+
const matrix = {"a":18.666666666666668,"c":0,"e":320,"b":0,"d":-18.666666666666668,"f":320};
|
|
101
|
+
// Manually invert and apply the affine transform
|
|
102
|
+
// Since we only use translate and scale, we can directly compute:
|
|
103
|
+
// x' = (x - tx) / sx
|
|
104
|
+
// y' = (y - ty) / sy
|
|
105
|
+
const sx = matrix.a;
|
|
106
|
+
const sy = matrix.d;
|
|
107
|
+
const tx = matrix.e;
|
|
108
|
+
const ty = matrix.f;
|
|
109
|
+
const realPoint = {
|
|
110
|
+
x: (x - tx) / sx,
|
|
111
|
+
y: (y - ty) / sy // Flip y back since we used negative scale
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
coords.textContent = `(${realPoint.x.toFixed(2)}, ${realPoint.y.toFixed(2)})`;
|
|
115
|
+
coords.setAttribute('x', (x + 5).toString());
|
|
116
|
+
coords.setAttribute('y', (y - 5).toString());
|
|
117
|
+
});
|
|
118
|
+
document.currentScript.parentElement.addEventListener('mouseleave', () => {
|
|
119
|
+
document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
|
|
120
|
+
});
|
|
121
|
+
]]></script></svg>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import simpleRouteJson from "../../test-assets/example-simple-route.json"
|
|
3
|
+
import { RectDiffSolver } from "../../lib/solvers/RectDiffSolver"
|
|
4
|
+
import { getSvgFromGraphicsObject } from "graphics-debug"
|
|
5
|
+
|
|
6
|
+
test("example01", () => {
|
|
7
|
+
const solver = new RectDiffSolver({ simpleRouteJson })
|
|
8
|
+
|
|
9
|
+
solver.solve()
|
|
10
|
+
|
|
11
|
+
expect(getSvgFromGraphicsObject(solver.visualize())).toMatchSvgSnapshot(
|
|
12
|
+
import.meta.path,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
// Gap detection test - check coverage with a fine grid
|
|
16
|
+
const bounds = simpleRouteJson.bounds
|
|
17
|
+
const step = 0.004
|
|
18
|
+
const layerCount = simpleRouteJson.layerCount || 2
|
|
19
|
+
const state = (solver as any).state
|
|
20
|
+
const obstacles = state.obstaclesByLayer
|
|
21
|
+
const placed = state.placed
|
|
22
|
+
|
|
23
|
+
const gapPoints: Array<{ x: number; y: number; z: number }> = []
|
|
24
|
+
|
|
25
|
+
for (let z = 0; z < layerCount; z++) {
|
|
26
|
+
const layerObstacles = obstacles[z] || []
|
|
27
|
+
const layerPlaced = placed
|
|
28
|
+
.filter((p: any) => p.zLayers.includes(z))
|
|
29
|
+
.map((p: any) => p.rect)
|
|
30
|
+
|
|
31
|
+
for (let x = bounds.minX; x <= bounds.maxX; x += step) {
|
|
32
|
+
for (let y = bounds.minY; y <= bounds.maxY; y += step) {
|
|
33
|
+
// Skip if in obstacle
|
|
34
|
+
if (
|
|
35
|
+
layerObstacles.some(
|
|
36
|
+
(o: any) =>
|
|
37
|
+
x >= o.x && x <= o.x + o.width && y >= o.y && y <= o.y + o.height,
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
continue
|
|
41
|
+
|
|
42
|
+
// Check if covered by any placed rectangle
|
|
43
|
+
const covered = layerPlaced.some(
|
|
44
|
+
(r: any) =>
|
|
45
|
+
x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if (!covered) {
|
|
49
|
+
gapPoints.push({ x, y, z })
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const totalArea = (bounds.maxX - bounds.minX) * (bounds.maxY - bounds.minY)
|
|
56
|
+
const gapArea = gapPoints.length * step * step
|
|
57
|
+
const coveragePercent = ((totalArea - gapArea) / totalArea) * 100
|
|
58
|
+
|
|
59
|
+
console.log(
|
|
60
|
+
`Coverage: ${coveragePercent.toFixed(2)}%, Rectangles: ${placed.length}`,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
// We expect >99% coverage (with the improved edge analysis)
|
|
64
|
+
expect(coveragePercent).toBeGreaterThan(99)
|
|
65
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "bun-match-svg"
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { RectDiffSolver } from "../lib/solvers/RectDiffSolver"
|
|
3
|
+
import type { SimpleRouteJson } from "../lib/types/srj-types"
|
|
4
|
+
|
|
5
|
+
test("RectDiffSolver supports incremental stepping", () => {
|
|
6
|
+
const simpleRouteJson: SimpleRouteJson = {
|
|
7
|
+
bounds: { minX: 0, maxX: 10, minY: 0, maxY: 10 },
|
|
8
|
+
obstacles: [
|
|
9
|
+
{
|
|
10
|
+
type: "rect",
|
|
11
|
+
layers: ["top"],
|
|
12
|
+
center: { x: 5, y: 5 },
|
|
13
|
+
width: 2,
|
|
14
|
+
height: 2,
|
|
15
|
+
connectedTo: [],
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
connections: [],
|
|
19
|
+
layerCount: 2,
|
|
20
|
+
minTraceWidth: 0.15,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const solver = new RectDiffSolver({ simpleRouteJson })
|
|
24
|
+
|
|
25
|
+
// Setup initializes state
|
|
26
|
+
solver.setup()
|
|
27
|
+
expect(solver.solved).toBe(false)
|
|
28
|
+
expect(solver.stats.phase).toBe("GRID")
|
|
29
|
+
|
|
30
|
+
// Step advances one candidate at a time
|
|
31
|
+
let stepCount = 0
|
|
32
|
+
const maxSteps = 1000 // safety limit
|
|
33
|
+
|
|
34
|
+
while (!solver.solved && stepCount < maxSteps) {
|
|
35
|
+
solver.step()
|
|
36
|
+
stepCount++
|
|
37
|
+
|
|
38
|
+
// Progress should increase (or stay at 1.0 when done)
|
|
39
|
+
if (!solver.solved) {
|
|
40
|
+
expect(solver.stats.phase).toBeDefined()
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
expect(solver.solved).toBe(true)
|
|
45
|
+
expect(stepCount).toBeGreaterThan(0)
|
|
46
|
+
expect(stepCount).toBeLessThan(maxSteps)
|
|
47
|
+
|
|
48
|
+
const output = solver.getOutput()
|
|
49
|
+
expect(output.meshNodes.length).toBeGreaterThan(0)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test("RectDiffSolver.solve() still works (backward compatibility)", () => {
|
|
53
|
+
const simpleRouteJson: SimpleRouteJson = {
|
|
54
|
+
bounds: { minX: 0, maxX: 10, minY: 0, maxY: 10 },
|
|
55
|
+
obstacles: [],
|
|
56
|
+
connections: [],
|
|
57
|
+
layerCount: 1,
|
|
58
|
+
minTraceWidth: 0.1,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const solver = new RectDiffSolver({ simpleRouteJson })
|
|
62
|
+
|
|
63
|
+
// Old-style: just call solve()
|
|
64
|
+
solver.solve()
|
|
65
|
+
|
|
66
|
+
expect(solver.solved).toBe(true)
|
|
67
|
+
const output = solver.getOutput()
|
|
68
|
+
expect(output.meshNodes.length).toBeGreaterThan(0)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test("RectDiffSolver exposes progress during solve", () => {
|
|
72
|
+
const simpleRouteJson: SimpleRouteJson = {
|
|
73
|
+
bounds: { minX: 0, maxX: 20, minY: 0, maxY: 20 },
|
|
74
|
+
obstacles: [],
|
|
75
|
+
connections: [],
|
|
76
|
+
layerCount: 3,
|
|
77
|
+
minTraceWidth: 0.2,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const solver = new RectDiffSolver({ simpleRouteJson })
|
|
81
|
+
solver.setup()
|
|
82
|
+
|
|
83
|
+
const progressValues: number[] = []
|
|
84
|
+
|
|
85
|
+
// Step and collect progress
|
|
86
|
+
for (let i = 0; i < 20 && !solver.solved; i++) {
|
|
87
|
+
solver.step()
|
|
88
|
+
const progress = solver.computeProgress()
|
|
89
|
+
progressValues.push(progress)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Progress should generally increase (or stay at 1.0)
|
|
93
|
+
expect(progressValues.length).toBeGreaterThan(0)
|
|
94
|
+
expect(progressValues[0]).toBeGreaterThanOrEqual(0)
|
|
95
|
+
expect(progressValues[0]).toBeLessThanOrEqual(1)
|
|
96
|
+
|
|
97
|
+
// Finish
|
|
98
|
+
solver.solve()
|
|
99
|
+
expect(solver.computeProgress()).toBe(1)
|
|
100
|
+
})
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { RectDiffSolver } from "../lib/solvers/RectDiffSolver"
|
|
3
|
+
import type { SimpleRouteJson } from "../lib/types/srj-types"
|
|
4
|
+
|
|
5
|
+
test("RectDiffSolver creates mesh nodes with grid-based approach", () => {
|
|
6
|
+
const simpleRouteJson: SimpleRouteJson = {
|
|
7
|
+
bounds: {
|
|
8
|
+
minX: 0,
|
|
9
|
+
maxX: 10,
|
|
10
|
+
minY: 0,
|
|
11
|
+
maxY: 10,
|
|
12
|
+
},
|
|
13
|
+
obstacles: [
|
|
14
|
+
{
|
|
15
|
+
type: "rect",
|
|
16
|
+
layers: ["top"],
|
|
17
|
+
center: { x: 2.5, y: 2.5 },
|
|
18
|
+
width: 2,
|
|
19
|
+
height: 2,
|
|
20
|
+
connectedTo: [],
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
connections: [],
|
|
24
|
+
layerCount: 2,
|
|
25
|
+
minTraceWidth: 0.15,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const solver = new RectDiffSolver({
|
|
29
|
+
simpleRouteJson,
|
|
30
|
+
mode: "grid",
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
solver.solve()
|
|
34
|
+
|
|
35
|
+
const output = solver.getOutput()
|
|
36
|
+
|
|
37
|
+
// Should have created some mesh nodes
|
|
38
|
+
expect(output.meshNodes.length).toBeGreaterThan(0)
|
|
39
|
+
|
|
40
|
+
// All mesh nodes should have valid dimensions
|
|
41
|
+
for (const node of output.meshNodes) {
|
|
42
|
+
expect(node.width).toBeGreaterThan(0)
|
|
43
|
+
expect(node.height).toBeGreaterThan(0)
|
|
44
|
+
expect(node.availableZ).toBeDefined()
|
|
45
|
+
expect(Array.isArray(node.availableZ)).toBe(true)
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test("RectDiffSolver handles multi-layer spans", () => {
|
|
50
|
+
const simpleRouteJson: SimpleRouteJson = {
|
|
51
|
+
bounds: {
|
|
52
|
+
minX: 0,
|
|
53
|
+
maxX: 10,
|
|
54
|
+
minY: 0,
|
|
55
|
+
maxY: 10,
|
|
56
|
+
},
|
|
57
|
+
obstacles: [],
|
|
58
|
+
connections: [],
|
|
59
|
+
layerCount: 3,
|
|
60
|
+
minTraceWidth: 0.2,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const solver = new RectDiffSolver({
|
|
64
|
+
simpleRouteJson,
|
|
65
|
+
mode: "grid",
|
|
66
|
+
gridOptions: {
|
|
67
|
+
minSingle: { width: 0.4, height: 0.4 },
|
|
68
|
+
minMulti: { width: 1.0, height: 1.0, minLayers: 2 },
|
|
69
|
+
preferMultiLayer: true,
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
solver.solve()
|
|
74
|
+
|
|
75
|
+
const output = solver.getOutput()
|
|
76
|
+
|
|
77
|
+
// Should have created mesh nodes
|
|
78
|
+
expect(output.meshNodes.length).toBeGreaterThan(0)
|
|
79
|
+
|
|
80
|
+
// Check if any nodes span multiple layers
|
|
81
|
+
const multiLayerNodes = output.meshNodes.filter(
|
|
82
|
+
(n) => n.availableZ && n.availableZ.length >= 2
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
// With no obstacles and preferMultiLayer=true, we should get multi-layer nodes
|
|
86
|
+
expect(multiLayerNodes.length).toBeGreaterThan(0)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test("RectDiffSolver respects single-layer minimums", () => {
|
|
90
|
+
const simpleRouteJson: SimpleRouteJson = {
|
|
91
|
+
bounds: {
|
|
92
|
+
minX: 0,
|
|
93
|
+
maxX: 5,
|
|
94
|
+
minY: 0,
|
|
95
|
+
maxY: 5,
|
|
96
|
+
},
|
|
97
|
+
obstacles: [],
|
|
98
|
+
connections: [],
|
|
99
|
+
layerCount: 1,
|
|
100
|
+
minTraceWidth: 0.1,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const minWidth = 0.5
|
|
104
|
+
const minHeight = 0.5
|
|
105
|
+
|
|
106
|
+
const solver = new RectDiffSolver({
|
|
107
|
+
simpleRouteJson,
|
|
108
|
+
mode: "grid",
|
|
109
|
+
gridOptions: {
|
|
110
|
+
minSingle: { width: minWidth, height: minHeight },
|
|
111
|
+
minMulti: { width: 1.0, height: 1.0, minLayers: 2 },
|
|
112
|
+
},
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
solver.solve()
|
|
116
|
+
|
|
117
|
+
const output = solver.getOutput()
|
|
118
|
+
|
|
119
|
+
// All nodes should meet minimum requirements
|
|
120
|
+
for (const node of output.meshNodes) {
|
|
121
|
+
expect(node.width).toBeGreaterThanOrEqual(minWidth - 1e-6)
|
|
122
|
+
expect(node.height).toBeGreaterThanOrEqual(minHeight - 1e-6)
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test("disruptive placement resizes single-layer nodes", () => {
|
|
127
|
+
const srj: SimpleRouteJson = {
|
|
128
|
+
bounds: { minX: 0, maxX: 10, minY: 0, maxY: 10 },
|
|
129
|
+
obstacles: [],
|
|
130
|
+
connections: [],
|
|
131
|
+
layerCount: 3,
|
|
132
|
+
minTraceWidth: 0.2,
|
|
133
|
+
}
|
|
134
|
+
const solver = new RectDiffSolver({ simpleRouteJson: srj, mode: "grid" })
|
|
135
|
+
solver.setup()
|
|
136
|
+
|
|
137
|
+
// Manually seed a soft, single-layer node occupying center (simulate early placement)
|
|
138
|
+
const state = (solver as any).state
|
|
139
|
+
const r = { x: 4, y: 4, width: 2, height: 2 }
|
|
140
|
+
state.placed.push({ rect: r, zLayers: [1] })
|
|
141
|
+
state.placedByLayer[1].push(r)
|
|
142
|
+
|
|
143
|
+
// Run to completion
|
|
144
|
+
solver.solve()
|
|
145
|
+
|
|
146
|
+
// Expect at least one node spanning multiple layers at/through the center
|
|
147
|
+
const mesh = solver.getOutput().meshNodes
|
|
148
|
+
const throughCenter = mesh.find(n =>
|
|
149
|
+
Math.abs(n.center.x - 5) < 0.6 &&
|
|
150
|
+
Math.abs(n.center.y - 5) < 0.6 &&
|
|
151
|
+
(n.availableZ?.length ?? 0) >= 2
|
|
152
|
+
)
|
|
153
|
+
expect(throughCenter).toBeTruthy()
|
|
154
|
+
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
|
|
3
|
+
const testSvg = `<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
|
|
4
|
+
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
|
|
5
|
+
</svg>`
|
|
6
|
+
|
|
7
|
+
test("svg snapshot example", async () => {
|
|
8
|
+
// First run will create the snapshot
|
|
9
|
+
// Subsequent runs will compare against the saved snapshot
|
|
10
|
+
await expect(testSvg).toMatchSvgSnapshot(import.meta.path)
|
|
11
|
+
})
|
|
12
|
+
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Environment setup & latest features
|
|
4
|
+
"lib": ["ESNext", "DOM"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "Preserve",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
|
|
11
|
+
// Bundler mode
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"resolveJsonModule": false,
|
|
17
|
+
|
|
18
|
+
// Best practices
|
|
19
|
+
"strict": true,
|
|
20
|
+
"skipLibCheck": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true,
|
|
22
|
+
"noUncheckedIndexedAccess": true,
|
|
23
|
+
"noImplicitOverride": true,
|
|
24
|
+
|
|
25
|
+
// Some stricter flags (disabled by default)
|
|
26
|
+
"noUnusedLocals": false,
|
|
27
|
+
"noUnusedParameters": false,
|
|
28
|
+
"noPropertyAccessFromIndexSignature": false
|
|
29
|
+
}
|
|
30
|
+
}
|