three-slicer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE.txt +661 -0
  2. package/README.md +44 -0
  3. package/components/README.md +39 -0
  4. package/components/dist/SettingsPanel.js +180 -0
  5. package/components/styles.css +84 -0
  6. package/data/README.md +22 -0
  7. package/data/config-schema-builddump.json +12921 -0
  8. package/data/config-schema.json +12685 -0
  9. package/data/invalidation-map.json +631 -0
  10. package/data/toggle-rules.json +2277 -0
  11. package/data/ui-tree.json +1586 -0
  12. package/engine/README.md +52 -0
  13. package/engine/examples/headless.mjs +37 -0
  14. package/engine/index.d.ts +29 -0
  15. package/engine/index.js +45 -0
  16. package/engine/src/settings.js +130 -0
  17. package/engine/src/slicer.worker.js +73 -0
  18. package/engine/src/slicer_core.js +0 -0
  19. package/engine/src/slicer_core.mt.js +0 -0
  20. package/engine/src/toggle_eval.js +78 -0
  21. package/package.json +74 -0
  22. package/viewer/README.md +68 -0
  23. package/viewer/dist/Viewport.js +1108 -0
  24. package/viewer/dist/make_worker.js +9 -0
  25. package/viewer/dist/model_loaders.js +144 -0
  26. package/viewer/dist/toolpath_gpu.js +386 -0
  27. package/viewer/src/Viewport.jsx +1353 -0
  28. package/viewer/src/assets/add.svg +1 -0
  29. package/viewer/src/assets/arrange.svg +1 -0
  30. package/viewer/src/assets/boolean.svg +1 -0
  31. package/viewer/src/assets/cut.svg +1 -0
  32. package/viewer/src/assets/delete.svg +1 -0
  33. package/viewer/src/assets/deleteall.svg +1 -0
  34. package/viewer/src/assets/duplicate.svg +1 -0
  35. package/viewer/src/assets/measure.svg +1 -0
  36. package/viewer/src/assets/mmu.svg +1 -0
  37. package/viewer/src/assets/move.svg +1 -0
  38. package/viewer/src/assets/negative.svg +1 -0
  39. package/viewer/src/assets/onbed.svg +1 -0
  40. package/viewer/src/assets/open.svg +1 -0
  41. package/viewer/src/assets/orient.svg +1 -0
  42. package/viewer/src/assets/paint.svg +1 -0
  43. package/viewer/src/assets/rotate.svg +1 -0
  44. package/viewer/src/assets/scale.svg +1 -0
  45. package/viewer/src/assets/seam.svg +1 -0
  46. package/viewer/src/assets/split.svg +1 -0
  47. package/viewer/src/assets/textmark.svg +1 -0
  48. package/viewer/src/assets/varlayer.svg +1 -0
  49. package/viewer/src/icons.js +23 -0
  50. package/viewer/src/make_worker.js +9 -0
  51. package/viewer/src/model_loaders.js +178 -0
  52. package/viewer/src/shadow_host.jsx +30 -0
  53. package/viewer/src/toolpath_gpu.js +430 -0
  54. package/viewer/styles.css +231 -0
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Zm3-6h3m2,0h3m-4-4v8" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M33.5,35H5.5A1.5,1.5,0,0,1,4,33.5V5.5A1.5,1.5,0,0,1,5.5,4h28A1.5,1.5,0,0,1,35,5.5v28A1.5,1.5,0,0,1,33.5,35ZM5.5,5a.5.5,0,0,0-.5.5v28a.5.5,0,0,0,.5.5h28a.5.5,0,0,0,.5-.5V5.5a.5.5,0,0,0-.5-.5Z" style="fill:#b6b6b6"/><path d="M30.5,32H8.5A1.5,1.5,0,0,1,7,30.5v-6A1.5,1.5,0,0,1,8.5,23h22A1.5,1.5,0,0,1,32,24.5v6A1.5,1.5,0,0,1,30.5,32Zm-22-8a.5.5,0,0,0-.5.5v6a.5.5,0,0,0,.5.5h22a.5.5,0,0,0,.5-.5v-6a.5.5,0,0,0-.5-.5Zm22-2h-6A1.5,1.5,0,0,1,23,20.5V8.5A1.5,1.5,0,0,1,24.5,7h6A1.5,1.5,0,0,1,32,8.5v12A1.5,1.5,0,0,1,30.5,22Zm-6-14a.5.5,0,0,0-.5.5v12a.5.5,0,0,0,.5.5h6a.5.5,0,0,0,.5-.5V8.5a.5.5,0,0,0-.5-.5Zm-6,6h-8a3.5,3.5,0,0,1,0-7h8a3.5,3.5,0,0,1,0,7Zm-8-6a2.5,2.5,0,0,0,0,5h8a2.5,2.5,0,0,0,0-5Zm8,14A3.5,3.5,0,1,1,22,18.5,3.5,3.5,0,0,1,18.5,22Zm0-6A2.5,2.5,0,1,0,21,18.5,2.5,2.5,0,0,0,18.5,16Zm-8,6A3.5,3.5,0,1,1,14,18.5,3.5,3.5,0,0,1,10.5,22Zm0-6A2.5,2.5,0,1,0,13,18.5,2.5,2.5,0,0,0,10.5,16Z" style="fill:#009688"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M6.2,8a2.6,2.6,0,1,0,5.2,0a2.6,2.6,0,1,0,-5.2,0" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M4.6,8a2.6,2.6,0,1,0,5.2,0a2.6,2.6,0,1,0,-5.2,0" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M3.5,9h2m2,0h2m2,0h2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3.5l1.8,3h-3.6z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Zm3-6h8" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M5,5l6,6" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M11,5l-6,6" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M4,4h5v5H4z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M7,7h5v5H7z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M2.5,8h11" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M2.5,6v4" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M13.5,6v4" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M6,7v2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M10,7v2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M4,8a4,4,0,1,0,8,0a4,4,0,1,0,-8,0" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,4a4,4,0,0,1,0,8z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1;fill:#949494;stroke:none"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M34.854,19.146l-4-4a.5.5,0,0,0-.708.708L33.293,19H28.5a.5.5,0,0,0,0,1h4.793l-3.147,3.146a.5.5,0,0,0,.708.708l4-4A.5.5,0,0,0,34.854,19.146Z" style="fill:#009688"/><path d="M19.854,4.146a.5.5,0,0,0-.708,0l-4,4a.5.5,0,0,0,.708.708L19,5.707V10.5a.5.5,0,0,0,1,0V5.707l3.146,3.147a.5.5,0,0,0,.708-.708Z" style="fill:#009688"/><path d="M23.146,30.146,20,33.293V28.5a.5.5,0,0,0-1,0v4.793l-3.146-3.147a.5.5,0,0,0-.708.708l4,4a.5.5,0,0,0,.708,0l4-4a.5.5,0,0,0-.708-.708Z" style="fill:#009688"/><path d="M10.5,19H5.707l3.147-3.146a.5.5,0,0,0-.708-.708l-4,4a.5.5,0,0,0,0,.708l4,4a.5.5,0,0,0,.708-.708L5.707,20H10.5a.5.5,0,0,0,0-1Z" style="fill:#009688"/><path d="M24.5,26h-10A1.5,1.5,0,0,1,13,24.5v-10A1.5,1.5,0,0,1,14.5,13h10A1.5,1.5,0,0,1,26,14.5v10A1.5,1.5,0,0,1,24.5,26Zm-10-12a.5.5,0,0,0-.5.5v10a.5.5,0,0,0,.5.5h10a.5.5,0,0,0,.5-.5v-10a.5.5,0,0,0-.5-.5Z" style="fill:#b6b6b6"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M5,5h6v6H5z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1;stroke-dasharray:2 1.5"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3.5v5m-2-2 2,2 2-2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M4,11.5h8" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M34.923,10.25a.392.392,0,0,0-.033-.05.5.5,0,0,0-.2-.162l0,0-15-6a.5.5,0,0,0-.372,0l-15,6,0,0a.5.5,0,0,0-.2.162.392.392,0,0,0-.033.05A.478.478,0,0,0,4,10.5v18a.464.464,0,0,0,.028.138.429.429,0,0,0,.011.054l.01.015a.51.51,0,0,0,.1.144l.008,0a.5.5,0,0,0,.156.1l0,0,14.989,6,0,0,.006,0a.5.5,0,0,0,.372,0l.006,0,0,0,1.989-.8a.5.5,0,0,0-.372-.928L20,33.762V16.839l14-5.6V21.5a.5.5,0,0,0,1,0v-11A.481.481,0,0,0,34.923,10.25ZM19,33.762l-14-5.6V11.239l14,5.6Zm.5-17.8L5.846,10.5,19.5,5.039,33.153,10.5Z" style="fill:#b6b6b6"/><path d="M33.5,24h-8A1.5,1.5,0,0,0,24,25.5v8A1.5,1.5,0,0,0,25.5,35h8A1.5,1.5,0,0,0,35,33.5v-8A1.5,1.5,0,0,0,33.5,24Zm.5,9.5a.5.5,0,0,1-.5.5h-8a.5.5,0,0,1-.5-.5v-8a.5.5,0,0,1,.5-.5h8a.5.5,0,0,1,.5.5Z" style="fill:#009688"/><path d="M32.5,29H30V26.5a.5.5,0,0,0-1,0V29H26.5a.5.5,0,0,0,0,1H29v2.5a.5.5,0,0,0,1,0V30h2.5a.5.5,0,0,0,0-1Z" style="fill:#009688"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M25.5,22a.5.5,0,0,1-.5-.5V18H18v3.5a.5.5,0,0,1-1,0v-6a.5.5,0,0,1,.084-.277l2-3A.5.5,0,0,1,19.5,12h4a.5.5,0,0,1,.416.223l2,3A.5.5,0,0,1,26,15.5v6A.5.5,0,0,1,25.5,22ZM18,17h7V15.651L23.232,13H19.768L18,15.651Z" style="fill:#009688"/><path d="M11.964,29.314a.5.5,0,0,0-.65-.278L7.152,30.7a17.321,17.321,0,0,1,3.739-6.888.5.5,0,1,0-.782-.624A19.312,19.312,0,0,0,6.359,29.8L4.964,26.314a.5.5,0,1,0-.928.372l2,5c0,.01.016.015.021.025a.475.475,0,0,0,.15.185c.009.007.016.017.025.023a.5.5,0,0,0,.2.076A.367.367,0,0,0,6.5,32h0a.475.475,0,0,0,.181-.036h0l5-2A.5.5,0,0,0,11.964,29.314Z" style="fill:#009688"/><rect x="4" y="34" width="31" height="1" style="fill:#009688"/><path d="M34.854,17.146l-13-13a.5.5,0,0,0-.708,0l-13,13a.5.5,0,0,0,.708.708L21.5,5.207,33.793,17.5,21.146,30.146a.5.5,0,0,0,.708.708l13-13A.5.5,0,0,0,34.854,17.146Z" style="fill:#b6b6b6"/><path d="M21.5,31a.5.5,0,0,1-.354-.146l-13-13a.5.5,0,0,1,.708-.708l13,13A.5.5,0,0,1,21.5,31Z" style="fill:#009688"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M27.878,34.926h-4.4c-.509,0-.845,0-.955-.394a.593.593,0,0,1,.36-.67,2.3,2.3,0,0,0,1.2-2.189,3.253,3.253,0,1,1,3.8,3.206Zm-3.538-1h2.988a2.253,2.253,0,1,0-2.253-2.253A3.445,3.445,0,0,1,24.34,33.926Z" style="fill:#009688"/><path d="M30.5,30.289l-.846-.535,4.364-6.9a.222.222,0,0,0-.36-.258l-5.4,6.644-.776-.631,5.4-6.644a1.222,1.222,0,0,1,1.982,1.423Z" style="fill:#009688"/><path d="M33.5,35h-1a.5.5,0,0,1,0-1h1a.5.5,0,0,0,.5-.5v-5a.5.5,0,0,1,1,0v5A1.5,1.5,0,0,1,33.5,35Z" style="fill:#b6b6b6"/><path d="M7.5,35a.5.5,0,0,1-.5-.5v-20a.5.5,0,0,1,1,0v20A.5.5,0,0,1,7.5,35Z" style="fill:#009688"/><path d="M10.5,35a.5.5,0,0,1-.5-.5v-20a.5.5,0,0,1,1,0v20A.5.5,0,0,1,10.5,35Z" style="fill:#009688"/><path d="M13.5,35h-9a.5.5,0,0,1-.5-.5v-20a.5.5,0,0,1,.5-.5h9a.5.5,0,0,1,.5.5v20A.5.5,0,0,1,13.5,35ZM5,34h8V15H5Z" style="fill:#009688"/><path d="M22.5,29a.5.5,0,0,1-.5-.5V15H4.5a.5.5,0,0,1-.5-.5v-9A1.5,1.5,0,0,1,5.5,4h28A1.5,1.5,0,0,1,35,5.5v13a.5.5,0,0,1-1,0V5.5a.5.5,0,0,0-.5-.5H5.5a.5.5,0,0,0-.5.5V14H22.5a.5.5,0,0,1,.5.5v14A.5.5,0,0,1,22.5,29Z" style="fill:#b6b6b6"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M24.5,26h-10A1.5,1.5,0,0,1,13,24.5v-10A1.5,1.5,0,0,1,14.5,13h10A1.5,1.5,0,0,1,26,14.5v10A1.5,1.5,0,0,1,24.5,26Zm-10-12a.5.5,0,0,0-.5.5v10a.5.5,0,0,0,.5.5h10a.5.5,0,0,0,.5-.5v-10a.5.5,0,0,0-.5-.5Z" style="fill:#b6b6b6"/><path d="M35,19.5A15.517,15.517,0,0,1,19.5,35a15.767,15.767,0,0,1-2.063-.136.5.5,0,0,1-.43-.562.507.507,0,0,1,.562-.43A14.5,14.5,0,1,0,19.5,5,14.43,14.43,0,0,0,7.751,11H11.5a.5.5,0,0,1,0,1h-5a.5.5,0,0,1-.5-.5v-5a.5.5,0,0,1,1,0v3.836A15.5,15.5,0,0,1,35,19.5ZM5.943,24.66a.5.5,0,1,0-.933.356A15.426,15.426,0,0,0,7.354,29.13a.5.5,0,0,0,.783-.621A14.533,14.533,0,0,1,5.943,24.66Zm8.4,8.4a14.523,14.523,0,0,1-3.85-2.194.5.5,0,0,0-.621.784,15.455,15.455,0,0,0,4.115,2.343.487.487,0,0,0,.178.033.5.5,0,0,0,.178-.966ZM5.128,21.431A14.621,14.621,0,0,1,5,19.5a.5.5,0,0,0-1,0,15.729,15.729,0,0,0,.136,2.062.5.5,0,0,0,.992-.131Z" style="fill:#009688"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path d="M25.5,5h-2a.5.5,0,0,1,0-1h2a.5.5,0,0,1,0,1ZM21,4.5a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,21,4.5Zm-5,0a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,16,4.5Zm10,30a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,26,34.5Zm-5,0a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,21,34.5Zm-5,0a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,16,34.5ZM5,15.5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm30-10v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm-9-1v-10A1.5,1.5,0,0,0,24.5,13h-10A1.5,1.5,0,0,0,13,14.5v10A1.5,1.5,0,0,0,14.5,26h10A1.5,1.5,0,0,0,26,24.5ZM24.5,14a.5.5,0,0,1,.5.5v10a.5.5,0,0,1-.5.5h-10a.5.5,0,0,1-.5-.5v-10a.5.5,0,0,1,.5-.5Z" style="fill:#b6b6b6"/><path d="M34.962,4.309A.5.5,0,0,0,34.5,4h-5a.5.5,0,0,0,0,1h3.793l-6.147,6.146a.5.5,0,0,0,.708.708L34,5.707V9.5a.5.5,0,0,0,1,0v-5A.5.5,0,0,0,34.962,4.309Z" style="fill:#009688"/><path d="M5.707,5H9.5a.5.5,0,0,0,0-1h-5a.5.5,0,0,0-.5.5v5a.5.5,0,0,0,1,0V5.707l6.146,6.147a.5.5,0,0,0,.708-.708Z" style="fill:#009688"/><path d="M34.5,29a.5.5,0,0,0-.5.5v3.793l-6.146-6.147a.5.5,0,0,0-.708.708L33.293,34H29.5a.5.5,0,0,0,0,1h5a.5.5,0,0,0,.5-.5v-5A.5.5,0,0,0,34.5,29Z" style="fill:#009688"/><path d="M11.146,27.146,5,33.293V29.5a.5.5,0,0,0-1,0v5a.5.5,0,0,0,.5.5h5a.5.5,0,0,0,0-1H5.707l6.147-6.146a.5.5,0,0,0-.708-.708Z" style="fill:#009688"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3.5v2m0,2v2m0,2v2" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,3.5v9" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M6,6.5,4,8l2,1.5" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M10,6.5,12,8l-2,1.5" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M4.5,5h7" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M8,5v6.5" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M3,4.5h10" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M3,7h10" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M3,9h10" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M3,10.5h10" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/><path d="M3,11.6h10" style="fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1"/></svg>
@@ -0,0 +1,23 @@
1
+ // 아이콘 데이터 URI — assets/*.svg 를 인라인(번들러 svg 로더 의존 제거).
2
+ // 재생성: node -e 로 assets 를 순회해 이 파일을 다시 쓴다(수동 편집 금지).
3
+ export const addIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Zm3-6h3m2,0h3m-4-4v8\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/></svg>")
4
+ export const arrangeIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"40\" height=\"40\" viewBox=\"0 0 40 40\"><path d=\"M33.5,35H5.5A1.5,1.5,0,0,1,4,33.5V5.5A1.5,1.5,0,0,1,5.5,4h28A1.5,1.5,0,0,1,35,5.5v28A1.5,1.5,0,0,1,33.5,35ZM5.5,5a.5.5,0,0,0-.5.5v28a.5.5,0,0,0,.5.5h28a.5.5,0,0,0,.5-.5V5.5a.5.5,0,0,0-.5-.5Z\" style=\"fill:#b6b6b6\"/><path d=\"M30.5,32H8.5A1.5,1.5,0,0,1,7,30.5v-6A1.5,1.5,0,0,1,8.5,23h22A1.5,1.5,0,0,1,32,24.5v6A1.5,1.5,0,0,1,30.5,32Zm-22-8a.5.5,0,0,0-.5.5v6a.5.5,0,0,0,.5.5h22a.5.5,0,0,0,.5-.5v-6a.5.5,0,0,0-.5-.5Zm22-2h-6A1.5,1.5,0,0,1,23,20.5V8.5A1.5,1.5,0,0,1,24.5,7h6A1.5,1.5,0,0,1,32,8.5v12A1.5,1.5,0,0,1,30.5,22Zm-6-14a.5.5,0,0,0-.5.5v12a.5.5,0,0,0,.5.5h6a.5.5,0,0,0,.5-.5V8.5a.5.5,0,0,0-.5-.5Zm-6,6h-8a3.5,3.5,0,0,1,0-7h8a3.5,3.5,0,0,1,0,7Zm-8-6a2.5,2.5,0,0,0,0,5h8a2.5,2.5,0,0,0,0-5Zm8,14A3.5,3.5,0,1,1,22,18.5,3.5,3.5,0,0,1,18.5,22Zm0-6A2.5,2.5,0,1,0,21,18.5,2.5,2.5,0,0,0,18.5,16Zm-8,6A3.5,3.5,0,1,1,14,18.5,3.5,3.5,0,0,1,10.5,22Zm0-6A2.5,2.5,0,1,0,13,18.5,2.5,2.5,0,0,0,10.5,16Z\" style=\"fill:#009688\"/></svg>")
5
+ export const booleanIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M6.2,8a2.6,2.6,0,1,0,5.2,0a2.6,2.6,0,1,0,-5.2,0\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M4.6,8a2.6,2.6,0,1,0,5.2,0a2.6,2.6,0,1,0,-5.2,0\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/></svg>")
6
+ export const cutIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M3.5,9h2m2,0h2m2,0h2\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M8,3.5l1.8,3h-3.6z\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/></svg>")
7
+ export const deleteIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Zm3-6h8\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/></svg>")
8
+ export const deleteallIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M5,5l6,6\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M11,5l-6,6\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/></svg>")
9
+ export const duplicateIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M4,4h5v5H4z\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M7,7h5v5H7z\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/></svg>")
10
+ export const measureIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2.5,8h11\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M2.5,6v4\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M13.5,6v4\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M6,7v2\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M10,7v2\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/></svg>")
11
+ export const mmuIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M4,8a4,4,0,1,0,8,0a4,4,0,1,0,-8,0\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M8,4a4,4,0,0,1,0,8z\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1;fill:#949494;stroke:none\"/></svg>")
12
+ export const moveIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"40\" height=\"40\" viewBox=\"0 0 40 40\"><path d=\"M34.854,19.146l-4-4a.5.5,0,0,0-.708.708L33.293,19H28.5a.5.5,0,0,0,0,1h4.793l-3.147,3.146a.5.5,0,0,0,.708.708l4-4A.5.5,0,0,0,34.854,19.146Z\" style=\"fill:#009688\"/><path d=\"M19.854,4.146a.5.5,0,0,0-.708,0l-4,4a.5.5,0,0,0,.708.708L19,5.707V10.5a.5.5,0,0,0,1,0V5.707l3.146,3.147a.5.5,0,0,0,.708-.708Z\" style=\"fill:#009688\"/><path d=\"M23.146,30.146,20,33.293V28.5a.5.5,0,0,0-1,0v4.793l-3.146-3.147a.5.5,0,0,0-.708.708l4,4a.5.5,0,0,0,.708,0l4-4a.5.5,0,0,0-.708-.708Z\" style=\"fill:#009688\"/><path d=\"M10.5,19H5.707l3.147-3.146a.5.5,0,0,0-.708-.708l-4,4a.5.5,0,0,0,0,.708l4,4a.5.5,0,0,0,.708-.708L5.707,20H10.5a.5.5,0,0,0,0-1Z\" style=\"fill:#009688\"/><path d=\"M24.5,26h-10A1.5,1.5,0,0,1,13,24.5v-10A1.5,1.5,0,0,1,14.5,13h10A1.5,1.5,0,0,1,26,14.5v10A1.5,1.5,0,0,1,24.5,26Zm-10-12a.5.5,0,0,0-.5.5v10a.5.5,0,0,0,.5.5h10a.5.5,0,0,0,.5-.5v-10a.5.5,0,0,0-.5-.5Z\" style=\"fill:#b6b6b6\"/></svg>")
13
+ export const negativeIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M5,5h6v6H5z\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1;stroke-dasharray:2 1.5\"/></svg>")
14
+ export const onbedIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M8,3.5v5m-2-2 2,2 2-2\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M4,11.5h8\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/></svg>")
15
+ export const openIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"40\" height=\"40\" viewBox=\"0 0 40 40\"><path d=\"M34.923,10.25a.392.392,0,0,0-.033-.05.5.5,0,0,0-.2-.162l0,0-15-6a.5.5,0,0,0-.372,0l-15,6,0,0a.5.5,0,0,0-.2.162.392.392,0,0,0-.033.05A.478.478,0,0,0,4,10.5v18a.464.464,0,0,0,.028.138.429.429,0,0,0,.011.054l.01.015a.51.51,0,0,0,.1.144l.008,0a.5.5,0,0,0,.156.1l0,0,14.989,6,0,0,.006,0a.5.5,0,0,0,.372,0l.006,0,0,0,1.989-.8a.5.5,0,0,0-.372-.928L20,33.762V16.839l14-5.6V21.5a.5.5,0,0,0,1,0v-11A.481.481,0,0,0,34.923,10.25ZM19,33.762l-14-5.6V11.239l14,5.6Zm.5-17.8L5.846,10.5,19.5,5.039,33.153,10.5Z\" style=\"fill:#b6b6b6\"/><path d=\"M33.5,24h-8A1.5,1.5,0,0,0,24,25.5v8A1.5,1.5,0,0,0,25.5,35h8A1.5,1.5,0,0,0,35,33.5v-8A1.5,1.5,0,0,0,33.5,24Zm.5,9.5a.5.5,0,0,1-.5.5h-8a.5.5,0,0,1-.5-.5v-8a.5.5,0,0,1,.5-.5h8a.5.5,0,0,1,.5.5Z\" style=\"fill:#009688\"/><path d=\"M32.5,29H30V26.5a.5.5,0,0,0-1,0V29H26.5a.5.5,0,0,0,0,1H29v2.5a.5.5,0,0,0,1,0V30h2.5a.5.5,0,0,0,0-1Z\" style=\"fill:#009688\"/></svg>")
16
+ export const orientIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"40\" height=\"40\" viewBox=\"0 0 40 40\"><path d=\"M25.5,22a.5.5,0,0,1-.5-.5V18H18v3.5a.5.5,0,0,1-1,0v-6a.5.5,0,0,1,.084-.277l2-3A.5.5,0,0,1,19.5,12h4a.5.5,0,0,1,.416.223l2,3A.5.5,0,0,1,26,15.5v6A.5.5,0,0,1,25.5,22ZM18,17h7V15.651L23.232,13H19.768L18,15.651Z\" style=\"fill:#009688\"/><path d=\"M11.964,29.314a.5.5,0,0,0-.65-.278L7.152,30.7a17.321,17.321,0,0,1,3.739-6.888.5.5,0,1,0-.782-.624A19.312,19.312,0,0,0,6.359,29.8L4.964,26.314a.5.5,0,1,0-.928.372l2,5c0,.01.016.015.021.025a.475.475,0,0,0,.15.185c.009.007.016.017.025.023a.5.5,0,0,0,.2.076A.367.367,0,0,0,6.5,32h0a.475.475,0,0,0,.181-.036h0l5-2A.5.5,0,0,0,11.964,29.314Z\" style=\"fill:#009688\"/><rect x=\"4\" y=\"34\" width=\"31\" height=\"1\" style=\"fill:#009688\"/><path d=\"M34.854,17.146l-13-13a.5.5,0,0,0-.708,0l-13,13a.5.5,0,0,0,.708.708L21.5,5.207,33.793,17.5,21.146,30.146a.5.5,0,0,0,.708.708l13-13A.5.5,0,0,0,34.854,17.146Z\" style=\"fill:#b6b6b6\"/><path d=\"M21.5,31a.5.5,0,0,1-.354-.146l-13-13a.5.5,0,0,1,.708-.708l13,13A.5.5,0,0,1,21.5,31Z\" style=\"fill:#009688\"/></svg>")
17
+ export const paintIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"40\" height=\"40\" viewBox=\"0 0 40 40\"><path d=\"M27.878,34.926h-4.4c-.509,0-.845,0-.955-.394a.593.593,0,0,1,.36-.67,2.3,2.3,0,0,0,1.2-2.189,3.253,3.253,0,1,1,3.8,3.206Zm-3.538-1h2.988a2.253,2.253,0,1,0-2.253-2.253A3.445,3.445,0,0,1,24.34,33.926Z\" style=\"fill:#009688\"/><path d=\"M30.5,30.289l-.846-.535,4.364-6.9a.222.222,0,0,0-.36-.258l-5.4,6.644-.776-.631,5.4-6.644a1.222,1.222,0,0,1,1.982,1.423Z\" style=\"fill:#009688\"/><path d=\"M33.5,35h-1a.5.5,0,0,1,0-1h1a.5.5,0,0,0,.5-.5v-5a.5.5,0,0,1,1,0v5A1.5,1.5,0,0,1,33.5,35Z\" style=\"fill:#b6b6b6\"/><path d=\"M7.5,35a.5.5,0,0,1-.5-.5v-20a.5.5,0,0,1,1,0v20A.5.5,0,0,1,7.5,35Z\" style=\"fill:#009688\"/><path d=\"M10.5,35a.5.5,0,0,1-.5-.5v-20a.5.5,0,0,1,1,0v20A.5.5,0,0,1,10.5,35Z\" style=\"fill:#009688\"/><path d=\"M13.5,35h-9a.5.5,0,0,1-.5-.5v-20a.5.5,0,0,1,.5-.5h9a.5.5,0,0,1,.5.5v20A.5.5,0,0,1,13.5,35ZM5,34h8V15H5Z\" style=\"fill:#009688\"/><path d=\"M22.5,29a.5.5,0,0,1-.5-.5V15H4.5a.5.5,0,0,1-.5-.5v-9A1.5,1.5,0,0,1,5.5,4h28A1.5,1.5,0,0,1,35,5.5v13a.5.5,0,0,1-1,0V5.5a.5.5,0,0,0-.5-.5H5.5a.5.5,0,0,0-.5.5V14H22.5a.5.5,0,0,1,.5.5v14A.5.5,0,0,1,22.5,29Z\" style=\"fill:#b6b6b6\"/></svg>")
18
+ export const rotateIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"40\" height=\"40\" viewBox=\"0 0 40 40\"><path d=\"M24.5,26h-10A1.5,1.5,0,0,1,13,24.5v-10A1.5,1.5,0,0,1,14.5,13h10A1.5,1.5,0,0,1,26,14.5v10A1.5,1.5,0,0,1,24.5,26Zm-10-12a.5.5,0,0,0-.5.5v10a.5.5,0,0,0,.5.5h10a.5.5,0,0,0,.5-.5v-10a.5.5,0,0,0-.5-.5Z\" style=\"fill:#b6b6b6\"/><path d=\"M35,19.5A15.517,15.517,0,0,1,19.5,35a15.767,15.767,0,0,1-2.063-.136.5.5,0,0,1-.43-.562.507.507,0,0,1,.562-.43A14.5,14.5,0,1,0,19.5,5,14.43,14.43,0,0,0,7.751,11H11.5a.5.5,0,0,1,0,1h-5a.5.5,0,0,1-.5-.5v-5a.5.5,0,0,1,1,0v3.836A15.5,15.5,0,0,1,35,19.5ZM5.943,24.66a.5.5,0,1,0-.933.356A15.426,15.426,0,0,0,7.354,29.13a.5.5,0,0,0,.783-.621A14.533,14.533,0,0,1,5.943,24.66Zm8.4,8.4a14.523,14.523,0,0,1-3.85-2.194.5.5,0,0,0-.621.784,15.455,15.455,0,0,0,4.115,2.343.487.487,0,0,0,.178.033.5.5,0,0,0,.178-.966ZM5.128,21.431A14.621,14.621,0,0,1,5,19.5a.5.5,0,0,0-1,0,15.729,15.729,0,0,0,.136,2.062.5.5,0,0,0,.992-.131Z\" style=\"fill:#009688\"/></svg>")
19
+ export const scaleIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"40\" height=\"40\" viewBox=\"0 0 40 40\"><path d=\"M25.5,5h-2a.5.5,0,0,1,0-1h2a.5.5,0,0,1,0,1ZM21,4.5a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,21,4.5Zm-5,0a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,16,4.5Zm10,30a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,26,34.5Zm-5,0a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,21,34.5Zm-5,0a.5.5,0,0,0-.5-.5h-2a.5.5,0,0,0,0,1h2A.5.5,0,0,0,16,34.5ZM5,15.5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm30-10v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm0,5v-2a.5.5,0,0,0-1,0v2a.5.5,0,0,0,1,0Zm-9-1v-10A1.5,1.5,0,0,0,24.5,13h-10A1.5,1.5,0,0,0,13,14.5v10A1.5,1.5,0,0,0,14.5,26h10A1.5,1.5,0,0,0,26,24.5ZM24.5,14a.5.5,0,0,1,.5.5v10a.5.5,0,0,1-.5.5h-10a.5.5,0,0,1-.5-.5v-10a.5.5,0,0,1,.5-.5Z\" style=\"fill:#b6b6b6\"/><path d=\"M34.962,4.309A.5.5,0,0,0,34.5,4h-5a.5.5,0,0,0,0,1h3.793l-6.147,6.146a.5.5,0,0,0,.708.708L34,5.707V9.5a.5.5,0,0,0,1,0v-5A.5.5,0,0,0,34.962,4.309Z\" style=\"fill:#009688\"/><path d=\"M5.707,5H9.5a.5.5,0,0,0,0-1h-5a.5.5,0,0,0-.5.5v5a.5.5,0,0,0,1,0V5.707l6.146,6.147a.5.5,0,0,0,.708-.708Z\" style=\"fill:#009688\"/><path d=\"M34.5,29a.5.5,0,0,0-.5.5v3.793l-6.146-6.147a.5.5,0,0,0-.708.708L33.293,34H29.5a.5.5,0,0,0,0,1h5a.5.5,0,0,0,.5-.5v-5A.5.5,0,0,0,34.5,29Z\" style=\"fill:#009688\"/><path d=\"M11.146,27.146,5,33.293V29.5a.5.5,0,0,0-1,0v5a.5.5,0,0,0,.5.5h5a.5.5,0,0,0,0-1H5.707l6.147-6.146a.5.5,0,0,0-.708-.708Z\" style=\"fill:#009688\"/></svg>")
20
+ export const seamIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M8,3.5v2m0,2v2m0,2v2\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/></svg>")
21
+ export const splitIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M8,3.5v9\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M6,6.5,4,8l2,1.5\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M10,6.5,12,8l-2,1.5\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/></svg>")
22
+ export const textmarkIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M.5,13.5V1.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1v12a1,1,0,0,1-1,1H1.5A1,1,0,0,1,.5,13.5Z\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M4.5,5h7\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M8,5v6.5\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/></svg>")
23
+ export const varlayerIcon = 'data:image/svg+xml;utf8,' + encodeURIComponent("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M3,4.5h10\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M3,7h10\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M3,9h10\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M3,10.5h10\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/><path d=\"M3,11.6h10\" style=\"fill:none;stroke:#949494;stroke-linecap:square;stroke-linejoin:round;opacity:1\"/></svg>")
@@ -0,0 +1,9 @@
1
+ // 슬라이서 워커 생성 — 이 파일은 번들되지 않고 dist/ 에 원형 복사된다 (build 스크립트).
2
+ // 이유: 소비자 번들러(Vite/webpack)가 워커를 청크로 인식하려면 `new Worker(new URL('리터럴', import.meta.url))`
3
+ // 정적 패턴이 소비자에게 보이는 파일에 그대로 남아 있어야 한다. viewer 자체 lib 빌드가 이걸 처리하면
4
+ // 사이트 루트 기준 절대경로 자산으로 굳어져 소비자 앱에서 404 가 난다.
5
+ // 경로 '../../engine/…' 은 dist/ 기준: 모노레포(packages/viewer/dist→packages/engine)와
6
+ // npm 설치(@three-slicer/ 스코프 형제 디렉토리) 양쪽에서 engine 패키지로 해석된다.
7
+ export function makeSlicerWorker() {
8
+ return new Worker(new URL('../../engine/src/slicer.worker.js', import.meta.url), { type: 'module' })
9
+ }
@@ -0,0 +1,178 @@
1
+ // 26단계: 통일 모델 임포트 파이프라인 — STL/OBJ/3MF/AMF/PLY.
2
+ // three/examples/jsm 로더 재사용(신규 npm 의존성 없음). 각 로더 결과(BufferGeometry|Group) →
3
+ // 월드행렬 베이크 → 비인덱스 삼각형 → **model z-up** flat 배열(N*9). 슬라이스 경로(addObject→buildMergedSTL→커널) 재사용.
4
+ // 좌표계(실측): 3D 프린팅 포맷은 모두 z-up mm. three 로더는 축 변환을 하지 않고 파일 원좌표를 그대로 로드하므로,
5
+ // STL/3MF/AMF/PLY 는 z-up 그대로 model 좌표로 사용. OBJ 는 관례상 y-up(그래픽) 가능성이 있으나 프린팅 OBJ 는 z-up 이
6
+ // 일반적 → z-up 기본(부적절 시 기즈모 회전). 이 판단은 프린팅 워크플로 기준(근거 주석).
7
+ import * as THREE from 'three'
8
+ import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'
9
+ import { PLYLoader } from 'three/examples/jsm/loaders/PLYLoader.js'
10
+ import { ThreeMFLoader } from 'three/examples/jsm/loaders/3MFLoader.js'
11
+ import { AMFLoader } from 'three/examples/jsm/loaders/AMFLoader.js'
12
+
13
+ export const SUPPORTED_EXT = ['stl', 'obj', '3mf', 'amf', 'ply']
14
+
15
+ export function fileExt(name) { const i = name.lastIndexOf('.'); return i < 0 ? '' : name.slice(i + 1).toLowerCase() }
16
+
17
+ // --- STL 파서 (바이너리 + ASCII) → model 좌표 position (N*9) --- (기존 Viewport 로직 이관)
18
+ function parseSTL(buffer) {
19
+ const dv = new DataView(buffer)
20
+ if (buffer.byteLength >= 84) {
21
+ const n = dv.getUint32(80, true)
22
+ if (buffer.byteLength === 84 + n * 50) {
23
+ const pos = new Float32Array(n * 9)
24
+ let off = 84, p = 0
25
+ for (let i = 0; i < n; i++) {
26
+ off += 12
27
+ for (let v = 0; v < 3; v++) { pos[p++] = dv.getFloat32(off, true); pos[p++] = dv.getFloat32(off + 4, true); pos[p++] = dv.getFloat32(off + 8, true); off += 12 }
28
+ off += 2
29
+ }
30
+ return pos
31
+ }
32
+ }
33
+ const text = new TextDecoder().decode(buffer)
34
+ const re = /vertex\s+([-\d.eE+]+)\s+([-\d.eE+]+)\s+([-\d.eE+]+)/g
35
+ const nums = []; let m
36
+ while ((m = re.exec(text))) nums.push(+m[1], +m[2], +m[3])
37
+ if (nums.length < 9) return null
38
+ return new Float32Array(nums)
39
+ }
40
+
41
+ // BufferGeometry(+월드행렬) → 비인덱스 삼각형 model z-up flat 배열(N*9). 로더 좌표=z-up 그대로 사용.
42
+ function geoToModelTris(geometry, matrix) {
43
+ const pos = geometry?.attributes?.position
44
+ if (!pos || pos.count < 3) return null
45
+ const idx = geometry.index
46
+ const v = new THREE.Vector3()
47
+ const nTri = Math.floor((idx ? idx.count : pos.count) / 3)
48
+ const out = new Float32Array(nTri * 9)
49
+ let o = 0
50
+ const emit = (i) => { v.fromBufferAttribute(pos, i); if (matrix) v.applyMatrix4(matrix); out[o++] = v.x; out[o++] = v.y; out[o++] = v.z }
51
+ if (idx) { for (let i = 0; i < nTri * 3; i++) emit(idx.getX(i)) }
52
+ else { for (let i = 0; i < nTri * 3; i++) emit(i) }
53
+ return out
54
+ }
55
+
56
+ // Group/Object3D 재귀 → [{geometry, matrixWorld}] (월드행렬 베이크용).
57
+ function collectMeshes(root) {
58
+ root.updateMatrixWorld(true)
59
+ const meshes = []
60
+ root.traverse(o => { if (o.isMesh && o.geometry) meshes.push({ geometry: o.geometry, matrix: o.matrixWorld }) })
61
+ return meshes
62
+ }
63
+
64
+ // 로더 결과 → 오브젝트 목록. multi=true(3mf/amf) 면 메시별 개별 오브젝트, 아니면 하나로 병합.
65
+ function meshesToObjects(baseName, meshes, multi) {
66
+ const parts = meshes.map(m => geoToModelTris(m.geometry, m.matrix)).filter(Boolean)
67
+ if (!parts.length) return []
68
+ if (multi && parts.length > 1) {
69
+ return parts.map((p, i) => ({ name: `${baseName}#${i + 1}`, modelPos: p }))
70
+ }
71
+ if (parts.length === 1) return [{ name: baseName, modelPos: parts[0] }]
72
+ // 병합
73
+ let total = 0; for (const p of parts) total += p.length
74
+ const merged = new Float32Array(total); let o = 0
75
+ for (const p of parts) { merged.set(p, o); o += p.length }
76
+ return [{ name: baseName, modelPos: merged }]
77
+ }
78
+
79
+ // 공개 API: (파일명, ArrayBuffer) → Promise<[{name, modelPos(N*9, model z-up)}]>. 커널 무관 순수.
80
+ export async function loadModel(name, buffer) {
81
+ const ext = fileExt(name)
82
+ switch (ext) {
83
+ case 'stl': {
84
+ const pos = parseSTL(buffer)
85
+ if (!pos) throw new Error('STL 파싱 실패')
86
+ return [{ name, modelPos: pos }]
87
+ }
88
+ case 'obj': {
89
+ const g = new OBJLoader().parse(new TextDecoder().decode(buffer)) // Group
90
+ return meshesToObjects(name, collectMeshes(g), false)
91
+ }
92
+ case 'ply': {
93
+ const geo = new PLYLoader().parse(buffer) // BufferGeometry
94
+ const tris = geoToModelTris(geo, null)
95
+ if (!tris) throw new Error('PLY 지오메트리 없음')
96
+ return [{ name, modelPos: tris }]
97
+ }
98
+ case '3mf': {
99
+ const g = new ThreeMFLoader().parse(buffer) // Group (fflate 로 unzip, three 내부)
100
+ const objs = meshesToObjects(name, collectMeshes(g), true) // 복수 오브젝트 개별 등록
101
+ if (!objs.length) throw new Error('3MF 메시 없음')
102
+ return objs
103
+ }
104
+ case 'amf': {
105
+ const g = new AMFLoader().parse(buffer) // Group (DOMParser 사용 — 브라우저)
106
+ const objs = meshesToObjects(name, collectMeshes(g), true)
107
+ if (!objs.length) throw new Error('AMF 메시 없음')
108
+ return objs
109
+ }
110
+ default:
111
+ throw new Error(`지원하지 않는 포맷: .${ext} (STEP 은 범위 외 — OCCT 필요)`)
112
+ }
113
+ }
114
+
115
+ // ---- 33단계: 객체 분리 (원본 Split to objects / TriangleMesh::its_split) ----
116
+ // 원본 정의: "면은 **에지를 공유하면** 연결된 것으로 본다(공유 에지 = 정점 인덱스 2개 공유)"
117
+ // (TriangleMesh.cpp:1505). 그 연결 성분(patch)마다 독립 오브젝트가 된다.
118
+ // 우리 localPos 는 비인덱스 삼각형 스트림(정점 9개/삼각형)이라, 먼저 좌표를 양자화해 정점을 용접하고
119
+ // 에지를 만든 뒤 union-find 로 성분을 모은다. 인접 리스트를 만들지 않아 메모리/시간이 선형에 가깝다.
120
+ //
121
+ // 반환: 성분이 2개 이상이면 Float32Array[] (각 성분의 localPos, 좌표계는 입력 그대로),
122
+ // 1개면 null (분리 불가).
123
+ export function splitConnectedComponents(localPos) {
124
+ const nTri = Math.floor(localPos.length / 9)
125
+ if (nTri < 2) return null
126
+
127
+ // 정점 용접: 부동소수 오차로 같은 점이 갈라지지 않도록 1e-4mm 격자로 양자화
128
+ const Q = 1e4
129
+ const vmap = new Map()
130
+ const vidx = new Int32Array(nTri * 3)
131
+ for (let t = 0; t < nTri; t++) {
132
+ for (let k = 0; k < 3; k++) {
133
+ const o = t * 9 + k * 3
134
+ const key = `${Math.round(localPos[o] * Q)},${Math.round(localPos[o + 1] * Q)},${Math.round(localPos[o + 2] * Q)}`
135
+ let id = vmap.get(key)
136
+ if (id === undefined) { id = vmap.size; vmap.set(key, id) }
137
+ vidx[t * 3 + k] = id
138
+ }
139
+ }
140
+
141
+ // union-find
142
+ const parent = new Int32Array(nTri)
143
+ for (let i = 0; i < nTri; i++) parent[i] = i
144
+ const find = (x) => { while (parent[x] !== x) { parent[x] = parent[parent[x]]; x = parent[x] } return x }
145
+ const union = (a, b) => { a = find(a); b = find(b); if (a !== b) parent[b] = a }
146
+
147
+ // 에지(정점쌍) → 처음 본 면. 같은 에지를 다시 만나면 두 면을 합친다.
148
+ // 에지당 면이 3개 이상인 비-매니폴드도 첫 면 기준으로 전부 이어져 문제없다.
149
+ const edge = new Map()
150
+ for (let t = 0; t < nTri; t++) {
151
+ for (let e = 0; e < 3; e++) {
152
+ const a = vidx[t * 3 + e], b = vidx[t * 3 + (e + 1) % 3]
153
+ const key = a < b ? a * 4294967296 + b : b * 4294967296 + a // 정점 인덱스 2개를 하나의 수로
154
+ const prev = edge.get(key)
155
+ if (prev === undefined) edge.set(key, t)
156
+ else union(t, prev)
157
+ }
158
+ }
159
+
160
+ // 성분별 면 수집
161
+ const groups = new Map()
162
+ for (let t = 0; t < nTri; t++) {
163
+ const r = find(t)
164
+ let g = groups.get(r)
165
+ if (!g) { g = []; groups.set(r, g) }
166
+ g.push(t)
167
+ }
168
+ if (groups.size < 2) return null
169
+
170
+ // 큰 성분부터 (원본도 부피 순으로 정렬해 첫 오브젝트가 본체가 되게 한다)
171
+ const parts = [...groups.values()].sort((a, b) => b.length - a.length)
172
+ return parts.map(faces => {
173
+ const out = new Float32Array(faces.length * 9)
174
+ let w = 0
175
+ for (const t of faces) { const o = t * 9; for (let k = 0; k < 9; k++) out[w++] = localPos[o + k] }
176
+ return out
177
+ })
178
+ }
@@ -0,0 +1,30 @@
1
+ import React, { useLayoutEffect, useRef, useState } from 'react'
2
+ import { createPortal } from 'react-dom'
3
+
4
+ // Shadow DOM 격리 호스트 — 컴포넌트 스타일을 shadow root 에 주입해 호스트 앱 CSS 와
5
+ // 양방향 완전 차단한다 (클래스명 충돌 불가). React 17+ 는 포털 컨테이너에도 이벤트
6
+ // 리스너를 직접 붙이므로(preparePortalMount) shadow 안에서도 합성 이벤트가 정상 동작.
7
+ // 호스트 div/컨테이너는 display:contents 로 레이아웃 투명 — 자식이 종전처럼 배치된다.
8
+ //
9
+ // 포털 컨테이너는 렌더 전에 미리 만든다. shadow root 생성을 기다렸다 조건부로 포털을
10
+ // 열면 자식 ref 가 붙기 전에 자식의 mount useEffect 가 ref=null 로 소진된다
11
+ // (three.js 초기화 영구 불발). 첫 커밋부터 detached 컨테이너에 자식을 마운트하고,
12
+ // layout effect 에서 컨테이너를 shadow root 로 옮긴다 — paint 전이라 깜빡임 없음.
13
+ // (document 를 렌더 중 만지므로 SSR 불가 — Next 등에서는 ssr:false 로 로드할 것.)
14
+ export default function ShadowHost({ css, className, children }) {
15
+ const hostRef = useRef(null)
16
+ const [container] = useState(() => {
17
+ const el = document.createElement('div')
18
+ el.style.display = 'contents'
19
+ return el
20
+ })
21
+ useLayoutEffect(() => {
22
+ const host = hostRef.current
23
+ const shadow = host.shadowRoot || host.attachShadow({ mode: 'open' })
24
+ let style = shadow.querySelector('style[data-ts-css]')
25
+ if (!style) { style = document.createElement('style'); style.setAttribute('data-ts-css', ''); shadow.append(style) }
26
+ style.textContent = css
27
+ if (!shadow.contains(container)) shadow.append(container)
28
+ }, [css, container])
29
+ return <div ref={hostRef} className={className}>{createPortal(children, container)}</div>
30
+ }