@ahrowe/ui 0.33.0 → 0.35.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/README.md +101 -40
  2. package/dist/esm/common/accordion/accordion.mjs +1 -1
  3. package/dist/esm/common/accordion/accordion.mjs.map +1 -1
  4. package/dist/esm/common/accordion/accordion.module.mjs +1 -1
  5. package/dist/esm/common/accordion/accordion.module.mjs.map +1 -1
  6. package/dist/esm/common/accordion/accordion.plain.module.mjs +1 -1
  7. package/dist/esm/common/accordion/accordion.plain.module.mjs.map +1 -1
  8. package/dist/esm/common/accordion/accordion.primary.module.mjs +1 -1
  9. package/dist/esm/common/accordion/accordion.primary.module.mjs.map +1 -1
  10. package/dist/esm/common/accordion/accordion.types.mjs +1 -1
  11. package/dist/esm/common/accordion/accordion.types.mjs.map +1 -1
  12. package/dist/esm/common/actionButtons/actionButtons.mjs +1 -1
  13. package/dist/esm/common/actionButtons/actionButtons.mjs.map +1 -1
  14. package/dist/esm/common/actionButtons/actionButtons.module.mjs +1 -1
  15. package/dist/esm/common/actionIcon/actionIcon.mjs +1 -1
  16. package/dist/esm/common/actionIcon/actionIcon.mjs.map +1 -1
  17. package/dist/esm/common/actionIcon/actionIcon.module.mjs +1 -1
  18. package/dist/esm/common/actionIcon/actionIcon.module.mjs.map +1 -1
  19. package/dist/esm/common/alert/alert.mjs +1 -1
  20. package/dist/esm/common/alert/alert.mjs.map +1 -1
  21. package/dist/esm/common/alert/alert.module.mjs +1 -1
  22. package/dist/esm/common/alert/alert.types.mjs +1 -1
  23. package/dist/esm/common/alert/alert.types.mjs.map +1 -1
  24. package/dist/esm/common/animatedIcon/animatedIcon.mjs +1 -1
  25. package/dist/esm/common/animatedIcon/animatedIcon.mjs.map +1 -1
  26. package/dist/esm/common/animatedIcon/animatedIcon.module.mjs +1 -1
  27. package/dist/esm/common/animatedLogo/animatedLogo.mjs +1 -1
  28. package/dist/esm/common/animatedLogo/animatedLogo.mjs.map +1 -1
  29. package/dist/esm/common/animatedLogo/animatedLogo.module.mjs +1 -1
  30. package/dist/esm/common/animatedText/animatedText.mjs +1 -1
  31. package/dist/esm/common/animatedText/animatedText.mjs.map +1 -1
  32. package/dist/esm/common/animatedText/animatedText.module.mjs +1 -1
  33. package/dist/esm/common/animatedText/animatedText.types.mjs +1 -1
  34. package/dist/esm/common/animatedText/animatedText.types.mjs.map +1 -1
  35. package/dist/esm/common/avatar/avatar.mjs +1 -1
  36. package/dist/esm/common/avatar/avatar.mjs.map +1 -1
  37. package/dist/esm/common/avatar/avatar.module.mjs +1 -1
  38. package/dist/esm/common/avatar/avatarGroup.mjs +1 -1
  39. package/dist/esm/common/avatar/avatarGroup.mjs.map +1 -1
  40. package/dist/esm/common/badge/badge.mjs +1 -1
  41. package/dist/esm/common/badge/badge.mjs.map +1 -1
  42. package/dist/esm/common/badge/badge.module.mjs +1 -1
  43. package/dist/esm/common/badge/badge.types.mjs +1 -1
  44. package/dist/esm/common/badge/badge.types.mjs.map +1 -1
  45. package/dist/esm/common/bodyEnd/bodyEnd.mjs +1 -1
  46. package/dist/esm/common/bodyEnd/bodyEnd.mjs.map +1 -1
  47. package/dist/esm/common/breadcrumb/breadcrumb.mjs +1 -1
  48. package/dist/esm/common/breadcrumb/breadcrumb.mjs.map +1 -1
  49. package/dist/esm/common/breadcrumb/breadcrumb.module.mjs +1 -1
  50. package/dist/esm/common/breadcrumb/breadcrumb.module.mjs.map +1 -1
  51. package/dist/esm/common/button/button.mjs +1 -1
  52. package/dist/esm/common/button/button.mjs.map +1 -1
  53. package/dist/esm/common/button/button.module.mjs +1 -1
  54. package/dist/esm/common/button/button.types.mjs +1 -1
  55. package/dist/esm/common/button/button.types.mjs.map +1 -1
  56. package/dist/esm/common/buttonGroup/buttonGroup.mjs +1 -1
  57. package/dist/esm/common/buttonGroup/buttonGroup.mjs.map +1 -1
  58. package/dist/esm/common/buttonGroup/buttonGroup.module.mjs +1 -1
  59. package/dist/esm/common/buttonGroup/buttonGroup.types.mjs +1 -1
  60. package/dist/esm/common/buttonGroup/buttonGroup.types.mjs.map +1 -1
  61. package/dist/esm/common/card/Card.mjs +1 -1
  62. package/dist/esm/common/card/Card.mjs.map +1 -1
  63. package/dist/esm/common/card/CardActions.mjs +1 -1
  64. package/dist/esm/common/card/CardActions.mjs.map +1 -1
  65. package/dist/esm/common/card/CardContent.mjs +1 -1
  66. package/dist/esm/common/card/CardContent.mjs.map +1 -1
  67. package/dist/esm/common/card/CardHeader.mjs +1 -1
  68. package/dist/esm/common/card/CardHeader.mjs.map +1 -1
  69. package/dist/esm/common/card/CardMedia.mjs +1 -1
  70. package/dist/esm/common/card/CardMedia.mjs.map +1 -1
  71. package/dist/esm/common/card/card.module.mjs +1 -1
  72. package/dist/esm/common/card/card.module.mjs.map +1 -1
  73. package/dist/esm/common/card/card.types.mjs +1 -1
  74. package/dist/esm/common/card/card.types.mjs.map +1 -1
  75. package/dist/esm/common/carousel/carousel.mjs +1 -1
  76. package/dist/esm/common/carousel/carousel.mjs.map +1 -1
  77. package/dist/esm/common/carousel/carousel.module.mjs +1 -1
  78. package/dist/esm/common/checkbox/checkbox.mjs +1 -1
  79. package/dist/esm/common/checkbox/checkbox.mjs.map +1 -1
  80. package/dist/esm/common/checkbox/checkbox.module.mjs +1 -1
  81. package/dist/esm/common/checkbox/checkbox.module.mjs.map +1 -1
  82. package/dist/esm/common/chip/chip.mjs +1 -1
  83. package/dist/esm/common/chip/chip.mjs.map +1 -1
  84. package/dist/esm/common/chip/chip.module.mjs +1 -1
  85. package/dist/esm/common/chip/chip.types.mjs +1 -1
  86. package/dist/esm/common/chip/chip.types.mjs.map +1 -1
  87. package/dist/esm/common/chip/chipContainer.mjs +1 -1
  88. package/dist/esm/common/chip/chipContainer.mjs.map +1 -1
  89. package/dist/esm/common/colorPicker/colorPicker.mjs +1 -1
  90. package/dist/esm/common/colorPicker/colorPicker.mjs.map +1 -1
  91. package/dist/esm/common/colorPicker/colorPicker.module.mjs +1 -1
  92. package/dist/esm/common/colorPicker/colorPicker.module.mjs.map +1 -1
  93. package/dist/esm/common/colorPicker/toHex.mjs +1 -1
  94. package/dist/esm/common/colorPicker/toHex.mjs.map +1 -1
  95. package/dist/esm/common/configProvider/configProvider.mjs +1 -1
  96. package/dist/esm/common/configProvider/configProvider.mjs.map +1 -1
  97. package/dist/esm/common/configProvider/presentation.types.mjs +2 -0
  98. package/dist/esm/common/configProvider/presentation.types.mjs.map +1 -0
  99. package/dist/esm/common/configProvider/useComponentDefaults.mjs +1 -1
  100. package/dist/esm/common/configProvider/useComponentDefaults.mjs.map +1 -1
  101. package/dist/esm/common/configProvider/useLabels.mjs +1 -1
  102. package/dist/esm/common/configProvider/useLabels.mjs.map +1 -1
  103. package/dist/esm/common/configProvider/usePresentationDefault.mjs +1 -1
  104. package/dist/esm/common/configProvider/usePresentationDefault.mjs.map +1 -1
  105. package/dist/esm/common/confirmModal/confirmModal.mjs +1 -1
  106. package/dist/esm/common/confirmModal/confirmModal.mjs.map +1 -1
  107. package/dist/esm/common/datePicker/components/datePickerMonth/datePickerMonth.mjs +1 -1
  108. package/dist/esm/common/datePicker/components/datePickerMonth/datePickerMonth.mjs.map +1 -1
  109. package/dist/esm/common/datePicker/components/datePickerMonth/datePickerMonth.module.mjs +1 -1
  110. package/dist/esm/common/datePicker/components/datePickerMonth/datePickerMonth.module.mjs.map +1 -1
  111. package/dist/esm/common/datePicker/dateFunctions.mjs +1 -1
  112. package/dist/esm/common/datePicker/dateFunctions.mjs.map +1 -1
  113. package/dist/esm/common/datePicker/datePicker.mjs +1 -1
  114. package/dist/esm/common/datePicker/datePicker.mjs.map +1 -1
  115. package/dist/esm/common/datePicker/datePicker.module.mjs +1 -1
  116. package/dist/esm/common/divider/divider.mjs +1 -1
  117. package/dist/esm/common/divider/divider.mjs.map +1 -1
  118. package/dist/esm/common/divider/divider.module.mjs +1 -1
  119. package/dist/esm/common/divider/divider.types.mjs +1 -1
  120. package/dist/esm/common/divider/divider.types.mjs.map +1 -1
  121. package/dist/esm/common/drawer/drawer.mjs +1 -1
  122. package/dist/esm/common/drawer/drawer.mjs.map +1 -1
  123. package/dist/esm/common/drawer/drawer.module.mjs +1 -1
  124. package/dist/esm/common/dropZone/dropZone.mjs +1 -1
  125. package/dist/esm/common/dropZone/dropZone.mjs.map +1 -1
  126. package/dist/esm/common/dropZone/dropZone.module.mjs +1 -1
  127. package/dist/esm/common/dropZone/dropZone.types.mjs +1 -1
  128. package/dist/esm/common/dropZone/dropZone.types.mjs.map +1 -1
  129. package/dist/esm/common/dropdown/dropdown.mjs +1 -1
  130. package/dist/esm/common/dropdown/dropdown.mjs.map +1 -1
  131. package/dist/esm/common/dropdown/dropdown.module.mjs +1 -1
  132. package/dist/esm/common/dropdown/dropdown.module.mjs.map +1 -1
  133. package/dist/esm/common/emptyState/emptyState.mjs +1 -1
  134. package/dist/esm/common/emptyState/emptyState.mjs.map +1 -1
  135. package/dist/esm/common/emptyState/emptyState.module.mjs +1 -1
  136. package/dist/esm/common/errorBoundary/errorBoundary.mjs +1 -1
  137. package/dist/esm/common/errorBoundary/errorBoundary.mjs.map +1 -1
  138. package/dist/esm/common/errorBoundary/errorBoundary.module.mjs +1 -1
  139. package/dist/esm/common/fab/components/fabBase/fabBase.mjs +1 -1
  140. package/dist/esm/common/fab/components/fabBase/fabBase.mjs.map +1 -1
  141. package/dist/esm/common/fab/components/fabBase/fabBase.module.mjs +1 -1
  142. package/dist/esm/common/fab/fab.mjs +1 -1
  143. package/dist/esm/common/fab/fab.mjs.map +1 -1
  144. package/dist/esm/common/fab/fab.module.mjs +1 -1
  145. package/dist/esm/common/fab/fab.module.mjs.map +1 -1
  146. package/dist/esm/common/flip/flip.mjs +1 -1
  147. package/dist/esm/common/flip/flip.mjs.map +1 -1
  148. package/dist/esm/common/flip/flip.module.mjs +1 -1
  149. package/dist/esm/common/floorPlan/floorPlan.faces.mjs +1 -1
  150. package/dist/esm/common/floorPlan/floorPlan.faces.mjs.map +1 -1
  151. package/dist/esm/common/floorPlan/floorPlan.geometry.mjs +1 -1
  152. package/dist/esm/common/floorPlan/floorPlan.geometry.mjs.map +1 -1
  153. package/dist/esm/common/floorPlan/floorPlan.graph.mjs +1 -1
  154. package/dist/esm/common/floorPlan/floorPlan.graph.mjs.map +1 -1
  155. package/dist/esm/common/floorPlan/floorPlan.hit.mjs +1 -1
  156. package/dist/esm/common/floorPlan/floorPlan.hit.mjs.map +1 -1
  157. package/dist/esm/common/floorPlan/floorPlan.json.mjs +1 -1
  158. package/dist/esm/common/floorPlan/floorPlan.json.mjs.map +1 -1
  159. package/dist/esm/common/floorPlan/floorPlan.mesh.mjs +1 -1
  160. package/dist/esm/common/floorPlan/floorPlan.mesh.mjs.map +1 -1
  161. package/dist/esm/common/floorPlan/floorPlan.path.mjs +1 -1
  162. package/dist/esm/common/floorPlan/floorPlan.path.mjs.map +1 -1
  163. package/dist/esm/common/floorPlan/floorPlan.plan.mjs +1 -1
  164. package/dist/esm/common/floorPlan/floorPlan.plan.mjs.map +1 -1
  165. package/dist/esm/common/floorPlan/floorPlan.rooms.mjs +1 -1
  166. package/dist/esm/common/floorPlan/floorPlan.rooms.mjs.map +1 -1
  167. package/dist/esm/common/floorPlan/floorPlan.snap.mjs +1 -1
  168. package/dist/esm/common/floorPlan/floorPlan.snap.mjs.map +1 -1
  169. package/dist/esm/common/floorPlan/floorPlan.triangulate.mjs +1 -1
  170. package/dist/esm/common/floorPlan/floorPlan.triangulate.mjs.map +1 -1
  171. package/dist/esm/common/floorPlan/floorPlan.types.mjs +1 -1
  172. package/dist/esm/common/floorPlan/floorPlan.types.mjs.map +1 -1
  173. package/dist/esm/common/floorPlan/legacySvg.mjs +1 -1
  174. package/dist/esm/common/floorPlan/legacySvg.mjs.map +1 -1
  175. package/dist/esm/common/hooks/useAnchorTracking.mjs +1 -1
  176. package/dist/esm/common/hooks/useAnchorTracking.mjs.map +1 -1
  177. package/dist/esm/common/hooks/useCoarsePointer.mjs +1 -1
  178. package/dist/esm/common/hooks/useCoarsePointer.mjs.map +1 -1
  179. package/dist/esm/common/hooks/useFocusBoundary.mjs +1 -1
  180. package/dist/esm/common/hooks/useFocusBoundary.mjs.map +1 -1
  181. package/dist/esm/common/hooks/useMediaQuery.mjs +1 -1
  182. package/dist/esm/common/hooks/useMediaQuery.mjs.map +1 -1
  183. package/dist/esm/common/hooks/useOverlay.mjs +1 -1
  184. package/dist/esm/common/hooks/useOverlay.mjs.map +1 -1
  185. package/dist/esm/common/hooks/useSheet.mjs +1 -1
  186. package/dist/esm/common/hooks/useSheet.mjs.map +1 -1
  187. package/dist/esm/common/hooks/useSwipeDismiss.mjs +1 -1
  188. package/dist/esm/common/hooks/useSwipeDismiss.mjs.map +1 -1
  189. package/dist/esm/common/hooks/useVisualViewportSize.mjs +1 -1
  190. package/dist/esm/common/hooks/useVisualViewportSize.mjs.map +1 -1
  191. package/dist/esm/common/iconPicker/iconPicker.mjs +1 -1
  192. package/dist/esm/common/iconPicker/iconPicker.mjs.map +1 -1
  193. package/dist/esm/common/iconPicker/iconPicker.module.mjs +1 -1
  194. package/dist/esm/common/idleManager/idleManager.mjs +1 -1
  195. package/dist/esm/common/idleManager/idleManager.mjs.map +1 -1
  196. package/dist/esm/common/infiniteBlock/infiniteBlock.mjs +1 -1
  197. package/dist/esm/common/infiniteBlock/infiniteBlock.mjs.map +1 -1
  198. package/dist/esm/common/infiniteBlock/infiniteBlock.module.mjs +1 -1
  199. package/dist/esm/common/input/input.mjs +1 -1
  200. package/dist/esm/common/input/input.mjs.map +1 -1
  201. package/dist/esm/common/input/input.module.mjs +1 -1
  202. package/dist/esm/common/input/input.module.mjs.map +1 -1
  203. package/dist/esm/common/input/input.types.mjs +1 -1
  204. package/dist/esm/common/input/input.types.mjs.map +1 -1
  205. package/dist/esm/common/inputDropdown/inputDropdown.mjs +1 -1
  206. package/dist/esm/common/inputDropdown/inputDropdown.mjs.map +1 -1
  207. package/dist/esm/common/inputDropdown/inputDropdown.module.mjs +1 -1
  208. package/dist/esm/common/inputDropdown/inputDropdown.module.mjs.map +1 -1
  209. package/dist/esm/common/interactableDiv/index.mjs +1 -1
  210. package/dist/esm/common/interactableDiv/index.mjs.map +1 -1
  211. package/dist/esm/common/interactableDiv/interactableDiv.mjs +1 -1
  212. package/dist/esm/common/interactableDiv/interactableDiv.mjs.map +1 -1
  213. package/dist/esm/common/interactableDiv/interactableDiv.module.mjs +1 -1
  214. package/dist/esm/common/kanbanBoard/KanbanBoard.mjs +1 -1
  215. package/dist/esm/common/kanbanBoard/KanbanBoard.mjs.map +1 -1
  216. package/dist/esm/common/kanbanBoard/KanbanColumn.mjs +1 -1
  217. package/dist/esm/common/kanbanBoard/KanbanColumn.mjs.map +1 -1
  218. package/dist/esm/common/kanbanBoard/KanbanItem.mjs +1 -1
  219. package/dist/esm/common/kanbanBoard/KanbanItem.mjs.map +1 -1
  220. package/dist/esm/common/kanbanBoard/kanbanBoard.module.mjs +1 -1
  221. package/dist/esm/common/kanbanBoard/kanbanBoard.utils.mjs +1 -1
  222. package/dist/esm/common/kanbanBoard/kanbanBoard.utils.mjs.map +1 -1
  223. package/dist/esm/common/klipyPicker/components/gifPreview/gifPreview.mjs +1 -1
  224. package/dist/esm/common/klipyPicker/components/gifPreview/gifPreview.mjs.map +1 -1
  225. package/dist/esm/common/klipyPicker/components/gifPreview/gifPreview.module.mjs +1 -1
  226. package/dist/esm/common/klipyPicker/components/gifView/gifView.mjs +1 -1
  227. package/dist/esm/common/klipyPicker/components/gifView/gifView.mjs.map +1 -1
  228. package/dist/esm/common/klipyPicker/components/gifView/gifView.module.mjs +1 -1
  229. package/dist/esm/common/klipyPicker/klipyPicker.mjs +1 -1
  230. package/dist/esm/common/klipyPicker/klipyPicker.mjs.map +1 -1
  231. package/dist/esm/common/klipyPicker/klipyPicker.module.mjs +1 -1
  232. package/dist/esm/common/loading/iconLoading/iconLoading.mjs +1 -1
  233. package/dist/esm/common/loading/iconLoading/iconLoading.mjs.map +1 -1
  234. package/dist/esm/common/loading/iconLoading/iconLoading.module.mjs +1 -1
  235. package/dist/esm/common/loading/iconLoading/index.mjs +1 -1
  236. package/dist/esm/common/loading/iconLoading/index.mjs.map +1 -1
  237. package/dist/esm/common/loading/spinnerLoading/index.mjs +1 -1
  238. package/dist/esm/common/loading/spinnerLoading/index.mjs.map +1 -1
  239. package/dist/esm/common/loading/spinnerLoading/spinnerLoading.mjs +1 -1
  240. package/dist/esm/common/loading/spinnerLoading/spinnerLoading.mjs.map +1 -1
  241. package/dist/esm/common/loading/spinnerLoading/spinnerLoading.module.mjs +1 -1
  242. package/dist/esm/common/menu/menu.mjs +2 -0
  243. package/dist/esm/common/menu/menu.mjs.map +1 -0
  244. package/dist/esm/common/menu/menu.module.mjs +2 -0
  245. package/dist/esm/common/menu/menu.module.mjs.map +1 -0
  246. package/dist/esm/common/menu/menu.types.mjs +2 -0
  247. package/dist/esm/common/menu/menu.types.mjs.map +1 -0
  248. package/dist/esm/common/modal/modal.mjs +1 -1
  249. package/dist/esm/common/modal/modal.mjs.map +1 -1
  250. package/dist/esm/common/modal/modal.module.mjs +1 -1
  251. package/dist/esm/common/modal/modal.module.mjs.map +1 -1
  252. package/dist/esm/common/multiSelect/multiSelect.mjs +1 -1
  253. package/dist/esm/common/multiSelect/multiSelect.mjs.map +1 -1
  254. package/dist/esm/common/multiSelect/multiSelect.module.mjs +1 -1
  255. package/dist/esm/common/multiSelect/multiSelect.module.mjs.map +1 -1
  256. package/dist/esm/common/numberInput/index.mjs +1 -1
  257. package/dist/esm/common/numberInput/index.mjs.map +1 -1
  258. package/dist/esm/common/numberInput/numberInput.mjs +1 -1
  259. package/dist/esm/common/numberInput/numberInput.mjs.map +1 -1
  260. package/dist/esm/common/numberInput/numberInput.module.mjs +1 -1
  261. package/dist/esm/common/numberInput/useStepRepeat.mjs +1 -1
  262. package/dist/esm/common/numberInput/useStepRepeat.mjs.map +1 -1
  263. package/dist/esm/common/optionPicker/optionPicker.mjs +1 -1
  264. package/dist/esm/common/optionPicker/optionPicker.mjs.map +1 -1
  265. package/dist/esm/common/optionPicker/optionPicker.module.mjs +1 -1
  266. package/dist/esm/common/otpInput/otpInput.mjs +1 -1
  267. package/dist/esm/common/otpInput/otpInput.mjs.map +1 -1
  268. package/dist/esm/common/otpInput/otpInput.module.mjs +1 -1
  269. package/dist/esm/common/otpInput/otpInput.types.mjs +1 -1
  270. package/dist/esm/common/otpInput/otpInput.types.mjs.map +1 -1
  271. package/dist/esm/common/overscroll/overscroll.mjs +1 -1
  272. package/dist/esm/common/overscroll/overscroll.mjs.map +1 -1
  273. package/dist/esm/common/overscroll/overscroll.module.mjs +1 -1
  274. package/dist/esm/common/pagination/pagination.mjs +1 -1
  275. package/dist/esm/common/pagination/pagination.mjs.map +1 -1
  276. package/dist/esm/common/pagination/pagination.module.mjs +1 -1
  277. package/dist/esm/common/planCanvas/planCanvas.mjs +1 -1
  278. package/dist/esm/common/planCanvas/planCanvas.mjs.map +1 -1
  279. package/dist/esm/common/planCanvas/planCanvas.module.mjs +1 -1
  280. package/dist/esm/common/popover/popover.mjs +1 -1
  281. package/dist/esm/common/popover/popover.mjs.map +1 -1
  282. package/dist/esm/common/popover/popover.module.mjs +1 -1
  283. package/dist/esm/common/popover/popover.module.mjs.map +1 -1
  284. package/dist/esm/common/popover/popover.types.mjs +1 -1
  285. package/dist/esm/common/popover/popover.types.mjs.map +1 -1
  286. package/dist/esm/common/popover/usePopoverPosition.mjs +1 -1
  287. package/dist/esm/common/popover/usePopoverPosition.mjs.map +1 -1
  288. package/dist/esm/common/progressBar/progressBar.mjs +1 -1
  289. package/dist/esm/common/progressBar/progressBar.mjs.map +1 -1
  290. package/dist/esm/common/progressBar/progressBar.module.mjs +1 -1
  291. package/dist/esm/common/progressBar/progressBar.module.mjs.map +1 -1
  292. package/dist/esm/common/radioGroup/radioGroup.mjs +1 -1
  293. package/dist/esm/common/radioGroup/radioGroup.mjs.map +1 -1
  294. package/dist/esm/common/radioGroup/radioGroup.module.mjs +1 -1
  295. package/dist/esm/common/radioGroup/radioGroup.module.mjs.map +1 -1
  296. package/dist/esm/common/radioGroup/radioGroup.types.mjs +1 -1
  297. package/dist/esm/common/radioGroup/radioGroup.types.mjs.map +1 -1
  298. package/dist/esm/common/rating/rating.mjs +1 -1
  299. package/dist/esm/common/rating/rating.mjs.map +1 -1
  300. package/dist/esm/common/rating/rating.module.mjs +1 -1
  301. package/dist/esm/common/revealLens/revealLens.mjs +1 -1
  302. package/dist/esm/common/revealLens/revealLens.mjs.map +1 -1
  303. package/dist/esm/common/revealLens/revealLens.module.mjs +1 -1
  304. package/dist/esm/common/ripple/ripple.mjs +1 -1
  305. package/dist/esm/common/ripple/ripple.mjs.map +1 -1
  306. package/dist/esm/common/ripple/ripple.module.mjs +1 -1
  307. package/dist/esm/common/ripple/ripple.module.mjs.map +1 -1
  308. package/dist/esm/common/roomDrawer/roomDrawer.mjs +1 -1
  309. package/dist/esm/common/roomDrawer/roomDrawer.mjs.map +1 -1
  310. package/dist/esm/common/roomDrawer/roomDrawer.module.mjs +1 -1
  311. package/dist/esm/common/roomDrawer/usePlanHistory.mjs +1 -1
  312. package/dist/esm/common/roomDrawer/usePlanHistory.mjs.map +1 -1
  313. package/dist/esm/common/roomViewer/roomViewer.mjs +1 -1
  314. package/dist/esm/common/roomViewer/roomViewer.mjs.map +1 -1
  315. package/dist/esm/common/roomViewer/roomViewer.module.mjs +1 -1
  316. package/dist/esm/common/scrollbarProvider/index.mjs +1 -1
  317. package/dist/esm/common/scrollbarProvider/index.mjs.map +1 -1
  318. package/dist/esm/common/scrollbarProvider/scrollbarProvider.mjs +1 -1
  319. package/dist/esm/common/scrollbarProvider/scrollbarProvider.mjs.map +1 -1
  320. package/dist/esm/common/scrollbarProvider/scrollbarProvider.module.mjs +1 -1
  321. package/dist/esm/common/searchInput/searchInput.mjs +1 -1
  322. package/dist/esm/common/searchInput/searchInput.mjs.map +1 -1
  323. package/dist/esm/common/searchInput/searchInput.module.mjs +1 -1
  324. package/dist/esm/common/sectionHeader/sectionHeader.mjs +1 -1
  325. package/dist/esm/common/sectionHeader/sectionHeader.mjs.map +1 -1
  326. package/dist/esm/common/sectionHeader/sectionHeader.module.mjs +1 -1
  327. package/dist/esm/common/sectionHeader/sectionHeader.types.mjs +1 -1
  328. package/dist/esm/common/sectionHeader/sectionHeader.types.mjs.map +1 -1
  329. package/dist/esm/common/sheetBackdrop/sheetBackdrop.mjs +1 -1
  330. package/dist/esm/common/sheetBackdrop/sheetBackdrop.mjs.map +1 -1
  331. package/dist/esm/common/skeleton/skeleton.mjs +1 -1
  332. package/dist/esm/common/skeleton/skeleton.mjs.map +1 -1
  333. package/dist/esm/common/skeleton/skeleton.module.mjs +1 -1
  334. package/dist/esm/common/skeleton/skeleton.types.mjs +1 -1
  335. package/dist/esm/common/skeleton/skeleton.types.mjs.map +1 -1
  336. package/dist/esm/common/slider/slider.mjs +1 -1
  337. package/dist/esm/common/slider/slider.mjs.map +1 -1
  338. package/dist/esm/common/slider/slider.module.mjs +1 -1
  339. package/dist/esm/common/splitButton/splitButton.mjs +1 -1
  340. package/dist/esm/common/splitButton/splitButton.mjs.map +1 -1
  341. package/dist/esm/common/splitButton/splitButton.module.mjs +1 -1
  342. package/dist/esm/common/splitButton/splitButton.module.mjs.map +1 -1
  343. package/dist/esm/common/stepper/stepper.mjs +1 -1
  344. package/dist/esm/common/stepper/stepper.mjs.map +1 -1
  345. package/dist/esm/common/stepper/stepper.module.mjs +1 -1
  346. package/dist/esm/common/stepper/stepper.types.mjs +1 -1
  347. package/dist/esm/common/stepper/stepper.types.mjs.map +1 -1
  348. package/dist/esm/common/sticky/sticky.mjs +1 -1
  349. package/dist/esm/common/sticky/sticky.mjs.map +1 -1
  350. package/dist/esm/common/sticky/sticky.module.mjs +1 -1
  351. package/dist/esm/common/sticky/stickyStack.mjs +1 -1
  352. package/dist/esm/common/sticky/stickyStack.mjs.map +1 -1
  353. package/dist/esm/common/styles/sheet.module.mjs +1 -1
  354. package/dist/esm/common/styles/sheet.module.mjs.map +1 -1
  355. package/dist/esm/common/switch/switch.mjs +1 -1
  356. package/dist/esm/common/switch/switch.mjs.map +1 -1
  357. package/dist/esm/common/switch/switch.module.mjs +1 -1
  358. package/dist/esm/common/switch/switch.module.mjs.map +1 -1
  359. package/dist/esm/common/tabHeader/tabHeader.mjs +1 -1
  360. package/dist/esm/common/tabHeader/tabHeader.mjs.map +1 -1
  361. package/dist/esm/common/tabHeader/tabHeader.module.mjs +1 -1
  362. package/dist/esm/common/textarea/textarea.mjs +1 -1
  363. package/dist/esm/common/textarea/textarea.mjs.map +1 -1
  364. package/dist/esm/common/themeProvider/defaultTheme.mjs +1 -1
  365. package/dist/esm/common/themeProvider/defaultTheme.mjs.map +1 -1
  366. package/dist/esm/common/themeProvider/lightTheme.mjs +2 -0
  367. package/dist/esm/common/themeProvider/lightTheme.mjs.map +1 -0
  368. package/dist/esm/common/themeProvider/theme.module.mjs +1 -1
  369. package/dist/esm/common/themeProvider/themeProvider.mjs +1 -1
  370. package/dist/esm/common/themeProvider/themeProvider.mjs.map +1 -1
  371. package/dist/esm/common/themeProvider/themeVariablesContext.mjs +1 -1
  372. package/dist/esm/common/themeProvider/themeVariablesContext.mjs.map +1 -1
  373. package/dist/esm/common/themeProvider/useTheme.mjs +1 -1
  374. package/dist/esm/common/themeProvider/useTheme.mjs.map +1 -1
  375. package/dist/esm/common/tilt/tilt.mjs +1 -1
  376. package/dist/esm/common/tilt/tilt.mjs.map +1 -1
  377. package/dist/esm/common/tilt/tilt.module.mjs +1 -1
  378. package/dist/esm/common/tilt/tiltMath.mjs +1 -1
  379. package/dist/esm/common/tilt/tiltMath.mjs.map +1 -1
  380. package/dist/esm/common/tiltGroup/tiltGroup.mjs +1 -1
  381. package/dist/esm/common/tiltGroup/tiltGroup.mjs.map +1 -1
  382. package/dist/esm/common/timeInput/components/clockDial/clockDial.mjs +1 -1
  383. package/dist/esm/common/timeInput/components/clockDial/clockDial.mjs.map +1 -1
  384. package/dist/esm/common/timeInput/components/clockDial/clockDial.module.mjs +1 -1
  385. package/dist/esm/common/timeInput/timeFunctions.mjs +1 -1
  386. package/dist/esm/common/timeInput/timeFunctions.mjs.map +1 -1
  387. package/dist/esm/common/timeInput/timeInput.mjs +1 -1
  388. package/dist/esm/common/timeInput/timeInput.mjs.map +1 -1
  389. package/dist/esm/common/timeInput/timeInput.module.mjs +1 -1
  390. package/dist/esm/common/timeline/timeline.mjs +1 -1
  391. package/dist/esm/common/timeline/timeline.mjs.map +1 -1
  392. package/dist/esm/common/timeline/timeline.module.mjs +1 -1
  393. package/dist/esm/common/timeline/timeline.types.mjs +1 -1
  394. package/dist/esm/common/timeline/timeline.types.mjs.map +1 -1
  395. package/dist/esm/common/timer/timer.chime.mjs +1 -1
  396. package/dist/esm/common/timer/timer.chime.mjs.map +1 -1
  397. package/dist/esm/common/timer/timer.mjs +1 -1
  398. package/dist/esm/common/timer/timer.mjs.map +1 -1
  399. package/dist/esm/common/timer/timer.module.mjs +1 -1
  400. package/dist/esm/common/toast/toast.mjs +1 -1
  401. package/dist/esm/common/toast/toast.mjs.map +1 -1
  402. package/dist/esm/common/toast/toast.module.mjs +1 -1
  403. package/dist/esm/common/toast/toast.module.mjs.map +1 -1
  404. package/dist/esm/common/toast/toastProvider.mjs +1 -1
  405. package/dist/esm/common/toast/toastProvider.mjs.map +1 -1
  406. package/dist/esm/common/toast/toastService.mjs +1 -1
  407. package/dist/esm/common/toast/toastService.mjs.map +1 -1
  408. package/dist/esm/common/tooltip/tooltip.mjs +1 -1
  409. package/dist/esm/common/tooltip/tooltip.mjs.map +1 -1
  410. package/dist/esm/common/tooltip/tooltip.module.mjs +1 -1
  411. package/dist/esm/common/tooltip/tooltip.module.mjs.map +1 -1
  412. package/dist/esm/common/tree/flattenTree.mjs +1 -1
  413. package/dist/esm/common/tree/flattenTree.mjs.map +1 -1
  414. package/dist/esm/common/tree/tree.mjs +1 -1
  415. package/dist/esm/common/tree/tree.mjs.map +1 -1
  416. package/dist/esm/common/tree/tree.module.mjs +1 -1
  417. package/dist/esm/common/tree/tree.module.mjs.map +1 -1
  418. package/dist/esm/common/utils/activationKey.mjs +1 -1
  419. package/dist/esm/common/utils/activationKey.mjs.map +1 -1
  420. package/dist/esm/common/utils/mergeRefs.mjs +2 -0
  421. package/dist/esm/common/utils/mergeRefs.mjs.map +1 -0
  422. package/dist/esm/common/utils/renderIcon.mjs +1 -1
  423. package/dist/esm/common/utils/renderIcon.mjs.map +1 -1
  424. package/dist/esm/common/utils/scrollAncestors.mjs +1 -1
  425. package/dist/esm/common/utils/scrollAncestors.mjs.map +1 -1
  426. package/dist/esm/common/utils/visualViewport.mjs +1 -1
  427. package/dist/esm/common/utils/visualViewport.mjs.map +1 -1
  428. package/dist/esm/common/virtualList/useColumnResize.mjs +1 -1
  429. package/dist/esm/common/virtualList/useColumnResize.mjs.map +1 -1
  430. package/dist/esm/common/virtualList/useColumns.mjs +1 -1
  431. package/dist/esm/common/virtualList/useColumns.mjs.map +1 -1
  432. package/dist/esm/common/virtualList/useHeaderNavigation.mjs +1 -1
  433. package/dist/esm/common/virtualList/useHeaderNavigation.mjs.map +1 -1
  434. package/dist/esm/common/virtualList/useRowDrag.mjs +1 -1
  435. package/dist/esm/common/virtualList/useRowDrag.mjs.map +1 -1
  436. package/dist/esm/common/virtualList/useRowNavigation.mjs +1 -1
  437. package/dist/esm/common/virtualList/useRowNavigation.mjs.map +1 -1
  438. package/dist/esm/common/virtualList/useSelection.mjs +1 -1
  439. package/dist/esm/common/virtualList/useSelection.mjs.map +1 -1
  440. package/dist/esm/common/virtualList/useSorting.mjs +1 -1
  441. package/dist/esm/common/virtualList/useSorting.mjs.map +1 -1
  442. package/dist/esm/common/virtualList/useVirtualWindow.mjs +1 -1
  443. package/dist/esm/common/virtualList/useVirtualWindow.mjs.map +1 -1
  444. package/dist/esm/common/virtualList/virtualList.mjs +1 -1
  445. package/dist/esm/common/virtualList/virtualList.mjs.map +1 -1
  446. package/dist/esm/common/virtualList/virtualList.module.mjs +1 -1
  447. package/dist/esm/common/virtualList/virtualList.module.mjs.map +1 -1
  448. package/dist/esm/common/virtualList/virtualList.utils.mjs +1 -1
  449. package/dist/esm/common/virtualList/virtualList.utils.mjs.map +1 -1
  450. package/dist/esm/common/virtualList/virtualListHeader.mjs +1 -1
  451. package/dist/esm/common/virtualList/virtualListHeader.mjs.map +1 -1
  452. package/dist/esm/common/virtualList/virtualRow.mjs +1 -1
  453. package/dist/esm/common/virtualList/virtualRow.mjs.map +1 -1
  454. package/dist/esm/common/wizard/wizard.mjs +1 -1
  455. package/dist/esm/common/wizard/wizard.mjs.map +1 -1
  456. package/dist/esm/common/wizard/wizard.module.mjs +1 -1
  457. package/dist/esm/hooks/useWindowSize.mjs +1 -1
  458. package/dist/esm/hooks/useWindowSize.mjs.map +1 -1
  459. package/dist/esm/index.mjs +1 -1
  460. package/dist/esm/services/formValidation/FormValidatorGroupWithForm.mjs +1 -1
  461. package/dist/esm/services/formValidation/FormValidatorGroupWithForm.mjs.map +1 -1
  462. package/dist/esm/services/formValidation/index.mjs +1 -1
  463. package/dist/esm/services/formValidation/useFormValidatorGroup.mjs +1 -1
  464. package/dist/esm/services/formValidation/useFormValidatorGroup.mjs.map +1 -1
  465. package/dist/esm/services/formValidation/validatableComponent.mjs +1 -1
  466. package/dist/esm/services/formValidation/validatableComponent.mjs.map +1 -1
  467. package/dist/esm/services/klipy.mjs +1 -1
  468. package/dist/esm/services/klipy.mjs.map +1 -1
  469. package/dist/esm/services/localization.mjs +1 -1
  470. package/dist/esm/services/localization.mjs.map +1 -1
  471. package/dist/index.cjs +3 -3
  472. package/dist/index.cjs.map +1 -1
  473. package/dist/mcp.js +21096 -5
  474. package/dist/style.css +1 -1
  475. package/dist/types/common/actionIcon/actionIcon.types.d.ts +3 -1
  476. package/dist/types/common/card/card.types.d.ts +3 -1
  477. package/dist/types/common/checkbox/checkbox.types.d.ts +3 -1
  478. package/dist/types/common/colorPicker/colorPicker.types.d.ts +4 -0
  479. package/dist/types/common/configProvider/configProvider.d.ts +1 -1
  480. package/dist/types/common/configProvider/configProvider.types.d.ts +6 -3
  481. package/dist/types/common/configProvider/index.d.ts +1 -0
  482. package/dist/types/common/configProvider/presentation.types.d.ts +14 -0
  483. package/dist/types/common/configProvider/usePresentationDefault.d.ts +1 -1
  484. package/dist/types/common/dropdown/dropdown.types.d.ts +1 -1
  485. package/dist/types/common/flip/flip.d.ts +2 -2
  486. package/dist/types/common/flip/flip.types.d.ts +3 -1
  487. package/dist/types/common/hooks/useAnchorTracking.d.ts +1 -1
  488. package/dist/types/common/hooks/useFocusBoundary.d.ts +3 -3
  489. package/dist/types/common/hooks/useSheet.d.ts +1 -1
  490. package/dist/types/common/input/input.types.d.ts +5 -0
  491. package/dist/types/common/interactableDiv/interactableDiv.d.ts +3 -2
  492. package/dist/types/common/loading/spinnerLoading/spinnerLoading.d.ts +0 -5
  493. package/dist/types/common/menu/index.d.ts +2 -0
  494. package/dist/types/common/menu/menu.d.ts +14 -0
  495. package/dist/types/common/menu/menu.types.d.ts +55 -0
  496. package/dist/types/common/modal/modal.types.d.ts +1 -1
  497. package/dist/types/common/multiSelect/multiSelect.types.d.ts +1 -1
  498. package/dist/types/common/numberInput/numberInput.types.d.ts +2 -0
  499. package/dist/types/common/otpInput/otpInput.types.d.ts +2 -0
  500. package/dist/types/common/planCanvas/planCanvas.d.ts +1 -1
  501. package/dist/types/common/planCanvas/planCanvas.types.d.ts +3 -1
  502. package/dist/types/common/popover/popover.types.d.ts +34 -3
  503. package/dist/types/common/popover/usePopoverPosition.d.ts +20 -1
  504. package/dist/types/common/ripple/ripple.d.ts +1 -1
  505. package/dist/types/common/ripple/ripple.types.d.ts +1 -0
  506. package/dist/types/common/roomDrawer/roomDrawer.d.ts +2 -2
  507. package/dist/types/common/roomDrawer/roomDrawer.types.d.ts +3 -1
  508. package/dist/types/common/roomViewer/roomViewer.d.ts +2 -2
  509. package/dist/types/common/roomViewer/roomViewer.types.d.ts +3 -1
  510. package/dist/types/common/searchInput/searchInput.d.ts +1 -1
  511. package/dist/types/common/searchInput/searchInput.types.d.ts +3 -0
  512. package/dist/types/common/splitButton/splitButton.types.d.ts +3 -3
  513. package/dist/types/common/switch/switch.types.d.ts +3 -1
  514. package/dist/types/common/themeProvider/index.d.ts +1 -0
  515. package/dist/types/common/themeProvider/lightTheme.d.ts +13 -0
  516. package/dist/types/common/tilt/tilt.d.ts +2 -2
  517. package/dist/types/common/tilt/tilt.types.d.ts +3 -1
  518. package/dist/types/common/timer/timer.d.ts +2 -2
  519. package/dist/types/common/timer/timer.types.d.ts +3 -1
  520. package/dist/types/common/tooltip/tooltip.d.ts +1 -1
  521. package/dist/types/common/tooltip/tooltip.types.d.ts +7 -0
  522. package/dist/types/common/types/baseProps.types.d.ts +6 -0
  523. package/dist/types/common/utils/mergeRefs.d.ts +12 -0
  524. package/dist/types/common/virtualList/virtualList.d.ts +2 -4
  525. package/dist/types/common/virtualList/virtualList.types.d.ts +3 -1
  526. package/dist/types/index.d.ts +2 -2
  527. package/docs/Accordion.md +9 -0
  528. package/docs/ActionIcon.md +1 -0
  529. package/docs/Breadcrumb.md +2 -2
  530. package/docs/ButtonGroup.md +1 -1
  531. package/docs/CLAUDE.md +38 -1
  532. package/docs/Card.md +2 -1
  533. package/docs/Checkbox.md +1 -0
  534. package/docs/ColorPicker.md +13 -1
  535. package/docs/ConfigProvider.md +2 -2
  536. package/docs/Dropdown.md +1 -1
  537. package/docs/Input.md +1 -0
  538. package/docs/InputDropdown.md +1 -1
  539. package/docs/InteractableDiv.md +2 -0
  540. package/docs/KlipyPicker.md +1 -1
  541. package/docs/Menu.md +107 -0
  542. package/docs/Modal.md +1 -1
  543. package/docs/NumberInput.md +1 -0
  544. package/docs/OtpInput.md +1 -0
  545. package/docs/Popover.md +11 -5
  546. package/docs/SearchInput.md +1 -0
  547. package/docs/SplitButton.md +8 -4
  548. package/docs/Switch.md +1 -0
  549. package/docs/Textarea.md +1 -0
  550. package/docs/ThemeProvider.md +31 -2
  551. package/docs/Tooltip.md +23 -1
  552. package/docs/VirtualList.md +2 -2
  553. package/package.json +8 -7
  554. package/dist/esm/common/floatingMenu/floatingMenu.mjs +0 -2
  555. package/dist/esm/common/floatingMenu/floatingMenu.mjs.map +0 -1
  556. package/dist/esm/common/floatingMenu/floatingMenu.module.mjs +0 -2
  557. package/dist/esm/common/floatingMenu/floatingMenu.module.mjs.map +0 -1
  558. package/dist/esm/common/floatingMenu/floatingMenu.types.mjs +0 -2
  559. package/dist/esm/common/floatingMenu/floatingMenu.types.mjs.map +0 -1
  560. package/dist/esm/common/floatingMenu/useFloatingPosition.mjs +0 -2
  561. package/dist/esm/common/floatingMenu/useFloatingPosition.mjs.map +0 -1
  562. package/dist/types/common/floatingMenu/floatingMenu.d.ts +0 -3
  563. package/dist/types/common/floatingMenu/floatingMenu.types.d.ts +0 -32
  564. package/dist/types/common/floatingMenu/index.d.ts +0 -2
  565. package/dist/types/common/floatingMenu/useFloatingPosition.d.ts +0 -27
  566. package/docs/FloatingMenu.md +0 -90
@@ -1 +1 @@
1
- {"version":3,"file":"fab.module.mjs","names":[],"sources":["../../../../package/common/fab/fab.module.pcss"],"sourcesContent":[".fab {\n animation: appear 0.3s forwards;\n opacity: 0;\n transform: scale(0);\n box-shadow:\n 0 3px 5px -1px rgb(0 0 0 / 20%),\n 0 6px 10px 0 rgb(0 0 0 / 14%),\n 0 1px 18px 0 rgb(0 0 0 / 12%);\n border-radius: 50%;\n\n &-container {\n position: fixed;\n bottom: 16px;\n right: 16px;\n\n @media (--breakpoint-tablet) {\n z-index: 2;\n }\n }\n\n &-menu {\n position: absolute;\n top: 0;\n transform: translateY(calc(-100% - 16px));\n width: 100%;\n display: flex;\n flex-flow: column-reverse;\n gap: 16px;\n align-items: center;\n pointer-events: none;\n user-select: none;\n\n &-backdrop {\n width: 0%;\n height: 0%;\n position: fixed;\n top: 0;\n left: 0;\n background: black;\n opacity: 0;\n transition: opacity 0.4s;\n\n &-open {\n width: 100%;\n height: 100%;\n opacity: 0.5;\n }\n }\n\n &-entry {\n position: relative;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n opacity: 0;\n transform: scale(0);\n transition: all 0.2s;\n pointer-events: all;\n\n &:nth-child(2) {\n transition-delay: 0.05s;\n }\n\n &:nth-child(3) {\n transition-delay: 0.1s;\n }\n\n &:nth-child(4) {\n transition-delay: 0.15s;\n }\n\n &:nth-child(5) {\n transition-delay: 0.2s;\n }\n\n &:nth-child(6) {\n transition-delay: 0.25s;\n }\n\n &:nth-child(7) {\n transition-delay: 0.3s;\n }\n\n &-shown {\n opacity: 1;\n transform: scale(1);\n }\n\n &-label {\n background: var(--background-accent-light);\n border-radius: var(--default-border-radius);\n padding: 4px 8px;\n\n &-container {\n position: absolute;\n width: max-content;\n transform: translateX(-100%);\n height: 100%;\n margin-left: -8px;\n align-items: center;\n display: flex;\n }\n }\n }\n }\n\n &-icon {\n transition: all 0.4s;\n display: flex;\n justify-content: center;\n align-items: center;\n position: relative;\n width: 100%;\n height: 100%;\n\n &-menuOpen {\n transform: rotate(225deg);\n }\n\n &-content {\n position: absolute;\n width: var(--custom-fab-icon-size);\n height: var(--custom-fab-icon-size);\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0;\n transition: opacity 0.3s;\n\n &-shown {\n opacity: 1;\n }\n }\n }\n\n @keyframes appear {\n to {\n opacity: 1;\n transform: scale(1);\n }\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"fab.module.mjs","names":[],"sources":["../../../../package/common/fab/fab.module.pcss"],"sourcesContent":[".fab {\n animation: appear 0.3s forwards;\n opacity: 0;\n transform: scale(0);\n box-shadow:\n 0 3px 5px -1px rgb(0 0 0 / 20%),\n 0 6px 10px 0 rgb(0 0 0 / 14%),\n 0 1px 18px 0 rgb(0 0 0 / 12%);\n border-radius: 50%;\n\n &-container {\n position: fixed;\n bottom: 16px;\n right: 16px;\n\n @media (--breakpoint-tablet) {\n z-index: 2;\n }\n }\n\n &-menu {\n position: absolute;\n top: 0;\n transform: translateY(calc(-100% - 16px));\n width: 100%;\n display: flex;\n flex-flow: column-reverse;\n gap: 16px;\n align-items: center;\n pointer-events: none;\n user-select: none;\n\n &-backdrop {\n width: 0%;\n height: 0%;\n position: fixed;\n top: 0;\n left: 0;\n background: black;\n opacity: 0;\n transition: opacity 0.4s;\n\n &-open {\n width: 100%;\n height: 100%;\n opacity: 0.5;\n }\n }\n\n &-entry {\n position: relative;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n opacity: 0;\n transform: scale(0);\n transition: all 0.2s;\n pointer-events: all;\n\n &:nth-child(2) {\n transition-delay: 0.05s;\n }\n\n &:nth-child(3) {\n transition-delay: 0.1s;\n }\n\n &:nth-child(4) {\n transition-delay: 0.15s;\n }\n\n &:nth-child(5) {\n transition-delay: 0.2s;\n }\n\n &:nth-child(6) {\n transition-delay: 0.25s;\n }\n\n &:nth-child(7) {\n transition-delay: 0.3s;\n }\n\n &-shown {\n opacity: 1;\n transform: scale(1);\n }\n\n &-label {\n background: var(--background-accent-light);\n border-radius: var(--default-border-radius);\n padding: 4px 8px;\n\n &-container {\n position: absolute;\n width: max-content;\n transform: translateX(-100%);\n height: 100%;\n margin-left: -8px;\n align-items: center;\n display: flex;\n }\n }\n }\n }\n\n &-icon {\n transition: all 0.4s;\n display: flex;\n justify-content: center;\n align-items: center;\n position: relative;\n width: 100%;\n height: 100%;\n\n &-menuOpen {\n transform: rotate(225deg);\n }\n\n &-content {\n position: absolute;\n width: var(--custom-fab-icon-size);\n height: var(--custom-fab-icon-size);\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0;\n transition: opacity 0.3s;\n\n &-shown {\n opacity: 1;\n }\n }\n }\n\n @keyframes appear {\n to {\n opacity: 1;\n transform: scale(1);\n }\n }\n}\n\n/* `.fab` is opacity 0 and scale 0 until `appear` runs, so removing the animation has to put it\n at the end state by hand or the button never shows up at all. */\n@media (prefers-reduced-motion: reduce) {\n .fab {\n animation: none;\n opacity: 1;\n transform: none;\n }\n\n .fab-menu-entry {\n transition: none;\n }\n}\n"],"mappings":""}
@@ -1,2 +1,2 @@
1
- import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{isSelfActivation as t}from"../utils/activationKey.mjs";import n from"./flip.module.mjs";import{forwardRef as r,useEffect as i,useImperativeHandle as a,useRef as o,useState as s}from"react";import c from"classnames";import{jsx as l,jsxs as u}from"react/jsx-runtime";function d(){return typeof process<`u`&&!1}function f(){return typeof window<`u`&&window.matchMedia?.(`(prefers-reduced-motion: reduce)`).matches===!0}var p=r(function(r,p){let{front:m,back:h,flipped:g,defaultFlipped:_=!1,flipOnClick:v=!1,flipAxis:y=`y`,reverse:b=!1,flipDuration:x=600,perspective:S=800,disabled:C=!1,onFlip:w,onFlipEnd:T,className:E,style:D,classNames:O,styles:k,...A}=e(`Flip`,r),j=g!==void 0,[M,N]=s(_),P=j?g:M;function F(e){j||N(e),w?.(e)}let I=()=>{v&&!C&&F(!P)},L=e=>{v&&!C&&t(e)&&(e.preventDefault(),F(!P))},R=o(!1);i(()=>{if(!R.current){R.current=!0;return}f()&&T?.(P)},[P]);let z=e=>{e.target===e.currentTarget&&e.propertyName===`transform`&&T?.(P)};a(p,()=>({flip(){if(j){d()&&console.warn("Flip: flip() called on a controlled instance, update the `flipped` prop instead.");return}F(!P)},setFlipped(e){if(j){d()&&console.warn("Flip: setFlipped() called on a controlled instance, update the `flipped` prop instead.");return}F(e)},reset(){j||N(!1)}}));let B=b?-180:180,V=y===`x`&&P?B:0,H=y===`y`&&P?B:0,U=y===`x`?B:0,W=y===`y`?B:0;return l(`div`,{...A,className:c(n.flip,v&&n.flipClickable,E,O?.root),style:{...D,...k?.root},onClick:I,onKeyDown:v?L:void 0,role:v?`button`:void 0,tabIndex:v?0:void 0,"aria-pressed":v?P:void 0,children:l(`div`,{className:c(n.flipPerspective,O?.perspective),style:{perspective:S===`none`?`none`:`${S}px`,...k?.perspective},children:u(`div`,{className:c(n.flipSurface,O?.surface),style:{transform:`rotateX(${V}deg) rotateY(${H}deg)`,"--flip-duration":`${x}ms`,...k?.surface},"data-flipped":P,onTransitionEnd:z,children:[l(`div`,{className:c(n.flipFront,O?.front),style:k?.front,children:m}),l(`div`,{className:c(n.flipBack,O?.back),style:{transform:`rotateX(${U}deg) rotateY(${W}deg)`,...k?.back},children:h})]})})})});export{p as default};
1
+ "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{isSelfActivation as t}from"../utils/activationKey.mjs";import n from"./flip.module.mjs";import{useEffect as r,useImperativeHandle as i,useRef as a,useState as o}from"react";import s from"classnames";import{jsx as c,jsxs as l}from"react/jsx-runtime";function u(){return typeof process<`u`&&!1}function d(){return typeof window<`u`&&window.matchMedia?.(`(prefers-reduced-motion: reduce)`).matches===!0}function f(f){let{ref:p,front:m,back:h,flipped:g,defaultFlipped:_=!1,flipOnClick:v=!1,flipAxis:y=`y`,reverse:b=!1,flipDuration:x=600,perspective:S=800,disabled:C=!1,onFlip:w,onFlipEnd:T,className:E,style:D,classNames:O,styles:k,...A}=e(`Flip`,f),j=g!==void 0,[M,N]=o(_),P=j?g:M;function F(e){j||N(e),w?.(e)}let I=()=>{v&&!C&&F(!P)},L=e=>{v&&!C&&t(e)&&(e.preventDefault(),F(!P))},R=a(!1);r(()=>{if(!R.current){R.current=!0;return}d()&&T?.(P)},[P]);let z=e=>{e.target===e.currentTarget&&e.propertyName===`transform`&&T?.(P)};i(p,()=>({flip(){if(j){u()&&console.warn("Flip: flip() called on a controlled instance, update the `flipped` prop instead.");return}F(!P)},setFlipped(e){if(j){u()&&console.warn("Flip: setFlipped() called on a controlled instance, update the `flipped` prop instead.");return}F(e)},reset(){j||N(!1)}}));let B=b?-180:180,V=y===`x`&&P?B:0,H=y===`y`&&P?B:0,U=y===`x`?B:0,W=y===`y`?B:0;return c(`div`,{...A,className:s(n.flip,v&&n.flipClickable,E,O?.root),style:{...D,...k?.root},onClick:I,onKeyDown:v?L:void 0,role:v?`button`:void 0,tabIndex:v?0:void 0,"aria-pressed":v?P:void 0,children:c(`div`,{className:s(n.flipPerspective,O?.perspective),style:{perspective:S===`none`?`none`:`${S}px`,...k?.perspective},children:l(`div`,{className:s(n.flipSurface,O?.surface),style:{transform:`rotateX(${V}deg) rotateY(${H}deg)`,"--flip-duration":`${x}ms`,...k?.surface},"data-flipped":P,onTransitionEnd:z,children:[c(`div`,{className:s(n.flipFront,O?.front),style:k?.front,children:m}),c(`div`,{className:s(n.flipBack,O?.back),style:{transform:`rotateX(${U}deg) rotateY(${W}deg)`,...k?.back},children:h})]})})})}export{f as default};
2
2
  //# sourceMappingURL=flip.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"flip.mjs","names":[],"sources":["../../../../package/common/flip/flip.tsx"],"sourcesContent":["import {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n} from 'react';\nimport cx from 'classnames';\n\nimport { useComponentDefaults } from '../configProvider';\nimport type { FlipHandle, FlipProps } from './flip.types';\nimport styles from './flip.module.pcss';\nimport { isSelfActivation } from '../utils/activationKey';\n\n// Module-scoped ambient typing for the bundler-replaced NODE_ENV guard below. See bodyEnd.tsx.\ndeclare const process: { env: { NODE_ENV?: string } };\n\nfunction isDev(): boolean {\n return typeof process !== 'undefined' && process.env.NODE_ENV !== 'production';\n}\n\nfunction prefersReducedMotion(): boolean {\n return typeof window !== 'undefined' && window.matchMedia?.('(prefers-reduced-motion: reduce)').matches === true;\n}\n\nconst Flip = forwardRef<FlipHandle, FlipProps>(function Flip(props, ref) {\n const {\n front,\n back,\n flipped,\n defaultFlipped = false,\n flipOnClick = false,\n flipAxis = 'y',\n reverse = false,\n flipDuration = 600,\n perspective = 800,\n disabled = false,\n onFlip,\n onFlipEnd,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('Flip', props);\n\n const isControlled = flipped !== undefined;\n const [internalFlipped, setInternalFlipped] = useState(defaultFlipped);\n const effectiveFlipped = isControlled ? flipped : internalFlipped;\n\n function doFlip(nextFlipped: boolean) {\n if (!isControlled) setInternalFlipped(nextFlipped);\n onFlip?.(nextFlipped);\n }\n\n const handleClick = () => {\n if (!flipOnClick || disabled) return;\n doFlip(!effectiveFlipped);\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (!flipOnClick || disabled) return;\n if (!isSelfActivation(event)) return;\n event.preventDefault();\n doFlip(!effectiveFlipped);\n };\n\n const mountedRef = useRef(false);\n useEffect(() => {\n if (!mountedRef.current) {\n mountedRef.current = true;\n return;\n }\n if (prefersReducedMotion()) onFlipEnd?.(effectiveFlipped);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [effectiveFlipped]);\n\n const handleTransitionEnd = (event: React.TransitionEvent<HTMLDivElement>) => {\n if (event.target !== event.currentTarget || event.propertyName !== 'transform') return;\n onFlipEnd?.(effectiveFlipped);\n };\n\n useImperativeHandle(ref, () => ({\n flip() {\n if (isControlled) {\n if (isDev()) {\n console.warn('Flip: flip() called on a controlled instance, update the `flipped` prop instead.');\n }\n return;\n }\n doFlip(!effectiveFlipped);\n },\n setFlipped(value: boolean) {\n if (isControlled) {\n if (isDev()) {\n console.warn('Flip: setFlipped() called on a controlled instance, update the `flipped` prop instead.');\n }\n return;\n }\n doFlip(value);\n },\n reset() {\n if (!isControlled) setInternalFlipped(false);\n },\n }));\n\n // `reverse` flips the rotation sign on the same hinge axis (+180deg vs -180deg), so adjacent\n // flips can sweep opposite ways without changing which edge they hinge on. The back face's\n // own static rotation (below) always uses this same sign, unconditionally: two rotations of\n // the same sign about the same axis (surface's + back's own) sum to +/-360deg, which is a full\n // turn back to a right-reading, forward-facing back, regardless of whether `reverse` is set.\n const turn = reverse ? -180 : 180;\n const flipOffsetX = flipAxis === 'x' && effectiveFlipped ? turn : 0;\n const flipOffsetY = flipAxis === 'y' && effectiveFlipped ? turn : 0;\n const backOffsetX = flipAxis === 'x' ? turn : 0;\n const backOffsetY = flipAxis === 'y' ? turn : 0;\n\n return (\n <div\n {...rest}\n className={cx(styles.flip, flipOnClick && styles.flipClickable, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n onClick={handleClick}\n onKeyDown={flipOnClick ? handleKeyDown : undefined}\n role={flipOnClick ? 'button' : undefined}\n tabIndex={flipOnClick ? 0 : undefined}\n aria-pressed={flipOnClick ? effectiveFlipped : undefined}\n >\n <div\n className={cx(styles.flipPerspective, classNames?.perspective)}\n style={{\n perspective: perspective === 'none' ? 'none' : `${perspective}px`,\n ...slotStyles?.perspective,\n }}\n >\n <div\n className={cx(styles.flipSurface, classNames?.surface)}\n style={\n {\n transform: `rotateX(${flipOffsetX}deg) rotateY(${flipOffsetY}deg)`,\n '--flip-duration': `${flipDuration}ms`,\n ...slotStyles?.surface,\n } as CSSProperties & Record<'--flip-duration', string>\n }\n data-flipped={effectiveFlipped}\n onTransitionEnd={handleTransitionEnd}\n >\n <div className={cx(styles.flipFront, classNames?.front)} style={slotStyles?.front}>\n {front}\n </div>\n <div\n className={cx(styles.flipBack, classNames?.back)}\n style={{\n transform: `rotateX(${backOffsetX}deg) rotateY(${backOffsetY}deg)`,\n ...slotStyles?.back,\n }}\n >\n {back}\n </div>\n </div>\n </div>\n </div>\n );\n});\n\nexport default Flip;\n"],"mappings":"kWAmBA,SAAS,GAAiB,CACxB,OAAO,OAAO,QAAY,KAAe,EAC3C,CAEA,SAAS,GAAgC,CACvC,OAAO,OAAO,OAAW,KAAe,OAAO,aAAa,kCAAkC,CAAC,CAAC,UAAY,EAC9G,CAEA,IAAM,EAAO,EAAkC,SAAc,EAAO,EAAK,CACvE,GAAM,CACJ,QACA,OACA,UACA,iBAAiB,GACjB,cAAc,GACd,WAAW,IACX,UAAU,GACV,eAAe,IACf,cAAc,IACd,WAAW,GACX,SACA,YACA,YACA,QACA,aACA,OAAQ,EACR,GAAG,GACD,EAAqB,OAAQ,CAAK,EAEhC,EAAe,IAAY,IAAA,GAC3B,CAAC,EAAiB,GAAsB,EAAS,CAAc,EAC/D,EAAmB,EAAe,EAAU,EAElD,SAAS,EAAO,EAAsB,CAC/B,GAAc,EAAmB,CAAW,EACjD,IAAS,CAAW,CACtB,CAEA,IAAM,MAAoB,CACnB,GAAe,IACpB,EAAO,CAAC,CAAgB,CAC1B,EAEM,EAAiB,GAAyC,CACzD,GAAe,IACf,EAAiB,CAAK,IAC3B,EAAM,eAAe,EACrB,EAAO,CAAC,CAAgB,EAC1B,EAEM,EAAa,EAAO,EAAK,EAC/B,MAAgB,CACd,GAAI,CAAC,EAAW,QAAS,CACvB,EAAW,QAAU,GACrB,MACF,CACI,EAAqB,GAAG,IAAY,CAAgB,CAE1D,EAAG,CAAC,CAAgB,CAAC,EAErB,IAAM,EAAuB,GAAiD,CACxE,EAAM,SAAW,EAAM,eAAiB,EAAM,eAAiB,aACnE,IAAY,CAAgB,CAC9B,EAEA,EAAoB,OAAY,CAC9B,MAAO,CACL,GAAI,EAAc,CACZ,EAAM,GACR,QAAQ,KAAK,kFAAkF,EAEjG,MACF,CACA,EAAO,CAAC,CAAgB,CAC1B,EACA,WAAW,EAAgB,CACzB,GAAI,EAAc,CACZ,EAAM,GACR,QAAQ,KAAK,wFAAwF,EAEvG,MACF,CACA,EAAO,CAAK,CACd,EACA,OAAQ,CACD,GAAc,EAAmB,EAAK,CAC7C,CACF,EAAE,EAOF,IAAM,EAAO,EAAU,KAAO,IACxB,EAAc,IAAa,KAAO,EAAmB,EAAO,EAC5D,EAAc,IAAa,KAAO,EAAmB,EAAO,EAC5D,EAAc,IAAa,IAAM,EAAO,EACxC,EAAc,IAAa,IAAM,EAAO,EAE9C,OACE,EAAC,MAAD,CACE,GAAI,EACJ,UAAW,EAAG,EAAO,KAAM,GAAe,EAAO,cAAe,EAAW,GAAY,IAAI,EAC3F,MAAO,CAAE,GAAG,EAAO,GAAG,GAAY,IAAK,EACvC,QAAS,EACT,UAAW,EAAc,EAAgB,IAAA,GACzC,KAAM,EAAc,SAAW,IAAA,GAC/B,SAAU,EAAc,EAAI,IAAA,GAC5B,eAAc,EAAc,EAAmB,IAAA,GAE/C,SAAA,EAAC,MAAD,CACE,UAAW,EAAG,EAAO,gBAAiB,GAAY,WAAW,EAC7D,MAAO,CACL,YAAa,IAAgB,OAAS,OAAS,GAAG,EAAY,IAC9D,GAAG,GAAY,WACjB,EAEA,SAAA,EAAC,MAAD,CACE,UAAW,EAAG,EAAO,YAAa,GAAY,OAAO,EACrD,MACE,CACE,UAAW,WAAW,EAAY,eAAe,EAAY,MAC7D,kBAAmB,GAAG,EAAa,IACnC,GAAG,GAAY,OACjB,EAEF,eAAc,EACd,gBAAiB,EAVnB,SAAA,CAYE,EAAC,MAAD,CAAK,UAAW,EAAG,EAAO,UAAW,GAAY,KAAK,EAAG,MAAO,GAAY,MACzE,SAAA,CACE,CAAA,EACL,EAAC,MAAD,CACE,UAAW,EAAG,EAAO,SAAU,GAAY,IAAI,EAC/C,MAAO,CACL,UAAW,WAAW,EAAY,eAAe,EAAY,MAC7D,GAAG,GAAY,IACjB,EAEC,SAAA,CACE,CAAA,CACF,GACF,CAAA,CACF,CAAA,CAET,CAAC"}
1
+ {"version":3,"file":"flip.mjs","names":[],"sources":["../../../../package/common/flip/flip.tsx"],"sourcesContent":["import { useEffect, useImperativeHandle, useRef, useState, type CSSProperties, type KeyboardEvent } from 'react';\nimport cx from 'classnames';\n\nimport { useComponentDefaults } from '../configProvider';\nimport type { FlipProps } from './flip.types';\nimport styles from './flip.module.pcss';\nimport { isSelfActivation } from '../utils/activationKey';\n\n// Module-scoped ambient typing for the bundler-replaced NODE_ENV guard below. See bodyEnd.tsx.\ndeclare const process: { env: { NODE_ENV?: string } };\n\nfunction isDev(): boolean {\n return typeof process !== 'undefined' && process.env.NODE_ENV !== 'production';\n}\n\nfunction prefersReducedMotion(): boolean {\n return typeof window !== 'undefined' && window.matchMedia?.('(prefers-reduced-motion: reduce)').matches === true;\n}\n\nfunction Flip(props: FlipProps) {\n const {\n ref,\n front,\n back,\n flipped,\n defaultFlipped = false,\n flipOnClick = false,\n flipAxis = 'y',\n reverse = false,\n flipDuration = 600,\n perspective = 800,\n disabled = false,\n onFlip,\n onFlipEnd,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('Flip', props);\n\n const isControlled = flipped !== undefined;\n const [internalFlipped, setInternalFlipped] = useState(defaultFlipped);\n const effectiveFlipped = isControlled ? flipped : internalFlipped;\n\n function doFlip(nextFlipped: boolean) {\n if (!isControlled) setInternalFlipped(nextFlipped);\n onFlip?.(nextFlipped);\n }\n\n const handleClick = () => {\n if (!flipOnClick || disabled) return;\n doFlip(!effectiveFlipped);\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (!flipOnClick || disabled) return;\n if (!isSelfActivation(event)) return;\n event.preventDefault();\n doFlip(!effectiveFlipped);\n };\n\n const mountedRef = useRef(false);\n useEffect(() => {\n if (!mountedRef.current) {\n mountedRef.current = true;\n return;\n }\n if (prefersReducedMotion()) onFlipEnd?.(effectiveFlipped);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [effectiveFlipped]);\n\n const handleTransitionEnd = (event: React.TransitionEvent<HTMLDivElement>) => {\n if (event.target !== event.currentTarget || event.propertyName !== 'transform') return;\n onFlipEnd?.(effectiveFlipped);\n };\n\n useImperativeHandle(ref, () => ({\n flip() {\n if (isControlled) {\n if (isDev()) {\n console.warn('Flip: flip() called on a controlled instance, update the `flipped` prop instead.');\n }\n return;\n }\n doFlip(!effectiveFlipped);\n },\n setFlipped(value: boolean) {\n if (isControlled) {\n if (isDev()) {\n console.warn('Flip: setFlipped() called on a controlled instance, update the `flipped` prop instead.');\n }\n return;\n }\n doFlip(value);\n },\n reset() {\n if (!isControlled) setInternalFlipped(false);\n },\n }));\n\n // `reverse` flips the rotation sign on the same hinge axis (+180deg vs -180deg), so adjacent\n // flips can sweep opposite ways without changing which edge they hinge on. The back face's\n // own static rotation (below) always uses this same sign, unconditionally: two rotations of\n // the same sign about the same axis (surface's + back's own) sum to +/-360deg, which is a full\n // turn back to a right-reading, forward-facing back, regardless of whether `reverse` is set.\n const turn = reverse ? -180 : 180;\n const flipOffsetX = flipAxis === 'x' && effectiveFlipped ? turn : 0;\n const flipOffsetY = flipAxis === 'y' && effectiveFlipped ? turn : 0;\n const backOffsetX = flipAxis === 'x' ? turn : 0;\n const backOffsetY = flipAxis === 'y' ? turn : 0;\n\n return (\n <div\n {...rest}\n className={cx(styles.flip, flipOnClick && styles.flipClickable, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n onClick={handleClick}\n onKeyDown={flipOnClick ? handleKeyDown : undefined}\n role={flipOnClick ? 'button' : undefined}\n tabIndex={flipOnClick ? 0 : undefined}\n aria-pressed={flipOnClick ? effectiveFlipped : undefined}\n >\n <div\n className={cx(styles.flipPerspective, classNames?.perspective)}\n style={{\n perspective: perspective === 'none' ? 'none' : `${perspective}px`,\n ...slotStyles?.perspective,\n }}\n >\n <div\n className={cx(styles.flipSurface, classNames?.surface)}\n style={\n {\n transform: `rotateX(${flipOffsetX}deg) rotateY(${flipOffsetY}deg)`,\n '--flip-duration': `${flipDuration}ms`,\n ...slotStyles?.surface,\n } as CSSProperties & Record<'--flip-duration', string>\n }\n data-flipped={effectiveFlipped}\n onTransitionEnd={handleTransitionEnd}\n >\n <div className={cx(styles.flipFront, classNames?.front)} style={slotStyles?.front}>\n {front}\n </div>\n <div\n className={cx(styles.flipBack, classNames?.back)}\n style={{\n transform: `rotateX(${backOffsetX}deg) rotateY(${backOffsetY}deg)`,\n ...slotStyles?.back,\n }}\n >\n {back}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nexport default Flip;\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- var e=`flip_bL-UK`,t=`flip-clickable_YcfaN`,n=`flip-perspective_i9Km7`,r=`flip-surface_wu3Rt`,i=`flip-front_2kwDN`,a=`flip-back_4eqrK`,o={flip:e,"flip-clickable":`flip-clickable_YcfaN`,flipClickable:t,"flip-perspective":`flip-perspective_i9Km7`,flipPerspective:n,"flip-surface":`flip-surface_wu3Rt`,flipSurface:r,"flip-front":`flip-front_2kwDN`,flipFront:i,"flip-back":`flip-back_4eqrK`,flipBack:a};export{o as default,e as flip,a as flipBack,t as flipClickable,i as flipFront,n as flipPerspective,r as flipSurface};
1
+ "use client";var e=`flip_bL-UK`,t=`flip-clickable_YcfaN`,n=`flip-perspective_i9Km7`,r=`flip-surface_wu3Rt`,i=`flip-front_2kwDN`,a=`flip-back_4eqrK`,o={flip:e,"flip-clickable":`flip-clickable_YcfaN`,flipClickable:t,"flip-perspective":`flip-perspective_i9Km7`,flipPerspective:n,"flip-surface":`flip-surface_wu3Rt`,flipSurface:r,"flip-front":`flip-front_2kwDN`,flipFront:i,"flip-back":`flip-back_4eqrK`,flipBack:a};export{o as default,e as flip,a as flipBack,t as flipClickable,i as flipFront,n as flipPerspective,r as flipSurface};
2
2
  //# sourceMappingURL=flip.module.mjs.map
@@ -1,2 +1,2 @@
1
- import{dist as e,perimeter as t,pointInRing as n,representativePoint as r,signedArea as i}from"./floorPlan.geometry.mjs";function a(t){return Object.values(t.walls).filter(n=>{if(n.a===n.b)return!1;let r=t.nodes[n.a],i=t.nodes[n.b];return!r||!i?!1:e(r,i)>=10})}function o(e){let t=a(e),n=Array(t.length*2);for(let e=0;e<t.length;e++){let r=t[e];n[2*e]={wallId:r.id,from:r.a,to:r.b},n[2*e+1]={wallId:r.id,from:r.b,to:r.a}}let r=t=>e.nodes[t],i=new Map;for(let e=0;e<n.length;e++){let t=i.get(n[e].from);t?t.push(e):i.set(n[e].from,[e])}let o=e=>{let t=r(n[e].from),i=r(n[e].to);return Math.atan2(i.y-t.y,i.x-t.x)};for(let e of i.values())e.sort((e,t)=>{let r=o(e),i=o(t);if(r!==i)return r-i;let a=n[e].wallId,s=n[t].wallId;return a===s?e-t:a<s?-1:1});let s=new Int32Array(n.length);for(let e of i.values())for(let t=0;t<e.length;t++)s[e[t]]=t;return{darts:n,outgoing:i,ringIndex:s,nodeOf:r}}function s(e,t){let n=e.outgoing.get(e.darts[t].to);return!n||n.length===0?t^1:n[(e.ringIndex[t^1]-1+n.length)%n.length]}function c(e){let t=new Uint8Array(e.darts.length),n=[];for(let r=0;r<e.darts.length;r++){if(t[r])continue;let i=[],a=r,o=0;do if(t[a]=1,i.push(a),a=s(e,a),++o>e.darts.length+1)break;while(a!==r&&!t[a]);n.push(i)}return n}function l(e){let t=new Map,n=e=>{let n=e;for(;t.get(n)!==n;)n=t.get(n)??n;let r=e;for(;t.get(r)!==n;){let e=t.get(r)??n;t.set(r,n),r=e}return n};for(let n of e.outgoing.keys())t.set(n,n);for(let r=0;r<e.darts.length;r+=2){let i=n(e.darts[r].from),a=n(e.darts[r].to);i!==a&&t.set(i,a)}let r=new Map;for(let t of e.outgoing.keys())r.set(t,n(t));return r}function u(e,t){return t.map(t=>{let n=e.nodeOf(e.darts[t].from);return{x:n.x,y:n.y}})}function d(e,t){let n=``;for(let r of t){let t=`${e.darts[r].wallId}>${e.darts[r].from}`;(n===``||t<n)&&(n=t)}return`f:${n}`}function f(e){let a=o(e);if(a.darts.length===0)return[];let s=l(a),f=c(a),p=[],m=new Map;for(let e of f){let n=u(a,e),r=i(n),o=s.get(a.darts[e[0]].from)??``;r>100?p.push({face:{id:d(a,e),outer:n,holes:[],area:r,perimeter:t(n),wallIds:e.map(e=>a.darts[e].wallId),nodeIds:e.map(e=>a.darts[e].from)},component:o,outerArea:r}):r<-100&&m.set(o,n)}for(let[e,t]of m){let a=Math.abs(i(t));if(a<=100)continue;let o=r(t),s=null;for(let t of p)t.component!==e&&(t.outerArea<=a||n(o,t.face.outer)&&(!s||t.outerArea<s.outerArea)&&(s=t));s&&s.face.holes.push(t)}for(let e of p){let t=e.outerArea;for(let n of e.face.holes)t-=Math.abs(i(n));e.face.area=t}return p.map(e=>e.face)}function p(e){let t=new Map;for(let n of a(e))t.set(n.a,(t.get(n.a)??0)+1),t.set(n.b,(t.get(n.b)??0)+1);return t}export{f as deriveFaces,p as nodeDegrees};
1
+ "use client";import{dist as e,perimeter as t,pointInRing as n,representativePoint as r,signedArea as i}from"./floorPlan.geometry.mjs";function a(t){return Object.values(t.walls).filter(n=>{if(n.a===n.b)return!1;let r=t.nodes[n.a],i=t.nodes[n.b];return!r||!i?!1:e(r,i)>=10})}function o(e){let t=a(e),n=Array(t.length*2);for(let e=0;e<t.length;e++){let r=t[e];n[2*e]={wallId:r.id,from:r.a,to:r.b},n[2*e+1]={wallId:r.id,from:r.b,to:r.a}}let r=t=>e.nodes[t],i=new Map;for(let e=0;e<n.length;e++){let t=i.get(n[e].from);t?t.push(e):i.set(n[e].from,[e])}let o=e=>{let t=r(n[e].from),i=r(n[e].to);return Math.atan2(i.y-t.y,i.x-t.x)};for(let e of i.values())e.sort((e,t)=>{let r=o(e),i=o(t);if(r!==i)return r-i;let a=n[e].wallId,s=n[t].wallId;return a===s?e-t:a<s?-1:1});let s=new Int32Array(n.length);for(let e of i.values())for(let t=0;t<e.length;t++)s[e[t]]=t;return{darts:n,outgoing:i,ringIndex:s,nodeOf:r}}function s(e,t){let n=e.outgoing.get(e.darts[t].to);return!n||n.length===0?t^1:n[(e.ringIndex[t^1]-1+n.length)%n.length]}function c(e){let t=new Uint8Array(e.darts.length),n=[];for(let r=0;r<e.darts.length;r++){if(t[r])continue;let i=[],a=r,o=0;do if(t[a]=1,i.push(a),a=s(e,a),++o>e.darts.length+1)break;while(a!==r&&!t[a]);n.push(i)}return n}function l(e){let t=new Map,n=e=>{let n=e;for(;t.get(n)!==n;)n=t.get(n)??n;let r=e;for(;t.get(r)!==n;){let e=t.get(r)??n;t.set(r,n),r=e}return n};for(let n of e.outgoing.keys())t.set(n,n);for(let r=0;r<e.darts.length;r+=2){let i=n(e.darts[r].from),a=n(e.darts[r].to);i!==a&&t.set(i,a)}let r=new Map;for(let t of e.outgoing.keys())r.set(t,n(t));return r}function u(e,t){return t.map(t=>{let n=e.nodeOf(e.darts[t].from);return{x:n.x,y:n.y}})}function d(e,t){let n=``;for(let r of t){let t=`${e.darts[r].wallId}>${e.darts[r].from}`;(n===``||t<n)&&(n=t)}return`f:${n}`}function f(e){let a=o(e);if(a.darts.length===0)return[];let s=l(a),f=c(a),p=[],m=new Map;for(let e of f){let n=u(a,e),r=i(n),o=s.get(a.darts[e[0]].from)??``;r>100?p.push({face:{id:d(a,e),outer:n,holes:[],area:r,perimeter:t(n),wallIds:e.map(e=>a.darts[e].wallId),nodeIds:e.map(e=>a.darts[e].from)},component:o,outerArea:r}):r<-100&&m.set(o,n)}for(let[e,t]of m){let a=Math.abs(i(t));if(a<=100)continue;let o=r(t),s=null;for(let t of p)t.component!==e&&(t.outerArea<=a||n(o,t.face.outer)&&(!s||t.outerArea<s.outerArea)&&(s=t));s&&s.face.holes.push(t)}for(let e of p){let t=e.outerArea;for(let n of e.face.holes)t-=Math.abs(i(n));e.face.area=t}return p.map(e=>e.face)}function p(e){let t=new Map;for(let n of a(e))t.set(n.a,(t.get(n.a)??0)+1),t.set(n.b,(t.get(n.b)??0)+1);return t}export{f as deriveFaces,p as nodeDegrees};
2
2
  //# sourceMappingURL=floorPlan.faces.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"floorPlan.faces.mjs","names":[],"sources":["../../../../package/common/floorPlan/floorPlan.faces.ts"],"sourcesContent":["/**\n * Planar face extraction: turns the wall graph into the enclosed rooms it forms.\n *\n * Each wall becomes two darts (directed half-edges), wall `i` owning darts `2i` and\n * `2i + 1`, so `twin(d) === d ^ 1` with no lookup. The darts leaving each node are\n * sorted by angle, and the face walk is:\n *\n * next(d) = the cyclic PREDECESSOR of twin(d) in the sorted ring at head(d)\n *\n * Because the shoelace sum runs on the same raw coordinates as the angular sort, the\n * rule needs no orientation convention: bounded faces come out with POSITIVE signed\n * area and each connected component's outer boundary comes out negative, whichever way\n * you read the y axis.\n *\n * A dangling wall (one endpoint of degree 1) is handled by the rule itself: a\n * one-element ring's cyclic predecessor is itself, so the walk goes out along the spur\n * and straight back. Both of its darts land in the same cycle, no extra face appears,\n * and the face gains a zero-width slit that contributes nothing to area, fill or\n * point-in-polygon.\n */\n\nimport {\n EPS_AREA,\n MIN_WALL_LENGTH,\n areaCentroid,\n dist,\n perimeter as ringPerimeter,\n pointInRing,\n representativePoint,\n signedArea,\n} from './floorPlan.geometry';\nimport type { PlanFace, PlanFloor, PlanNode, PlanPoint, PlanWall } from './floorPlan.types';\n\ninterface Dart {\n wallId: string;\n from: string;\n to: string;\n}\n\ninterface DartGraph {\n darts: Dart[];\n /** Node id to its outgoing dart indices, sorted by angle ascending. */\n outgoing: Map<string, number[]>;\n /** Dart index to its position within its own outgoing ring. */\n ringIndex: Int32Array;\n nodeOf: (id: string) => PlanNode;\n}\n\n/** Walls that are safe to traverse: real nodes at both ends, and long enough to have an angle. */\nfunction usableWalls(floor: PlanFloor): PlanWall[] {\n return Object.values(floor.walls).filter((w) => {\n if (w.a === w.b) return false;\n const a = floor.nodes[w.a];\n const b = floor.nodes[w.b];\n if (!a || !b) return false;\n return dist(a, b) >= MIN_WALL_LENGTH;\n });\n}\n\nfunction buildDartGraph(floor: PlanFloor): DartGraph {\n const walls = usableWalls(floor);\n const darts: Dart[] = new Array(walls.length * 2);\n\n for (let i = 0; i < walls.length; i++) {\n const w = walls[i];\n darts[2 * i] = { wallId: w.id, from: w.a, to: w.b };\n darts[2 * i + 1] = { wallId: w.id, from: w.b, to: w.a };\n }\n\n const nodeOf = (id: string): PlanNode => floor.nodes[id];\n\n const outgoing = new Map<string, number[]>();\n for (let d = 0; d < darts.length; d++) {\n const list = outgoing.get(darts[d].from);\n if (list) list.push(d);\n else outgoing.set(darts[d].from, [d]);\n }\n\n const angleOf = (d: number): number => {\n const from = nodeOf(darts[d].from);\n const to = nodeOf(darts[d].to);\n return Math.atan2(to.y - from.y, to.x - from.x);\n };\n\n for (const ring of outgoing.values()) {\n ring.sort((x, y) => {\n const ax = angleOf(x);\n const ay = angleOf(y);\n if (ax !== ay) return ax - ay;\n // Deterministic tie-break. A genuine tie means two walls leave this node in the\n // same direction, which validatePlan reports as OverlappingWalls: the sort stays\n // stable so the walk cannot hang, but the resulting face is still wrong.\n const wx = darts[x].wallId;\n const wy = darts[y].wallId;\n if (wx !== wy) return wx < wy ? -1 : 1;\n return x - y;\n });\n }\n\n const ringIndex = new Int32Array(darts.length);\n for (const ring of outgoing.values()) {\n for (let i = 0; i < ring.length; i++) ringIndex[ring[i]] = i;\n }\n\n return { darts, outgoing, ringIndex, nodeOf };\n}\n\nfunction nextDart(g: DartGraph, d: number): number {\n const ring = g.outgoing.get(g.darts[d].to);\n /* c8 ignore next -- head() always has the twin in its ring by construction */\n if (!ring || ring.length === 0) return d ^ 1;\n const i = g.ringIndex[d ^ 1];\n return ring[(i - 1 + ring.length) % ring.length];\n}\n\n/** Every dart belongs to exactly one cycle. */\nfunction walkCycles(g: DartGraph): number[][] {\n const visited = new Uint8Array(g.darts.length);\n const cycles: number[][] = [];\n\n for (let d0 = 0; d0 < g.darts.length; d0++) {\n if (visited[d0]) continue;\n const cycle: number[] = [];\n let d = d0;\n let guard = 0;\n do {\n visited[d] = 1;\n cycle.push(d);\n d = nextDart(g, d);\n // Only reachable if the dart permutation is corrupt (duplicate walls producing\n // an exact angular tie). Bail rather than spin.\n if (++guard > g.darts.length + 1) break;\n } while (d !== d0 && !visited[d]);\n cycles.push(cycle);\n }\n\n return cycles;\n}\n\n/** Union-find over node ids, joined by each wall. */\nfunction connectedComponents(g: DartGraph): Map<string, string> {\n const parent = new Map<string, string>();\n const find = (x: string): string => {\n let root = x;\n while (parent.get(root) !== root) root = parent.get(root) ?? root;\n let cur = x;\n while (parent.get(cur) !== root) {\n const nxt = parent.get(cur) ?? root;\n parent.set(cur, root);\n cur = nxt;\n }\n return root;\n };\n\n for (const nodeId of g.outgoing.keys()) parent.set(nodeId, nodeId);\n for (let d = 0; d < g.darts.length; d += 2) {\n const ra = find(g.darts[d].from);\n const rb = find(g.darts[d].to);\n if (ra !== rb) parent.set(ra, rb);\n }\n\n const out = new Map<string, string>();\n for (const nodeId of g.outgoing.keys()) out.set(nodeId, find(nodeId));\n return out;\n}\n\nfunction cycleRing(g: DartGraph, cycle: number[]): PlanPoint[] {\n return cycle.map((d) => {\n const n = g.nodeOf(g.darts[d].from);\n return { x: n.x, y: n.y };\n });\n}\n\n/**\n * A face id derived from the boundary itself, so it stays stable across derives for as\n * long as the geometry is unchanged.\n */\nfunction faceId(g: DartGraph, cycle: number[]): string {\n let best = '';\n for (const d of cycle) {\n const key = `${g.darts[d].wallId}>${g.darts[d].from}`;\n if (best === '' || key < best) best = key;\n }\n return `f:${best}`;\n}\n\n/**\n * Extracts every enclosed face from the wall graph, with nested components attached as\n * holes.\n *\n * Returned faces are positively wound (visually clockwise in SVG space) and their\n * `area` already has holes subtracted.\n */\nexport function deriveFaces(floor: PlanFloor): PlanFace[] {\n const g = buildDartGraph(floor);\n if (g.darts.length === 0) return [];\n\n const componentOf = connectedComponents(g);\n const cycles = walkCycles(g);\n\n interface Candidate {\n face: PlanFace;\n component: string;\n outerArea: number;\n }\n\n const candidates: Candidate[] = [];\n /** One outer (negatively wound) cycle per connected component. */\n const outerRings = new Map<string, PlanPoint[]>();\n\n for (const cycle of cycles) {\n const ring = cycleRing(g, cycle);\n const area = signedArea(ring);\n const component = componentOf.get(g.darts[cycle[0]].from) ?? '';\n\n if (area > EPS_AREA) {\n candidates.push({\n face: {\n id: faceId(g, cycle),\n outer: ring,\n holes: [],\n area,\n perimeter: ringPerimeter(ring),\n wallIds: cycle.map((d) => g.darts[d].wallId),\n nodeIds: cycle.map((d) => g.darts[d].from),\n },\n component,\n outerArea: area,\n });\n } else if (area < -EPS_AREA) {\n // This component's outer boundary. Kept at its negative winding, which is what\n // `PlanFace.holes` promises: opposite to the parent's positively wound `outer`.\n outerRings.set(component, ring);\n }\n // |area| <= EPS_AREA encloses nothing: a tree component or an isolated stub.\n }\n\n // Attach each component's outer boundary as a hole in the smallest face from a\n // different component that encloses it. Smallest-containing-area is what makes\n // arbitrary nesting depth work: with A > B > C, C's ring goes to B, never to A.\n for (const [component, ring] of outerRings) {\n const ringArea = Math.abs(signedArea(ring));\n if (ringArea <= EPS_AREA) continue;\n // An interior probe rather than a vertex, so two components that happen to touch\n // do not produce an on-boundary coin flip.\n const probe = representativePoint(ring);\n let parent: Candidate | null = null;\n for (const cand of candidates) {\n if (cand.component === component) continue;\n // The probe test alone is not sufficient: with one component nested inside\n // another, the OUTER component's probe also falls inside the inner component's\n // face, which would attach the big room as a hole of the small one. A face can\n // only enclose a ring smaller than itself.\n if (cand.outerArea <= ringArea) continue;\n if (!pointInRing(probe, cand.face.outer)) continue;\n if (!parent || cand.outerArea < parent.outerArea) parent = cand;\n }\n // No parent simply means this is a top-level component.\n if (parent) parent.face.holes.push(ring);\n }\n\n for (const cand of candidates) {\n let area = cand.outerArea;\n for (const hole of cand.face.holes) area -= Math.abs(signedArea(hole));\n cand.face.area = area;\n }\n\n return candidates.map((c) => c.face);\n}\n\n/**\n * Euler's formula per connected component: `V - E + F === 2C`, counting every cycle as\n * a face, including each component's own outer boundary.\n *\n * Note it is `2C`, not the `1 + C` of a single connected embedding. The dart walk\n * cannot bridge between disconnected components, so the region between two of them is\n * produced once per component rather than once in total.\n *\n * Exported for tests and dev assertions: it is the cheapest whole-graph check that the\n * dart permutation is intact.\n */\nexport function eulerCheck(floor: PlanFloor): {\n v: number;\n e: number;\n f: number;\n c: number;\n holds: boolean;\n} {\n const g = buildDartGraph(floor);\n const componentOf = connectedComponents(g);\n const v = g.outgoing.size;\n const e = g.darts.length / 2;\n const f = walkCycles(g).length;\n const c = new Set(componentOf.values()).size;\n return { v, e, f, c, holds: v - e + f === 2 * c };\n}\n\n/** Test/diagnostic helper: how many cycles each dart appears in. Must be all 1s. */\nexport function dartCycleCounts(floor: PlanFloor): number[] {\n const g = buildDartGraph(floor);\n const counts = new Array<number>(g.darts.length).fill(0);\n for (const cycle of walkCycles(g)) for (const d of cycle) counts[d] += 1;\n return counts;\n}\n\n/** Degree of each node, by id. Used by wall-path chaining and by validation. */\nexport function nodeDegrees(floor: PlanFloor): Map<string, number> {\n const degrees = new Map<string, number>();\n for (const w of usableWalls(floor)) {\n degrees.set(w.a, (degrees.get(w.a) ?? 0) + 1);\n degrees.set(w.b, (degrees.get(w.b) ?? 0) + 1);\n }\n return degrees;\n}\n\nexport { areaCentroid };\n"],"mappings":"yHAiDA,SAAS,EAAY,EAA8B,CACjD,OAAO,OAAO,OAAO,EAAM,KAAK,CAAC,CAAC,OAAQ,GAAM,CAC9C,GAAI,EAAE,IAAM,EAAE,EAAG,MAAO,GACxB,IAAM,EAAI,EAAM,MAAM,EAAE,GAClB,EAAI,EAAM,MAAM,EAAE,GAExB,MADI,CAAC,GAAK,CAAC,EAAU,GACd,EAAK,EAAG,CAAC,GAAA,EAClB,CAAC,CACH,CAEA,SAAS,EAAe,EAA6B,CACnD,IAAM,EAAQ,EAAY,CAAK,EACzB,EAAoB,MAAM,EAAM,OAAS,CAAC,EAEhD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACrC,IAAM,EAAI,EAAM,GAChB,EAAM,EAAI,GAAK,CAAE,OAAQ,EAAE,GAAI,KAAM,EAAE,EAAG,GAAI,EAAE,CAAE,EAClD,EAAM,EAAI,EAAI,GAAK,CAAE,OAAQ,EAAE,GAAI,KAAM,EAAE,EAAG,GAAI,EAAE,CAAE,CACxD,CAEA,IAAM,EAAU,GAAyB,EAAM,MAAM,GAE/C,EAAW,IAAI,IACrB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACrC,IAAM,EAAO,EAAS,IAAI,EAAM,EAAE,CAAC,IAAI,EACnC,EAAM,EAAK,KAAK,CAAC,EAChB,EAAS,IAAI,EAAM,EAAE,CAAC,KAAM,CAAC,CAAC,CAAC,CACtC,CAEA,IAAM,EAAW,GAAsB,CACrC,IAAM,EAAO,EAAO,EAAM,EAAE,CAAC,IAAI,EAC3B,EAAK,EAAO,EAAM,EAAE,CAAC,EAAE,EAC7B,OAAO,KAAK,MAAM,EAAG,EAAI,EAAK,EAAG,EAAG,EAAI,EAAK,CAAC,CAChD,EAEA,IAAK,IAAM,KAAQ,EAAS,OAAO,EACjC,EAAK,MAAM,EAAG,IAAM,CAClB,IAAM,EAAK,EAAQ,CAAC,EACd,EAAK,EAAQ,CAAC,EACpB,GAAI,IAAO,EAAI,OAAO,EAAK,EAI3B,IAAM,EAAK,EAAM,EAAE,CAAC,OACd,EAAK,EAAM,EAAE,CAAC,OAEpB,OADI,IAAO,EACJ,EAAI,EADW,EAAK,EAAK,GAAK,CAEvC,CAAC,EAGH,IAAM,EAAY,IAAI,WAAW,EAAM,MAAM,EAC7C,IAAK,IAAM,KAAQ,EAAS,OAAO,EACjC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,EAAU,EAAK,IAAM,EAG7D,MAAO,CAAE,QAAO,WAAU,YAAW,QAAO,CAC9C,CAEA,SAAS,EAAS,EAAc,EAAmB,CACjD,IAAM,EAAO,EAAE,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,EAIzC,MAFI,CAAC,GAAQ,EAAK,SAAW,EAAU,EAAI,EAEpC,GADG,EAAE,UAAU,EAAI,GACT,EAAI,EAAK,QAAU,EAAK,OAC3C,CAGA,SAAS,EAAW,EAA0B,CAC5C,IAAM,EAAU,IAAI,WAAW,EAAE,MAAM,MAAM,EACvC,EAAqB,CAAC,EAE5B,IAAK,IAAI,EAAK,EAAG,EAAK,EAAE,MAAM,OAAQ,IAAM,CAC1C,GAAI,EAAQ,GAAK,SACjB,IAAM,EAAkB,CAAC,EACrB,EAAI,EACJ,EAAQ,EACZ,GAME,GALA,EAAQ,GAAK,EACb,EAAM,KAAK,CAAC,EACZ,EAAI,EAAS,EAAG,CAAC,EAGb,EAAE,EAAQ,EAAE,MAAM,OAAS,EAAG,YAC3B,IAAM,GAAM,CAAC,EAAQ,IAC9B,EAAO,KAAK,CAAK,CACnB,CAEA,OAAO,CACT,CAGA,SAAS,EAAoB,EAAmC,CAC9D,IAAM,EAAS,IAAI,IACb,EAAQ,GAAsB,CAClC,IAAI,EAAO,EACX,KAAO,EAAO,IAAI,CAAI,IAAM,GAAM,EAAO,EAAO,IAAI,CAAI,GAAK,EAC7D,IAAI,EAAM,EACV,KAAO,EAAO,IAAI,CAAG,IAAM,GAAM,CAC/B,IAAM,EAAM,EAAO,IAAI,CAAG,GAAK,EAC/B,EAAO,IAAI,EAAK,CAAI,EACpB,EAAM,CACR,CACA,OAAO,CACT,EAEA,IAAK,IAAM,KAAU,EAAE,SAAS,KAAK,EAAG,EAAO,IAAI,EAAQ,CAAM,EACjE,IAAK,IAAI,EAAI,EAAG,EAAI,EAAE,MAAM,OAAQ,GAAK,EAAG,CAC1C,IAAM,EAAK,EAAK,EAAE,MAAM,EAAE,CAAC,IAAI,EACzB,EAAK,EAAK,EAAE,MAAM,EAAE,CAAC,EAAE,EACzB,IAAO,GAAI,EAAO,IAAI,EAAI,CAAE,CAClC,CAEA,IAAM,EAAM,IAAI,IAChB,IAAK,IAAM,KAAU,EAAE,SAAS,KAAK,EAAG,EAAI,IAAI,EAAQ,EAAK,CAAM,CAAC,EACpE,OAAO,CACT,CAEA,SAAS,EAAU,EAAc,EAA8B,CAC7D,OAAO,EAAM,IAAK,GAAM,CACtB,IAAM,EAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,IAAI,EAClC,MAAO,CAAE,EAAG,EAAE,EAAG,EAAG,EAAE,CAAE,CAC1B,CAAC,CACH,CAMA,SAAS,EAAO,EAAc,EAAyB,CACrD,IAAI,EAAO,GACX,IAAK,IAAM,KAAK,EAAO,CACrB,IAAM,EAAM,GAAG,EAAE,MAAM,EAAE,CAAC,OAAO,GAAG,EAAE,MAAM,EAAE,CAAC,QAC3C,IAAS,IAAM,EAAM,KAAM,EAAO,EACxC,CACA,MAAO,KAAK,GACd,CASA,SAAgB,EAAY,EAA8B,CACxD,IAAM,EAAI,EAAe,CAAK,EAC9B,GAAI,EAAE,MAAM,SAAW,EAAG,MAAO,CAAC,EAElC,IAAM,EAAc,EAAoB,CAAC,EACnC,EAAS,EAAW,CAAC,EAQrB,EAA0B,CAAC,EAE3B,EAAa,IAAI,IAEvB,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAO,EAAU,EAAG,CAAK,EACzB,EAAO,EAAW,CAAI,EACtB,EAAY,EAAY,IAAI,EAAE,MAAM,EAAM,GAAG,CAAC,IAAI,GAAK,GAEzD,EAAA,IACF,EAAW,KAAK,CACd,KAAM,CACJ,GAAI,EAAO,EAAG,CAAK,EACnB,MAAO,EACP,MAAO,CAAC,EACR,OACA,UAAW,EAAc,CAAI,EAC7B,QAAS,EAAM,IAAK,GAAM,EAAE,MAAM,EAAE,CAAC,MAAM,EAC3C,QAAS,EAAM,IAAK,GAAM,EAAE,MAAM,EAAE,CAAC,IAAI,CAC3C,EACA,YACA,UAAW,CACb,CAAC,EACQ,EAAO,MAGhB,EAAW,IAAI,EAAW,CAAI,CAGlC,CAKA,IAAK,GAAM,CAAC,EAAW,KAAS,EAAY,CAC1C,IAAM,EAAW,KAAK,IAAI,EAAW,CAAI,CAAC,EAC1C,GAAI,GAAA,IAAsB,SAG1B,IAAM,EAAQ,EAAoB,CAAI,EAClC,EAA2B,KAC/B,IAAK,IAAM,KAAQ,EACb,EAAK,YAAc,IAKnB,EAAK,WAAa,GACjB,EAAY,EAAO,EAAK,KAAK,KAAK,IACnC,CAAC,GAAU,EAAK,UAAY,EAAO,aAAW,EAAS,IAGzD,GAAQ,EAAO,KAAK,MAAM,KAAK,CAAI,CACzC,CAEA,IAAK,IAAM,KAAQ,EAAY,CAC7B,IAAI,EAAO,EAAK,UAChB,IAAK,IAAM,KAAQ,EAAK,KAAK,MAAO,GAAQ,KAAK,IAAI,EAAW,CAAI,CAAC,EACrE,EAAK,KAAK,KAAO,CACnB,CAEA,OAAO,EAAW,IAAK,GAAM,EAAE,IAAI,CACrC,CAsCA,SAAgB,EAAY,EAAuC,CACjE,IAAM,EAAU,IAAI,IACpB,IAAK,IAAM,KAAK,EAAY,CAAK,EAC/B,EAAQ,IAAI,EAAE,GAAI,EAAQ,IAAI,EAAE,CAAC,GAAK,GAAK,CAAC,EAC5C,EAAQ,IAAI,EAAE,GAAI,EAAQ,IAAI,EAAE,CAAC,GAAK,GAAK,CAAC,EAE9C,OAAO,CACT"}
1
+ {"version":3,"file":"floorPlan.faces.mjs","names":[],"sources":["../../../../package/common/floorPlan/floorPlan.faces.ts"],"sourcesContent":["/**\n * Planar face extraction: turns the wall graph into the enclosed rooms it forms.\n *\n * Each wall becomes two darts (directed half-edges), wall `i` owning darts `2i` and\n * `2i + 1`, so `twin(d) === d ^ 1` with no lookup. The darts leaving each node are\n * sorted by angle, and the face walk is:\n *\n * next(d) = the cyclic PREDECESSOR of twin(d) in the sorted ring at head(d)\n *\n * Because the shoelace sum runs on the same raw coordinates as the angular sort, the\n * rule needs no orientation convention: bounded faces come out with POSITIVE signed\n * area and each connected component's outer boundary comes out negative, whichever way\n * you read the y axis.\n *\n * A dangling wall (one endpoint of degree 1) is handled by the rule itself: a\n * one-element ring's cyclic predecessor is itself, so the walk goes out along the spur\n * and straight back. Both of its darts land in the same cycle, no extra face appears,\n * and the face gains a zero-width slit that contributes nothing to area, fill or\n * point-in-polygon.\n */\n\nimport {\n EPS_AREA,\n MIN_WALL_LENGTH,\n areaCentroid,\n dist,\n perimeter as ringPerimeter,\n pointInRing,\n representativePoint,\n signedArea,\n} from './floorPlan.geometry';\nimport type { PlanFace, PlanFloor, PlanNode, PlanPoint, PlanWall } from './floorPlan.types';\n\ninterface Dart {\n wallId: string;\n from: string;\n to: string;\n}\n\ninterface DartGraph {\n darts: Dart[];\n /** Node id to its outgoing dart indices, sorted by angle ascending. */\n outgoing: Map<string, number[]>;\n /** Dart index to its position within its own outgoing ring. */\n ringIndex: Int32Array;\n nodeOf: (id: string) => PlanNode;\n}\n\n/** Walls that are safe to traverse: real nodes at both ends, and long enough to have an angle. */\nfunction usableWalls(floor: PlanFloor): PlanWall[] {\n return Object.values(floor.walls).filter((w) => {\n if (w.a === w.b) return false;\n const a = floor.nodes[w.a];\n const b = floor.nodes[w.b];\n if (!a || !b) return false;\n return dist(a, b) >= MIN_WALL_LENGTH;\n });\n}\n\nfunction buildDartGraph(floor: PlanFloor): DartGraph {\n const walls = usableWalls(floor);\n const darts: Dart[] = new Array(walls.length * 2);\n\n for (let i = 0; i < walls.length; i++) {\n const w = walls[i];\n darts[2 * i] = { wallId: w.id, from: w.a, to: w.b };\n darts[2 * i + 1] = { wallId: w.id, from: w.b, to: w.a };\n }\n\n const nodeOf = (id: string): PlanNode => floor.nodes[id];\n\n const outgoing = new Map<string, number[]>();\n for (let d = 0; d < darts.length; d++) {\n const list = outgoing.get(darts[d].from);\n if (list) list.push(d);\n else outgoing.set(darts[d].from, [d]);\n }\n\n const angleOf = (d: number): number => {\n const from = nodeOf(darts[d].from);\n const to = nodeOf(darts[d].to);\n return Math.atan2(to.y - from.y, to.x - from.x);\n };\n\n for (const ring of outgoing.values()) {\n ring.sort((x, y) => {\n const ax = angleOf(x);\n const ay = angleOf(y);\n if (ax !== ay) return ax - ay;\n // Deterministic tie-break. A genuine tie means two walls leave this node in the\n // same direction, which validatePlan reports as OverlappingWalls: the sort stays\n // stable so the walk cannot hang, but the resulting face is still wrong.\n const wx = darts[x].wallId;\n const wy = darts[y].wallId;\n if (wx !== wy) return wx < wy ? -1 : 1;\n return x - y;\n });\n }\n\n const ringIndex = new Int32Array(darts.length);\n for (const ring of outgoing.values()) {\n for (let i = 0; i < ring.length; i++) ringIndex[ring[i]] = i;\n }\n\n return { darts, outgoing, ringIndex, nodeOf };\n}\n\nfunction nextDart(g: DartGraph, d: number): number {\n const ring = g.outgoing.get(g.darts[d].to);\n /* c8 ignore next -- head() always has the twin in its ring by construction */\n if (!ring || ring.length === 0) return d ^ 1;\n const i = g.ringIndex[d ^ 1];\n return ring[(i - 1 + ring.length) % ring.length];\n}\n\n/** Every dart belongs to exactly one cycle. */\nfunction walkCycles(g: DartGraph): number[][] {\n const visited = new Uint8Array(g.darts.length);\n const cycles: number[][] = [];\n\n for (let d0 = 0; d0 < g.darts.length; d0++) {\n if (visited[d0]) continue;\n const cycle: number[] = [];\n let d = d0;\n let guard = 0;\n do {\n visited[d] = 1;\n cycle.push(d);\n d = nextDart(g, d);\n // Only reachable if the dart permutation is corrupt (duplicate walls producing\n // an exact angular tie). Bail rather than spin.\n if (++guard > g.darts.length + 1) break;\n } while (d !== d0 && !visited[d]);\n cycles.push(cycle);\n }\n\n return cycles;\n}\n\n/** Union-find over node ids, joined by each wall. */\nfunction connectedComponents(g: DartGraph): Map<string, string> {\n const parent = new Map<string, string>();\n const find = (x: string): string => {\n let root = x;\n while (parent.get(root) !== root) root = parent.get(root) ?? root;\n let cur = x;\n while (parent.get(cur) !== root) {\n const nxt = parent.get(cur) ?? root;\n parent.set(cur, root);\n cur = nxt;\n }\n return root;\n };\n\n for (const nodeId of g.outgoing.keys()) parent.set(nodeId, nodeId);\n for (let d = 0; d < g.darts.length; d += 2) {\n const ra = find(g.darts[d].from);\n const rb = find(g.darts[d].to);\n if (ra !== rb) parent.set(ra, rb);\n }\n\n const out = new Map<string, string>();\n for (const nodeId of g.outgoing.keys()) out.set(nodeId, find(nodeId));\n return out;\n}\n\nfunction cycleRing(g: DartGraph, cycle: number[]): PlanPoint[] {\n return cycle.map((d) => {\n const n = g.nodeOf(g.darts[d].from);\n return { x: n.x, y: n.y };\n });\n}\n\n/**\n * A face id derived from the boundary itself, so it stays stable across derives for as\n * long as the geometry is unchanged.\n */\nfunction faceId(g: DartGraph, cycle: number[]): string {\n let best = '';\n for (const d of cycle) {\n const key = `${g.darts[d].wallId}>${g.darts[d].from}`;\n if (best === '' || key < best) best = key;\n }\n return `f:${best}`;\n}\n\n/**\n * Extracts every enclosed face from the wall graph, with nested components attached as\n * holes.\n *\n * Returned faces are positively wound (visually clockwise in SVG space) and their\n * `area` already has holes subtracted.\n */\nexport function deriveFaces(floor: PlanFloor): PlanFace[] {\n const g = buildDartGraph(floor);\n if (g.darts.length === 0) return [];\n\n const componentOf = connectedComponents(g);\n const cycles = walkCycles(g);\n\n interface Candidate {\n face: PlanFace;\n component: string;\n outerArea: number;\n }\n\n const candidates: Candidate[] = [];\n /** One outer (negatively wound) cycle per connected component. */\n const outerRings = new Map<string, PlanPoint[]>();\n\n for (const cycle of cycles) {\n const ring = cycleRing(g, cycle);\n const area = signedArea(ring);\n const component = componentOf.get(g.darts[cycle[0]].from) ?? '';\n\n if (area > EPS_AREA) {\n candidates.push({\n face: {\n id: faceId(g, cycle),\n outer: ring,\n holes: [],\n area,\n perimeter: ringPerimeter(ring),\n wallIds: cycle.map((d) => g.darts[d].wallId),\n nodeIds: cycle.map((d) => g.darts[d].from),\n },\n component,\n outerArea: area,\n });\n } else if (area < -EPS_AREA) {\n // This component's outer boundary. Kept at its negative winding, which is what\n // `PlanFace.holes` promises: opposite to the parent's positively wound `outer`.\n outerRings.set(component, ring);\n }\n // |area| <= EPS_AREA encloses nothing: a tree component or an isolated stub.\n }\n\n // Attach each component's outer boundary as a hole in the smallest face from a\n // different component that encloses it. Smallest-containing-area is what makes\n // arbitrary nesting depth work: with A > B > C, C's ring goes to B, never to A.\n for (const [component, ring] of outerRings) {\n const ringArea = Math.abs(signedArea(ring));\n if (ringArea <= EPS_AREA) continue;\n // An interior probe rather than a vertex, so two components that happen to touch\n // do not produce an on-boundary coin flip.\n const probe = representativePoint(ring);\n let parent: Candidate | null = null;\n for (const cand of candidates) {\n if (cand.component === component) continue;\n // The probe test alone is not sufficient: with one component nested inside\n // another, the OUTER component's probe also falls inside the inner component's\n // face, which would attach the big room as a hole of the small one. A face can\n // only enclose a ring smaller than itself.\n if (cand.outerArea <= ringArea) continue;\n if (!pointInRing(probe, cand.face.outer)) continue;\n if (!parent || cand.outerArea < parent.outerArea) parent = cand;\n }\n // No parent simply means this is a top-level component.\n if (parent) parent.face.holes.push(ring);\n }\n\n for (const cand of candidates) {\n let area = cand.outerArea;\n for (const hole of cand.face.holes) area -= Math.abs(signedArea(hole));\n cand.face.area = area;\n }\n\n return candidates.map((c) => c.face);\n}\n\n/**\n * Euler's formula per connected component: `V - E + F === 2C`, counting every cycle as\n * a face, including each component's own outer boundary.\n *\n * Note it is `2C`, not the `1 + C` of a single connected embedding. The dart walk\n * cannot bridge between disconnected components, so the region between two of them is\n * produced once per component rather than once in total.\n *\n * Exported for tests and dev assertions: it is the cheapest whole-graph check that the\n * dart permutation is intact.\n */\nexport function eulerCheck(floor: PlanFloor): {\n v: number;\n e: number;\n f: number;\n c: number;\n holds: boolean;\n} {\n const g = buildDartGraph(floor);\n const componentOf = connectedComponents(g);\n const v = g.outgoing.size;\n const e = g.darts.length / 2;\n const f = walkCycles(g).length;\n const c = new Set(componentOf.values()).size;\n return { v, e, f, c, holds: v - e + f === 2 * c };\n}\n\n/** Test/diagnostic helper: how many cycles each dart appears in. Must be all 1s. */\nexport function dartCycleCounts(floor: PlanFloor): number[] {\n const g = buildDartGraph(floor);\n const counts = new Array<number>(g.darts.length).fill(0);\n for (const cycle of walkCycles(g)) for (const d of cycle) counts[d] += 1;\n return counts;\n}\n\n/** Degree of each node, by id. Used by wall-path chaining and by validation. */\nexport function nodeDegrees(floor: PlanFloor): Map<string, number> {\n const degrees = new Map<string, number>();\n for (const w of usableWalls(floor)) {\n degrees.set(w.a, (degrees.get(w.a) ?? 0) + 1);\n degrees.set(w.b, (degrees.get(w.b) ?? 0) + 1);\n }\n return degrees;\n}\n\nexport { areaCentroid };\n"],"mappings":"sIAiDA,SAAS,EAAY,EAA8B,CACjD,OAAO,OAAO,OAAO,EAAM,KAAK,CAAC,CAAC,OAAQ,GAAM,CAC9C,GAAI,EAAE,IAAM,EAAE,EAAG,MAAO,GACxB,IAAM,EAAI,EAAM,MAAM,EAAE,GAClB,EAAI,EAAM,MAAM,EAAE,GAExB,MADI,CAAC,GAAK,CAAC,EAAU,GACd,EAAK,EAAG,CAAC,GAAA,EAClB,CAAC,CACH,CAEA,SAAS,EAAe,EAA6B,CACnD,IAAM,EAAQ,EAAY,CAAK,EACzB,EAAoB,MAAM,EAAM,OAAS,CAAC,EAEhD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACrC,IAAM,EAAI,EAAM,GAChB,EAAM,EAAI,GAAK,CAAE,OAAQ,EAAE,GAAI,KAAM,EAAE,EAAG,GAAI,EAAE,CAAE,EAClD,EAAM,EAAI,EAAI,GAAK,CAAE,OAAQ,EAAE,GAAI,KAAM,EAAE,EAAG,GAAI,EAAE,CAAE,CACxD,CAEA,IAAM,EAAU,GAAyB,EAAM,MAAM,GAE/C,EAAW,IAAI,IACrB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACrC,IAAM,EAAO,EAAS,IAAI,EAAM,EAAE,CAAC,IAAI,EACnC,EAAM,EAAK,KAAK,CAAC,EAChB,EAAS,IAAI,EAAM,EAAE,CAAC,KAAM,CAAC,CAAC,CAAC,CACtC,CAEA,IAAM,EAAW,GAAsB,CACrC,IAAM,EAAO,EAAO,EAAM,EAAE,CAAC,IAAI,EAC3B,EAAK,EAAO,EAAM,EAAE,CAAC,EAAE,EAC7B,OAAO,KAAK,MAAM,EAAG,EAAI,EAAK,EAAG,EAAG,EAAI,EAAK,CAAC,CAChD,EAEA,IAAK,IAAM,KAAQ,EAAS,OAAO,EACjC,EAAK,MAAM,EAAG,IAAM,CAClB,IAAM,EAAK,EAAQ,CAAC,EACd,EAAK,EAAQ,CAAC,EACpB,GAAI,IAAO,EAAI,OAAO,EAAK,EAI3B,IAAM,EAAK,EAAM,EAAE,CAAC,OACd,EAAK,EAAM,EAAE,CAAC,OAEpB,OADI,IAAO,EACJ,EAAI,EADW,EAAK,EAAK,GAAK,CAEvC,CAAC,EAGH,IAAM,EAAY,IAAI,WAAW,EAAM,MAAM,EAC7C,IAAK,IAAM,KAAQ,EAAS,OAAO,EACjC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,EAAU,EAAK,IAAM,EAG7D,MAAO,CAAE,QAAO,WAAU,YAAW,QAAO,CAC9C,CAEA,SAAS,EAAS,EAAc,EAAmB,CACjD,IAAM,EAAO,EAAE,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,EAIzC,MAFI,CAAC,GAAQ,EAAK,SAAW,EAAU,EAAI,EAEpC,GADG,EAAE,UAAU,EAAI,GACT,EAAI,EAAK,QAAU,EAAK,OAC3C,CAGA,SAAS,EAAW,EAA0B,CAC5C,IAAM,EAAU,IAAI,WAAW,EAAE,MAAM,MAAM,EACvC,EAAqB,CAAC,EAE5B,IAAK,IAAI,EAAK,EAAG,EAAK,EAAE,MAAM,OAAQ,IAAM,CAC1C,GAAI,EAAQ,GAAK,SACjB,IAAM,EAAkB,CAAC,EACrB,EAAI,EACJ,EAAQ,EACZ,GAME,GALA,EAAQ,GAAK,EACb,EAAM,KAAK,CAAC,EACZ,EAAI,EAAS,EAAG,CAAC,EAGb,EAAE,EAAQ,EAAE,MAAM,OAAS,EAAG,YAC3B,IAAM,GAAM,CAAC,EAAQ,IAC9B,EAAO,KAAK,CAAK,CACnB,CAEA,OAAO,CACT,CAGA,SAAS,EAAoB,EAAmC,CAC9D,IAAM,EAAS,IAAI,IACb,EAAQ,GAAsB,CAClC,IAAI,EAAO,EACX,KAAO,EAAO,IAAI,CAAI,IAAM,GAAM,EAAO,EAAO,IAAI,CAAI,GAAK,EAC7D,IAAI,EAAM,EACV,KAAO,EAAO,IAAI,CAAG,IAAM,GAAM,CAC/B,IAAM,EAAM,EAAO,IAAI,CAAG,GAAK,EAC/B,EAAO,IAAI,EAAK,CAAI,EACpB,EAAM,CACR,CACA,OAAO,CACT,EAEA,IAAK,IAAM,KAAU,EAAE,SAAS,KAAK,EAAG,EAAO,IAAI,EAAQ,CAAM,EACjE,IAAK,IAAI,EAAI,EAAG,EAAI,EAAE,MAAM,OAAQ,GAAK,EAAG,CAC1C,IAAM,EAAK,EAAK,EAAE,MAAM,EAAE,CAAC,IAAI,EACzB,EAAK,EAAK,EAAE,MAAM,EAAE,CAAC,EAAE,EACzB,IAAO,GAAI,EAAO,IAAI,EAAI,CAAE,CAClC,CAEA,IAAM,EAAM,IAAI,IAChB,IAAK,IAAM,KAAU,EAAE,SAAS,KAAK,EAAG,EAAI,IAAI,EAAQ,EAAK,CAAM,CAAC,EACpE,OAAO,CACT,CAEA,SAAS,EAAU,EAAc,EAA8B,CAC7D,OAAO,EAAM,IAAK,GAAM,CACtB,IAAM,EAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,IAAI,EAClC,MAAO,CAAE,EAAG,EAAE,EAAG,EAAG,EAAE,CAAE,CAC1B,CAAC,CACH,CAMA,SAAS,EAAO,EAAc,EAAyB,CACrD,IAAI,EAAO,GACX,IAAK,IAAM,KAAK,EAAO,CACrB,IAAM,EAAM,GAAG,EAAE,MAAM,EAAE,CAAC,OAAO,GAAG,EAAE,MAAM,EAAE,CAAC,QAC3C,IAAS,IAAM,EAAM,KAAM,EAAO,EACxC,CACA,MAAO,KAAK,GACd,CASA,SAAgB,EAAY,EAA8B,CACxD,IAAM,EAAI,EAAe,CAAK,EAC9B,GAAI,EAAE,MAAM,SAAW,EAAG,MAAO,CAAC,EAElC,IAAM,EAAc,EAAoB,CAAC,EACnC,EAAS,EAAW,CAAC,EAQrB,EAA0B,CAAC,EAE3B,EAAa,IAAI,IAEvB,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAO,EAAU,EAAG,CAAK,EACzB,EAAO,EAAW,CAAI,EACtB,EAAY,EAAY,IAAI,EAAE,MAAM,EAAM,GAAG,CAAC,IAAI,GAAK,GAEzD,EAAA,IACF,EAAW,KAAK,CACd,KAAM,CACJ,GAAI,EAAO,EAAG,CAAK,EACnB,MAAO,EACP,MAAO,CAAC,EACR,OACA,UAAW,EAAc,CAAI,EAC7B,QAAS,EAAM,IAAK,GAAM,EAAE,MAAM,EAAE,CAAC,MAAM,EAC3C,QAAS,EAAM,IAAK,GAAM,EAAE,MAAM,EAAE,CAAC,IAAI,CAC3C,EACA,YACA,UAAW,CACb,CAAC,EACQ,EAAO,MAGhB,EAAW,IAAI,EAAW,CAAI,CAGlC,CAKA,IAAK,GAAM,CAAC,EAAW,KAAS,EAAY,CAC1C,IAAM,EAAW,KAAK,IAAI,EAAW,CAAI,CAAC,EAC1C,GAAI,GAAA,IAAsB,SAG1B,IAAM,EAAQ,EAAoB,CAAI,EAClC,EAA2B,KAC/B,IAAK,IAAM,KAAQ,EACb,EAAK,YAAc,IAKnB,EAAK,WAAa,GACjB,EAAY,EAAO,EAAK,KAAK,KAAK,IACnC,CAAC,GAAU,EAAK,UAAY,EAAO,aAAW,EAAS,IAGzD,GAAQ,EAAO,KAAK,MAAM,KAAK,CAAI,CACzC,CAEA,IAAK,IAAM,KAAQ,EAAY,CAC7B,IAAI,EAAO,EAAK,UAChB,IAAK,IAAM,KAAQ,EAAK,KAAK,MAAO,GAAQ,KAAK,IAAI,EAAW,CAAI,CAAC,EACrE,EAAK,KAAK,KAAO,CACnB,CAEA,OAAO,EAAW,IAAK,GAAM,EAAE,IAAI,CACrC,CAsCA,SAAgB,EAAY,EAAuC,CACjE,IAAM,EAAU,IAAI,IACpB,IAAK,IAAM,KAAK,EAAY,CAAK,EAC/B,EAAQ,IAAI,EAAE,GAAI,EAAQ,IAAI,EAAE,CAAC,GAAK,GAAK,CAAC,EAC5C,EAAQ,IAAI,EAAE,GAAI,EAAQ,IAAI,EAAE,CAAC,GAAK,GAAK,CAAC,EAE9C,OAAO,CACT"}
@@ -1,2 +1,2 @@
1
- var e=10,t=1e-4,n=100;function r(e,t){return Math.hypot(e.x-t.x,e.y-t.y)}function i(e,t,n){return(t.x-e.x)*(n.y-e.y)-(t.y-e.y)*(n.x-e.x)}function a(e,t,n){let i=n.x-t.x,a=n.y-t.y,o=i*i+a*a;if(o===0)return{dist:r(e,t),t:0,point:{x:t.x,y:t.y}};let s=((e.x-t.x)*i+(e.y-t.y)*a)/o,c=s<0?0:s>1?1:s,l={x:t.x+c*i,y:t.y+c*a};return{dist:r(e,l),t:c,point:l}}function o(e,t,n){return a(e,t,n).point}function s(e,t,n,a,o=1){let s=r(e,t),c=r(n,a);if(s<1e-9||c<1e-9)return{kind:`none`};let l=t.x-e.x,u=t.y-e.y,d=a.x-n.x,f=a.y-n.y,p=l*f-u*d,m=Math.abs(i(e,t,n))/s,h=Math.abs(i(e,t,a))/s;if(Math.abs(p)<1e-12||m<o&&h<o){if(m>=o||h>=o)return{kind:`none`};let t=l*l+u*u,r=((n.x-e.x)*l+(n.y-e.y)*u)/t,i=((a.x-e.x)*l+(a.y-e.y)*u)/t,c=Math.min(r,i),d=Math.max(r,i),f=o/s;return d<f||c>1-f||Math.min(d,1)-Math.max(c,0)<f?{kind:`collinearDisjoint`}:{kind:`collinearOverlap`}}let g=((n.x-e.x)*f-(n.y-e.y)*d)/p,_=((n.x-e.x)*u-(n.y-e.y)*l)/p,v=o/s,y=o/c;if(g<-v||g>1+v||_<-y||_>1+y)return{kind:`none`};let b={x:e.x+g*l,y:e.y+g*u},x=g<=v||g>=1-v,S=_<=y||_>=1-y;return x||S?{kind:`touch`,t:g,s:_,point:b}:{kind:`proper`,t:g,s:_,point:b}}function c(e){let t=e.length;if(t<3)return 0;let n=0;for(let r=0,i=t-1;r<t;i=r++)n+=e[i].x*e[r].y-e[r].x*e[i].y;return n/2}function l(e){let t=e.length;if(t<2)return 0;let n=0;for(let i=0,a=t-1;i<t;a=i++)n+=r(e[a],e[i]);return n}function u(e){let t=e.length;if(t===0)return{x:0,y:0};if(t<3)return{x:e.reduce((e,t)=>e+t.x,0)/t,y:e.reduce((e,t)=>e+t.y,0)/t};let n=0,r=0,i=0;for(let a=0,o=t-1;a<t;o=a++){let t=e[o].x*e[a].y-e[a].x*e[o].y;n+=t,r+=(e[o].x+e[a].x)*t,i+=(e[o].y+e[a].y)*t}return Math.abs(n)<1e-9?{x:e.reduce((e,t)=>e+t.x,0)/t,y:e.reduce((e,t)=>e+t.y,0)/t}:{x:r/(3*n),y:i/(3*n)}}function d(e,t){let n=!1;for(let r=0,i=t.length-1;r<t.length;i=r++){let a=t[r].x,o=t[r].y,s=t[i].x,c=t[i].y;o>e.y!=c>e.y&&e.x<(s-a)*(e.y-o)/(c-o)+a&&(n=!n)}return n}function f(e,t,n=[]){if(!d(e,t))return!1;for(let t of n)if(d(e,t))return!1;return!0}function p(e,t=[]){if(e.length===0)return{x:0,y:0};let n=u(e);if(f(n,e,t))return n;let r=1/0,i=-1/0;for(let t of e)t.y<r&&(r=t.y),t.y>i&&(i=t.y);if(!Number.isFinite(r)||i-r<1e-9)return n;let a=[e,...t],o=null;for(let e=1;e<=7;e++){let t=r+(i-r)*e/8,n=[];for(let e of a)for(let r=0,i=e.length-1;r<e.length;i=r++){let a=e[i],o=e[r];a.y>t!=o.y>t&&n.push(a.x+(t-a.y)*(o.x-a.x)/(o.y-a.y))}n.sort((e,t)=>e-t);for(let e=0;e+1<n.length;e+=2){let r=n[e+1]-n[e];r>2&&(!o||r>o.width)&&(o={point:{x:(n[e]+n[e+1])/2,y:t},width:r})}}return o?o.point:n}function m(e){let t=e.slice(),n=!0;for(;n&&t.length>3;){n=!1;let e=[],i=t.length;for(let a=0;a<i;a++){let o=t[(a-1+i)%i],s=t[(a+1)%i];if(r(o,s)<1){n=!0;continue}e.push(t[a])}n&&(t=e.filter((t,n)=>r(t,e[(n+1)%e.length])>=1))}return t}function h(e){if(e.length===0)return null;let t=1/0,n=1/0,r=-1/0,i=-1/0;for(let a of e)a.x<t&&(t=a.x),a.y<n&&(n=a.y),a.x>r&&(r=a.x),a.y>i&&(i=a.y);return{minX:t,minY:n,maxX:r,maxY:i}}export{t as EPS_ANGLE,n as MIN_OPENING_WIDTH,e as MIN_WALL_LENGTH,u as areaCentroid,h as boundsOf,i as cross,r as dist,l as perimeter,d as pointInRing,f as pointInRings,a as pointToSegment,o as projectOnSegment,p as representativePoint,s as segmentIntersection,c as signedArea,m as stripSpurs};
1
+ "use client";var e=10,t=1e-4,n=100;function r(e,t){return Math.hypot(e.x-t.x,e.y-t.y)}function i(e,t,n){return(t.x-e.x)*(n.y-e.y)-(t.y-e.y)*(n.x-e.x)}function a(e,t,n){let i=n.x-t.x,a=n.y-t.y,o=i*i+a*a;if(o===0)return{dist:r(e,t),t:0,point:{x:t.x,y:t.y}};let s=((e.x-t.x)*i+(e.y-t.y)*a)/o,c=s<0?0:s>1?1:s,l={x:t.x+c*i,y:t.y+c*a};return{dist:r(e,l),t:c,point:l}}function o(e,t,n){return a(e,t,n).point}function s(e,t,n,a,o=1){let s=r(e,t),c=r(n,a);if(s<1e-9||c<1e-9)return{kind:`none`};let l=t.x-e.x,u=t.y-e.y,d=a.x-n.x,f=a.y-n.y,p=l*f-u*d,m=Math.abs(i(e,t,n))/s,h=Math.abs(i(e,t,a))/s;if(Math.abs(p)<1e-12||m<o&&h<o){if(m>=o||h>=o)return{kind:`none`};let t=l*l+u*u,r=((n.x-e.x)*l+(n.y-e.y)*u)/t,i=((a.x-e.x)*l+(a.y-e.y)*u)/t,c=Math.min(r,i),d=Math.max(r,i),f=o/s;return d<f||c>1-f||Math.min(d,1)-Math.max(c,0)<f?{kind:`collinearDisjoint`}:{kind:`collinearOverlap`}}let g=((n.x-e.x)*f-(n.y-e.y)*d)/p,_=((n.x-e.x)*u-(n.y-e.y)*l)/p,v=o/s,y=o/c;if(g<-v||g>1+v||_<-y||_>1+y)return{kind:`none`};let b={x:e.x+g*l,y:e.y+g*u},x=g<=v||g>=1-v,S=_<=y||_>=1-y;return x||S?{kind:`touch`,t:g,s:_,point:b}:{kind:`proper`,t:g,s:_,point:b}}function c(e){let t=e.length;if(t<3)return 0;let n=0;for(let r=0,i=t-1;r<t;i=r++)n+=e[i].x*e[r].y-e[r].x*e[i].y;return n/2}function l(e){let t=e.length;if(t<2)return 0;let n=0;for(let i=0,a=t-1;i<t;a=i++)n+=r(e[a],e[i]);return n}function u(e){let t=e.length;if(t===0)return{x:0,y:0};if(t<3)return{x:e.reduce((e,t)=>e+t.x,0)/t,y:e.reduce((e,t)=>e+t.y,0)/t};let n=0,r=0,i=0;for(let a=0,o=t-1;a<t;o=a++){let t=e[o].x*e[a].y-e[a].x*e[o].y;n+=t,r+=(e[o].x+e[a].x)*t,i+=(e[o].y+e[a].y)*t}return Math.abs(n)<1e-9?{x:e.reduce((e,t)=>e+t.x,0)/t,y:e.reduce((e,t)=>e+t.y,0)/t}:{x:r/(3*n),y:i/(3*n)}}function d(e,t){let n=!1;for(let r=0,i=t.length-1;r<t.length;i=r++){let a=t[r].x,o=t[r].y,s=t[i].x,c=t[i].y;o>e.y!=c>e.y&&e.x<(s-a)*(e.y-o)/(c-o)+a&&(n=!n)}return n}function f(e,t,n=[]){if(!d(e,t))return!1;for(let t of n)if(d(e,t))return!1;return!0}function p(e,t=[]){if(e.length===0)return{x:0,y:0};let n=u(e);if(f(n,e,t))return n;let r=1/0,i=-1/0;for(let t of e)t.y<r&&(r=t.y),t.y>i&&(i=t.y);if(!Number.isFinite(r)||i-r<1e-9)return n;let a=[e,...t],o=null;for(let e=1;e<=7;e++){let t=r+(i-r)*e/8,n=[];for(let e of a)for(let r=0,i=e.length-1;r<e.length;i=r++){let a=e[i],o=e[r];a.y>t!=o.y>t&&n.push(a.x+(t-a.y)*(o.x-a.x)/(o.y-a.y))}n.sort((e,t)=>e-t);for(let e=0;e+1<n.length;e+=2){let r=n[e+1]-n[e];r>2&&(!o||r>o.width)&&(o={point:{x:(n[e]+n[e+1])/2,y:t},width:r})}}return o?o.point:n}function m(e){let t=e.slice(),n=!0;for(;n&&t.length>3;){n=!1;let e=[],i=t.length;for(let a=0;a<i;a++){let o=t[(a-1+i)%i],s=t[(a+1)%i];if(r(o,s)<1){n=!0;continue}e.push(t[a])}n&&(t=e.filter((t,n)=>r(t,e[(n+1)%e.length])>=1))}return t}function h(e){if(e.length===0)return null;let t=1/0,n=1/0,r=-1/0,i=-1/0;for(let a of e)a.x<t&&(t=a.x),a.y<n&&(n=a.y),a.x>r&&(r=a.x),a.y>i&&(i=a.y);return{minX:t,minY:n,maxX:r,maxY:i}}export{t as EPS_ANGLE,n as MIN_OPENING_WIDTH,e as MIN_WALL_LENGTH,u as areaCentroid,h as boundsOf,i as cross,r as dist,l as perimeter,d as pointInRing,f as pointInRings,a as pointToSegment,o as projectOnSegment,p as representativePoint,s as segmentIntersection,c as signedArea,m as stripSpurs};
2
2
  //# sourceMappingURL=floorPlan.geometry.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"floorPlan.geometry.mjs","names":[],"sources":["../../../../package/common/floorPlan/floorPlan.geometry.ts"],"sourcesContent":["/**\n * Pure vector and polygon primitives. Nothing here knows about the plan model.\n *\n * All lengths are millimetres (see floorPlan.types.ts).\n */\n\nimport type { PlanPoint } from './floorPlan.types';\n\n// ─── Model-integrity epsilons ──────────────────────────────────────────────────\n// These are FIXED millimetre values describing when the model itself is degenerate.\n// They are a separate family from the user-intent tolerances in floorPlan.snap.ts,\n// which are screen pixels converted through the current zoom. Conflating the two\n// gives you either a model that accumulates 0.2mm walls, or snapping that stops\n// working when zoomed in.\n\n/** Shortest wall the graph will accept. Keeps zero-length walls out entirely. */\nexport const MIN_WALL_LENGTH = 10;\n/** Two points closer than this are the same point. */\nexport const EPS_COINCIDENT = 1;\n/** A ring with |area| below this encloses nothing (0.0001 m²). */\nexport const EPS_AREA = 100;\n/** Angular tolerance for \"these two walls are collinear\", in radians. */\nexport const EPS_ANGLE = 1e-4;\n/** Narrowest scanline span that counts as room interior rather than a slit. */\nexport const EPS_SPAN = 2;\n/** Narrowest opening worth keeping when a wall shortens under it. */\nexport const MIN_OPENING_WIDTH = 100;\n\n// ─── Vectors ───────────────────────────────────────────────────────────────────\n\nexport function dist(a: PlanPoint, b: PlanPoint): number {\n return Math.hypot(a.x - b.x, a.y - b.y);\n}\n\nexport function distSq(a: PlanPoint, b: PlanPoint): number {\n const dx = a.x - b.x;\n const dy = a.y - b.y;\n return dx * dx + dy * dy;\n}\n\n/** Cross product of ab and ac. Positive when c is clockwise of b in SVG's y-down space. */\nexport function cross(a: PlanPoint, b: PlanPoint, c: PlanPoint): number {\n return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);\n}\n\nexport function lerp(a: PlanPoint, b: PlanPoint, t: number): PlanPoint {\n return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };\n}\n\n/** Unit vector from a to b, or null when they are coincident. */\nexport function direction(a: PlanPoint, b: PlanPoint): PlanPoint | null {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const len = Math.hypot(dx, dy);\n if (len < 1e-9) return null;\n return { x: dx / len, y: dy / len };\n}\n\n/** Left-hand normal of the direction from a to b, or null when coincident. */\nexport function normal(a: PlanPoint, b: PlanPoint): PlanPoint | null {\n const d = direction(a, b);\n return d ? { x: -d.y, y: d.x } : null;\n}\n\n// ─── Point / segment ───────────────────────────────────────────────────────────\n\n/**\n * Distance from p to segment ab, plus the clamped parameter t of the closest point.\n * `t` is clamped to [0, 1], so the closest point is always on the segment.\n */\nexport function pointToSegment(\n p: PlanPoint,\n a: PlanPoint,\n b: PlanPoint,\n): { dist: number; t: number; point: PlanPoint } {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const len2 = dx * dx + dy * dy;\n if (len2 === 0) return { dist: dist(p, a), t: 0, point: { x: a.x, y: a.y } };\n const raw = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;\n const t = raw < 0 ? 0 : raw > 1 ? 1 : raw;\n const point = { x: a.x + t * dx, y: a.y + t * dy };\n return { dist: dist(p, point), t, point };\n}\n\n/**\n * Exact projection of p onto segment ab, clamped to the segment.\n *\n * Used after a fuzzy hit tolerance has decided that p belongs to this segment: intent\n * is allowed to be approximate, the stored model never is.\n */\nexport function projectOnSegment(p: PlanPoint, a: PlanPoint, b: PlanPoint): PlanPoint {\n return pointToSegment(p, a, b).point;\n}\n\nexport type SegmentIntersectionKind =\n /** The segments cross strictly inside both. */\n | 'proper'\n /** They are parallel and do not touch. */\n | 'none'\n /** They lie on the same line and overlap over a non-zero length. */\n | 'collinearOverlap'\n /** They lie on the same line but do not overlap. */\n | 'collinearDisjoint'\n /** An endpoint of one lies on the other, or they share an endpoint. */\n | 'touch';\n\nexport interface SegmentIntersection {\n kind: SegmentIntersectionKind;\n /** Parameter along ab, for 'proper' and 'touch'. */\n t?: number;\n /** Parameter along cd, for 'proper' and 'touch'. */\n s?: number;\n point?: PlanPoint;\n}\n\n/**\n * Classifies how segments ab and cd meet.\n *\n * `eps` is a millimetre distance tolerance, not a parameter tolerance, so the result\n * does not change with segment length.\n */\nexport function segmentIntersection(\n a: PlanPoint,\n b: PlanPoint,\n c: PlanPoint,\n d: PlanPoint,\n eps = EPS_COINCIDENT,\n): SegmentIntersection {\n const abLen = dist(a, b);\n const cdLen = dist(c, d);\n if (abLen < 1e-9 || cdLen < 1e-9) return { kind: 'none' };\n\n const rx = b.x - a.x;\n const ry = b.y - a.y;\n const sx = d.x - c.x;\n const sy = d.y - c.y;\n const denom = rx * sy - ry * sx;\n\n // Perpendicular distance of c and d from line ab, used to tell parallel from collinear.\n const distC = Math.abs(cross(a, b, c)) / abLen;\n const distD = Math.abs(cross(a, b, d)) / abLen;\n\n if (Math.abs(denom) < 1e-12 || (distC < eps && distD < eps)) {\n if (distC >= eps || distD >= eps) return { kind: 'none' };\n // Collinear: compare parameters along ab.\n const len2 = rx * rx + ry * ry;\n const tc = ((c.x - a.x) * rx + (c.y - a.y) * ry) / len2;\n const td = ((d.x - a.x) * rx + (d.y - a.y) * ry) / len2;\n const lo = Math.min(tc, td);\n const hi = Math.max(tc, td);\n const epsT = eps / abLen;\n if (hi < epsT || lo > 1 - epsT) return { kind: 'collinearDisjoint' };\n if (Math.min(hi, 1) - Math.max(lo, 0) < epsT) return { kind: 'collinearDisjoint' };\n return { kind: 'collinearOverlap' };\n }\n\n const t = ((c.x - a.x) * sy - (c.y - a.y) * sx) / denom;\n const s = ((c.x - a.x) * ry - (c.y - a.y) * rx) / denom;\n const epsT = eps / abLen;\n const epsS = eps / cdLen;\n\n if (t < -epsT || t > 1 + epsT || s < -epsS || s > 1 + epsS) return { kind: 'none' };\n\n const point = { x: a.x + t * rx, y: a.y + t * ry };\n const atAbEnd = t <= epsT || t >= 1 - epsT;\n const atCdEnd = s <= epsS || s >= 1 - epsS;\n if (atAbEnd || atCdEnd) return { kind: 'touch', t, s, point };\n return { kind: 'proper', t, s, point };\n}\n\n// ─── Rings ─────────────────────────────────────────────────────────────────────\n\n/**\n * Shoelace area of a closed ring (first vertex not repeated).\n *\n * Positive means visually clockwise in SVG's y-down space. See `PlanFace.outer`.\n */\nexport function signedArea(ring: readonly PlanPoint[]): number {\n const n = ring.length;\n if (n < 3) return 0;\n let sum = 0;\n for (let i = 0, j = n - 1; i < n; j = i++) {\n sum += ring[j].x * ring[i].y - ring[i].x * ring[j].y;\n }\n return sum / 2;\n}\n\nexport function perimeter(ring: readonly PlanPoint[]): number {\n const n = ring.length;\n if (n < 2) return 0;\n let sum = 0;\n for (let i = 0, j = n - 1; i < n; j = i++) sum += dist(ring[j], ring[i]);\n return sum;\n}\n\n/**\n * Area-weighted centroid of a ring.\n *\n * Deliberately not the vertex mean: a dangling wall adds an out-and-back vertex pair\n * that skews the mean but contributes nothing to the area moment. Falls back to the\n * vertex mean only for a degenerate (zero-area) ring.\n */\nexport function areaCentroid(ring: readonly PlanPoint[]): PlanPoint {\n const n = ring.length;\n if (n === 0) return { x: 0, y: 0 };\n if (n < 3) {\n return {\n x: ring.reduce((s, p) => s + p.x, 0) / n,\n y: ring.reduce((s, p) => s + p.y, 0) / n,\n };\n }\n let a = 0;\n let cx = 0;\n let cy = 0;\n for (let i = 0, j = n - 1; i < n; j = i++) {\n const f = ring[j].x * ring[i].y - ring[i].x * ring[j].y;\n a += f;\n cx += (ring[j].x + ring[i].x) * f;\n cy += (ring[j].y + ring[i].y) * f;\n }\n if (Math.abs(a) < 1e-9) {\n return {\n x: ring.reduce((s, p) => s + p.x, 0) / n,\n y: ring.reduce((s, p) => s + p.y, 0) / n,\n };\n }\n return { x: cx / (3 * a), y: cy / (3 * a) };\n}\n\n/** Ray-casting point-in-ring test. Points exactly on an edge are unspecified. */\nexport function pointInRing(p: PlanPoint, ring: readonly PlanPoint[]): boolean {\n let inside = false;\n for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {\n const xi = ring[i].x;\n const yi = ring[i].y;\n const xj = ring[j].x;\n const yj = ring[j].y;\n if (yi > p.y !== yj > p.y && p.x < ((xj - xi) * (p.y - yi)) / (yj - yi) + xi) {\n inside = !inside;\n }\n }\n return inside;\n}\n\n/** Inside `outer` and outside every hole. */\nexport function pointInRings(\n p: PlanPoint,\n outer: readonly PlanPoint[],\n holes: readonly (readonly PlanPoint[])[] = [],\n): boolean {\n if (!pointInRing(p, outer)) return false;\n for (const hole of holes) if (pointInRing(p, hole)) return false;\n return true;\n}\n\n/**\n * A point guaranteed to lie strictly inside the ring set, placed somewhere visually\n * central where possible.\n *\n * The area centroid is preferred, but it is not guaranteed to be inside a concave\n * polygon (an L-shaped room's centroid can land in the notch), so a scanline fallback\n * picks the widest interior span across several horizontal lines. The EPS_SPAN test on\n * that span is what stops an anchor landing inside the zero-width slit that a dangling\n * wall leaves in a face.\n */\nexport function representativePoint(\n outer: readonly PlanPoint[],\n holes: readonly (readonly PlanPoint[])[] = [],\n): PlanPoint {\n if (outer.length === 0) return { x: 0, y: 0 };\n\n const centre = areaCentroid(outer);\n if (pointInRings(centre, outer, holes)) return centre;\n\n let ymin = Infinity;\n let ymax = -Infinity;\n for (const p of outer) {\n if (p.y < ymin) ymin = p.y;\n if (p.y > ymax) ymax = p.y;\n }\n if (!Number.isFinite(ymin) || ymax - ymin < 1e-9) return centre;\n\n const rings: readonly (readonly PlanPoint[])[] = [outer, ...holes];\n let best: { point: PlanPoint; width: number } | null = null;\n\n const SAMPLES = 7;\n for (let k = 1; k <= SAMPLES; k++) {\n const y = ymin + ((ymax - ymin) * k) / (SAMPLES + 1);\n const xs: number[] = [];\n // One crossing list across ALL rings, so even-odd pairing yields interior spans\n // directly. Running the scanline per ring would not compose with holes.\n for (const ring of rings) {\n for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {\n const p = ring[j];\n const q = ring[i];\n if (p.y > y !== q.y > y) {\n xs.push(p.x + ((y - p.y) * (q.x - p.x)) / (q.y - p.y));\n }\n }\n }\n xs.sort((m, n) => m - n);\n for (let i = 0; i + 1 < xs.length; i += 2) {\n const width = xs[i + 1] - xs[i];\n if (width > EPS_SPAN && (!best || width > best.width)) {\n best = { point: { x: (xs[i] + xs[i + 1]) / 2, y }, width };\n }\n }\n }\n\n return best ? best.point : centre;\n}\n\n/**\n * Removes out-and-back spurs (`... P, Q, P ...`) from a ring, repeatedly until none\n * remain.\n *\n * A face containing a dangling wall is a valid fill but is not a simple polygon, which\n * breaks polygon offsetting and makes some triangulators emit zero-area triangles. Run\n * this before triangulating a floor slab; keep the raw ring for the 2D fill, where the\n * slit is harmless and `wallIds` alignment matters.\n */\nexport function stripSpurs(ring: readonly PlanPoint[]): PlanPoint[] {\n let out = ring.slice();\n let changed = true;\n while (changed && out.length > 3) {\n changed = false;\n const next: PlanPoint[] = [];\n const n = out.length;\n for (let i = 0; i < n; i++) {\n const prev = out[(i - 1 + n) % n];\n const nextPt = out[(i + 1) % n];\n // The middle of an out-and-back is the vertex whose neighbours coincide.\n if (dist(prev, nextPt) < EPS_COINCIDENT) {\n changed = true;\n continue;\n }\n next.push(out[i]);\n }\n if (changed) {\n // Drop the now-duplicated turning point left behind by each removed spur tip.\n out = next.filter((p, i) => dist(p, next[(i + 1) % next.length]) >= EPS_COINCIDENT);\n }\n }\n return out;\n}\n\nexport function rectToPoints(x: number, y: number, w: number, h: number): PlanPoint[] {\n return [\n { x, y },\n { x: x + w, y },\n { x: x + w, y: y + h },\n { x, y: y + h },\n ];\n}\n\nexport function boundsOf(points: readonly PlanPoint[]): {\n minX: number;\n minY: number;\n maxX: number;\n maxY: number;\n} | null {\n if (points.length === 0) return null;\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const p of points) {\n if (p.x < minX) minX = p.x;\n if (p.y < minY) minY = p.y;\n if (p.x > maxX) maxX = p.x;\n if (p.y > maxY) maxY = p.y;\n }\n return { minX, minY, maxX, maxY };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"floorPlan.geometry.mjs","names":[],"sources":["../../../../package/common/floorPlan/floorPlan.geometry.ts"],"sourcesContent":["/**\n * Pure vector and polygon primitives. Nothing here knows about the plan model.\n *\n * All lengths are millimetres (see floorPlan.types.ts).\n */\n\nimport type { PlanPoint } from './floorPlan.types';\n\n// ─── Model-integrity epsilons ──────────────────────────────────────────────────\n// These are FIXED millimetre values describing when the model itself is degenerate.\n// They are a separate family from the user-intent tolerances in floorPlan.snap.ts,\n// which are screen pixels converted through the current zoom. Conflating the two\n// gives you either a model that accumulates 0.2mm walls, or snapping that stops\n// working when zoomed in.\n\n/** Shortest wall the graph will accept. Keeps zero-length walls out entirely. */\nexport const MIN_WALL_LENGTH = 10;\n/** Two points closer than this are the same point. */\nexport const EPS_COINCIDENT = 1;\n/** A ring with |area| below this encloses nothing (0.0001 m²). */\nexport const EPS_AREA = 100;\n/** Angular tolerance for \"these two walls are collinear\", in radians. */\nexport const EPS_ANGLE = 1e-4;\n/** Narrowest scanline span that counts as room interior rather than a slit. */\nexport const EPS_SPAN = 2;\n/** Narrowest opening worth keeping when a wall shortens under it. */\nexport const MIN_OPENING_WIDTH = 100;\n\n// ─── Vectors ───────────────────────────────────────────────────────────────────\n\nexport function dist(a: PlanPoint, b: PlanPoint): number {\n return Math.hypot(a.x - b.x, a.y - b.y);\n}\n\nexport function distSq(a: PlanPoint, b: PlanPoint): number {\n const dx = a.x - b.x;\n const dy = a.y - b.y;\n return dx * dx + dy * dy;\n}\n\n/** Cross product of ab and ac. Positive when c is clockwise of b in SVG's y-down space. */\nexport function cross(a: PlanPoint, b: PlanPoint, c: PlanPoint): number {\n return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);\n}\n\nexport function lerp(a: PlanPoint, b: PlanPoint, t: number): PlanPoint {\n return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };\n}\n\n/** Unit vector from a to b, or null when they are coincident. */\nexport function direction(a: PlanPoint, b: PlanPoint): PlanPoint | null {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const len = Math.hypot(dx, dy);\n if (len < 1e-9) return null;\n return { x: dx / len, y: dy / len };\n}\n\n/** Left-hand normal of the direction from a to b, or null when coincident. */\nexport function normal(a: PlanPoint, b: PlanPoint): PlanPoint | null {\n const d = direction(a, b);\n return d ? { x: -d.y, y: d.x } : null;\n}\n\n// ─── Point / segment ───────────────────────────────────────────────────────────\n\n/**\n * Distance from p to segment ab, plus the clamped parameter t of the closest point.\n * `t` is clamped to [0, 1], so the closest point is always on the segment.\n */\nexport function pointToSegment(\n p: PlanPoint,\n a: PlanPoint,\n b: PlanPoint,\n): { dist: number; t: number; point: PlanPoint } {\n const dx = b.x - a.x;\n const dy = b.y - a.y;\n const len2 = dx * dx + dy * dy;\n if (len2 === 0) return { dist: dist(p, a), t: 0, point: { x: a.x, y: a.y } };\n const raw = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;\n const t = raw < 0 ? 0 : raw > 1 ? 1 : raw;\n const point = { x: a.x + t * dx, y: a.y + t * dy };\n return { dist: dist(p, point), t, point };\n}\n\n/**\n * Exact projection of p onto segment ab, clamped to the segment.\n *\n * Used after a fuzzy hit tolerance has decided that p belongs to this segment: intent\n * is allowed to be approximate, the stored model never is.\n */\nexport function projectOnSegment(p: PlanPoint, a: PlanPoint, b: PlanPoint): PlanPoint {\n return pointToSegment(p, a, b).point;\n}\n\nexport type SegmentIntersectionKind =\n /** The segments cross strictly inside both. */\n | 'proper'\n /** They are parallel and do not touch. */\n | 'none'\n /** They lie on the same line and overlap over a non-zero length. */\n | 'collinearOverlap'\n /** They lie on the same line but do not overlap. */\n | 'collinearDisjoint'\n /** An endpoint of one lies on the other, or they share an endpoint. */\n | 'touch';\n\nexport interface SegmentIntersection {\n kind: SegmentIntersectionKind;\n /** Parameter along ab, for 'proper' and 'touch'. */\n t?: number;\n /** Parameter along cd, for 'proper' and 'touch'. */\n s?: number;\n point?: PlanPoint;\n}\n\n/**\n * Classifies how segments ab and cd meet.\n *\n * `eps` is a millimetre distance tolerance, not a parameter tolerance, so the result\n * does not change with segment length.\n */\nexport function segmentIntersection(\n a: PlanPoint,\n b: PlanPoint,\n c: PlanPoint,\n d: PlanPoint,\n eps = EPS_COINCIDENT,\n): SegmentIntersection {\n const abLen = dist(a, b);\n const cdLen = dist(c, d);\n if (abLen < 1e-9 || cdLen < 1e-9) return { kind: 'none' };\n\n const rx = b.x - a.x;\n const ry = b.y - a.y;\n const sx = d.x - c.x;\n const sy = d.y - c.y;\n const denom = rx * sy - ry * sx;\n\n // Perpendicular distance of c and d from line ab, used to tell parallel from collinear.\n const distC = Math.abs(cross(a, b, c)) / abLen;\n const distD = Math.abs(cross(a, b, d)) / abLen;\n\n if (Math.abs(denom) < 1e-12 || (distC < eps && distD < eps)) {\n if (distC >= eps || distD >= eps) return { kind: 'none' };\n // Collinear: compare parameters along ab.\n const len2 = rx * rx + ry * ry;\n const tc = ((c.x - a.x) * rx + (c.y - a.y) * ry) / len2;\n const td = ((d.x - a.x) * rx + (d.y - a.y) * ry) / len2;\n const lo = Math.min(tc, td);\n const hi = Math.max(tc, td);\n const epsT = eps / abLen;\n if (hi < epsT || lo > 1 - epsT) return { kind: 'collinearDisjoint' };\n if (Math.min(hi, 1) - Math.max(lo, 0) < epsT) return { kind: 'collinearDisjoint' };\n return { kind: 'collinearOverlap' };\n }\n\n const t = ((c.x - a.x) * sy - (c.y - a.y) * sx) / denom;\n const s = ((c.x - a.x) * ry - (c.y - a.y) * rx) / denom;\n const epsT = eps / abLen;\n const epsS = eps / cdLen;\n\n if (t < -epsT || t > 1 + epsT || s < -epsS || s > 1 + epsS) return { kind: 'none' };\n\n const point = { x: a.x + t * rx, y: a.y + t * ry };\n const atAbEnd = t <= epsT || t >= 1 - epsT;\n const atCdEnd = s <= epsS || s >= 1 - epsS;\n if (atAbEnd || atCdEnd) return { kind: 'touch', t, s, point };\n return { kind: 'proper', t, s, point };\n}\n\n// ─── Rings ─────────────────────────────────────────────────────────────────────\n\n/**\n * Shoelace area of a closed ring (first vertex not repeated).\n *\n * Positive means visually clockwise in SVG's y-down space. See `PlanFace.outer`.\n */\nexport function signedArea(ring: readonly PlanPoint[]): number {\n const n = ring.length;\n if (n < 3) return 0;\n let sum = 0;\n for (let i = 0, j = n - 1; i < n; j = i++) {\n sum += ring[j].x * ring[i].y - ring[i].x * ring[j].y;\n }\n return sum / 2;\n}\n\nexport function perimeter(ring: readonly PlanPoint[]): number {\n const n = ring.length;\n if (n < 2) return 0;\n let sum = 0;\n for (let i = 0, j = n - 1; i < n; j = i++) sum += dist(ring[j], ring[i]);\n return sum;\n}\n\n/**\n * Area-weighted centroid of a ring.\n *\n * Deliberately not the vertex mean: a dangling wall adds an out-and-back vertex pair\n * that skews the mean but contributes nothing to the area moment. Falls back to the\n * vertex mean only for a degenerate (zero-area) ring.\n */\nexport function areaCentroid(ring: readonly PlanPoint[]): PlanPoint {\n const n = ring.length;\n if (n === 0) return { x: 0, y: 0 };\n if (n < 3) {\n return {\n x: ring.reduce((s, p) => s + p.x, 0) / n,\n y: ring.reduce((s, p) => s + p.y, 0) / n,\n };\n }\n let a = 0;\n let cx = 0;\n let cy = 0;\n for (let i = 0, j = n - 1; i < n; j = i++) {\n const f = ring[j].x * ring[i].y - ring[i].x * ring[j].y;\n a += f;\n cx += (ring[j].x + ring[i].x) * f;\n cy += (ring[j].y + ring[i].y) * f;\n }\n if (Math.abs(a) < 1e-9) {\n return {\n x: ring.reduce((s, p) => s + p.x, 0) / n,\n y: ring.reduce((s, p) => s + p.y, 0) / n,\n };\n }\n return { x: cx / (3 * a), y: cy / (3 * a) };\n}\n\n/** Ray-casting point-in-ring test. Points exactly on an edge are unspecified. */\nexport function pointInRing(p: PlanPoint, ring: readonly PlanPoint[]): boolean {\n let inside = false;\n for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {\n const xi = ring[i].x;\n const yi = ring[i].y;\n const xj = ring[j].x;\n const yj = ring[j].y;\n if (yi > p.y !== yj > p.y && p.x < ((xj - xi) * (p.y - yi)) / (yj - yi) + xi) {\n inside = !inside;\n }\n }\n return inside;\n}\n\n/** Inside `outer` and outside every hole. */\nexport function pointInRings(\n p: PlanPoint,\n outer: readonly PlanPoint[],\n holes: readonly (readonly PlanPoint[])[] = [],\n): boolean {\n if (!pointInRing(p, outer)) return false;\n for (const hole of holes) if (pointInRing(p, hole)) return false;\n return true;\n}\n\n/**\n * A point guaranteed to lie strictly inside the ring set, placed somewhere visually\n * central where possible.\n *\n * The area centroid is preferred, but it is not guaranteed to be inside a concave\n * polygon (an L-shaped room's centroid can land in the notch), so a scanline fallback\n * picks the widest interior span across several horizontal lines. The EPS_SPAN test on\n * that span is what stops an anchor landing inside the zero-width slit that a dangling\n * wall leaves in a face.\n */\nexport function representativePoint(\n outer: readonly PlanPoint[],\n holes: readonly (readonly PlanPoint[])[] = [],\n): PlanPoint {\n if (outer.length === 0) return { x: 0, y: 0 };\n\n const centre = areaCentroid(outer);\n if (pointInRings(centre, outer, holes)) return centre;\n\n let ymin = Infinity;\n let ymax = -Infinity;\n for (const p of outer) {\n if (p.y < ymin) ymin = p.y;\n if (p.y > ymax) ymax = p.y;\n }\n if (!Number.isFinite(ymin) || ymax - ymin < 1e-9) return centre;\n\n const rings: readonly (readonly PlanPoint[])[] = [outer, ...holes];\n let best: { point: PlanPoint; width: number } | null = null;\n\n const SAMPLES = 7;\n for (let k = 1; k <= SAMPLES; k++) {\n const y = ymin + ((ymax - ymin) * k) / (SAMPLES + 1);\n const xs: number[] = [];\n // One crossing list across ALL rings, so even-odd pairing yields interior spans\n // directly. Running the scanline per ring would not compose with holes.\n for (const ring of rings) {\n for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {\n const p = ring[j];\n const q = ring[i];\n if (p.y > y !== q.y > y) {\n xs.push(p.x + ((y - p.y) * (q.x - p.x)) / (q.y - p.y));\n }\n }\n }\n xs.sort((m, n) => m - n);\n for (let i = 0; i + 1 < xs.length; i += 2) {\n const width = xs[i + 1] - xs[i];\n if (width > EPS_SPAN && (!best || width > best.width)) {\n best = { point: { x: (xs[i] + xs[i + 1]) / 2, y }, width };\n }\n }\n }\n\n return best ? best.point : centre;\n}\n\n/**\n * Removes out-and-back spurs (`... P, Q, P ...`) from a ring, repeatedly until none\n * remain.\n *\n * A face containing a dangling wall is a valid fill but is not a simple polygon, which\n * breaks polygon offsetting and makes some triangulators emit zero-area triangles. Run\n * this before triangulating a floor slab; keep the raw ring for the 2D fill, where the\n * slit is harmless and `wallIds` alignment matters.\n */\nexport function stripSpurs(ring: readonly PlanPoint[]): PlanPoint[] {\n let out = ring.slice();\n let changed = true;\n while (changed && out.length > 3) {\n changed = false;\n const next: PlanPoint[] = [];\n const n = out.length;\n for (let i = 0; i < n; i++) {\n const prev = out[(i - 1 + n) % n];\n const nextPt = out[(i + 1) % n];\n // The middle of an out-and-back is the vertex whose neighbours coincide.\n if (dist(prev, nextPt) < EPS_COINCIDENT) {\n changed = true;\n continue;\n }\n next.push(out[i]);\n }\n if (changed) {\n // Drop the now-duplicated turning point left behind by each removed spur tip.\n out = next.filter((p, i) => dist(p, next[(i + 1) % next.length]) >= EPS_COINCIDENT);\n }\n }\n return out;\n}\n\nexport function rectToPoints(x: number, y: number, w: number, h: number): PlanPoint[] {\n return [\n { x, y },\n { x: x + w, y },\n { x: x + w, y: y + h },\n { x, y: y + h },\n ];\n}\n\nexport function boundsOf(points: readonly PlanPoint[]): {\n minX: number;\n minY: number;\n maxX: number;\n maxY: number;\n} | null {\n if (points.length === 0) return null;\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const p of points) {\n if (p.x < minX) minX = p.x;\n if (p.y < minY) minY = p.y;\n if (p.x > maxX) maxX = p.x;\n if (p.y > maxY) maxY = p.y;\n }\n return { minX, minY, maxX, maxY };\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{PlanIssueKind as e,WallType as t}from"./floorPlan.types.mjs";import{cross as n,dist as r,pointToSegment as i,projectOnSegment as a,segmentIntersection as o}from"./floorPlan.geometry.mjs";var s=0;function c(e){return s+=1,`${e}_${typeof crypto<`u`&&typeof crypto.randomUUID==`function`?crypto.randomUUID().slice(0,8):Math.random().toString(36).slice(2,10)}${s.toString(36)}`}function l(e){return{...e,nodes:{...e.nodes},walls:{...e.walls},openings:{...e.openings},rooms:{...e.rooms}}}function u(e,t,n={}){return{kind:e,message:t,...n}}function d(e,t){let n=e.nodes[t.a],i=e.nodes[t.b];return!n||!i?0:r(n,i)}function f(e,t){return Object.values(e.walls).filter(e=>e.a===t||e.b===t)}function p(e,t,n){for(let r of Object.values(e.walls))if(r.a===t&&r.b===n||r.a===n&&r.b===t)return r;return null}var m=250;function h(e,t){return`${Math.round(e/m)}|${Math.round(t/m)}`}function g(e){let t=new Map;for(let n of Object.values(e.nodes)){let e=h(n.x,n.y),r=t.get(e);r?r.push(n):t.set(e,[n])}return t}function _(e,t,n){let i=g(e),a=Math.round(t.x/m),o=Math.round(t.y/m),s=null,c=n,l=Math.max(1,Math.ceil(n/m));for(let e=-l;e<=l;e++)for(let n=-l;n<=l;n++)for(let l of i.get(`${a+e}|${o+n}`)??[]){let e=r(t,l);e<=c&&(s=l,c=e)}return s}function v(e,t,n,r){let a=null;for(let o of Object.values(e.walls)){if(r?.has(o.id))continue;let s=e.nodes[o.a],c=e.nodes[o.b];if(!s||!c)continue;let l=i(t,s,c);l.dist<=n&&(!a||l.dist<a.dist)&&(a={wall:o,t:l.t,point:l.point,dist:l.dist})}return a}function y(t,n,i){let o=t.walls[n];if(!o)return{floor:t,result:{nodeId:``,wallIds:null,droppedOpeningIds:[]},issues:[]};let s=t.nodes[o.a],d=t.nodes[o.b];if(!s||!d)return{floor:t,result:{nodeId:``,wallIds:null,droppedOpeningIds:[]},issues:[]};let f=r(s,d),p=typeof i==`number`?{x:s.x+(d.x-s.x)*i,y:s.y+(d.y-s.y)*i}:i,m=a(p,s,d),h=r(s,m);if(h<10)return{floor:t,result:{nodeId:o.a,wallIds:null,droppedOpeningIds:[]},issues:[]};if(f-h<10)return{floor:t,result:{nodeId:o.b,wallIds:null,droppedOpeningIds:[]},issues:[]};let g=l(t),_=c(`n`);g.nodes[_]={id:_,x:m.x,y:m.y};let v=c(`w`),y=c(`w`),b={thickness:o.thickness,type:o.type,height:o.height,baseHeight:o.baseHeight};g.walls[v]={...b,id:v,a:o.a,b:_},g.walls[y]={...b,id:y,a:_,b:o.b},delete g.walls[n];let x=[],S=[];for(let r of Object.values(t.openings)){if(r.wallId!==n)continue;let t=r.offset<=h,i=t?v:y,a=t?h:f-h,o=t?r.offset:r.offset-h,s=Math.min(r.width,a-2,o*2,(a-o)*2);if(s<100){delete g.openings[r.id],x.push(r.id),S.push(u(e.OpeningDropped,`An opening was removed: the wall it sat on became too short for it.`,{openingIds:[r.id],wallIds:[n]}));continue}g.openings[r.id]={...r,wallId:i,offset:o,width:s}}return{floor:g,result:{nodeId:_,wallIds:[v,y],droppedOpeningIds:x},issues:S}}function b(e,t,n){let r=_(e,t,n);if(r)return{floor:e,result:{nodeId:r.id,createdNode:!1,splitWallId:null},issues:[]};let i=v(e,t,n);if(i){let t=y(e,i.wall.id,i.point);if(t.result.nodeId)return{floor:t.floor,result:{nodeId:t.result.nodeId,createdNode:t.result.wallIds!==null,splitWallId:t.result.wallIds?i.wall.id:null},issues:t.issues}}let a=l(e),o=c(`n`);return a.nodes[o]={id:o,x:t.x,y:t.y},{floor:a,result:{nodeId:o,createdNode:!0,splitWallId:null},issues:[]}}var x={thickness:200,type:t.Interior};function S(t,n,a,s=x,l=1){let d=[];if(r(n,a)<10)return{floor:t,result:{wallIds:[],nodeIds:[]},issues:d};let f=b(t,n,l),m=f.floor;d.push(...f.issues);let h=b(m,a,l);m=h.floor,d.push(...h.issues);let g=f.result.nodeId,_=h.result.nodeId;if(g===_)return{floor:t,result:{wallIds:[],nodeIds:[]},issues:d};let v=m.nodes[g],S=m.nodes[_],C=r(v,S);if(C<10)return{floor:t,result:{wallIds:[],nodeIds:[]},issues:d};let w=[{t:0,nodeId:g},{t:1,nodeId:_}],T=new Map,E=[],D=e=>i(e,v,S).t;for(let e of Object.values(m.walls)){let t=m.nodes[e.a],n=m.nodes[e.b];if(!t||!n)continue;let r=o(v,S,t,n,l);if(r.kind===`collinearOverlap`){let e=D(t),r=D(n);E.push([Math.min(e,r),Math.max(e,r)]);for(let[i,a]of[[t,e],[n,r]])a*C>10&&(1-a)*C>10&&w.push({t:a,nodeId:i.id});continue}if(r.kind===`proper`&&r.t!==void 0&&r.s!==void 0){let t=T.get(e.id);t?t.push(r.s):T.set(e.id,[r.s]),w.push({t:r.t,nodeId:null,pending:{wallId:e.id,s:r.s}});continue}if(r.kind===`touch`&&r.t!==void 0&&r.s!==void 0){let e=r.s<=.5?t:n,a=D(e);i(e,v,S).dist<=l&&a*C>10&&(1-a)*C>10&&w.push({t:a,nodeId:e.id})}}for(let e of Object.values(m.nodes)){if(e.id===g||e.id===_)continue;let t=i(e,v,S);t.dist>l||t.t*C<=10||(1-t.t)*C<=10||w.push({t:t.t,nodeId:e.id})}for(let[e,n]of T){let r=[...n].sort((e,t)=>t-e),i=e,a=new Set(r);for(let n of r){let r=m.walls[i];if(!r)break;let o=m.nodes[r.a],s=m.nodes[r.b];if(!o||!s)break;let c=t.walls[e],l=m.nodes[c?.a??r.a]??o,u=m.nodes[c?.b??r.b]??s,f={x:l.x+(u.x-l.x)*n,y:l.y+(u.y-l.y)*n},p=y(m,i,f);m=p.floor,d.push(...p.issues),a.delete(n);for(let t of w)t.pending&&t.pending.wallId===e&&t.pending.s===n&&(t.nodeId=p.result.nodeId,t.pending=void 0);if(p.result.wallIds){let[e,t]=p.result.wallIds,r=Math.max(...a,-1);i=r>=0&&r>n?t:e}}}let O=w.filter(e=>!!e.nodeId);O.sort((e,t)=>e.t-t.t);let k=[];for(let e of O){let t=k[k.length-1];if(!t){k.push(e);continue}if(t.nodeId!==e.nodeId){if((e.t-t.t)*C<10){e.t>=1-1e-9&&(k[k.length-1]=e);continue}k.push(e)}}let A=[];for(let e=0;e+1<k.length;e++){let t=k[e],n=k[e+1],i=(t.t+n.t)/2;if(E.some(([e,t])=>i>e+1e-9&&i<t-1e-9)||p(m,t.nodeId,n.nodeId))continue;let a=m.nodes[t.nodeId],o=m.nodes[n.nodeId];if(!a||!o||r(a,o)<10)continue;let l=c(`w`);m={...m,walls:{...m.walls,[l]:{...s,id:l,a:t.nodeId,b:n.nodeId}}},A.push(l)}return A.length===0&&E.length>0&&d.push(u(e.DuplicateWall,`A wall already covers that span, so nothing was added.`)),{floor:m,result:{wallIds:A,nodeIds:k.map(e=>e.nodeId)},issues:d}}function C(e,t,n,r=x,i=1){let a=Math.min(t.x,n.x),o=Math.max(t.x,n.x),s=Math.min(t.y,n.y),c=Math.max(t.y,n.y);if(o-a<10||c-s<10)return{floor:e,result:{wallIds:[]},issues:[]};let l=[{x:a,y:s},{x:o,y:s},{x:o,y:c},{x:a,y:c}],u=e,d=[],f=[];for(let e=0;e<4;e++){let t=S(u,l[e],l[(e+1)%4],r,i);u=t.floor,d.push(...t.result.wallIds),f.push(...t.issues)}return{floor:u,result:{wallIds:d},issues:f}}function w(t,n,r){if(n===r||!t.nodes[n]||!t.nodes[r])return{floor:t,result:{removedWallIds:[]},issues:[]};let i=l(t),a=[],o=[];for(let e of Object.values(i.walls))e.a===r&&(i.walls[e.id]={...i.walls[e.id],a:n}),e.b===r&&(i.walls[e.id]={...i.walls[e.id],b:n});for(let e of Object.values(i.walls))if(e.a===e.b){delete i.walls[e.id],a.push(e.id);for(let t of Object.values(i.openings))t.wallId===e.id&&delete i.openings[t.id]}let s=new Map;for(let t of Object.values(i.walls)){let n=t.a<t.b?`${t.a}|${t.b}`:`${t.b}|${t.a}`,r=s.get(n);if(!r){s.set(n,t);continue}let c=t.thickness>=r.thickness?t:r,l=c===t?r:t;for(let e of Object.values(i.openings))e.wallId===l.id&&(i.openings[e.id]={...e,wallId:c.id});delete i.walls[l.id],a.push(l.id),s.set(n,c),o.push(u(e.DuplicateWall,`Two walls became the same wall and were merged.`,{wallIds:[c.id,l.id]}))}return delete i.nodes[r],{floor:i,result:{removedWallIds:a},issues:o}}function T(t,i,a={}){let{force:o=!1}=a,s=t.nodes[i];if(!s)return{floor:t,result:null,issues:[]};let p=f(t,i);if(p.length!==2)return{floor:t,result:null,issues:[]};let[m,h]=p,g=m.thickness===h.thickness&&m.type===h.type&&m.height===h.height&&m.baseHeight===h.baseHeight;if(!g&&!o)return{floor:t,result:null,issues:[]};let _=m.a===i?m.b:m.a,v=h.a===i?h.b:h.a;if(_===v)return{floor:t,result:null,issues:[]};let y=t.nodes[_],b=t.nodes[v];if(!y||!b)return{floor:t,result:null,issues:[]};let x=r(y,b);if(x<10||!o&&Math.abs(n(y,b,s))/x>Math.max(1,x*1e-4))return{floor:t,result:null,issues:[]};let S=!g&&m.thickness<h.thickness?h:m,C=l(t),w=c(`w`);C.walls[w]={id:w,a:_,b:v,thickness:S.thickness,type:S.type,height:S.height,baseHeight:S.baseHeight};let T=r(y,s),E=r(s,b),D=T+E>0?x/(T+E):1,O=[];for(let n of Object.values(t.openings)){if(n.wallId!==m.id&&n.wallId!==h.id)continue;let r=n.wallId===m.id,a=r?m:h,o=a.a===i,s=d(t,a),c=o?n.offset:s-n.offset,l=(r?T-c:T+c)*D,f=Math.min(n.width*D,x-2);if(f<100){O.push(u(e.OpeningDropped,`An opening was removed: the wall it sat on became too short for it.`,{openingIds:[n.id],wallIds:[a.id]})),delete C.openings[n.id];continue}C.openings[n.id]={...n,wallId:w,width:f,offset:Math.min(Math.max(l,f/2),x-f/2)}}return delete C.walls[m.id],delete C.walls[h.id],delete C.nodes[i],{floor:C,result:{wallId:w},issues:O}}function E(e,t,n){let r=e.walls[t];if(!r||r.a===r.b||Math.hypot(n.x,n.y)<10)return{floor:e,result:null,issues:[]};let i=[r.a,r.b],a=[r.a,r.b],o=[null,null],s=[],c=[],u=e;i.forEach((i,s)=>{let c=e.nodes[i];if(!c)return;let d={x:c.x+n.x,y:c.y+n.y},p=f(e,i).filter(e=>e.id!==t);if(p.length===0){let e=l(u);e.nodes[i]={...e.nodes[i],x:d.x,y:d.y},u=e;return}let m=b(u,d,1);u=m.floor,a[s]=m.result.nodeId,o[s]=p.length===1?p[0]:r});let d=l(u);d.walls[t]={...d.walls[t],a:a[0],b:a[1]},u=d,i.forEach((e,t)=>{let n=o[t],r=u.nodes[e],i=u.nodes[a[t]];if(!n||!r||!i)return;let l=S(u,r,i,{thickness:n.thickness,type:n.type,height:n.height,baseHeight:n.baseHeight});u=l.floor,s.push(...l.result.wallIds),c.push(...l.issues)});let p=[];return i.forEach((e,t)=>{if(a[t]===e)return;let n=T(u,e);n.result&&(u=n.floor,p.push(e),c.push(...n.issues))}),{floor:u,result:{nodeIds:a,connectorIds:s,dissolvedNodeIds:p},issues:c}}function D(e,t=10){let n=e,i=[],a=[];for(let o=Object.keys(e.walls).length;o>0;o--){let e=Object.values(n.walls).find(e=>{if(e.a===e.b)return!1;let i=n.nodes[e.a],a=n.nodes[e.b];return i&&a&&r(i,a)<t});if(!e)break;let o=f(n,e.b).length>f(n,e.a).length?e.b:e.a,s=w(n,o,o===e.a?e.b:e.a);if(s.floor===n)break;n=s.floor,i.push(e.id),a.push(...s.issues)}return{floor:n,result:{weldedWallIds:i},issues:a}}function O(e,t,n={}){let{pruneOrphanNodes:r=!0,dissolveCollinear:i=!0}=n,a=e.walls[t];if(!a)return{floor:e,result:{removedNodeIds:[],dissolvedWallIds:[]},issues:[]};let o=l(e);delete o.walls[t];for(let e of Object.values(o.openings))e.wallId===t&&delete o.openings[e.id];let s=[],c=[],u=[];for(let e of[a.a,a.b]){let t=f(o,e).length;if(t===0&&r)delete o.nodes[e],s.push(e);else if(t===2&&i){let t=T(o,e);t.result&&(o=t.floor,c.push(t.result.wallId),s.push(e),u.push(...t.issues))}}return{floor:o,result:{removedNodeIds:s,dissolvedWallIds:c},issues:u}}function k(e,t,n){let r=e.nodes[t];if(!r)return{floor:e,result:void 0,issues:[]};let i=l(e);return i.nodes[t]={...r,x:n.x,y:n.y},{floor:i,result:void 0,issues:[]}}function A(e,t,n){let r=l(e);for(let i of Object.values(e.nodes))r.nodes[i.id]={...i,x:i.x+t,y:i.y+n};for(let i of Object.values(e.rooms))r.rooms[i.id]={...i,anchor:{x:i.anchor.x+t,y:i.anchor.y+n}};return r}function j(e,t){let n=l(e);for(let r of Object.values(e.nodes))n.nodes[r.id]={...r,x:r.x*t,y:r.y*t};for(let r of Object.values(e.walls))n.walls[r.id]={...r,thickness:r.thickness*t,height:r.height===void 0?void 0:r.height*t,baseHeight:r.baseHeight===void 0?void 0:r.baseHeight*t};for(let r of Object.values(e.openings))n.openings[r.id]={...r,offset:r.offset*t,width:r.width*t,sill:r.sill*t,head:r.head*t};for(let r of Object.values(e.rooms))n.rooms[r.id]={...r,anchor:{x:r.anchor.x*t,y:r.anchor.y*t},area:r.area*t*t};return n}function M(t,n){let r=t.walls[n.wallId];if(!r)return{floor:t,result:{openingId:null},issues:[]};let i=d(t,r),a=Math.min(n.width,i-2);if(a<100)return{floor:t,result:{openingId:null},issues:[u(e.OpeningDropped,`That wall is too short for an opening.`,{wallIds:[r.id]})]};let o=Math.min(Math.max(n.offset,a/2),i-a/2),s=l(t),f=c(`o`);return s.openings[f]={...n,id:f,offset:o,width:a},{floor:s,result:{openingId:f},issues:[]}}function N(e,t,n){let r=e.openings[t];if(!r)return{floor:e,result:void 0,issues:[]};let i=e.walls[r.wallId];if(!i)return{floor:e,result:void 0,issues:[]};let a=d(e,i),o={...r,...n},s=Math.min(Math.max(o.width,100),Math.max(a-2,100)),c=Math.min(Math.max(o.offset,s/2),Math.max(a-s/2,s/2)),u=l(e);return u.openings[t]={...o,width:s,offset:c},{floor:u,result:void 0,issues:[]}}function P(e,t){if(!e.openings[t])return{floor:e,result:void 0,issues:[]};let n=l(e);return delete n.openings[t],{floor:n,result:void 0,issues:[]}}function F(t){let n=[],a=Object.values(t.walls);for(let i of a){let a=t.nodes[i.a],o=t.nodes[i.b];if(i.a===i.b){n.push(u(e.SelfLoopWall,`A wall starts and ends at the same node.`,{wallIds:[i.id]}));continue}if(!a||!o){n.push(u(e.ZeroLengthWall,`A wall references a missing node.`,{wallIds:[i.id]}));continue}r(a,o)<10&&n.push(u(e.ZeroLengthWall,`A wall is shorter than the minimum length.`,{wallIds:[i.id]}))}let s=new Map;for(let t of a){let r=t.a<t.b?`${t.a}|${t.b}`:`${t.b}|${t.a}`,i=s.get(r);i?n.push(u(e.DuplicateWall,`Two walls connect the same pair of nodes.`,{wallIds:[i,t.id]})):s.set(r,t.id)}let c=Object.values(t.nodes);for(let t=0;t<c.length;t++)for(let i=t+1;i<c.length;i++)r(c[t],c[i])<1&&n.push(u(e.CoincidentNodes,`Two nodes occupy the same position.`,{nodeIds:[c[t].id,c[i].id]}));for(let r=0;r<a.length;r++)for(let i=r+1;i<a.length;i++){let s=a[r],c=a[i],l=t.nodes[s.a],d=t.nodes[s.b],f=t.nodes[c.a],p=t.nodes[c.b];if(!l||!d||!f||!p)continue;let m=o(l,d,f,p);m.kind===`proper`?n.push(u(e.UnsplitCrossing,`Two walls cross with no node at the intersection.`,{wallIds:[s.id,c.id]})):m.kind===`collinearOverlap`&&n.push(u(e.OverlappingWalls,`Two walls overlap along the same line.`,{wallIds:[s.id,c.id]}))}for(let r of a){let a=t.nodes[r.a],o=t.nodes[r.b];if(a&&o)for(let t of c){if(t.id===r.a||t.id===r.b)continue;let s=i(t,a,o);s.dist<1&&s.t>0&&s.t<1&&n.push(u(e.UnsplitNodeOnWall,`A node sits on a wall that was never split there.`,{wallIds:[r.id],nodeIds:[t.id]}))}}for(let r of Object.values(t.openings))t.walls[r.wallId]||n.push(u(e.DanglingOpening,`An opening references a wall that no longer exists.`,{openingIds:[r.id]}));let l=new Set;for(let e of a)l.add(e.a),l.add(e.b);for(let t of c)l.has(t.id)||n.push(u(e.OrphanNode,`A node has no walls.`,{nodeIds:[t.id]}));return n}function I(t){let n=[],i=D(t),a=l(i.floor);for(let t of i.result.weldedWallIds)n.push(u(e.ZeroLengthWall,`Welded the two ends of a wall with no length.`,{wallIds:[t]}));for(let t of Object.values(a.walls)){let i=a.nodes[t.a],o=a.nodes[t.b];(t.a===t.b||!i||!o||r(i,o)<10)&&(delete a.walls[t.id],n.push(u(e.ZeroLengthWall,`Removed a degenerate wall.`,{wallIds:[t.id]})))}let o=new Map;for(let t of Object.values(a.walls)){let r=t.a<t.b?`${t.a}|${t.b}`:`${t.b}|${t.a}`,i=o.get(r);if(!i){o.set(r,t);continue}let s=t.thickness>=i.thickness?t:i,c=s===t?i:t;for(let e of Object.values(a.openings))e.wallId===c.id&&(a.openings[e.id]={...e,wallId:s.id});delete a.walls[c.id],o.set(r,s),n.push(u(e.DuplicateWall,`Removed a duplicate wall.`,{wallIds:[c.id]}))}for(let t of Object.values(a.openings))a.walls[t.wallId]||(delete a.openings[t.id],n.push(u(e.DanglingOpening,`Removed an opening with no wall.`,{openingIds:[t.id]})));let s=new Set;for(let e of Object.values(a.walls))s.add(e.a),s.add(e.b);for(let t of Object.values(a.nodes))s.has(t.id)||(delete a.nodes[t.id],n.push(u(e.OrphanNode,`Removed a node with no walls.`,{nodeIds:[t.id]})));return{floor:a,result:n,issues:n}}export{x as DEFAULT_WALL_ATTRS,M as addOpening,C as addRect,S as addWall,I as cleanupPlan,E as detachWall,T as dissolveNode,_ as findNodeNear,p as findWallBetween,v as findWallNear,w as mergeNodes,k as moveNode,c as newId,P as removeOpening,O as removeWall,b as resolveNodeAt,j as scalePlan,y as splitWall,A as transformPlan,N as updateOpening,F as validatePlan,d as wallLength,f as wallsAtNode,D as weldShortWalls};
1
+ "use client";import{PlanIssueKind as e,WallType as t}from"./floorPlan.types.mjs";import{cross as n,dist as r,pointToSegment as i,projectOnSegment as a,segmentIntersection as o}from"./floorPlan.geometry.mjs";var s=0;function c(e){return s+=1,`${e}_${typeof crypto<`u`&&typeof crypto.randomUUID==`function`?crypto.randomUUID().slice(0,8):Math.random().toString(36).slice(2,10)}${s.toString(36)}`}function l(e){return{...e,nodes:{...e.nodes},walls:{...e.walls},openings:{...e.openings},rooms:{...e.rooms}}}function u(e,t,n={}){return{kind:e,message:t,...n}}function d(e,t){let n=e.nodes[t.a],i=e.nodes[t.b];return!n||!i?0:r(n,i)}function f(e,t){return Object.values(e.walls).filter(e=>e.a===t||e.b===t)}function p(e,t,n){for(let r of Object.values(e.walls))if(r.a===t&&r.b===n||r.a===n&&r.b===t)return r;return null}var m=250;function h(e,t){return`${Math.round(e/m)}|${Math.round(t/m)}`}function g(e){let t=new Map;for(let n of Object.values(e.nodes)){let e=h(n.x,n.y),r=t.get(e);r?r.push(n):t.set(e,[n])}return t}function _(e,t,n){let i=g(e),a=Math.round(t.x/m),o=Math.round(t.y/m),s=null,c=n,l=Math.max(1,Math.ceil(n/m));for(let e=-l;e<=l;e++)for(let n=-l;n<=l;n++)for(let l of i.get(`${a+e}|${o+n}`)??[]){let e=r(t,l);e<=c&&(s=l,c=e)}return s}function v(e,t,n,r){let a=null;for(let o of Object.values(e.walls)){if(r?.has(o.id))continue;let s=e.nodes[o.a],c=e.nodes[o.b];if(!s||!c)continue;let l=i(t,s,c);l.dist<=n&&(!a||l.dist<a.dist)&&(a={wall:o,t:l.t,point:l.point,dist:l.dist})}return a}function y(t,n,i){let o=t.walls[n];if(!o)return{floor:t,result:{nodeId:``,wallIds:null,droppedOpeningIds:[]},issues:[]};let s=t.nodes[o.a],d=t.nodes[o.b];if(!s||!d)return{floor:t,result:{nodeId:``,wallIds:null,droppedOpeningIds:[]},issues:[]};let f=r(s,d),p=typeof i==`number`?{x:s.x+(d.x-s.x)*i,y:s.y+(d.y-s.y)*i}:i,m=a(p,s,d),h=r(s,m);if(h<10)return{floor:t,result:{nodeId:o.a,wallIds:null,droppedOpeningIds:[]},issues:[]};if(f-h<10)return{floor:t,result:{nodeId:o.b,wallIds:null,droppedOpeningIds:[]},issues:[]};let g=l(t),_=c(`n`);g.nodes[_]={id:_,x:m.x,y:m.y};let v=c(`w`),y=c(`w`),b={thickness:o.thickness,type:o.type,height:o.height,baseHeight:o.baseHeight};g.walls[v]={...b,id:v,a:o.a,b:_},g.walls[y]={...b,id:y,a:_,b:o.b},delete g.walls[n];let x=[],S=[];for(let r of Object.values(t.openings)){if(r.wallId!==n)continue;let t=r.offset<=h,i=t?v:y,a=t?h:f-h,o=t?r.offset:r.offset-h,s=Math.min(r.width,a-2,o*2,(a-o)*2);if(s<100){delete g.openings[r.id],x.push(r.id),S.push(u(e.OpeningDropped,`An opening was removed: the wall it sat on became too short for it.`,{openingIds:[r.id],wallIds:[n]}));continue}g.openings[r.id]={...r,wallId:i,offset:o,width:s}}return{floor:g,result:{nodeId:_,wallIds:[v,y],droppedOpeningIds:x},issues:S}}function b(e,t,n){let r=_(e,t,n);if(r)return{floor:e,result:{nodeId:r.id,createdNode:!1,splitWallId:null},issues:[]};let i=v(e,t,n);if(i){let t=y(e,i.wall.id,i.point);if(t.result.nodeId)return{floor:t.floor,result:{nodeId:t.result.nodeId,createdNode:t.result.wallIds!==null,splitWallId:t.result.wallIds?i.wall.id:null},issues:t.issues}}let a=l(e),o=c(`n`);return a.nodes[o]={id:o,x:t.x,y:t.y},{floor:a,result:{nodeId:o,createdNode:!0,splitWallId:null},issues:[]}}var x={thickness:200,type:t.Interior};function S(t,n,a,s=x,l=1){let d=[];if(r(n,a)<10)return{floor:t,result:{wallIds:[],nodeIds:[]},issues:d};let f=b(t,n,l),m=f.floor;d.push(...f.issues);let h=b(m,a,l);m=h.floor,d.push(...h.issues);let g=f.result.nodeId,_=h.result.nodeId;if(g===_)return{floor:t,result:{wallIds:[],nodeIds:[]},issues:d};let v=m.nodes[g],S=m.nodes[_],C=r(v,S);if(C<10)return{floor:t,result:{wallIds:[],nodeIds:[]},issues:d};let w=[{t:0,nodeId:g},{t:1,nodeId:_}],T=new Map,E=[],D=e=>i(e,v,S).t;for(let e of Object.values(m.walls)){let t=m.nodes[e.a],n=m.nodes[e.b];if(!t||!n)continue;let r=o(v,S,t,n,l);if(r.kind===`collinearOverlap`){let e=D(t),r=D(n);E.push([Math.min(e,r),Math.max(e,r)]);for(let[i,a]of[[t,e],[n,r]])a*C>10&&(1-a)*C>10&&w.push({t:a,nodeId:i.id});continue}if(r.kind===`proper`&&r.t!==void 0&&r.s!==void 0){let t=T.get(e.id);t?t.push(r.s):T.set(e.id,[r.s]),w.push({t:r.t,nodeId:null,pending:{wallId:e.id,s:r.s}});continue}if(r.kind===`touch`&&r.t!==void 0&&r.s!==void 0){let e=r.s<=.5?t:n,a=D(e);i(e,v,S).dist<=l&&a*C>10&&(1-a)*C>10&&w.push({t:a,nodeId:e.id})}}for(let e of Object.values(m.nodes)){if(e.id===g||e.id===_)continue;let t=i(e,v,S);t.dist>l||t.t*C<=10||(1-t.t)*C<=10||w.push({t:t.t,nodeId:e.id})}for(let[e,n]of T){let r=[...n].sort((e,t)=>t-e),i=e,a=new Set(r);for(let n of r){let r=m.walls[i];if(!r)break;let o=m.nodes[r.a],s=m.nodes[r.b];if(!o||!s)break;let c=t.walls[e],l=m.nodes[c?.a??r.a]??o,u=m.nodes[c?.b??r.b]??s,f={x:l.x+(u.x-l.x)*n,y:l.y+(u.y-l.y)*n},p=y(m,i,f);m=p.floor,d.push(...p.issues),a.delete(n);for(let t of w)t.pending&&t.pending.wallId===e&&t.pending.s===n&&(t.nodeId=p.result.nodeId,t.pending=void 0);if(p.result.wallIds){let[e,t]=p.result.wallIds,r=Math.max(...a,-1);i=r>=0&&r>n?t:e}}}let O=w.filter(e=>!!e.nodeId);O.sort((e,t)=>e.t-t.t);let k=[];for(let e of O){let t=k[k.length-1];if(!t){k.push(e);continue}if(t.nodeId!==e.nodeId){if((e.t-t.t)*C<10){e.t>=1-1e-9&&(k[k.length-1]=e);continue}k.push(e)}}let A=[];for(let e=0;e+1<k.length;e++){let t=k[e],n=k[e+1],i=(t.t+n.t)/2;if(E.some(([e,t])=>i>e+1e-9&&i<t-1e-9)||p(m,t.nodeId,n.nodeId))continue;let a=m.nodes[t.nodeId],o=m.nodes[n.nodeId];if(!a||!o||r(a,o)<10)continue;let l=c(`w`);m={...m,walls:{...m.walls,[l]:{...s,id:l,a:t.nodeId,b:n.nodeId}}},A.push(l)}return A.length===0&&E.length>0&&d.push(u(e.DuplicateWall,`A wall already covers that span, so nothing was added.`)),{floor:m,result:{wallIds:A,nodeIds:k.map(e=>e.nodeId)},issues:d}}function C(e,t,n,r=x,i=1){let a=Math.min(t.x,n.x),o=Math.max(t.x,n.x),s=Math.min(t.y,n.y),c=Math.max(t.y,n.y);if(o-a<10||c-s<10)return{floor:e,result:{wallIds:[]},issues:[]};let l=[{x:a,y:s},{x:o,y:s},{x:o,y:c},{x:a,y:c}],u=e,d=[],f=[];for(let e=0;e<4;e++){let t=S(u,l[e],l[(e+1)%4],r,i);u=t.floor,d.push(...t.result.wallIds),f.push(...t.issues)}return{floor:u,result:{wallIds:d},issues:f}}function w(t,n,r){if(n===r||!t.nodes[n]||!t.nodes[r])return{floor:t,result:{removedWallIds:[]},issues:[]};let i=l(t),a=[],o=[];for(let e of Object.values(i.walls))e.a===r&&(i.walls[e.id]={...i.walls[e.id],a:n}),e.b===r&&(i.walls[e.id]={...i.walls[e.id],b:n});for(let e of Object.values(i.walls))if(e.a===e.b){delete i.walls[e.id],a.push(e.id);for(let t of Object.values(i.openings))t.wallId===e.id&&delete i.openings[t.id]}let s=new Map;for(let t of Object.values(i.walls)){let n=t.a<t.b?`${t.a}|${t.b}`:`${t.b}|${t.a}`,r=s.get(n);if(!r){s.set(n,t);continue}let c=t.thickness>=r.thickness?t:r,l=c===t?r:t;for(let e of Object.values(i.openings))e.wallId===l.id&&(i.openings[e.id]={...e,wallId:c.id});delete i.walls[l.id],a.push(l.id),s.set(n,c),o.push(u(e.DuplicateWall,`Two walls became the same wall and were merged.`,{wallIds:[c.id,l.id]}))}return delete i.nodes[r],{floor:i,result:{removedWallIds:a},issues:o}}function T(t,i,a={}){let{force:o=!1}=a,s=t.nodes[i];if(!s)return{floor:t,result:null,issues:[]};let p=f(t,i);if(p.length!==2)return{floor:t,result:null,issues:[]};let[m,h]=p,g=m.thickness===h.thickness&&m.type===h.type&&m.height===h.height&&m.baseHeight===h.baseHeight;if(!g&&!o)return{floor:t,result:null,issues:[]};let _=m.a===i?m.b:m.a,v=h.a===i?h.b:h.a;if(_===v)return{floor:t,result:null,issues:[]};let y=t.nodes[_],b=t.nodes[v];if(!y||!b)return{floor:t,result:null,issues:[]};let x=r(y,b);if(x<10||!o&&Math.abs(n(y,b,s))/x>Math.max(1,x*1e-4))return{floor:t,result:null,issues:[]};let S=!g&&m.thickness<h.thickness?h:m,C=l(t),w=c(`w`);C.walls[w]={id:w,a:_,b:v,thickness:S.thickness,type:S.type,height:S.height,baseHeight:S.baseHeight};let T=r(y,s),E=r(s,b),D=T+E>0?x/(T+E):1,O=[];for(let n of Object.values(t.openings)){if(n.wallId!==m.id&&n.wallId!==h.id)continue;let r=n.wallId===m.id,a=r?m:h,o=a.a===i,s=d(t,a),c=o?n.offset:s-n.offset,l=(r?T-c:T+c)*D,f=Math.min(n.width*D,x-2);if(f<100){O.push(u(e.OpeningDropped,`An opening was removed: the wall it sat on became too short for it.`,{openingIds:[n.id],wallIds:[a.id]})),delete C.openings[n.id];continue}C.openings[n.id]={...n,wallId:w,width:f,offset:Math.min(Math.max(l,f/2),x-f/2)}}return delete C.walls[m.id],delete C.walls[h.id],delete C.nodes[i],{floor:C,result:{wallId:w},issues:O}}function E(e,t,n){let r=e.walls[t];if(!r||r.a===r.b||Math.hypot(n.x,n.y)<10)return{floor:e,result:null,issues:[]};let i=[r.a,r.b],a=[r.a,r.b],o=[null,null],s=[],c=[],u=e;i.forEach((i,s)=>{let c=e.nodes[i];if(!c)return;let d={x:c.x+n.x,y:c.y+n.y},p=f(e,i).filter(e=>e.id!==t);if(p.length===0){let e=l(u);e.nodes[i]={...e.nodes[i],x:d.x,y:d.y},u=e;return}let m=b(u,d,1);u=m.floor,a[s]=m.result.nodeId,o[s]=p.length===1?p[0]:r});let d=l(u);d.walls[t]={...d.walls[t],a:a[0],b:a[1]},u=d,i.forEach((e,t)=>{let n=o[t],r=u.nodes[e],i=u.nodes[a[t]];if(!n||!r||!i)return;let l=S(u,r,i,{thickness:n.thickness,type:n.type,height:n.height,baseHeight:n.baseHeight});u=l.floor,s.push(...l.result.wallIds),c.push(...l.issues)});let p=[];return i.forEach((e,t)=>{if(a[t]===e)return;let n=T(u,e);n.result&&(u=n.floor,p.push(e),c.push(...n.issues))}),{floor:u,result:{nodeIds:a,connectorIds:s,dissolvedNodeIds:p},issues:c}}function D(e,t=10){let n=e,i=[],a=[];for(let o=Object.keys(e.walls).length;o>0;o--){let e=Object.values(n.walls).find(e=>{if(e.a===e.b)return!1;let i=n.nodes[e.a],a=n.nodes[e.b];return i&&a&&r(i,a)<t});if(!e)break;let o=f(n,e.b).length>f(n,e.a).length?e.b:e.a,s=w(n,o,o===e.a?e.b:e.a);if(s.floor===n)break;n=s.floor,i.push(e.id),a.push(...s.issues)}return{floor:n,result:{weldedWallIds:i},issues:a}}function O(e,t,n={}){let{pruneOrphanNodes:r=!0,dissolveCollinear:i=!0}=n,a=e.walls[t];if(!a)return{floor:e,result:{removedNodeIds:[],dissolvedWallIds:[]},issues:[]};let o=l(e);delete o.walls[t];for(let e of Object.values(o.openings))e.wallId===t&&delete o.openings[e.id];let s=[],c=[],u=[];for(let e of[a.a,a.b]){let t=f(o,e).length;if(t===0&&r)delete o.nodes[e],s.push(e);else if(t===2&&i){let t=T(o,e);t.result&&(o=t.floor,c.push(t.result.wallId),s.push(e),u.push(...t.issues))}}return{floor:o,result:{removedNodeIds:s,dissolvedWallIds:c},issues:u}}function k(e,t,n){let r=e.nodes[t];if(!r)return{floor:e,result:void 0,issues:[]};let i=l(e);return i.nodes[t]={...r,x:n.x,y:n.y},{floor:i,result:void 0,issues:[]}}function A(e,t,n){let r=l(e);for(let i of Object.values(e.nodes))r.nodes[i.id]={...i,x:i.x+t,y:i.y+n};for(let i of Object.values(e.rooms))r.rooms[i.id]={...i,anchor:{x:i.anchor.x+t,y:i.anchor.y+n}};return r}function j(e,t){let n=l(e);for(let r of Object.values(e.nodes))n.nodes[r.id]={...r,x:r.x*t,y:r.y*t};for(let r of Object.values(e.walls))n.walls[r.id]={...r,thickness:r.thickness*t,height:r.height===void 0?void 0:r.height*t,baseHeight:r.baseHeight===void 0?void 0:r.baseHeight*t};for(let r of Object.values(e.openings))n.openings[r.id]={...r,offset:r.offset*t,width:r.width*t,sill:r.sill*t,head:r.head*t};for(let r of Object.values(e.rooms))n.rooms[r.id]={...r,anchor:{x:r.anchor.x*t,y:r.anchor.y*t},area:r.area*t*t};return n}function M(t,n){let r=t.walls[n.wallId];if(!r)return{floor:t,result:{openingId:null},issues:[]};let i=d(t,r),a=Math.min(n.width,i-2);if(a<100)return{floor:t,result:{openingId:null},issues:[u(e.OpeningDropped,`That wall is too short for an opening.`,{wallIds:[r.id]})]};let o=Math.min(Math.max(n.offset,a/2),i-a/2),s=l(t),f=c(`o`);return s.openings[f]={...n,id:f,offset:o,width:a},{floor:s,result:{openingId:f},issues:[]}}function N(e,t,n){let r=e.openings[t];if(!r)return{floor:e,result:void 0,issues:[]};let i=e.walls[r.wallId];if(!i)return{floor:e,result:void 0,issues:[]};let a=d(e,i),o={...r,...n},s=Math.min(Math.max(o.width,100),Math.max(a-2,100)),c=Math.min(Math.max(o.offset,s/2),Math.max(a-s/2,s/2)),u=l(e);return u.openings[t]={...o,width:s,offset:c},{floor:u,result:void 0,issues:[]}}function P(e,t){if(!e.openings[t])return{floor:e,result:void 0,issues:[]};let n=l(e);return delete n.openings[t],{floor:n,result:void 0,issues:[]}}function F(t){let n=[],a=Object.values(t.walls);for(let i of a){let a=t.nodes[i.a],o=t.nodes[i.b];if(i.a===i.b){n.push(u(e.SelfLoopWall,`A wall starts and ends at the same node.`,{wallIds:[i.id]}));continue}if(!a||!o){n.push(u(e.ZeroLengthWall,`A wall references a missing node.`,{wallIds:[i.id]}));continue}r(a,o)<10&&n.push(u(e.ZeroLengthWall,`A wall is shorter than the minimum length.`,{wallIds:[i.id]}))}let s=new Map;for(let t of a){let r=t.a<t.b?`${t.a}|${t.b}`:`${t.b}|${t.a}`,i=s.get(r);i?n.push(u(e.DuplicateWall,`Two walls connect the same pair of nodes.`,{wallIds:[i,t.id]})):s.set(r,t.id)}let c=Object.values(t.nodes);for(let t=0;t<c.length;t++)for(let i=t+1;i<c.length;i++)r(c[t],c[i])<1&&n.push(u(e.CoincidentNodes,`Two nodes occupy the same position.`,{nodeIds:[c[t].id,c[i].id]}));for(let r=0;r<a.length;r++)for(let i=r+1;i<a.length;i++){let s=a[r],c=a[i],l=t.nodes[s.a],d=t.nodes[s.b],f=t.nodes[c.a],p=t.nodes[c.b];if(!l||!d||!f||!p)continue;let m=o(l,d,f,p);m.kind===`proper`?n.push(u(e.UnsplitCrossing,`Two walls cross with no node at the intersection.`,{wallIds:[s.id,c.id]})):m.kind===`collinearOverlap`&&n.push(u(e.OverlappingWalls,`Two walls overlap along the same line.`,{wallIds:[s.id,c.id]}))}for(let r of a){let a=t.nodes[r.a],o=t.nodes[r.b];if(a&&o)for(let t of c){if(t.id===r.a||t.id===r.b)continue;let s=i(t,a,o);s.dist<1&&s.t>0&&s.t<1&&n.push(u(e.UnsplitNodeOnWall,`A node sits on a wall that was never split there.`,{wallIds:[r.id],nodeIds:[t.id]}))}}for(let r of Object.values(t.openings))t.walls[r.wallId]||n.push(u(e.DanglingOpening,`An opening references a wall that no longer exists.`,{openingIds:[r.id]}));let l=new Set;for(let e of a)l.add(e.a),l.add(e.b);for(let t of c)l.has(t.id)||n.push(u(e.OrphanNode,`A node has no walls.`,{nodeIds:[t.id]}));return n}function I(t){let n=[],i=D(t),a=l(i.floor);for(let t of i.result.weldedWallIds)n.push(u(e.ZeroLengthWall,`Welded the two ends of a wall with no length.`,{wallIds:[t]}));for(let t of Object.values(a.walls)){let i=a.nodes[t.a],o=a.nodes[t.b];(t.a===t.b||!i||!o||r(i,o)<10)&&(delete a.walls[t.id],n.push(u(e.ZeroLengthWall,`Removed a degenerate wall.`,{wallIds:[t.id]})))}let o=new Map;for(let t of Object.values(a.walls)){let r=t.a<t.b?`${t.a}|${t.b}`:`${t.b}|${t.a}`,i=o.get(r);if(!i){o.set(r,t);continue}let s=t.thickness>=i.thickness?t:i,c=s===t?i:t;for(let e of Object.values(a.openings))e.wallId===c.id&&(a.openings[e.id]={...e,wallId:s.id});delete a.walls[c.id],o.set(r,s),n.push(u(e.DuplicateWall,`Removed a duplicate wall.`,{wallIds:[c.id]}))}for(let t of Object.values(a.openings))a.walls[t.wallId]||(delete a.openings[t.id],n.push(u(e.DanglingOpening,`Removed an opening with no wall.`,{openingIds:[t.id]})));let s=new Set;for(let e of Object.values(a.walls))s.add(e.a),s.add(e.b);for(let t of Object.values(a.nodes))s.has(t.id)||(delete a.nodes[t.id],n.push(u(e.OrphanNode,`Removed a node with no walls.`,{nodeIds:[t.id]})));return{floor:a,result:n,issues:n}}export{x as DEFAULT_WALL_ATTRS,M as addOpening,C as addRect,S as addWall,I as cleanupPlan,E as detachWall,T as dissolveNode,_ as findNodeNear,p as findWallBetween,v as findWallNear,w as mergeNodes,k as moveNode,c as newId,P as removeOpening,O as removeWall,b as resolveNodeAt,j as scalePlan,y as splitWall,A as transformPlan,N as updateOpening,F as validatePlan,d as wallLength,f as wallsAtNode,D as weldShortWalls};
2
2
  //# sourceMappingURL=floorPlan.graph.mjs.map