@ifc-lite/viewer 1.22.0 → 1.23.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 (566) hide show
  1. package/.turbo/turbo-build.log +71 -44
  2. package/CHANGELOG.md +73 -0
  3. package/dist/assets/__vite-browser-external-B1O5LaIO.js +1 -0
  4. package/dist/assets/{arrow-fie-E7fe.js → arrow-CXWhTnNT.js} +1 -1
  5. package/dist/assets/{basketViewActivator-EHAhHlwN.js → basketViewActivator-Dn_bHUl2.js} +10 -10
  6. package/dist/assets/{bcf-Bhx-K17f.js → bcf-B9SFl84i.js} +2 -2
  7. package/dist/assets/browser-DXS29_v9.js +695 -0
  8. package/dist/assets/{cesium-B4ZIU9jS.js → cesium-BoVuJvTC.js} +5838 -5730
  9. package/dist/assets/{decode-worker-CYqSjk1n.js → decode-worker-CgM1iNSK.js} +1 -1
  10. package/dist/assets/deflate-yMpdCIqk.js +1 -0
  11. package/dist/assets/{drawing-2d-Bjy8YPrg.js → drawing-2d-C71b8Ugx.js} +1 -1
  12. package/dist/assets/emscripten-module-B1g2L2eS.wasm +0 -0
  13. package/dist/assets/emscripten-module-DHbYPfAp.wasm +0 -0
  14. package/dist/assets/emscripten-module-ZrHFMo7O.wasm +0 -0
  15. package/dist/assets/emscripten-module-uFzwHH0Y.wasm +0 -0
  16. package/dist/assets/emscripten-module.browser-BLJD5hhE.js +1 -0
  17. package/dist/assets/{esbuild-Cpd5nU_H.wasm → esbuild-CzsZLPr0.wasm} +0 -0
  18. package/dist/assets/esbuild-FgU11_Eg.js +1 -0
  19. package/dist/assets/event-B0kAzHa-.js +1 -0
  20. package/dist/assets/{exporters-KTio0Tdm.js → exporters-D-BvrNIg.js} +1361 -1330
  21. package/dist/assets/ffi-Boa1QuFa.js +1 -0
  22. package/dist/assets/{geometry-controller.worker-Cm2P_EJr.js → geometry-controller.worker-Cm5pvyR6.js} +2 -2
  23. package/dist/assets/geometry.worker-ClNvXIrj.js +1 -0
  24. package/dist/assets/geotiff-D1tvcDCb.js +3354 -0
  25. package/dist/assets/{ids-CS7VCFin.js → ids-DZLs0snJ.js} +6 -6
  26. package/dist/assets/{ifc-lite-C6wEhXa6.js → ifc-lite-BDg0iIbj.js} +2 -2
  27. package/dist/assets/ifc-lite_bg-BIryVCXQ.wasm +0 -0
  28. package/dist/assets/ifc-lite_bg-DyHX37GQ.wasm +0 -0
  29. package/dist/assets/{index-8k9h-ANq.js → index-CXSBhkcJ.js} +64181 -52931
  30. package/dist/assets/index-DMho-JA0.js +6 -0
  31. package/dist/assets/index-DS_xJQfP.css +1 -0
  32. package/dist/assets/jpeg-DUMcZp24.js +1 -0
  33. package/dist/assets/{laz-perf-DnSyzVYH.wasm → laz-perf-CFJp03W6.wasm} +0 -0
  34. package/dist/assets/laz-perf-DgUOSLeU.js +1 -0
  35. package/dist/assets/{laz-source-jj3xI5Y4.js → laz-source-BWjza0Iw.js} +2 -2
  36. package/dist/assets/{lens-CSASnhAL.js → lens-CpjUdqpw.js} +1 -1
  37. package/dist/assets/lerc-IN4uWojP.js +1 -0
  38. package/dist/assets/lzw-Cnw0hH-m.js +1 -0
  39. package/dist/assets/maplibre-gl-Do6O5tDc.js +800 -0
  40. package/dist/assets/{native-bridge-DNrEhx2R.js → native-bridge-BVf2uzoH.js} +3 -3
  41. package/dist/assets/packbits-BskJCwk0.js +1 -0
  42. package/dist/assets/pako.esm-Cram60i4.js +1 -0
  43. package/dist/assets/parquet_wasm_bg-DcKVfvto.wasm +0 -0
  44. package/dist/assets/{parser.worker-BcjkIo89.js → parser.worker-BdtkkaGf.js} +3 -3
  45. package/dist/assets/raw-DzTtEZIY.js +1 -0
  46. package/dist/assets/{sandbox-BSn5MyEJ.js → sandbox-VLI_y7cl.js} +1664 -1542
  47. package/dist/assets/{server-client-D-kU2XAF.js → server-client-BLcKaWQB.js} +4 -4
  48. package/dist/assets/three-CQBzFWY2.js +4104 -0
  49. package/dist/assets/wasm-bridge-BAfZh7YT.js +1 -0
  50. package/dist/assets/webimage-Db2xzze3.js +1 -0
  51. package/dist/assets/{workerHelpers-pUUnk9Wc.js → workerHelpers--sAYm9yN.js} +1 -1
  52. package/dist/assets/zstd-BDToOQyD.js +1 -0
  53. package/dist/cesium/{Workers/chunk-V7QEYVP3.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-236YIEYT.js} +2 -2
  54. package/dist/cesium/{Workers/chunk-5TJMAQVL.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-2ZYB3DYT.js} +2 -2
  55. package/dist/cesium/{Workers/chunk-UBOGZS7F.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-4M56RRIL.js} +2 -2
  56. package/dist/cesium/{Workers/chunk-OCWJRAXS.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-5BC2Q3QW.js} +2 -2
  57. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-5XHUDY37.js +26 -0
  58. package/dist/cesium/{Workers/chunk-Z3QF2EHT.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-6WMLAJJP.js} +2 -2
  59. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-72KUXMWU.js +26 -0
  60. package/dist/cesium/{Workers/chunk-EQ4YRVWL.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-7NQYTTAU.js} +2 -2
  61. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-A35GG5WJ.js +26 -0
  62. package/dist/cesium/{Workers/chunk-FC4ZZ65J.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-AXNBHUAG.js} +2 -2
  63. package/dist/cesium/{Workers/chunk-BTSYJ5XU.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-DC3K7YTH.js} +2 -2
  64. package/dist/cesium/{Workers/chunk-SLT4J352.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-DRBPXGI7.js} +2 -2
  65. package/dist/cesium/{Workers/chunk-2MJIIVP4.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-EARRZPMO.js} +2 -2
  66. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-EHC3BDVP.js +26 -0
  67. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-EYZUSGKM.js +26 -0
  68. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-EZSKHVA2.js +26 -0
  69. package/dist/cesium/{Workers/chunk-ICALLYLG.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-F3WJIFOO.js} +2 -2
  70. package/dist/cesium/{Workers/chunk-LSLE2RL4.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-FB7UV5BI.js} +2 -2
  71. package/dist/cesium/{Workers/chunk-CUUSNIVQ.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-FC6IYMYF.js} +2 -2
  72. package/dist/cesium/{Workers/chunk-TNSUQXWK.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-GF67PEXE.js} +2 -2
  73. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-GXEQRH2R.js +26 -0
  74. package/dist/cesium/{Workers/chunk-M4HLDBCG.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-I5NKQIWE.js} +2 -2
  75. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-IH7GXIUB.js +26 -0
  76. package/dist/cesium/{Workers/chunk-OIT7J4IC.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-IPP3UFGH.js} +2 -2
  77. package/dist/cesium/{Workers/chunk-WWWZVEEH.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-IYRGNBSH.js} +2 -2
  78. package/dist/cesium/{Workers/chunk-WPMZLB3Y.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-J6BM74AD.js} +2 -2
  79. package/dist/cesium/{Workers/chunk-QFM5DCMQ.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-KG2GJUJT.js} +1 -1
  80. package/dist/cesium/{Workers/chunk-XQHLGIO7.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-L7UE5MMF.js} +2 -2
  81. package/dist/cesium/{Workers/chunk-EFBN7QNX.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-LBZ34MHQ.js} +2 -2
  82. package/dist/cesium/{Workers/chunk-YP7I5QBZ.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-LOQDTQMX.js} +2 -2
  83. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-LYLRYC4L.js +29 -0
  84. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-MGPRMLLW.js +26 -0
  85. package/dist/cesium/{Workers/chunk-LI2ZSORM.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-NP26LKQA.js} +2 -2
  86. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-OMUAZ3NM.js +26 -0
  87. package/dist/cesium/{Workers/chunk-S44JILQT.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-PPFUDJN4.js} +2 -2
  88. package/dist/cesium/{Workers/chunk-EDVBB7SS.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-PQ3V63XF.js} +2 -2
  89. package/dist/cesium/{Workers/chunk-Q5BPHJQF.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-PWBQN4GK.js} +2 -2
  90. package/dist/cesium/{Workers/chunk-E7KYDCM5.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-QOTMLO2T.js} +2 -2
  91. package/dist/cesium/{Workers/chunk-XUSCFAVF.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-SP35IT73.js} +2 -2
  92. package/dist/cesium/{Workers/chunk-6BD4U3VO.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-T3ZGSZKA.js} +2 -2
  93. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-TM6SYYHO.js +28 -0
  94. package/dist/cesium/{Workers/chunk-VUKYSU4H.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-TSGIJVWH.js} +2 -2
  95. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-U3YGOX3C.js +63 -0
  96. package/dist/cesium/{Workers/chunk-7TVGLKQF.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-VBYOXOSM.js} +2 -2
  97. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-VCOHJNKB.js +26 -0
  98. package/dist/cesium/{Workers/chunk-WBOV35NL.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-VXAZXMUX.js} +2 -2
  99. package/dist/cesium/{Workers/chunk-IX4VMHEV.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-VXCJOT4W.js} +2 -2
  100. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-WPD3MB6X.js +26 -0
  101. package/dist/cesium/{Workers/chunk-V3OSTMM6.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-X4D5KUN5.js} +2 -2
  102. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-XEC656IT.js +26 -0
  103. package/dist/cesium/{Workers/chunk-MJHHSGEH.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-XR53QRQS.js} +2 -2
  104. package/dist/cesium/{Workers/chunk-XFIQ5DEQ.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-XR7MN4PJ.js} +2 -2
  105. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-XU6O4MRS.js +26 -0
  106. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-XZBHEBLF.js +29 -0
  107. package/dist/cesium/{Workers/chunk-OLZ3FYUM.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-Z2M4BF4E.js} +2 -2
  108. package/dist/cesium/{Workers/chunk-2ZBHLJST.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-Z4ERBZFB.js} +2 -2
  109. package/dist/cesium/{Workers/chunk-W37FE5GR.js → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/chunk-ZY2KCIWI.js} +2 -2
  110. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/combineGeometry.js +2 -2
  111. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createBoxGeometry.js +2 -2
  112. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createBoxOutlineGeometry.js +2 -2
  113. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createCircleGeometry.js +2 -2
  114. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createCircleOutlineGeometry.js +2 -2
  115. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createCoplanarPolygonGeometry.js +2 -2
  116. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/createCoplanarPolygonOutlineGeometry.js +26 -0
  117. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createCorridorGeometry.js +2 -2
  118. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createCorridorOutlineGeometry.js +2 -2
  119. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createCylinderGeometry.js +2 -2
  120. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createCylinderOutlineGeometry.js +2 -2
  121. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createEllipseGeometry.js +2 -2
  122. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createEllipseOutlineGeometry.js +2 -2
  123. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createEllipsoidGeometry.js +2 -2
  124. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createEllipsoidOutlineGeometry.js +2 -2
  125. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createFrustumGeometry.js +2 -2
  126. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createFrustumOutlineGeometry.js +2 -2
  127. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createGeometry.js +2 -2
  128. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/createGroundPolylineGeometry.js +26 -0
  129. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createPlaneGeometry.js +2 -2
  130. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createPlaneOutlineGeometry.js +2 -2
  131. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createPolygonGeometry.js +2 -2
  132. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createPolygonOutlineGeometry.js +2 -2
  133. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createPolylineGeometry.js +2 -2
  134. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createPolylineVolumeGeometry.js +2 -2
  135. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createPolylineVolumeOutlineGeometry.js +2 -2
  136. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createRectangleGeometry.js +2 -2
  137. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createRectangleOutlineGeometry.js +2 -2
  138. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createSimplePolylineGeometry.js +2 -2
  139. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createSphereGeometry.js +2 -2
  140. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createSphereOutlineGeometry.js +2 -2
  141. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createTaskProcessorWorker.js +2 -2
  142. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createVectorTileClampedPolylines.js +2 -2
  143. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createVectorTileGeometries.js +2 -2
  144. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createVectorTilePoints.js +2 -2
  145. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createVectorTilePolygons.js +2 -2
  146. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createVectorTilePolylines.js +2 -2
  147. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createVerticesFromCesium3DTilesTerrain.js +2 -2
  148. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createVerticesFromGoogleEarthEnterpriseBuffer.js +2 -2
  149. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createVerticesFromHeightmap.js +2 -2
  150. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createVerticesFromQuantizedTerrainMesh.js +2 -2
  151. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createWallGeometry.js +2 -2
  152. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/createWallOutlineGeometry.js +2 -2
  153. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/decodeDraco.js +2 -2
  154. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/decodeGoogleEarthEnterprisePacket.js +2 -2
  155. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/decodeI3S.js +2 -2
  156. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/gaussianSplatSorter.js +2 -2
  157. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/gaussianSplatTextureGenerator.js +2 -2
  158. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/incrementallyBuildTerrainPicker.js +2 -2
  159. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/transcodeKTX2.js +56 -0
  160. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/transferTypedArrayTest.js +1 -1
  161. package/dist/cesium/node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers/upsampleQuantizedTerrainMesh.js +26 -0
  162. package/dist/cesium/{Workers → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Workers}/upsampleVerticesFromCesium3DTilesTerrain.js +2 -2
  163. package/dist/index.html +11 -11
  164. package/package.json +52 -45
  165. package/src/components/mcp/McpLanding.tsx +1 -1
  166. package/src/components/mcp/McpPlayground.tsx +1 -1
  167. package/src/components/viewer/AddElementPanel.tsx +2 -2
  168. package/src/components/viewer/BasepointOverlay.tsx +239 -0
  169. package/src/components/viewer/CesiumOverlay.tsx +15 -59
  170. package/src/components/viewer/CesiumPlacementEditor.tsx +16 -1
  171. package/src/components/viewer/CommandPalette.tsx +25 -0
  172. package/src/components/viewer/GLBExportDialog.tsx +389 -0
  173. package/src/components/viewer/GeometryAxisRow.tsx +54 -0
  174. package/src/components/viewer/GeometryEditCard.tsx +392 -0
  175. package/src/components/viewer/HierarchyPanel.tsx +2 -2
  176. package/src/components/viewer/IDSAuditSummary.tsx +2 -2
  177. package/src/components/viewer/KeyboardShortcutsDialog.tsx +58 -4
  178. package/src/components/viewer/MainToolbar.tsx +292 -29
  179. package/src/components/viewer/PropertiesPanel.tsx +28 -31
  180. package/src/components/viewer/StatusBar.tsx +12 -0
  181. package/src/components/viewer/ToolOverlays.tsx +36 -0
  182. package/src/components/viewer/ViewerLayout.tsx +27 -0
  183. package/src/components/viewer/Viewport.tsx +49 -4
  184. package/src/components/viewer/ViewportContainer.tsx +88 -17
  185. package/src/components/viewer/ViewportOverlays.tsx +50 -11
  186. package/src/components/viewer/chat/ByokKeyModal.tsx +1 -1
  187. package/src/components/viewer/properties/FederationAlignmentControls.tsx +180 -0
  188. package/src/components/viewer/properties/GeoreferencingPanel.tsx +6 -0
  189. package/src/components/viewer/properties/PrecisionGridBadge.tsx +125 -0
  190. package/src/components/viewer/properties/raw-step-format.ts +5 -3
  191. package/src/components/viewer/selectionHandlers.ts +301 -10
  192. package/src/components/viewer/tools/GizmoOverlay.tsx +322 -0
  193. package/src/components/viewer/tools/SplitNumericInput.tsx +225 -0
  194. package/src/components/viewer/tools/SplitOverlay.tsx +242 -0
  195. package/src/components/viewer/tools/WallEndpointOverlay.tsx +252 -0
  196. package/src/components/viewer/useGeometryStreaming.ts +100 -1
  197. package/src/components/viewer/useMouseControls.ts +8 -1
  198. package/src/hooks/useBCF.ts +6 -6
  199. package/src/hooks/useCameraTickSubscription.ts +62 -0
  200. package/src/hooks/useIfc.ts +4 -0
  201. package/src/hooks/useIfcFederation.ts +368 -20
  202. package/src/hooks/useKeyboardShortcuts.ts +86 -0
  203. package/src/hooks/useLevelDisplayEffect.ts +160 -0
  204. package/src/hooks/useViewerSelectors.ts +8 -0
  205. package/src/lib/__test__/stubs.ts +110 -0
  206. package/src/lib/geo/cesium-bridge.ts +3 -3
  207. package/src/lib/geo/cesium-placement.test.ts +28 -12
  208. package/src/lib/geo/cesium-placement.ts +16 -10
  209. package/src/lib/geo/effective-georef.test.ts +23 -7
  210. package/src/lib/geo/geo-scale.ts +58 -2
  211. package/src/lib/geo/ifc-origin.test.ts +176 -0
  212. package/src/lib/geo/ifc-origin.ts +144 -0
  213. package/src/lib/geo/precision-grids.ts +444 -0
  214. package/src/lib/geo/reproject.test.ts +88 -0
  215. package/src/lib/geo/reproject.ts +168 -36
  216. package/src/lib/level-offsets.test.ts +153 -0
  217. package/src/lib/level-offsets.ts +140 -0
  218. package/src/lib/linear-element-edit.test.ts +220 -0
  219. package/src/lib/linear-element-edit.ts +327 -0
  220. package/src/lib/metadata-clone.test.ts +173 -0
  221. package/src/lib/metadata-clone.ts +124 -0
  222. package/src/lib/placement-core.ts +340 -0
  223. package/src/lib/placement-edit.boot.ts +21 -0
  224. package/src/lib/placement-edit.test.ts +464 -0
  225. package/src/lib/placement-edit.ts +47 -0
  226. package/src/lib/polygon-clip.test.ts +193 -0
  227. package/src/lib/polygon-clip.ts +199 -0
  228. package/src/lib/slab-edit.test.ts +169 -0
  229. package/src/lib/slab-edit.ts +312 -0
  230. package/src/lib/wall-edit.ts +342 -0
  231. package/src/lib/wall-opening-reassign.test.ts +291 -0
  232. package/src/lib/wall-opening-reassign.ts +241 -0
  233. package/src/main.tsx +4 -0
  234. package/src/store/index.ts +7 -0
  235. package/src/store/slices/cesiumSlice.ts +61 -8
  236. package/src/store/slices/dataSlice.ts +80 -0
  237. package/src/store/slices/levelDisplaySlice.ts +105 -0
  238. package/src/store/slices/modelSlice.test.ts +19 -0
  239. package/src/store/slices/mutationSlice.ts +1138 -1
  240. package/src/store/slices/splitToolSlice.ts +165 -0
  241. package/src/store/slices/uiSlice.edit-mode.test.ts +210 -0
  242. package/src/store/slices/uiSlice.ts +78 -1
  243. package/src/store/types.ts +57 -2
  244. package/src/utils/createBlankIfc.ts +37 -0
  245. package/tsconfig.json +1 -0
  246. package/.turbo/turbo-typecheck.log +0 -4
  247. package/dist/assets/arrow2_bg-BoXCojjR.wasm +0 -0
  248. package/dist/assets/browser-CVf8ATeW.js +0 -694
  249. package/dist/assets/emscripten-module-BTRCZGcB.wasm +0 -0
  250. package/dist/assets/emscripten-module-CGIn_cMh.wasm +0 -0
  251. package/dist/assets/emscripten-module-DYvzWiHh.wasm +0 -0
  252. package/dist/assets/emscripten-module-NWak2PoB.wasm +0 -0
  253. package/dist/assets/emscripten-module.browser-DcFZLAUx.js +0 -1
  254. package/dist/assets/esbuild-COv63sf-.js +0 -1
  255. package/dist/assets/event-DIOks52T.js +0 -1
  256. package/dist/assets/ffi-DlhRHxHv.js +0 -1
  257. package/dist/assets/geometry.worker-DchLBqZ8.js +0 -1
  258. package/dist/assets/ifc-lite_bg-CSeT3fNI.wasm +0 -0
  259. package/dist/assets/ifc-lite_bg-ns4cSnX2.wasm +0 -0
  260. package/dist/assets/index-BZC2YaOP.css +0 -1
  261. package/dist/assets/index-HqAIQkr6.js +0 -22
  262. package/dist/assets/laz-perf-Cvr_Lepg.js +0 -1
  263. package/dist/assets/maplibre-gl-C4LXKM6c.js +0 -808
  264. package/dist/assets/three-DwNDHx9-.js +0 -4049
  265. package/dist/assets/wasm-bridge-Cha08LdC.js +0 -1
  266. package/dist/cesium/Workers/chunk-23ZQ2IVV.js +0 -29
  267. package/dist/cesium/Workers/chunk-2EQO3Q56.js +0 -26
  268. package/dist/cesium/Workers/chunk-2TE5NTVD.js +0 -26
  269. package/dist/cesium/Workers/chunk-BXMEEOCS.js +0 -63
  270. package/dist/cesium/Workers/chunk-BYLCY7GP.js +0 -29
  271. package/dist/cesium/Workers/chunk-CTHM3W6I.js +0 -26
  272. package/dist/cesium/Workers/chunk-E3JOOS3S.js +0 -26
  273. package/dist/cesium/Workers/chunk-F6PRE7D6.js +0 -26
  274. package/dist/cesium/Workers/chunk-FFBVWF2L.js +0 -26
  275. package/dist/cesium/Workers/chunk-GBAA6GVX.js +0 -26
  276. package/dist/cesium/Workers/chunk-ILRYTWTP.js +0 -26
  277. package/dist/cesium/Workers/chunk-IRNLBSEJ.js +0 -26
  278. package/dist/cesium/Workers/chunk-L6QHHACZ.js +0 -26
  279. package/dist/cesium/Workers/chunk-NMVKML6W.js +0 -26
  280. package/dist/cesium/Workers/chunk-OIRKANTH.js +0 -26
  281. package/dist/cesium/Workers/chunk-QKUIYMGC.js +0 -28
  282. package/dist/cesium/Workers/chunk-SQMIIXB7.js +0 -26
  283. package/dist/cesium/Workers/chunk-TJ4XLGBQ.js +0 -26
  284. package/dist/cesium/Workers/createCoplanarPolygonOutlineGeometry.js +0 -26
  285. package/dist/cesium/Workers/createGroundPolylineGeometry.js +0 -26
  286. package/dist/cesium/Workers/transcodeKTX2.js +0 -56
  287. package/dist/cesium/Workers/upsampleQuantizedTerrainMesh.js +0 -26
  288. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_0.json +0 -0
  289. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_1.json +0 -0
  290. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_10.json +0 -0
  291. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_11.json +0 -0
  292. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_12.json +0 -0
  293. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_13.json +0 -0
  294. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_14.json +0 -0
  295. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_15.json +0 -0
  296. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_16.json +0 -0
  297. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_17.json +0 -0
  298. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_18.json +0 -0
  299. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_19.json +0 -0
  300. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_2.json +0 -0
  301. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_20.json +0 -0
  302. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_21.json +0 -0
  303. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_22.json +0 -0
  304. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_23.json +0 -0
  305. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_24.json +0 -0
  306. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_25.json +0 -0
  307. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_26.json +0 -0
  308. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_27.json +0 -0
  309. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_3.json +0 -0
  310. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_4.json +0 -0
  311. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_5.json +0 -0
  312. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_6.json +0 -0
  313. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_7.json +0 -0
  314. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_8.json +0 -0
  315. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/IAU2006_XYS/IAU2006_XYS_9.json +0 -0
  316. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Images/bing_maps_credit.png +0 -0
  317. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Images/cesium_credit.png +0 -0
  318. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Images/google_earth_credit.png +0 -0
  319. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Images/ion-credit.png +0 -0
  320. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/LensFlare/DirtMask.jpg +0 -0
  321. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/LensFlare/StarBurst.jpg +0 -0
  322. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/0/0/0.jpg +0 -0
  323. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/0/1/0.jpg +0 -0
  324. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/1/0/0.jpg +0 -0
  325. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/1/0/1.jpg +0 -0
  326. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/1/1/0.jpg +0 -0
  327. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/1/1/1.jpg +0 -0
  328. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/1/2/0.jpg +0 -0
  329. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/1/2/1.jpg +0 -0
  330. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/1/3/0.jpg +0 -0
  331. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/1/3/1.jpg +0 -0
  332. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/0/0.jpg +0 -0
  333. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/0/1.jpg +0 -0
  334. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/0/2.jpg +0 -0
  335. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/0/3.jpg +0 -0
  336. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/1/0.jpg +0 -0
  337. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/1/1.jpg +0 -0
  338. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/1/2.jpg +0 -0
  339. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/1/3.jpg +0 -0
  340. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/2/0.jpg +0 -0
  341. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/2/1.jpg +0 -0
  342. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/2/2.jpg +0 -0
  343. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/2/3.jpg +0 -0
  344. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/3/0.jpg +0 -0
  345. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/3/1.jpg +0 -0
  346. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/3/2.jpg +0 -0
  347. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/3/3.jpg +0 -0
  348. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/4/0.jpg +0 -0
  349. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/4/1.jpg +0 -0
  350. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/4/2.jpg +0 -0
  351. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/4/3.jpg +0 -0
  352. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/5/0.jpg +0 -0
  353. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/5/1.jpg +0 -0
  354. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/5/2.jpg +0 -0
  355. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/5/3.jpg +0 -0
  356. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/6/0.jpg +0 -0
  357. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/6/1.jpg +0 -0
  358. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/6/2.jpg +0 -0
  359. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/6/3.jpg +0 -0
  360. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/7/0.jpg +0 -0
  361. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/7/1.jpg +0 -0
  362. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/7/2.jpg +0 -0
  363. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/2/7/3.jpg +0 -0
  364. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/NaturalEarthII/tilemapresource.xml +0 -0
  365. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/SkyBox/tycho2t3_80_mx.jpg +0 -0
  366. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/SkyBox/tycho2t3_80_my.jpg +0 -0
  367. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/SkyBox/tycho2t3_80_mz.jpg +0 -0
  368. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/SkyBox/tycho2t3_80_px.jpg +0 -0
  369. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/SkyBox/tycho2t3_80_py.jpg +0 -0
  370. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/SkyBox/tycho2t3_80_pz.jpg +0 -0
  371. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/airfield.png +0 -0
  372. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/airport.png +0 -0
  373. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/alcohol-shop.png +0 -0
  374. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/america-football.png +0 -0
  375. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/art-gallery.png +0 -0
  376. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/bakery.png +0 -0
  377. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/bank.png +0 -0
  378. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/bar.png +0 -0
  379. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/baseball.png +0 -0
  380. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/basketball.png +0 -0
  381. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/beer.png +0 -0
  382. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/bicycle.png +0 -0
  383. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/building.png +0 -0
  384. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/bus.png +0 -0
  385. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/cafe.png +0 -0
  386. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/camera.png +0 -0
  387. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/campsite.png +0 -0
  388. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/car.png +0 -0
  389. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/cemetery.png +0 -0
  390. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/cesium.png +0 -0
  391. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/chemist.png +0 -0
  392. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/cinema.png +0 -0
  393. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/circle-stroked.png +0 -0
  394. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/circle.png +0 -0
  395. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/city.png +0 -0
  396. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/clothing-store.png +0 -0
  397. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/college.png +0 -0
  398. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/commercial.png +0 -0
  399. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/cricket.png +0 -0
  400. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/cross.png +0 -0
  401. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/dam.png +0 -0
  402. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/danger.png +0 -0
  403. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/disability.png +0 -0
  404. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/dog-park.png +0 -0
  405. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/embassy.png +0 -0
  406. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/emergency-telephone.png +0 -0
  407. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/entrance.png +0 -0
  408. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/farm.png +0 -0
  409. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/fast-food.png +0 -0
  410. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/ferry.png +0 -0
  411. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/fire-station.png +0 -0
  412. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/fuel.png +0 -0
  413. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/garden.png +0 -0
  414. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/gift.png +0 -0
  415. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/golf.png +0 -0
  416. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/grocery.png +0 -0
  417. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/hairdresser.png +0 -0
  418. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/harbor.png +0 -0
  419. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/heart.png +0 -0
  420. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/heliport.png +0 -0
  421. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/hospital.png +0 -0
  422. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/ice-cream.png +0 -0
  423. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/industrial.png +0 -0
  424. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/land-use.png +0 -0
  425. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/laundry.png +0 -0
  426. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/library.png +0 -0
  427. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/lighthouse.png +0 -0
  428. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/lodging.png +0 -0
  429. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/logging.png +0 -0
  430. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/london-underground.png +0 -0
  431. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/marker-stroked.png +0 -0
  432. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/marker.png +0 -0
  433. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/minefield.png +0 -0
  434. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/mobilephone.png +0 -0
  435. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/monument.png +0 -0
  436. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/museum.png +0 -0
  437. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/music.png +0 -0
  438. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/oil-well.png +0 -0
  439. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/park.png +0 -0
  440. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/park2.png +0 -0
  441. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/parking-garage.png +0 -0
  442. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/parking.png +0 -0
  443. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/pharmacy.png +0 -0
  444. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/pitch.png +0 -0
  445. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/place-of-worship.png +0 -0
  446. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/playground.png +0 -0
  447. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/police.png +0 -0
  448. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/polling-place.png +0 -0
  449. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/post.png +0 -0
  450. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/prison.png +0 -0
  451. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/rail-above.png +0 -0
  452. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/rail-light.png +0 -0
  453. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/rail-metro.png +0 -0
  454. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/rail-underground.png +0 -0
  455. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/rail.png +0 -0
  456. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/religious-christian.png +0 -0
  457. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/religious-jewish.png +0 -0
  458. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/religious-muslim.png +0 -0
  459. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/restaurant.png +0 -0
  460. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/roadblock.png +0 -0
  461. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/rocket.png +0 -0
  462. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/school.png +0 -0
  463. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/scooter.png +0 -0
  464. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/shop.png +0 -0
  465. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/skiing.png +0 -0
  466. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/slaughterhouse.png +0 -0
  467. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/soccer.png +0 -0
  468. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/square-stroked.png +0 -0
  469. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/square.png +0 -0
  470. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/star-stroked.png +0 -0
  471. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/star.png +0 -0
  472. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/suitcase.png +0 -0
  473. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/swimming.png +0 -0
  474. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/telephone.png +0 -0
  475. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/tennis.png +0 -0
  476. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/theatre.png +0 -0
  477. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/toilets.png +0 -0
  478. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/town-hall.png +0 -0
  479. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/town.png +0 -0
  480. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/triangle-stroked.png +0 -0
  481. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/triangle.png +0 -0
  482. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/village.png +0 -0
  483. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/warehouse.png +0 -0
  484. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/waste-basket.png +0 -0
  485. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/water.png +0 -0
  486. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/wetland.png +0 -0
  487. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/maki/zoo.png +0 -0
  488. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/moonSmall.jpg +0 -0
  489. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/pin.svg +0 -0
  490. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/waterNormals.jpg +0 -0
  491. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/Textures/waterNormalsSmall.jpg +0 -0
  492. /package/dist/cesium/{Assets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Assets}/approximateTerrainHeights.json +0 -0
  493. /package/dist/cesium/{ThirdParty → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/ThirdParty}/Workers/package.json +0 -0
  494. /package/dist/cesium/{ThirdParty → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/ThirdParty}/Workers/zip-web-worker.js +0 -0
  495. /package/dist/cesium/{ThirdParty → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/ThirdParty}/basis_transcoder.wasm +0 -0
  496. /package/dist/cesium/{ThirdParty → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/ThirdParty}/draco_decoder.wasm +0 -0
  497. /package/dist/cesium/{ThirdParty → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/ThirdParty}/google-earth-dbroot-parser.js +0 -0
  498. /package/dist/cesium/{ThirdParty → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/ThirdParty}/wasm_splats_bg.wasm +0 -0
  499. /package/dist/cesium/{ThirdParty → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/ThirdParty}/zip-module.wasm +0 -0
  500. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Animation/Animation.css +0 -0
  501. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Animation/lighter.css +0 -0
  502. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/BaseLayerPicker/BaseLayerPicker.css +0 -0
  503. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/BaseLayerPicker/lighter.css +0 -0
  504. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Cesium3DTilesInspector/Cesium3DTilesInspector.css +0 -0
  505. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/CesiumInspector/CesiumInspector.css +0 -0
  506. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/CesiumWidget/CesiumWidget.css +0 -0
  507. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/CesiumWidget/lighter.css +0 -0
  508. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/FullscreenButton/FullscreenButton.css +0 -0
  509. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Geocoder/Geocoder.css +0 -0
  510. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Geocoder/lighter.css +0 -0
  511. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/I3SBuildingSceneLayerExplorer/I3SBuildingSceneLayerExplorer.css +0 -0
  512. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/ArcGisMapServiceWorldHillshade.png +0 -0
  513. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/ArcGisMapServiceWorldImagery.png +0 -0
  514. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/ArcGisMapServiceWorldOcean.png +0 -0
  515. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/azureAerial.png +0 -0
  516. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/azureRoads.png +0 -0
  517. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/bingAerial.png +0 -0
  518. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/bingAerialLabels.png +0 -0
  519. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/bingRoads.png +0 -0
  520. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/blueMarble.png +0 -0
  521. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/earthAtNight.png +0 -0
  522. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/googleContour.png +0 -0
  523. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/googleRoadmap.png +0 -0
  524. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/googleSatellite.png +0 -0
  525. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/googleSatelliteLabels.png +0 -0
  526. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/mapQuestOpenStreetMap.png +0 -0
  527. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/mapboxSatellite.png +0 -0
  528. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/mapboxStreets.png +0 -0
  529. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/mapboxTerrain.png +0 -0
  530. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/naturalEarthII.png +0 -0
  531. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/openStreetMap.png +0 -0
  532. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/sentinel-2.png +0 -0
  533. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/stadiaAlidadeSmooth.png +0 -0
  534. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/stadiaAlidadeSmoothDark.png +0 -0
  535. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/stamenToner.png +0 -0
  536. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/ImageryProviders/stamenWatercolor.png +0 -0
  537. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/NavigationHelp/Mouse.svg +0 -0
  538. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/NavigationHelp/MouseLeft.svg +0 -0
  539. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/NavigationHelp/MouseMiddle.svg +0 -0
  540. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/NavigationHelp/MouseRight.svg +0 -0
  541. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/NavigationHelp/Touch.svg +0 -0
  542. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/NavigationHelp/TouchDrag.svg +0 -0
  543. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/NavigationHelp/TouchRotate.svg +0 -0
  544. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/NavigationHelp/TouchTilt.svg +0 -0
  545. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/NavigationHelp/TouchZoom.svg +0 -0
  546. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/TerrainProviders/CesiumWorldTerrain.png +0 -0
  547. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/TerrainProviders/Ellipsoid.png +0 -0
  548. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/TimelineIcons.png +0 -0
  549. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Images/info-loading.gif +0 -0
  550. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/InfoBox/InfoBox.css +0 -0
  551. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/InfoBox/InfoBoxDescription.css +0 -0
  552. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/NavigationHelpButton/NavigationHelpButton.css +0 -0
  553. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/NavigationHelpButton/lighter.css +0 -0
  554. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/PerformanceWatchdog/PerformanceWatchdog.css +0 -0
  555. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/ProjectionPicker/ProjectionPicker.css +0 -0
  556. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/SceneModePicker/SceneModePicker.css +0 -0
  557. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/SelectionIndicator/SelectionIndicator.css +0 -0
  558. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Timeline/Timeline.css +0 -0
  559. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Timeline/lighter.css +0 -0
  560. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/VRButton/VRButton.css +0 -0
  561. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/Viewer/Viewer.css +0 -0
  562. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/VoxelInspector/VoxelInspector.css +0 -0
  563. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/lighter.css +0 -0
  564. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/lighterShared.css +0 -0
  565. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/shared.css +0 -0
  566. /package/dist/cesium/{Widgets → node_modules/.pnpm/cesium@1.141.0/node_modules/cesium/Build/Cesium/Widgets}/widgets.css +0 -0
@@ -41,12 +41,35 @@ import {
41
41
  type WallInStoreParams,
42
42
  type WindowInStoreParams,
43
43
  } from '@ifc-lite/create';
44
- import type { MapConversion, ProjectedCRS } from '@ifc-lite/parser';
44
+ import { EntityExtractor, type MapConversion, type ProjectedCRS } from '@ifc-lite/parser';
45
45
  import type { MeshData } from '@ifc-lite/geometry';
46
46
  import { getEntityBounds } from '@/utils/viewportUtils';
47
47
  import { toGlobalIdFromModels } from '../globalId.js';
48
48
  import { buildElementMesh, type ElementMeshPayload } from './addElementMeshes.js';
49
49
  import type { AddElementType } from './addElementSlice.js';
50
+ import {
51
+ resolvePlacementChain,
52
+ resolveRotationState,
53
+ rotateProductYaw,
54
+ resolveWallEditChain,
55
+ resizeRectangleWall,
56
+ computeWallSplitGeometry,
57
+ projectOntoWallAxis,
58
+ } from '@/lib/placement-edit.js';
59
+ import { cloneElementMetadata } from '@/lib/metadata-clone.js';
60
+ import {
61
+ resolveLinearElementChain,
62
+ computeLinearElementSplitGeometry,
63
+ projectOntoLinearAxis,
64
+ type LinearElementType,
65
+ } from '@/lib/linear-element-edit.js';
66
+ import { reassignWallOpenings } from '@/lib/wall-opening-reassign.js';
67
+ import {
68
+ resolveSlabEditChain,
69
+ computeSlabSplitGeometry,
70
+ type SlabLikeType,
71
+ } from '@/lib/slab-edit.js';
72
+ import type { Point2D } from '@/lib/polygon-clip.js';
50
73
 
51
74
  /**
52
75
  * IFC-space directions for {@link MutationSlice.duplicateEntity}.
@@ -167,6 +190,35 @@ export interface MutationSlice {
167
190
  undoStacks: Map<string, Mutation[]>;
168
191
  /** Redo stack per model */
169
192
  redoStacks: Map<string, Mutation[]>;
193
+ /**
194
+ * Maps mutationId → batchId. Mutations created via
195
+ * `setPositionalAttributesBatch` share a single batchId so the
196
+ * undo / redo handlers can pop / push them as one atomic
197
+ * step — important for compound operations like `resizeWall`
198
+ * (4 positional writes) where the user expects one Ctrl+Z to
199
+ * undo the whole resize, not unwind through inconsistent
200
+ * intermediate states.
201
+ *
202
+ * Stored as a side-channel on the slice (vs an extra field on
203
+ * the published `Mutation` interface) so the batching is a
204
+ * viewer-local concern and doesn't ripple through @ifc-lite/
205
+ * mutations consumers.
206
+ */
207
+ mutationBatchTags: Map<string, string>;
208
+ /**
209
+ * Maps mutationId → the renderer-frame mesh translation that
210
+ * accompanied a placement-move mutation (`translateEntity` /
211
+ * `setEntityPosition`). The mutation itself only records the
212
+ * IfcCartesianPoint coordinate change; the rendered mesh moves
213
+ * via a separate `setPendingMeshTranslations` call. Undo / redo
214
+ * of the IFC value alone would leave the 3D mesh stranded at the
215
+ * moved position, so the handlers replay (redo) or negate (undo)
216
+ * the translation recorded here.
217
+ *
218
+ * Side-channel for the same reason as `mutationBatchTags`: keeps
219
+ * the renderer coupling out of the published Mutation interface.
220
+ */
221
+ mutationMeshTranslations: Map<string, { globalId: number; rendererDelta: [number, number, number] }>;
170
222
  /** Models with unsaved changes */
171
223
  dirtyModels: Set<string>;
172
224
  /** Version counter to trigger re-renders when mutations change */
@@ -272,11 +324,215 @@ export interface MutationSlice {
272
324
  index: number,
273
325
  value: IfcAttributeValue
274
326
  ) => Mutation | null;
327
+ /**
328
+ * Atomic batch of positional writes — undo / redo treat the
329
+ * whole list as one operation. Each entry produces a primitive
330
+ * `UPDATE_POSITIONAL_ATTRIBUTE` mutation under the hood (same
331
+ * shape as `setPositionalAttribute` so the undo handler stays
332
+ * uniform), but all entries share a batchId via
333
+ * `mutationBatchTags` so a single Ctrl+Z reverts the entire
334
+ * batch.
335
+ *
336
+ * Used by compound operations like `resizeWall` (4 coordinated
337
+ * positional writes) so the user doesn't have to press Ctrl+Z
338
+ * four times to undo one resize. Returns the batchId so callers
339
+ * can correlate; empty input is a no-op (returns null).
340
+ */
341
+ setPositionalAttributesBatch: (
342
+ modelId: string,
343
+ updates: Array<{ entityId: number; index: number; value: IfcAttributeValue }>,
344
+ ) => string | null;
275
345
  /**
276
346
  * Tombstone an entity (existing source entity) or forget it (overlay-only).
277
347
  * Returns true if the entity was known to the store or overlay.
278
348
  */
279
349
  removeEntity: (modelId: string, expressId: number) => boolean;
350
+ /**
351
+ * Translate an IfcProduct by a storey-local delta (IFC Z-up). Walks
352
+ * the placement chain to the terminal `IfcCartesianPoint` and writes
353
+ * the new coordinates via `setPositionalAttribute` so the edit
354
+ * stacks with other overlay mutations and undoes cleanly.
355
+ *
356
+ * Returns `{ ok: false }` for entities whose placement isn't a
357
+ * simple `IfcLocalPlacement → IfcAxis2Placement3D → IfcCartesianPoint`
358
+ * chain (mapped representations, 2D placements, non-product
359
+ * entities). The viewer surfaces the reason as a toast.
360
+ *
361
+ * `batchId` (optional) tags the mutation so a drag that emits
362
+ * many per-frame `translateEntity` calls collapses to one undo
363
+ * step. The gizmo passes one id per drag; omit it for a
364
+ * standalone move (e.g. a single numeric-input commit).
365
+ */
366
+ translateEntity: (
367
+ modelId: string,
368
+ expressId: number,
369
+ deltaIfc: [number, number, number],
370
+ batchId?: string,
371
+ ) => { ok: true; newCoordinates: [number, number, number] } | { ok: false; reason: string };
372
+ /**
373
+ * Absolute version of `translateEntity` — replaces the entity's
374
+ * storey-local position instead of adding a delta. Same chain
375
+ * requirements apply.
376
+ */
377
+ setEntityPosition: (
378
+ modelId: string,
379
+ expressId: number,
380
+ position: [number, number, number],
381
+ ) => { ok: true; newCoordinates: [number, number, number] } | { ok: false; reason: string };
382
+ /**
383
+ * Rotate an IfcProduct about the storey-up Z axis by `deltaYaw`
384
+ * radians. Updates RefDirection on the placement's
385
+ * IfcAxis2Placement3D when one already exists.
386
+ *
387
+ * Refuses with `{ ok: false }` when the entity's placement has
388
+ * no explicit RefDirection (the implicit `[1, 0, 0]` STEP
389
+ * default). Materialising a fresh IfcDirection there would
390
+ * require a multi-mutation atomic undo entry to avoid orphans
391
+ * on undo, which the store doesn't have yet. Every entity
392
+ * emitted by `@ifc-lite/create`'s in-store builders carries an
393
+ * explicit RefDirection, so the refusal only trips on
394
+ * hand-rolled source-buffer entities.
395
+ */
396
+ rotateEntity: (
397
+ modelId: string,
398
+ expressId: number,
399
+ deltaYaw: number,
400
+ ) => { ok: true; newYawZ: number } | { ok: false; reason: string };
401
+ /**
402
+ * Snapshot of the placement's current yaw about Z (radians) plus
403
+ * the metadata the UI needs to render a rotation gizmo. Returns
404
+ * null when the placement chain isn't translatable.
405
+ */
406
+ readEntityRotation: (
407
+ modelId: string,
408
+ expressId: number,
409
+ ) => { yawZ: number; refDirection: [number, number, number] } | null;
410
+ /**
411
+ * Read the entity's storey-local placement coordinates. Returns
412
+ * null when the placement chain isn't a simple
413
+ * `IfcLocalPlacement → IfcAxis2Placement3D → IfcCartesianPoint`
414
+ * (i.e. when `translateEntity` / `setEntityPosition` wouldn't work
415
+ * either). The action lazily creates the `StoreEditor` on first
416
+ * call so it works on a freshly-loaded model that hasn't seen any
417
+ * mutations yet — `MutablePropertyView` is the only thing
418
+ * `PropertiesPanel` registers up front, and the editor is a thin
419
+ * facade we can build on demand. Pairing the gate condition with
420
+ * the existing read-actions keeps "is this entity movable?" and
421
+ * "what are its coords?" answered by the same code path.
422
+ */
423
+ readEntityPosition: (
424
+ modelId: string,
425
+ expressId: number,
426
+ ) => [number, number, number] | null;
427
+ /**
428
+ * Resize a rectangular-profile wall by setting new start AND end
429
+ * points. Atomically updates the placement origin, RefDirection,
430
+ * profile length, and profile origin. Returns null for walls that
431
+ * don't follow the `addWallToStore` shape.
432
+ */
433
+ resizeWall: (
434
+ modelId: string,
435
+ expressId: number,
436
+ newStart: [number, number, number],
437
+ newEnd: [number, number, number],
438
+ ) => { ok: true; newLength: number } | { ok: false; reason: string };
439
+ /**
440
+ * Read a wall's current start/end so the UI can render endpoint
441
+ * handles. Returns null for non-rectangle walls.
442
+ */
443
+ readWallEndpoints: (
444
+ modelId: string,
445
+ expressId: number,
446
+ ) => { start: [number, number, number]; end: [number, number, number]; thickness: number } | null;
447
+ /**
448
+ * Split a rectangle-profile wall into two walls at `distance`
449
+ * metres along its axis (measured from the wall's start). Produces
450
+ * two new walls inheriting the source's Pset / Qto / classification
451
+ * / material / type relationships, then tombstones the source.
452
+ *
453
+ * Returns the two new walls' express ids and federation global
454
+ * ids on success. On failure (non-rectangle wall, distance too
455
+ * close to an end, missing storey, etc.) returns a descriptive
456
+ * reason for the UI to surface.
457
+ *
458
+ * Undo posture: the action lands as three primitive mutations on
459
+ * the model's undo stack (one per new wall create, one for the
460
+ * source delete), so a full revert needs three Ctrl+Z presses
461
+ * today. A batched-mutation primitive that collapses this to one
462
+ * step is on the follow-up list from PR #723.
463
+ */
464
+ splitWallAtDistance: (
465
+ modelId: string,
466
+ expressId: number,
467
+ distanceFromStart: number,
468
+ ) => { ok: true; left: { expressId: number; globalId: number }; right: { expressId: number; globalId: number }; openings: { toLeft: number; toRight: number; skipped: number } } | { ok: false; reason: string };
469
+ /**
470
+ * Read-only helper for the Split-tool live preview: projects an
471
+ * arbitrary storey-local 3D cursor onto the wall axis and returns
472
+ * how far along the wall (in metres from start) it lands, plus
473
+ * the wall's total length so the UI can show "1.42 m / 3.50 m".
474
+ *
475
+ * Returns null when the entity isn't a resizable wall.
476
+ */
477
+ readWallSplitProjection: (
478
+ modelId: string,
479
+ expressId: number,
480
+ cursorStoreyLocal: [number, number, number],
481
+ ) => { distance: number; length: number; cutPoint: [number, number, number]; axis: [number, number, number] } | null;
482
+ /**
483
+ * Split a linear element (`IfcBeam` / `IfcColumn` / `IfcMember`)
484
+ * at `distance` metres from start. Unlike walls, the source's
485
+ * extrusion is shrunk in place so the "left" half keeps the
486
+ * source's GlobalId and Pset rels — the choice is forced by the
487
+ * IFC representation (length lives on the extrusion `Depth`, not
488
+ * on the profile XDim), so one positional write covers it. A new
489
+ * element is added at the cut point to carry the "right" half.
490
+ */
491
+ splitLinearElementAtDistance: (
492
+ modelId: string,
493
+ expressId: number,
494
+ distanceFromStart: number,
495
+ ) => { ok: true; source: { expressId: number; globalId: number }; right: { expressId: number; globalId: number } } | { ok: false; reason: string };
496
+ /**
497
+ * Linear-element analogue of `readWallSplitProjection`. Returns
498
+ * null when the entity isn't an `addBeam` / `addColumn` /
499
+ * `addMember` -shaped element.
500
+ */
501
+ readLinearElementSplitProjection: (
502
+ modelId: string,
503
+ expressId: number,
504
+ cursorStoreyLocal: [number, number, number],
505
+ ) => { distance: number; length: number; cutPoint: [number, number, number]; axis: [number, number, number]; elementType: LinearElementType } | null;
506
+ /**
507
+ * Read a slab-like element's storey-local footprint polygon so
508
+ * the Split overlay can render the live cut-line preview. The
509
+ * footprint comes back in storey-local 2D (XY) with the
510
+ * placement origin already added. Returns null for non-slab
511
+ * selections or representations the chain resolver doesn't
512
+ * support (mapped shapes, tessellated faces, etc).
513
+ */
514
+ readSlabFootprint: (
515
+ modelId: string,
516
+ expressId: number,
517
+ ) => { footprint: Point2D[]; elementType: SlabLikeType; storeyElevation: number; thickness: number } | null;
518
+ /**
519
+ * Split a slab-like element (IfcSlab / IfcRoof / IfcPlate /
520
+ * IfcSpace) along a cut line defined by two storey-local 2D
521
+ * points. Builds two fresh elements with the clipped footprints
522
+ * (polygon-mode `IfcArbitraryClosedProfileDef` even when the
523
+ * source was a rectangle — most cuts produce non-rectangular
524
+ * halves), clones metadata onto both, then tombstones the
525
+ * source.
526
+ *
527
+ * Selection moves to whichever half contains the second click,
528
+ * so the user can keep editing the new piece immediately.
529
+ */
530
+ splitSlabByLine: (
531
+ modelId: string,
532
+ expressId: number,
533
+ cutA: [number, number],
534
+ cutB: [number, number],
535
+ ) => { ok: true; left: { expressId: number; globalId: number }; right: { expressId: number; globalId: number } } | { ok: false; reason: string };
280
536
  /**
281
537
  * Add a fully-anchored IfcColumn (and its sub-graph) to a parsed model.
282
538
  * Returns the new column's expressId, or null if the model can't be
@@ -441,6 +697,160 @@ function getOrCreateStoreEditor(
441
697
  return editor;
442
698
  }
443
699
 
700
+ /**
701
+ * IfcBuildingStorey.ObjectPlacement is optional in the schema —
702
+ * some authoring tools leave it null when the file was never
703
+ * meant to host geometry. Authoring actions need a placement to
704
+ * anchor their new entities against, so we materialise a default
705
+ * IfcLocalPlacement at the storey's elevation when one's missing
706
+ * and patch the storey's attribute via the overlay.
707
+ *
708
+ * Idempotent: if the storey already has a placement (number or
709
+ * `#X` string ref), this is a no-op. Returns true when a
710
+ * placement was created.
711
+ */
712
+ function ensureStoreyPlacement(
713
+ dataStore: import('@ifc-lite/parser').IfcDataStore,
714
+ editor: StoreEditor,
715
+ storeyExpressId: number,
716
+ ): boolean {
717
+ // Pull the storey's current attributes (overlay overrides + source).
718
+ const overlay = editor.getNewEntity(storeyExpressId);
719
+ let attrs: unknown[];
720
+ if (overlay) {
721
+ attrs = overlay.attributes.slice();
722
+ } else {
723
+ const ref = dataStore.entityIndex.byId.get(storeyExpressId);
724
+ if (!ref) return false;
725
+ const extractor = new EntityExtractor(dataStore.source);
726
+ const entity = extractor.extractEntity(ref);
727
+ if (!entity) return false;
728
+ attrs = entity.attributes.slice();
729
+ }
730
+
731
+ // IfcProduct.ObjectPlacement is at index 5 across IFC2X3 / IFC4.
732
+ // Accept both number refs and `#X` strings as "already present".
733
+ const existing = attrs[5];
734
+ if (typeof existing === 'number' && Number.isFinite(existing)) return false;
735
+ if (typeof existing === 'string' && existing.startsWith('#')) return false;
736
+
737
+ // Build a fresh placement at world origin. The storey's elevation
738
+ // (if any) carries through the geometry pipeline elsewhere; this
739
+ // placement gives the IFC graph what resolveSpatialAnchor needs.
740
+ const elevation = dataStore.spatialHierarchy?.storeyElevations?.get(storeyExpressId) ?? 0;
741
+ const originPt = editor.addEntity('IfcCartesianPoint', [[0, 0, elevation]]).expressId;
742
+ const axisPlacement = editor.addEntity('IfcAxis2Placement3D', [`#${originPt}`, null, null]).expressId;
743
+ const localPlacement = editor.addEntity('IfcLocalPlacement', [null, `#${axisPlacement}`]).expressId;
744
+
745
+ editor.setPositionalAttribute(storeyExpressId, 5, `#${localPlacement}`);
746
+ return true;
747
+ }
748
+
749
+ /**
750
+ * Resolve the (view, editor, dataStore, storey) tuple that every
751
+ * splitWall / splitLinearElement / splitSlab action needs. Returns
752
+ * an error result with a stable message when any piece is missing
753
+ * so each action's preamble collapses to a single early-return.
754
+ *
755
+ * Pass `requireStorey: false` when the caller resolves storey from
756
+ * a different source (none currently — but the flag keeps the
757
+ * helper reusable for non-storey-bound split-like flows).
758
+ */
759
+ type SplitContext = {
760
+ view: MutablePropertyView;
761
+ editor: StoreEditor;
762
+ dataStore: import('@ifc-lite/parser').IfcDataStore;
763
+ storeyExpressId: number;
764
+ };
765
+ function resolveSplitContext(
766
+ get: () => ViewerState,
767
+ set: (partial: Partial<ViewerState> | ((s: ViewerState) => Partial<ViewerState>)) => void,
768
+ modelId: string,
769
+ expressId: number,
770
+ notInStoreyMessage: string,
771
+ ): SplitContext | { ok: false; reason: string } {
772
+ const state = get();
773
+ const view = state.mutationViews.get(modelId);
774
+ if (!view) return { ok: false, reason: 'Model has no editable mutation view yet' };
775
+ const editor = getOrCreateStoreEditor(get, set, modelId);
776
+ if (!editor) return { ok: false, reason: 'Failed to resolve store editor' };
777
+ const dataStore = state.models.get(modelId)?.ifcDataStore;
778
+ if (!dataStore) return { ok: false, reason: `No model loaded for id "${modelId}"` };
779
+ const storeyExpressId = dataStore.spatialHierarchy?.elementToStorey.get(expressId);
780
+ if (storeyExpressId === undefined) return { ok: false, reason: notInStoreyMessage };
781
+ return { view, editor, dataStore, storeyExpressId };
782
+ }
783
+
784
+ /**
785
+ * Rollback helper for failed atomic operations (e.g. split where
786
+ * the left half was created but the right half's builder threw).
787
+ *
788
+ * Pops the most recent CREATE_ENTITY mutation for `expressId` off
789
+ * the model's undo stack, removes the overlay record via
790
+ * `view.deleteEntity`, and queues the renderer mesh for removal.
791
+ * No DELETE_ENTITY mutation is recorded — the operation never
792
+ * happened from the user's perspective, so the undo history is
793
+ * left clean (Ctrl+Z after a failed split shouldn't bring back
794
+ * the orphan half).
795
+ *
796
+ * Returns true when at least one undo entry was popped.
797
+ */
798
+ function rollbackOverlayCreate(
799
+ get: () => ViewerState,
800
+ set: (partial: Partial<ViewerState> | ((s: ViewerState) => Partial<ViewerState>)) => void,
801
+ modelId: string,
802
+ expressId: number,
803
+ ): boolean {
804
+ const state = get();
805
+ const view = state.mutationViews.get(modelId);
806
+ const editor = state.storeEditors.get(modelId);
807
+ if (!view || !editor) return false;
808
+
809
+ // Drop the entity from the overlay. The view.deleteEntity call
810
+ // is silent for already-gone entities — safe even if the caller
811
+ // gets the rollback path wrong.
812
+ editor.removeEntity(expressId);
813
+
814
+ // Pop the matching CREATE_ENTITY entry off the undo stack. The
815
+ // split flow always rolls back immediately after the failed
816
+ // create, so the entry is at top-of-stack — fast-path that case
817
+ // with a single `pop()`-style slice and only fall back to the
818
+ // linear scan if a follow-up mutation slipped in between.
819
+ set((s) => {
820
+ const stacks = new Map(s.undoStacks);
821
+ const stack = stacks.get(modelId);
822
+ if (!stack || stack.length === 0) return {};
823
+ const top = stack[stack.length - 1];
824
+ if (top.type === 'CREATE_ENTITY' && top.entityId === expressId) {
825
+ stacks.set(modelId, stack.slice(0, -1));
826
+ return {
827
+ undoStacks: stacks,
828
+ mutationVersion: s.mutationVersion + 1,
829
+ };
830
+ }
831
+ for (let i = stack.length - 2; i >= 0; i--) {
832
+ const m = stack[i];
833
+ if (m.type === 'CREATE_ENTITY' && m.entityId === expressId) {
834
+ const next = stack.slice();
835
+ next.splice(i, 1);
836
+ stacks.set(modelId, next);
837
+ return {
838
+ undoStacks: stacks,
839
+ mutationVersion: s.mutationVersion + 1,
840
+ };
841
+ }
842
+ }
843
+ return {};
844
+ });
845
+
846
+ // Drop the entity's mesh from the renderer so the user doesn't
847
+ // see a phantom half-element after the failed split. Uses the
848
+ // existing pendingMeshRemovals channel (same as Phase A).
849
+ const globalId = toGlobalIdFromModels(state.models, modelId, expressId);
850
+ state.setPendingMeshRemovals(new Set([globalId]));
851
+ return true;
852
+ }
853
+
444
854
  /**
445
855
  * Shared dispatcher for the wall/slab/beam in-store builders. Mirrors the
446
856
  * structure of `addColumn` (resolve store/view/editor/anchor → run the
@@ -468,6 +878,15 @@ function runInStoreElementBuilder(
468
878
  const editor = getOrCreateStoreEditor(get, set, modelId);
469
879
  if (!editor) return { error: 'Failed to create store editor' };
470
880
 
881
+ // Some source IFC files leave IfcBuildingStorey.ObjectPlacement
882
+ // null (it's optional in the schema). Without a placement,
883
+ // resolveSpatialAnchor throws "storey #N has no resolvable
884
+ // IfcLocalPlacement" — but a fresh IfcLocalPlacement at the
885
+ // origin is a valid default. Materialise one before the anchor
886
+ // walk so the user's authoring action doesn't get blocked by
887
+ // missing-but-recoverable IFC structure.
888
+ ensureStoreyPlacement(dataStore, editor, storeyExpressId);
889
+
471
890
  let entityId: number;
472
891
  try {
473
892
  const anchor = resolveSpatialAnchor(dataStore, storeyExpressId);
@@ -575,6 +994,8 @@ export const createMutationSlice: StateCreator<
575
994
  activeChangeSetId: null,
576
995
  undoStacks: new Map(),
577
996
  redoStacks: new Map(),
997
+ mutationBatchTags: new Map(),
998
+ mutationMeshTranslations: new Map(),
578
999
  dirtyModels: new Set(),
579
1000
  mutationVersion: 0,
580
1001
  georefMutations: new Map(),
@@ -921,6 +1342,653 @@ export const createMutationSlice: StateCreator<
921
1342
  return stack ? stack[stack.length - 1] : null;
922
1343
  },
923
1344
 
1345
+ setPositionalAttributesBatch: (modelId, updates) => {
1346
+ if (updates.length === 0) return null;
1347
+ // Generate the batch id once; every mutation created below
1348
+ // gets tagged with it so the undo / redo handlers can group
1349
+ // them. `crypto.randomUUID` is available in every browser the
1350
+ // viewer supports and avoids the collision risk of
1351
+ // Date.now() + Math.random concatenation.
1352
+ const batchId = `batch_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
1353
+ const tags = new Map(get().mutationBatchTags);
1354
+ for (const { entityId, index, value } of updates) {
1355
+ const mutation = get().setPositionalAttribute(modelId, entityId, index, value);
1356
+ if (mutation) tags.set(mutation.id, batchId);
1357
+ }
1358
+ set({ mutationBatchTags: tags });
1359
+ return batchId;
1360
+ },
1361
+
1362
+ translateEntity: (modelId, expressId, delta, batchId) => {
1363
+ // Read the existing placement chain WITHOUT committing the edit
1364
+ // yet — we'll route the actual write through `setPositionalAttribute`
1365
+ // below so undo/redo + dirty-tracking come for free.
1366
+ const view = get().mutationViews.get(modelId);
1367
+ if (!view) return { ok: false, reason: 'Model has no editable mutation view yet' };
1368
+ const editor = getOrCreateStoreEditor(get, set, modelId);
1369
+ if (!editor) return { ok: false, reason: 'Failed to resolve store editor' };
1370
+ const dataStore = get().models.get(modelId)?.ifcDataStore;
1371
+ if (!dataStore) return { ok: false, reason: `No model loaded for id "${modelId}"` };
1372
+
1373
+ const chain = resolvePlacementChain(dataStore, view, editor, expressId);
1374
+ if (!chain) {
1375
+ return {
1376
+ ok: false,
1377
+ reason:
1378
+ 'Entity placement is not a simple IfcLocalPlacement → IfcAxis2Placement3D → IfcCartesianPoint chain',
1379
+ };
1380
+ }
1381
+ const [x, y, z] = chain.coordinates;
1382
+ const next: [number, number, number] = [x + delta[0], y + delta[1], z + delta[2]];
1383
+ // Go through the slice's own `setPositionalAttribute` action so
1384
+ // the mutation lands on the undo stack with the standard envelope.
1385
+ const mutation = get().setPositionalAttribute(modelId, chain.cartesianPointId, 0, next);
1386
+
1387
+ // Push the renderer-frame delta so the visible mesh follows
1388
+ // the IFC mutation. IFC is Z-up; renderer is Y-up. Conversion:
1389
+ // renderer.x = ifc.x
1390
+ // renderer.y = ifc.z
1391
+ // renderer.z = -ifc.y
1392
+ const globalId = toGlobalIdFromModels(get().models, modelId, expressId);
1393
+ const rendererDelta: [number, number, number] = [delta[0], delta[2], -delta[1]];
1394
+ get().setPendingMeshTranslations(new Map([[globalId, rendererDelta]]));
1395
+
1396
+ // Record the mesh translation against the mutation id so undo /
1397
+ // redo can move the rendered mesh back / forward — the mutation
1398
+ // alone only carries the IfcCartesianPoint coordinate change.
1399
+ // When a `batchId` is supplied (gizmo drag), tag the mutation so
1400
+ // all the drag's per-frame translates collapse to one undo step.
1401
+ if (mutation) {
1402
+ const meshTags = new Map(get().mutationMeshTranslations);
1403
+ meshTags.set(mutation.id, { globalId, rendererDelta });
1404
+ if (batchId) {
1405
+ const batchTags = new Map(get().mutationBatchTags);
1406
+ batchTags.set(mutation.id, batchId);
1407
+ set({ mutationMeshTranslations: meshTags, mutationBatchTags: batchTags });
1408
+ } else {
1409
+ set({ mutationMeshTranslations: meshTags });
1410
+ }
1411
+ }
1412
+
1413
+ return { ok: true, newCoordinates: next };
1414
+ },
1415
+
1416
+ setEntityPosition: (modelId, expressId, position) => {
1417
+ const view = get().mutationViews.get(modelId);
1418
+ if (!view) return { ok: false, reason: 'Model has no editable mutation view yet' };
1419
+ const editor = getOrCreateStoreEditor(get, set, modelId);
1420
+ if (!editor) return { ok: false, reason: 'Failed to resolve store editor' };
1421
+ const dataStore = get().models.get(modelId)?.ifcDataStore;
1422
+ if (!dataStore) return { ok: false, reason: `No model loaded for id "${modelId}"` };
1423
+
1424
+ const chain = resolvePlacementChain(dataStore, view, editor, expressId);
1425
+ if (!chain) {
1426
+ return {
1427
+ ok: false,
1428
+ reason:
1429
+ 'Entity placement is not a simple IfcLocalPlacement → IfcAxis2Placement3D → IfcCartesianPoint chain',
1430
+ };
1431
+ }
1432
+ // Push the IFC → renderer delta for the rendered mesh. Same
1433
+ // Z-up → Y-up conversion as `translateEntity` above.
1434
+ const [oldX, oldY, oldZ] = chain.coordinates;
1435
+ const dx = position[0] - oldX;
1436
+ const dy = position[1] - oldY;
1437
+ const dz = position[2] - oldZ;
1438
+ const mutation = get().setPositionalAttribute(modelId, chain.cartesianPointId, 0, position);
1439
+ if (dx !== 0 || dy !== 0 || dz !== 0) {
1440
+ const globalId = toGlobalIdFromModels(get().models, modelId, expressId);
1441
+ const rendererDelta: [number, number, number] = [dx, dz, -dy];
1442
+ get().setPendingMeshTranslations(new Map([[globalId, rendererDelta]]));
1443
+ // Record so undo / redo can move the rendered mesh — see the
1444
+ // matching note in `translateEntity`.
1445
+ if (mutation) {
1446
+ const tags = new Map(get().mutationMeshTranslations);
1447
+ tags.set(mutation.id, { globalId, rendererDelta });
1448
+ set({ mutationMeshTranslations: tags });
1449
+ }
1450
+ }
1451
+ return { ok: true, newCoordinates: position };
1452
+ },
1453
+
1454
+ rotateEntity: (modelId, expressId, deltaYaw) => {
1455
+ const view = get().mutationViews.get(modelId);
1456
+ if (!view) return { ok: false, reason: 'Model has no editable mutation view yet' };
1457
+ const editor = getOrCreateStoreEditor(get, set, modelId);
1458
+ if (!editor) return { ok: false, reason: 'Failed to resolve store editor' };
1459
+ const dataStore = get().models.get(modelId)?.ifcDataStore;
1460
+ if (!dataStore) return { ok: false, reason: `No model loaded for id "${modelId}"` };
1461
+
1462
+ // resolveRotationState gives us both the current angle and
1463
+ // whether RefDirection is explicit. When it's null we refuse
1464
+ // — see the interface comment above for why; materialising
1465
+ // would require multi-mutation atomic undo to avoid orphans.
1466
+ // Every in-store builder emits an explicit RefDirection, so
1467
+ // this only trips on hand-rolled source-buffer entities.
1468
+ const state = resolveRotationState(dataStore, view, editor, expressId);
1469
+ if (!state) {
1470
+ return {
1471
+ ok: false,
1472
+ reason:
1473
+ 'Entity placement is not a simple IfcLocalPlacement → IfcAxis2Placement3D chain',
1474
+ };
1475
+ }
1476
+ if (state.refDirectionId === null) {
1477
+ // Implicit RefDirection means the axis placement points at no
1478
+ // IfcDirection — STEP `$` slot. Materialising a fresh
1479
+ // IfcDirection here would require a multi-mutation atomic undo
1480
+ // entry to avoid orphans; we don't have that primitive yet.
1481
+ // In practice every entity our in-store builders emit
1482
+ // (addColumn / addWall / addSlab / …) carries an explicit
1483
+ // RefDirection, so this branch only trips on hand-rolled
1484
+ // source-buffer entities. Surface a clear refusal so the UI
1485
+ // can show "rotate not supported for this entity" rather than
1486
+ // silently leaking entities.
1487
+ return {
1488
+ ok: false,
1489
+ reason:
1490
+ 'Entity has an implicit reference direction (no IfcDirection on its axis placement). Rotation would require materialising a new IfcDirection, which isn\'t undoable yet.',
1491
+ };
1492
+ }
1493
+ const newYaw = state.yawZ + deltaYaw;
1494
+ const newRatios: [number, number, number] = [
1495
+ Math.cos(newYaw),
1496
+ Math.sin(newYaw),
1497
+ state.refDirection[2],
1498
+ ];
1499
+ get().setPositionalAttribute(modelId, state.refDirectionId, 0, newRatios);
1500
+ return { ok: true, newYawZ: newYaw };
1501
+ },
1502
+
1503
+ readEntityRotation: (modelId, expressId) => {
1504
+ // Lazy editor creation — see the note on `readEntityPosition`
1505
+ // below. A freshly-loaded model has a mutation view but no
1506
+ // cached editor; building one on read so the rotation UI lights
1507
+ // up on first selection, not after the first unrelated edit.
1508
+ const view = get().mutationViews.get(modelId);
1509
+ if (!view) return null;
1510
+ const editor = getOrCreateStoreEditor(get, set, modelId);
1511
+ if (!editor) return null;
1512
+ const dataStore = get().models.get(modelId)?.ifcDataStore;
1513
+ if (!dataStore) return null;
1514
+ const state = resolveRotationState(dataStore, view, editor, expressId);
1515
+ if (!state) return null;
1516
+ return { yawZ: state.yawZ, refDirection: state.refDirection };
1517
+ },
1518
+
1519
+ readEntityPosition: (modelId, expressId) => {
1520
+ // Mirror of `readEntityRotation`'s lazy-create pattern. Used by
1521
+ // `GeometryEditCard` to seed its inputs AND by `GizmoOverlay`
1522
+ // as its "is this entity movable?" gate — one code path means
1523
+ // the controls and the visual gizmo agree on availability.
1524
+ const view = get().mutationViews.get(modelId);
1525
+ if (!view) return null;
1526
+ const editor = getOrCreateStoreEditor(get, set, modelId);
1527
+ if (!editor) return null;
1528
+ const dataStore = get().models.get(modelId)?.ifcDataStore;
1529
+ if (!dataStore) return null;
1530
+ const chain = resolvePlacementChain(dataStore, view, editor, expressId);
1531
+ return chain ? chain.coordinates : null;
1532
+ },
1533
+
1534
+ resizeWall: (modelId, expressId, newStart, newEnd) => {
1535
+ const view = get().mutationViews.get(modelId);
1536
+ if (!view) return { ok: false, reason: 'Model has no editable mutation view yet' };
1537
+ const editor = getOrCreateStoreEditor(get, set, modelId);
1538
+ if (!editor) return { ok: false, reason: 'Failed to resolve store editor' };
1539
+ const dataStore = get().models.get(modelId)?.ifcDataStore;
1540
+ if (!dataStore) return { ok: false, reason: `No model loaded for id "${modelId}"` };
1541
+
1542
+ // resolveWallEditChain reads all four ids without mutating.
1543
+ // The four writes are then committed as a single atomic batch
1544
+ // via setPositionalAttributesBatch — one Ctrl+Z reverts the
1545
+ // whole resize, no walking through inconsistent intermediate
1546
+ // wall states.
1547
+ const chain = resolveWallEditChain(dataStore, view, editor, expressId);
1548
+ if (!chain) {
1549
+ return {
1550
+ ok: false,
1551
+ reason:
1552
+ 'Wall does not have a simple IfcRectangleProfileDef → IfcExtrudedAreaSolid representation',
1553
+ };
1554
+ }
1555
+ const dx = newEnd[0] - newStart[0];
1556
+ const dy = newEnd[1] - newStart[1];
1557
+ const dz = newEnd[2] - newStart[2];
1558
+ const length = Math.hypot(dx, dy);
1559
+ if (length < 1e-6) return { ok: false, reason: 'Wall length must be greater than zero' };
1560
+ if (Math.abs(dz) > Math.max(1e-6 * length, 1e-9)) {
1561
+ return { ok: false, reason: 'Start and end must lie on the same storey plane' };
1562
+ }
1563
+ const dir: [number, number, number] = [dx / length, dy / length, 0];
1564
+
1565
+ get().setPositionalAttributesBatch(modelId, [
1566
+ { entityId: chain.startPointId, index: 0, value: newStart },
1567
+ { entityId: chain.refDirectionId, index: 0, value: dir },
1568
+ { entityId: chain.profileId, index: 3, value: length },
1569
+ { entityId: chain.profileOriginPointId, index: 0, value: [length / 2, 0] },
1570
+ ]);
1571
+
1572
+ return { ok: true, newLength: length };
1573
+ },
1574
+
1575
+ readWallEndpoints: (modelId, expressId) => {
1576
+ // Same lazy-create pattern as `readEntityRotation` /
1577
+ // `readEntityPosition` — handles need to surface on first
1578
+ // selection, not after an unrelated mutation has primed the
1579
+ // editor cache.
1580
+ const view = get().mutationViews.get(modelId);
1581
+ if (!view) return null;
1582
+ const editor = getOrCreateStoreEditor(get, set, modelId);
1583
+ if (!editor) return null;
1584
+ const dataStore = get().models.get(modelId)?.ifcDataStore;
1585
+ if (!dataStore) return null;
1586
+ const chain = resolveWallEditChain(dataStore, view, editor, expressId);
1587
+ if (!chain) return null;
1588
+ const [sx, sy, sz] = chain.startCoordinates;
1589
+ const [dx, dy, dz] = chain.refDirection;
1590
+ const end: [number, number, number] = [
1591
+ sx + dx * chain.wallLength,
1592
+ sy + dy * chain.wallLength,
1593
+ sz + dz * chain.wallLength,
1594
+ ];
1595
+ return { start: [sx, sy, sz], end, thickness: chain.thickness };
1596
+ },
1597
+
1598
+ readWallSplitProjection: (modelId, expressId, cursorStoreyLocal) => {
1599
+ const view = get().mutationViews.get(modelId);
1600
+ if (!view) return null;
1601
+ const editor = getOrCreateStoreEditor(get, set, modelId);
1602
+ if (!editor) return null;
1603
+ const dataStore = get().models.get(modelId)?.ifcDataStore;
1604
+ if (!dataStore) return null;
1605
+ const chain = resolveWallEditChain(dataStore, view, editor, expressId);
1606
+ if (!chain) return null;
1607
+ const distance = projectOntoWallAxis(chain, cursorStoreyLocal);
1608
+ const [sx, sy, sz] = chain.startCoordinates;
1609
+ const [dx, dy, dz] = chain.refDirection;
1610
+ const cutPoint: [number, number, number] = [
1611
+ sx + dx * distance,
1612
+ sy + dy * distance,
1613
+ sz + dz * distance,
1614
+ ];
1615
+ // Walls always lie on a storey plane (refDirection.z === 0 by
1616
+ // the builder's contract) but the type lets us carry whatever
1617
+ // the IFC actually says, so we surface it as-is.
1618
+ return { distance, length: chain.wallLength, cutPoint, axis: [dx, dy, dz] };
1619
+ },
1620
+
1621
+ splitWallAtDistance: (modelId, expressId, distanceFromStart) => {
1622
+ const ctx = resolveSplitContext(get, set, modelId, expressId, 'Wall is not contained in a building storey');
1623
+ if ('ok' in ctx) return ctx;
1624
+ const { view, editor, dataStore, storeyExpressId } = ctx;
1625
+ const state = get();
1626
+
1627
+ const chain = resolveWallEditChain(dataStore, view, editor, expressId);
1628
+ if (!chain) {
1629
+ return {
1630
+ ok: false,
1631
+ reason:
1632
+ 'Wall does not have a simple IfcRectangleProfileDef → IfcExtrudedAreaSolid representation. Split supports walls built by addWallToStore.',
1633
+ };
1634
+ }
1635
+ if (!Number.isFinite(chain.height) || chain.height <= 0) {
1636
+ return {
1637
+ ok: false,
1638
+ reason: 'Wall has no readable extrusion height',
1639
+ };
1640
+ }
1641
+
1642
+ const geo = computeWallSplitGeometry(chain, distanceFromStart, chain.height);
1643
+ if (!geo.ok) return geo;
1644
+
1645
+ // Build the two halves. Each `addWall` call already pushes a
1646
+ // CREATE_ENTITY mutation onto the undo stack AND emits a fresh
1647
+ // mesh via appendGeometryBatch, so the new walls appear in 3D
1648
+ // immediately. The source's mesh stays in the geometry result
1649
+ // but is tombstoned in the IFC overlay — for v1 we mark it
1650
+ // hidden via the existing hiddenEntities mechanism so the user
1651
+ // sees the split take effect.
1652
+ const left = state.addWall(modelId, storeyExpressId, {
1653
+ Start: geo.geometry.left.Start,
1654
+ End: geo.geometry.left.End,
1655
+ Thickness: geo.geometry.left.Thickness,
1656
+ Height: geo.geometry.left.Height,
1657
+ Name: 'Wall (split L)',
1658
+ });
1659
+ if ('error' in left) {
1660
+ return { ok: false, reason: `Couldn't build left half: ${left.error}` };
1661
+ }
1662
+ const right = state.addWall(modelId, storeyExpressId, {
1663
+ Start: geo.geometry.right.Start,
1664
+ End: geo.geometry.right.End,
1665
+ Thickness: geo.geometry.right.Thickness,
1666
+ Height: geo.geometry.right.Height,
1667
+ Name: 'Wall (split R)',
1668
+ });
1669
+ if ('error' in right) {
1670
+ // Roll back the left half via the no-history helper so the
1671
+ // failed split doesn't leave a phantom CREATE+DELETE pair on
1672
+ // the undo stack. `rollbackOverlayCreate` pops the orphan
1673
+ // CREATE_ENTITY entry, drops the overlay record, and removes
1674
+ // the renderer mesh.
1675
+ rollbackOverlayCreate(get, set, modelId, left.expressId);
1676
+ return { ok: false, reason: `Couldn't build right half: ${right.error}` };
1677
+ }
1678
+
1679
+ // Carry Pset / Qto / classification / material / type rels
1680
+ // from the source onto both new walls. Done AFTER the new walls
1681
+ // exist so the rels' RelatedObjects lists can include them.
1682
+ cloneElementMetadata(dataStore, view, editor, expressId, [left.expressId, right.expressId]);
1683
+
1684
+ // Reassign hosted openings (doors / windows / generic voids)
1685
+ // to whichever new half they geometrically belong to. The
1686
+ // canonical IFC convention places the opening's
1687
+ // ObjectPlacement relative to the wall's placement, with
1688
+ // local-X = distance along the wall axis — so we read each
1689
+ // opening's local-X to decide left vs right, and offset
1690
+ // right-half openings by -splitDistance so their world
1691
+ // positions stay fixed across the reparent.
1692
+ //
1693
+ // We resolve each new half's IfcLocalPlacement id by
1694
+ // re-walking the placement chain (it's the entity addWall
1695
+ // created internally; the action's return value only carries
1696
+ // the wall id).
1697
+ const leftChain = resolvePlacementChain(dataStore, view, editor, left.expressId);
1698
+ const rightChain = resolvePlacementChain(dataStore, view, editor, right.expressId);
1699
+ let openingSummary: { toLeft: number; toRight: number; skipped: number } = { toLeft: 0, toRight: 0, skipped: 0 };
1700
+ if (leftChain && rightChain) {
1701
+ const s = reassignWallOpenings(
1702
+ dataStore,
1703
+ view,
1704
+ editor,
1705
+ expressId,
1706
+ left.expressId,
1707
+ right.expressId,
1708
+ distanceFromStart,
1709
+ leftChain.localPlacementId,
1710
+ rightChain.localPlacementId,
1711
+ );
1712
+ openingSummary = { toLeft: s.toLeft, toRight: s.toRight, skipped: s.skipped };
1713
+ }
1714
+ void openingSummary; // surfaced as a toast hint by the caller (selectionHandlers)
1715
+
1716
+ // Tombstone the source. `removeEntity` returns false if the
1717
+ // entity wasn't known — shouldn't happen here (we just
1718
+ // resolved its chain), but defend anyway.
1719
+ const removed = state.removeEntity(modelId, expressId);
1720
+ if (!removed) {
1721
+ return {
1722
+ ok: false,
1723
+ reason: 'Wall was unexpectedly removed before split completed',
1724
+ };
1725
+ }
1726
+
1727
+ // Drop the source's mesh from the rendered scene. The entity
1728
+ // is tombstoned in the IFC overlay so it won't export; this
1729
+ // also clears its GPU buffers and bounding-box entry so picks
1730
+ // / bounds stop finding it. The two new walls already have
1731
+ // their meshes in the geometry (addWall emits them via
1732
+ // appendGeometryBatch).
1733
+ const sourceGlobalId = toGlobalIdFromModels(state.models, modelId, expressId);
1734
+ state.setPendingMeshRemovals(new Set([sourceGlobalId]));
1735
+
1736
+ const leftGlobalId = toGlobalIdFromModels(state.models, modelId, left.expressId);
1737
+ const rightGlobalId = toGlobalIdFromModels(state.models, modelId, right.expressId);
1738
+
1739
+ return {
1740
+ ok: true,
1741
+ left: { expressId: left.expressId, globalId: leftGlobalId },
1742
+ right: { expressId: right.expressId, globalId: rightGlobalId },
1743
+ openings: openingSummary,
1744
+ };
1745
+ },
1746
+
1747
+ readLinearElementSplitProjection: (modelId, expressId, cursorStoreyLocal) => {
1748
+ const view = get().mutationViews.get(modelId);
1749
+ if (!view) return null;
1750
+ const editor = getOrCreateStoreEditor(get, set, modelId);
1751
+ if (!editor) return null;
1752
+ const dataStore = get().models.get(modelId)?.ifcDataStore;
1753
+ if (!dataStore) return null;
1754
+ const chain = resolveLinearElementChain(dataStore, view, editor, expressId);
1755
+ if (!chain) return null;
1756
+ const distance = projectOntoLinearAxis(chain, cursorStoreyLocal);
1757
+ const [sx, sy, sz] = chain.startCoordinates;
1758
+ const [dx, dy, dz] = chain.axisDirection;
1759
+ const cutPoint: [number, number, number] = [
1760
+ sx + dx * distance,
1761
+ sy + dy * distance,
1762
+ sz + dz * distance,
1763
+ ];
1764
+ return {
1765
+ distance,
1766
+ length: chain.depth,
1767
+ cutPoint,
1768
+ axis: chain.axisDirection,
1769
+ elementType: chain.elementType,
1770
+ };
1771
+ },
1772
+
1773
+ splitLinearElementAtDistance: (modelId, expressId, distanceFromStart) => {
1774
+ const ctx = resolveSplitContext(get, set, modelId, expressId, 'Element is not contained in a building storey');
1775
+ if ('ok' in ctx) return ctx;
1776
+ const { view, editor, dataStore, storeyExpressId } = ctx;
1777
+ const state = get();
1778
+
1779
+ const chain = resolveLinearElementChain(dataStore, view, editor, expressId);
1780
+ if (!chain) {
1781
+ return {
1782
+ ok: false,
1783
+ reason:
1784
+ 'Element is not a rectangular-profile beam / column / member built by the in-store builders.',
1785
+ };
1786
+ }
1787
+ const geo = computeLinearElementSplitGeometry(chain, distanceFromStart);
1788
+ if (!geo.ok) return geo;
1789
+
1790
+ // Add the "right" half FIRST so a builder failure leaves the
1791
+ // source untouched (no partial-commit state). The source's
1792
+ // extrusion shrink happens only after the new half lands.
1793
+ // The dispatch is one-to-one with the chain's resolved
1794
+ // element type.
1795
+ let addResult: { expressId: number } | { error: string };
1796
+ if (chain.elementType === 'IfcBeam') {
1797
+ addResult = state.addBeam(modelId, storeyExpressId, {
1798
+ Start: geo.geometry.cutPoint,
1799
+ End: geo.geometry.endPoint,
1800
+ Width: geo.geometry.width,
1801
+ Height: geo.geometry.height,
1802
+ Name: 'Beam (split)',
1803
+ });
1804
+ } else if (chain.elementType === 'IfcColumn') {
1805
+ // Columns take a Position + Width + Depth + Height (extrusion
1806
+ // is along +Z). Width/Depth come from the cross-section
1807
+ // (profile XDim / YDim). Height is the right half's length.
1808
+ addResult = state.addColumn(modelId, storeyExpressId, {
1809
+ Position: geo.geometry.cutPoint,
1810
+ Width: geo.geometry.width,
1811
+ Depth: geo.geometry.height,
1812
+ Height: geo.geometry.rightDepth,
1813
+ Name: 'Column (split)',
1814
+ });
1815
+ } else {
1816
+ addResult = state.addMember(modelId, storeyExpressId, {
1817
+ Start: geo.geometry.cutPoint,
1818
+ End: geo.geometry.endPoint,
1819
+ Width: geo.geometry.width,
1820
+ Height: geo.geometry.height,
1821
+ Name: 'Member (split)',
1822
+ });
1823
+ }
1824
+ if ('error' in addResult) {
1825
+ return { ok: false, reason: `Couldn't build right half: ${addResult.error}` };
1826
+ }
1827
+
1828
+ // Right half built — now shrink the source's extrusion to the
1829
+ // "left" length. One write, one undo entry, identity
1830
+ // preserved. Goes through the slice's own
1831
+ // setPositionalAttribute action so undo recovers it.
1832
+ state.setPositionalAttribute(modelId, chain.extrudedSolidId, 3, geo.geometry.leftDepth);
1833
+
1834
+ // Carry Pset / classification / material rels onto the new
1835
+ // right half so it inherits the source's metadata. The source
1836
+ // keeps its own rels natively (we didn't tombstone it).
1837
+ cloneElementMetadata(dataStore, view, editor, expressId, [addResult.expressId]);
1838
+
1839
+ // Hide / re-show the source's mesh so the renderer reflects
1840
+ // the new shorter length. The new mesh for the right half
1841
+ // already came via the addElement pipeline's appendGeometryBatch.
1842
+ // For the source, the easiest visual update is to nudge the
1843
+ // geometryUpdateTick so consumers re-derive bounds — the
1844
+ // existing mesh data lingers at full length until the next
1845
+ // full reload (deferred mesh-update from PR #723). Users see
1846
+ // the new wall appear; the source mesh stays visually unchanged
1847
+ // for now. Documented as a known limitation.
1848
+ const sourceGlobalId = toGlobalIdFromModels(state.models, modelId, expressId);
1849
+ const rightGlobalId = toGlobalIdFromModels(state.models, modelId, addResult.expressId);
1850
+
1851
+ return {
1852
+ ok: true,
1853
+ source: { expressId, globalId: sourceGlobalId },
1854
+ right: { expressId: addResult.expressId, globalId: rightGlobalId },
1855
+ };
1856
+ },
1857
+
1858
+ readSlabFootprint: (modelId, expressId) => {
1859
+ const view = get().mutationViews.get(modelId);
1860
+ if (!view) return null;
1861
+ const editor = getOrCreateStoreEditor(get, set, modelId);
1862
+ if (!editor) return null;
1863
+ const dataStore = get().models.get(modelId)?.ifcDataStore;
1864
+ if (!dataStore) return null;
1865
+ const chain = resolveSlabEditChain(dataStore, view, editor, expressId);
1866
+ if (!chain) return null;
1867
+ const storeyId = dataStore.spatialHierarchy?.elementToStorey.get(expressId);
1868
+ const storeyElevation =
1869
+ (storeyId !== undefined
1870
+ ? dataStore.spatialHierarchy?.storeyElevations?.get(storeyId)
1871
+ : undefined) ?? 0;
1872
+ return {
1873
+ footprint: chain.footprint,
1874
+ elementType: chain.elementType,
1875
+ storeyElevation,
1876
+ thickness: chain.thickness,
1877
+ };
1878
+ },
1879
+
1880
+ splitSlabByLine: (modelId, expressId, cutA, cutB) => {
1881
+ const ctx = resolveSplitContext(get, set, modelId, expressId, 'Slab is not contained in a building storey');
1882
+ if ('ok' in ctx) return ctx;
1883
+ const { view, editor, dataStore, storeyExpressId } = ctx;
1884
+ const state = get();
1885
+
1886
+ const chain = resolveSlabEditChain(dataStore, view, editor, expressId);
1887
+ if (!chain) {
1888
+ return {
1889
+ ok: false,
1890
+ reason:
1891
+ 'Element representation is not a rectangle / polygon profile extruded along Z. Split supports slab-like elements built by addSlab / addRoof / addPlate / addSpace.',
1892
+ };
1893
+ }
1894
+ const geo = computeSlabSplitGeometry(chain, cutA, cutB);
1895
+ if (!geo.ok) return geo;
1896
+
1897
+ // The clipped footprints are in storey-local XY (placement
1898
+ // origin already added). The builders expect an `OuterCurve`
1899
+ // in *profile-local* 2D + a `Position` in storey-local 3D.
1900
+ // Easiest mapping: keep `Position` at `[0, 0, 0]` and pass the
1901
+ // clipped polygon verbatim — the builders fold profile-origin
1902
+ // and placement-origin into one identity.
1903
+ //
1904
+ // IfcSlab / IfcRoof / IfcPlate carry their extrusion depth on
1905
+ // a `Thickness` param; IfcSpace uses `Height`. Same chain
1906
+ // resolver feeds both because the underlying STEP shape is
1907
+ // identical (IfcExtrudedAreaSolid.Depth) — the divergence is
1908
+ // only in the in-store builder's parameter naming.
1909
+ const buildHalf = (outline: Point2D[], label: string) => {
1910
+ const name = `${chain.elementType.replace(/^Ifc/, '')} (split ${label})`;
1911
+ switch (chain.elementType) {
1912
+ case 'IfcSlab':
1913
+ return state.addSlab(modelId, storeyExpressId, {
1914
+ Profile: 'polygon',
1915
+ Position: [0, 0, 0],
1916
+ OuterCurve: outline,
1917
+ Thickness: geo.thickness,
1918
+ Name: name,
1919
+ });
1920
+ case 'IfcRoof':
1921
+ return state.addRoof(modelId, storeyExpressId, {
1922
+ Profile: 'polygon',
1923
+ Position: [0, 0, 0],
1924
+ OuterCurve: outline,
1925
+ Thickness: geo.thickness,
1926
+ Name: name,
1927
+ });
1928
+ case 'IfcPlate':
1929
+ return state.addPlate(modelId, storeyExpressId, {
1930
+ Profile: 'polygon',
1931
+ Position: [0, 0, 0],
1932
+ OuterCurve: outline,
1933
+ Thickness: geo.thickness,
1934
+ Name: name,
1935
+ });
1936
+ case 'IfcSpace':
1937
+ return state.addSpace(modelId, storeyExpressId, {
1938
+ Profile: 'polygon',
1939
+ Position: [0, 0, 0],
1940
+ OuterCurve: outline,
1941
+ Height: geo.thickness,
1942
+ Name: name,
1943
+ });
1944
+ default: {
1945
+ // Exhaustive switch — compile error here if a new
1946
+ // SlabLikeType lands without a builder dispatch.
1947
+ const exhaust: never = chain.elementType;
1948
+ throw new Error(`Unhandled slab-like type: ${String(exhaust)}`);
1949
+ }
1950
+ }
1951
+ };
1952
+
1953
+ const left = buildHalf(geo.leftFootprint, 'L');
1954
+ if ('error' in left) {
1955
+ return { ok: false, reason: `Couldn't build left half: ${left.error}` };
1956
+ }
1957
+ const right = buildHalf(geo.rightFootprint, 'R');
1958
+ if ('error' in right) {
1959
+ // Roll back the left half via the no-history helper — same
1960
+ // reasoning as the wall-split rollback above.
1961
+ rollbackOverlayCreate(get, set, modelId, left.expressId);
1962
+ return { ok: false, reason: `Couldn't build right half: ${right.error}` };
1963
+ }
1964
+
1965
+ cloneElementMetadata(dataStore, view, editor, expressId, [left.expressId, right.expressId]);
1966
+
1967
+ const removed = state.removeEntity(modelId, expressId);
1968
+ if (!removed) {
1969
+ return {
1970
+ ok: false,
1971
+ reason: 'Slab was unexpectedly removed before split completed',
1972
+ };
1973
+ }
1974
+
1975
+ // Hide source mesh so the user sees the cut take effect; the
1976
+ // two new halves already have meshes via addSlab's
1977
+ // appendGeometryBatch. The source's mesh is dropped from GPU
1978
+ // buffers + bbox map via setPendingMeshRemovals — the
1979
+ // streaming hook drains it on the next frame.
1980
+ const sourceGlobalId = toGlobalIdFromModels(state.models, modelId, expressId);
1981
+ state.setPendingMeshRemovals(new Set([sourceGlobalId]));
1982
+
1983
+ const leftGlobalId = toGlobalIdFromModels(state.models, modelId, left.expressId);
1984
+ const rightGlobalId = toGlobalIdFromModels(state.models, modelId, right.expressId);
1985
+ return {
1986
+ ok: true,
1987
+ left: { expressId: left.expressId, globalId: leftGlobalId },
1988
+ right: { expressId: right.expressId, globalId: rightGlobalId },
1989
+ };
1990
+ },
1991
+
924
1992
  removeEntity: (modelId, expressId) => {
925
1993
  const view = get().mutationViews.get(modelId);
926
1994
  if (!view) return false;
@@ -934,6 +2002,19 @@ export const createMutationSlice: StateCreator<
934
2002
  const removed = editor.removeEntity(expressId);
935
2003
  if (!removed) return false;
936
2004
 
2005
+ // Hide the entity's mesh — the IFC tombstone is what governs
2006
+ // exports; the renderer just needs the visual gone. We use
2007
+ // hideEntities (visibility set) rather than the harder
2008
+ // pendingMeshRemovals path so the undo handler can flip
2009
+ // visibility back without needing to re-materialise GPU
2010
+ // buffers (the mesh data stays in memory + buckets).
2011
+ //
2012
+ // The split-source removal path also flows through here; on
2013
+ // undo of a split, the source's mesh comes back via
2014
+ // `showEntities` in the DELETE_ENTITY undo branch.
2015
+ const globalIdForMesh = toGlobalIdFromModels(get().models, modelId, expressId);
2016
+ get().hideEntities([globalIdForMesh]);
2017
+
937
2018
  set((state) => {
938
2019
  const newRemoved = new Map(state.removedNewEntities);
939
2020
  if (overlayRecord) {
@@ -1275,6 +2356,13 @@ export const createMutationSlice: StateCreator<
1275
2356
  if (undoStack.length === 0) return;
1276
2357
 
1277
2358
  const mutation = undoStack[undoStack.length - 1];
2359
+ // Batch awareness: if the mutation we're about to undo was
2360
+ // tagged as part of a batch (via setPositionalAttributesBatch),
2361
+ // we want one Ctrl+Z to undo every mutation in that batch.
2362
+ // The tail-recurse at the end of this action handles that —
2363
+ // capture the batchId here, undo this single mutation, then
2364
+ // if the next top still shares the batchId, recurse.
2365
+ const batchId = state.mutationBatchTags.get(mutation.id);
1278
2366
 
1279
2367
  // Handle georef mutations directly on georefMutations map
1280
2368
  if (mutation.type === 'UPDATE_ATTRIBUTE' && mutation.attributeName?.startsWith('georef.')) {
@@ -1381,6 +2469,18 @@ export const createMutationSlice: StateCreator<
1381
2469
  view.setPositionalAttribute(mutation.entityId, index, mutation.oldValue as IfcAttributeValue, true);
1382
2470
  }
1383
2471
  }
2472
+ // If this mutation carried a mesh translation (gizmo / numeric
2473
+ // move), reverse it so the rendered mesh follows the undo.
2474
+ const meshMove = get().mutationMeshTranslations.get(mutation.id);
2475
+ if (meshMove) {
2476
+ get().setPendingMeshTranslations(
2477
+ new Map([[meshMove.globalId, [
2478
+ -meshMove.rendererDelta[0],
2479
+ -meshMove.rendererDelta[1],
2480
+ -meshMove.rendererDelta[2],
2481
+ ]]]),
2482
+ );
2483
+ }
1384
2484
  } else if (mutation.type === 'CREATE_ENTITY') {
1385
2485
  // Undo of a create: stash the NewEntity payload so a subsequent redo
1386
2486
  // can restore it. Without this, redo finds an empty stash and becomes
@@ -1433,6 +2533,20 @@ export const createMutationSlice: StateCreator<
1433
2533
  mutationVersion: s.mutationVersion + 1,
1434
2534
  };
1435
2535
  });
2536
+
2537
+ // Tail-recurse for the rest of the batch (if any). Reading
2538
+ // the stack via get() picks up the just-set state. Stops as
2539
+ // soon as the next top mutation either doesn't exist or
2540
+ // belongs to a different batch.
2541
+ if (batchId !== undefined) {
2542
+ const nextStack = get().undoStacks.get(modelId) || [];
2543
+ if (nextStack.length > 0) {
2544
+ const nextBatchId = get().mutationBatchTags.get(nextStack[nextStack.length - 1].id);
2545
+ if (nextBatchId === batchId) {
2546
+ get().undo(modelId);
2547
+ }
2548
+ }
2549
+ }
1436
2550
  },
1437
2551
 
1438
2552
  redo: (modelId) => {
@@ -1441,6 +2555,7 @@ export const createMutationSlice: StateCreator<
1441
2555
  if (redoStack.length === 0) return;
1442
2556
 
1443
2557
  const mutation = redoStack[redoStack.length - 1];
2558
+ const batchId = state.mutationBatchTags.get(mutation.id);
1444
2559
 
1445
2560
  // Handle georef mutations directly
1446
2561
  if (mutation.type === 'UPDATE_ATTRIBUTE' && mutation.attributeName?.startsWith('georef.')) {
@@ -1524,6 +2639,14 @@ export const createMutationSlice: StateCreator<
1524
2639
  if (index !== null && mutation.newValue !== undefined) {
1525
2640
  view.setPositionalAttribute(mutation.entityId, index, mutation.newValue as IfcAttributeValue, true);
1526
2641
  }
2642
+ // Replay the mesh translation forward so the rendered mesh
2643
+ // follows the redo — mirror of the undo reversal above.
2644
+ const meshMove = get().mutationMeshTranslations.get(mutation.id);
2645
+ if (meshMove) {
2646
+ get().setPendingMeshTranslations(
2647
+ new Map([[meshMove.globalId, meshMove.rendererDelta]]),
2648
+ );
2649
+ }
1527
2650
  } else if (mutation.type === 'CREATE_ENTITY') {
1528
2651
  // Redo of a create: replay from the stashed NewEntity. Symmetrical to
1529
2652
  // DELETE_ENTITY's undo — same map, same key.
@@ -1577,6 +2700,20 @@ export const createMutationSlice: StateCreator<
1577
2700
  mutationVersion: s.mutationVersion + 1,
1578
2701
  };
1579
2702
  });
2703
+
2704
+ // Tail-recurse for the rest of the batch — mirror of the
2705
+ // undo handler's batch tail. Stops as soon as the next top
2706
+ // of the redo stack either doesn't exist or belongs to a
2707
+ // different batch.
2708
+ if (batchId !== undefined) {
2709
+ const nextStack = get().redoStacks.get(modelId) || [];
2710
+ if (nextStack.length > 0) {
2711
+ const nextBatchId = get().mutationBatchTags.get(nextStack[nextStack.length - 1].id);
2712
+ if (nextBatchId === batchId) {
2713
+ get().redo(modelId);
2714
+ }
2715
+ }
2716
+ }
1580
2717
  },
1581
2718
 
1582
2719
  canUndo: (modelId) => {