mythik-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +4 -0
  3. package/README.md +83 -0
  4. package/dist/MythikApp.d.ts +61 -0
  5. package/dist/MythikApp.d.ts.map +1 -0
  6. package/dist/MythikApp.js +381 -0
  7. package/dist/MythikApp.js.map +1 -0
  8. package/dist/MythikRenderer.d.ts +31 -0
  9. package/dist/MythikRenderer.d.ts.map +1 -0
  10. package/dist/MythikRenderer.js +900 -0
  11. package/dist/MythikRenderer.js.map +1 -0
  12. package/dist/animation/index.d.ts +7 -0
  13. package/dist/animation/index.d.ts.map +1 -0
  14. package/dist/animation/index.js +5 -0
  15. package/dist/animation/index.js.map +1 -0
  16. package/dist/animation/stylesheet-singleton.d.ts +12 -0
  17. package/dist/animation/stylesheet-singleton.d.ts.map +1 -0
  18. package/dist/animation/stylesheet-singleton.js +107 -0
  19. package/dist/animation/stylesheet-singleton.js.map +1 -0
  20. package/dist/animation/useElementAnimations.d.ts +30 -0
  21. package/dist/animation/useElementAnimations.d.ts.map +1 -0
  22. package/dist/animation/useElementAnimations.js +254 -0
  23. package/dist/animation/useElementAnimations.js.map +1 -0
  24. package/dist/animation/usePrefersReducedMotion.d.ts +2 -0
  25. package/dist/animation/usePrefersReducedMotion.d.ts.map +1 -0
  26. package/dist/animation/usePrefersReducedMotion.js +29 -0
  27. package/dist/animation/usePrefersReducedMotion.js.map +1 -0
  28. package/dist/animation/useShapeAnimations.d.ts +21 -0
  29. package/dist/animation/useShapeAnimations.d.ts.map +1 -0
  30. package/dist/animation/useShapeAnimations.js +119 -0
  31. package/dist/animation/useShapeAnimations.js.map +1 -0
  32. package/dist/app-context.d.ts +15 -0
  33. package/dist/app-context.d.ts.map +1 -0
  34. package/dist/app-context.js +9 -0
  35. package/dist/app-context.js.map +1 -0
  36. package/dist/background/BackgroundLayer.d.ts +7 -0
  37. package/dist/background/BackgroundLayer.d.ts.map +1 -0
  38. package/dist/background/BackgroundLayer.js +50 -0
  39. package/dist/background/BackgroundLayer.js.map +1 -0
  40. package/dist/background/BackgroundStack.d.ts +19 -0
  41. package/dist/background/BackgroundStack.d.ts.map +1 -0
  42. package/dist/background/BackgroundStack.js +59 -0
  43. package/dist/background/BackgroundStack.js.map +1 -0
  44. package/dist/background/BlobLayer.d.ts +12 -0
  45. package/dist/background/BlobLayer.d.ts.map +1 -0
  46. package/dist/background/BlobLayer.js +60 -0
  47. package/dist/background/BlobLayer.js.map +1 -0
  48. package/dist/background/index.d.ts +3 -0
  49. package/dist/background/index.d.ts.map +1 -0
  50. package/dist/background/index.js +3 -0
  51. package/dist/background/index.js.map +1 -0
  52. package/dist/css-hover.d.ts +15 -0
  53. package/dist/css-hover.d.ts.map +1 -0
  54. package/dist/css-hover.js +51 -0
  55. package/dist/css-hover.js.map +1 -0
  56. package/dist/index.d.ts +10 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +11 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/primitives/accordion.d.ts +12 -0
  61. package/dist/primitives/accordion.d.ts.map +1 -0
  62. package/dist/primitives/accordion.js +25 -0
  63. package/dist/primitives/accordion.js.map +1 -0
  64. package/dist/primitives/area-chart.d.ts +14 -0
  65. package/dist/primitives/area-chart.d.ts.map +1 -0
  66. package/dist/primitives/area-chart.js +18 -0
  67. package/dist/primitives/area-chart.js.map +1 -0
  68. package/dist/primitives/audio-player.d.ts +9 -0
  69. package/dist/primitives/audio-player.d.ts.map +1 -0
  70. package/dist/primitives/audio-player.js +5 -0
  71. package/dist/primitives/audio-player.js.map +1 -0
  72. package/dist/primitives/bar-chart.d.ts +14 -0
  73. package/dist/primitives/bar-chart.d.ts.map +1 -0
  74. package/dist/primitives/bar-chart.js +22 -0
  75. package/dist/primitives/bar-chart.js.map +1 -0
  76. package/dist/primitives/box.d.ts +21 -0
  77. package/dist/primitives/box.d.ts.map +1 -0
  78. package/dist/primitives/box.js +54 -0
  79. package/dist/primitives/box.js.map +1 -0
  80. package/dist/primitives/button.d.ts +14 -0
  81. package/dist/primitives/button.d.ts.map +1 -0
  82. package/dist/primitives/button.js +28 -0
  83. package/dist/primitives/button.js.map +1 -0
  84. package/dist/primitives/camera.d.ts +15 -0
  85. package/dist/primitives/camera.d.ts.map +1 -0
  86. package/dist/primitives/camera.js +25 -0
  87. package/dist/primitives/camera.js.map +1 -0
  88. package/dist/primitives/checkbox.d.ts +12 -0
  89. package/dist/primitives/checkbox.d.ts.map +1 -0
  90. package/dist/primitives/checkbox.js +24 -0
  91. package/dist/primitives/checkbox.js.map +1 -0
  92. package/dist/primitives/divider.d.ts +9 -0
  93. package/dist/primitives/divider.d.ts.map +1 -0
  94. package/dist/primitives/divider.js +10 -0
  95. package/dist/primitives/divider.js.map +1 -0
  96. package/dist/primitives/drawer.d.ts +21 -0
  97. package/dist/primitives/drawer.d.ts.map +1 -0
  98. package/dist/primitives/drawer.js +38 -0
  99. package/dist/primitives/drawer.js.map +1 -0
  100. package/dist/primitives/file-upload.d.ts +27 -0
  101. package/dist/primitives/file-upload.d.ts.map +1 -0
  102. package/dist/primitives/file-upload.js +225 -0
  103. package/dist/primitives/file-upload.js.map +1 -0
  104. package/dist/primitives/grid.d.ts +13 -0
  105. package/dist/primitives/grid.d.ts.map +1 -0
  106. package/dist/primitives/grid.js +13 -0
  107. package/dist/primitives/grid.js.map +1 -0
  108. package/dist/primitives/icon.d.ts +22 -0
  109. package/dist/primitives/icon.d.ts.map +1 -0
  110. package/dist/primitives/icon.js +52 -0
  111. package/dist/primitives/icon.js.map +1 -0
  112. package/dist/primitives/image.d.ts +13 -0
  113. package/dist/primitives/image.d.ts.map +1 -0
  114. package/dist/primitives/image.js +38 -0
  115. package/dist/primitives/image.js.map +1 -0
  116. package/dist/primitives/index.d.ts +57 -0
  117. package/dist/primitives/index.d.ts.map +1 -0
  118. package/dist/primitives/index.js +106 -0
  119. package/dist/primitives/index.js.map +1 -0
  120. package/dist/primitives/input.d.ts +32 -0
  121. package/dist/primitives/input.d.ts.map +1 -0
  122. package/dist/primitives/input.js +192 -0
  123. package/dist/primitives/input.js.map +1 -0
  124. package/dist/primitives/kanban-board.d.ts +13 -0
  125. package/dist/primitives/kanban-board.d.ts.map +1 -0
  126. package/dist/primitives/kanban-board.js +5 -0
  127. package/dist/primitives/kanban-board.js.map +1 -0
  128. package/dist/primitives/line-chart.d.ts +14 -0
  129. package/dist/primitives/line-chart.d.ts.map +1 -0
  130. package/dist/primitives/line-chart.js +17 -0
  131. package/dist/primitives/line-chart.js.map +1 -0
  132. package/dist/primitives/list.d.ts +13 -0
  133. package/dist/primitives/list.d.ts.map +1 -0
  134. package/dist/primitives/list.js +10 -0
  135. package/dist/primitives/list.js.map +1 -0
  136. package/dist/primitives/modal.d.ts +20 -0
  137. package/dist/primitives/modal.d.ts.map +1 -0
  138. package/dist/primitives/modal.js +60 -0
  139. package/dist/primitives/modal.js.map +1 -0
  140. package/dist/primitives/pie-chart.d.ts +15 -0
  141. package/dist/primitives/pie-chart.d.ts.map +1 -0
  142. package/dist/primitives/pie-chart.js +36 -0
  143. package/dist/primitives/pie-chart.js.map +1 -0
  144. package/dist/primitives/screen-outlet.d.ts +9 -0
  145. package/dist/primitives/screen-outlet.d.ts.map +1 -0
  146. package/dist/primitives/screen-outlet.js +92 -0
  147. package/dist/primitives/screen-outlet.js.map +1 -0
  148. package/dist/primitives/screen.d.ts +9 -0
  149. package/dist/primitives/screen.d.ts.map +1 -0
  150. package/dist/primitives/screen.js +10 -0
  151. package/dist/primitives/screen.js.map +1 -0
  152. package/dist/primitives/scroll.d.ts +11 -0
  153. package/dist/primitives/scroll.d.ts.map +1 -0
  154. package/dist/primitives/scroll.js +10 -0
  155. package/dist/primitives/scroll.js.map +1 -0
  156. package/dist/primitives/select.d.ts +19 -0
  157. package/dist/primitives/select.d.ts.map +1 -0
  158. package/dist/primitives/select.js +109 -0
  159. package/dist/primitives/select.js.map +1 -0
  160. package/dist/primitives/signature.d.ts +13 -0
  161. package/dist/primitives/signature.d.ts.map +1 -0
  162. package/dist/primitives/signature.js +45 -0
  163. package/dist/primitives/signature.js.map +1 -0
  164. package/dist/primitives/skeleton.d.ts +14 -0
  165. package/dist/primitives/skeleton.d.ts.map +1 -0
  166. package/dist/primitives/skeleton.js +41 -0
  167. package/dist/primitives/skeleton.js.map +1 -0
  168. package/dist/primitives/slider.d.ts +15 -0
  169. package/dist/primitives/slider.d.ts.map +1 -0
  170. package/dist/primitives/slider.js +7 -0
  171. package/dist/primitives/slider.js.map +1 -0
  172. package/dist/primitives/spacer.d.ts +9 -0
  173. package/dist/primitives/spacer.d.ts.map +1 -0
  174. package/dist/primitives/spacer.js +9 -0
  175. package/dist/primitives/spacer.js.map +1 -0
  176. package/dist/primitives/spatial-map-editing.d.ts +472 -0
  177. package/dist/primitives/spatial-map-editing.d.ts.map +1 -0
  178. package/dist/primitives/spatial-map-editing.js +886 -0
  179. package/dist/primitives/spatial-map-editing.js.map +1 -0
  180. package/dist/primitives/spatial-map.d.ts +1073 -0
  181. package/dist/primitives/spatial-map.d.ts.map +1 -0
  182. package/dist/primitives/spatial-map.js +1705 -0
  183. package/dist/primitives/spatial-map.js.map +1 -0
  184. package/dist/primitives/stack.d.ts +13 -0
  185. package/dist/primitives/stack.d.ts.map +1 -0
  186. package/dist/primitives/stack.js +12 -0
  187. package/dist/primitives/stack.js.map +1 -0
  188. package/dist/primitives/table.d.ts +115 -0
  189. package/dist/primitives/table.d.ts.map +1 -0
  190. package/dist/primitives/table.js +498 -0
  191. package/dist/primitives/table.js.map +1 -0
  192. package/dist/primitives/tabs.d.ts +17 -0
  193. package/dist/primitives/tabs.d.ts.map +1 -0
  194. package/dist/primitives/tabs.js +13 -0
  195. package/dist/primitives/tabs.js.map +1 -0
  196. package/dist/primitives/text.d.ts +11 -0
  197. package/dist/primitives/text.d.ts.map +1 -0
  198. package/dist/primitives/text.js +69 -0
  199. package/dist/primitives/text.js.map +1 -0
  200. package/dist/primitives/textarea.d.ts +15 -0
  201. package/dist/primitives/textarea.d.ts.map +1 -0
  202. package/dist/primitives/textarea.js +23 -0
  203. package/dist/primitives/textarea.js.map +1 -0
  204. package/dist/primitives/toast-container.d.ts +15 -0
  205. package/dist/primitives/toast-container.d.ts.map +1 -0
  206. package/dist/primitives/toast-container.js +160 -0
  207. package/dist/primitives/toast-container.js.map +1 -0
  208. package/dist/primitives/toggle.d.ts +12 -0
  209. package/dist/primitives/toggle.d.ts.map +1 -0
  210. package/dist/primitives/toggle.js +18 -0
  211. package/dist/primitives/toggle.js.map +1 -0
  212. package/dist/primitives/touchable.d.ts +10 -0
  213. package/dist/primitives/touchable.d.ts.map +1 -0
  214. package/dist/primitives/touchable.js +6 -0
  215. package/dist/primitives/touchable.js.map +1 -0
  216. package/dist/primitives/use-design-tokens.d.ts +127 -0
  217. package/dist/primitives/use-design-tokens.d.ts.map +1 -0
  218. package/dist/primitives/use-design-tokens.js +251 -0
  219. package/dist/primitives/use-design-tokens.js.map +1 -0
  220. package/dist/primitives/use-theme.d.ts +11 -0
  221. package/dist/primitives/use-theme.d.ts.map +1 -0
  222. package/dist/primitives/use-theme.js +17 -0
  223. package/dist/primitives/use-theme.js.map +1 -0
  224. package/dist/primitives/wizard.d.ts +11 -0
  225. package/dist/primitives/wizard.d.ts.map +1 -0
  226. package/dist/primitives/wizard.js +15 -0
  227. package/dist/primitives/wizard.js.map +1 -0
  228. package/dist/runtime/context-dispatcher.d.ts +3 -0
  229. package/dist/runtime/context-dispatcher.d.ts.map +1 -0
  230. package/dist/runtime/context-dispatcher.js +11 -0
  231. package/dist/runtime/context-dispatcher.js.map +1 -0
  232. package/dist/runtime/row-dispatcher.d.ts +19 -0
  233. package/dist/runtime/row-dispatcher.d.ts.map +1 -0
  234. package/dist/runtime/row-dispatcher.js +25 -0
  235. package/dist/runtime/row-dispatcher.js.map +1 -0
  236. package/dist/types.d.ts +10 -0
  237. package/dist/types.d.ts.map +1 -0
  238. package/dist/types.js +2 -0
  239. package/dist/types.js.map +1 -0
  240. package/dist/use-device-context.d.ts +8 -0
  241. package/dist/use-device-context.d.ts.map +1 -0
  242. package/dist/use-device-context.js +54 -0
  243. package/dist/use-device-context.js.map +1 -0
  244. package/package.json +59 -0
@@ -0,0 +1,886 @@
1
+ export const DEFAULT_GRID_SIZE = 20;
2
+ export const DEFAULT_SNAP_THRESHOLD = 8;
3
+ function finiteNumber(value, fallback) {
4
+ return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
5
+ }
6
+ function positiveFiniteNumber(value, fallback) {
7
+ return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : fallback;
8
+ }
9
+ function nonNegativeFiniteNumber(value, fallback) {
10
+ return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;
11
+ }
12
+ function normalizeScaleLimit(value, fallback) {
13
+ return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : fallback;
14
+ }
15
+ function normalizeGridSize(size) {
16
+ if (typeof size === 'number') {
17
+ const value = positiveFiniteNumber(size, DEFAULT_GRID_SIZE);
18
+ return { x: value, y: value };
19
+ }
20
+ return {
21
+ x: positiveFiniteNumber(size?.x, DEFAULT_GRID_SIZE),
22
+ y: positiveFiniteNumber(size?.y, DEFAULT_GRID_SIZE),
23
+ };
24
+ }
25
+ function normalizePointConfig(point) {
26
+ return {
27
+ x: finiteNumber(point?.x, 0),
28
+ y: finiteNumber(point?.y, 0),
29
+ };
30
+ }
31
+ function normalizeSnapPolicy(policy) {
32
+ const enabled = policy?.enabled === true;
33
+ return {
34
+ enabled,
35
+ grid: {
36
+ enabled: enabled && policy.grid?.enabled === true,
37
+ size: normalizeGridSize(policy?.grid?.size),
38
+ offset: normalizePointConfig(policy?.grid?.offset),
39
+ threshold: nonNegativeFiniteNumber(policy?.grid?.threshold, DEFAULT_SNAP_THRESHOLD),
40
+ },
41
+ itemCenters: {
42
+ enabled: enabled && policy?.itemCenters?.enabled === true,
43
+ threshold: nonNegativeFiniteNumber(policy?.itemCenters?.threshold, DEFAULT_SNAP_THRESHOLD),
44
+ },
45
+ };
46
+ }
47
+ function normalizeGuidePolicy(policy) {
48
+ const enabled = policy?.enabled === true;
49
+ return {
50
+ enabled,
51
+ showCoordinates: enabled && policy?.showCoordinates === true,
52
+ showSnapLines: enabled && policy?.showSnapLines !== false,
53
+ };
54
+ }
55
+ export function getEditPolicy(mode, policy) {
56
+ const editing = mode === 'edit';
57
+ const configured = {
58
+ bounds: policy?.bounds ?? 'viewBox',
59
+ keyboardStep: policy?.keyboardStep ?? 1,
60
+ keyboardLargeStep: policy?.keyboardLargeStep ?? 10,
61
+ coordinatePrecision: policy?.coordinatePrecision ?? 0,
62
+ };
63
+ const minScale = normalizeScaleLimit(policy?.minScale, 0.1);
64
+ const rawMaxScale = normalizeScaleLimit(policy?.maxScale, 10);
65
+ const maxScale = rawMaxScale > minScale ? rawMaxScale : 10;
66
+ return {
67
+ dragItems: editing && policy?.dragItems !== false,
68
+ dragZones: editing && policy?.dragZones !== false,
69
+ keyboardMoveItems: editing && policy?.keyboardMoveItems !== false,
70
+ keyboardMoveZones: editing && policy?.keyboardMoveZones !== false,
71
+ resizeItems: editing && policy?.resizeItems !== false,
72
+ resizeZones: editing && policy?.resizeZones !== false,
73
+ rotateItems: editing && policy?.rotateItems !== false,
74
+ keyboardResizeItems: editing && policy?.keyboardResizeItems !== false,
75
+ keyboardResizeZones: editing && policy?.keyboardResizeZones !== false,
76
+ keyboardRotateItems: editing && policy?.keyboardRotateItems !== false,
77
+ shapeEditZones: editing && policy?.shapeEditZones !== false,
78
+ keyboardShapeEditZones: editing && policy?.keyboardShapeEditZones !== false,
79
+ bounds: configured.bounds,
80
+ boundsMode: 'position',
81
+ keyboardStep: configured.keyboardStep,
82
+ keyboardLargeStep: configured.keyboardLargeStep,
83
+ resizeStep: positiveFiniteNumber(policy?.resizeStep, 0.05),
84
+ resizeLargeStep: positiveFiniteNumber(policy?.resizeLargeStep, 0.25),
85
+ rotationStep: nonNegativeFiniteNumber(policy?.rotationStep, 5),
86
+ rotationLargeStep: nonNegativeFiniteNumber(policy?.rotationLargeStep, 15),
87
+ minScale,
88
+ maxScale,
89
+ handles: {
90
+ visible: editing && policy?.handles?.visible !== false,
91
+ resize: editing && policy?.handles?.resize !== false,
92
+ rotate: editing && policy?.handles?.rotate !== false,
93
+ },
94
+ coordinatePrecision: configured.coordinatePrecision,
95
+ snap: editing
96
+ ? normalizeSnapPolicy(policy?.snap)
97
+ : normalizeSnapPolicy(),
98
+ guides: editing
99
+ ? normalizeGuidePolicy(policy?.guides)
100
+ : normalizeGuidePolicy(),
101
+ };
102
+ }
103
+ export function clampPointToRect(point, rect) {
104
+ return {
105
+ x: Math.min(rect.x + rect.width, Math.max(rect.x, point.x)),
106
+ y: Math.min(rect.y + rect.height, Math.max(rect.y, point.y)),
107
+ };
108
+ }
109
+ export function roundPoint(point, precision) {
110
+ const safePrecision = Number.isFinite(precision) && precision > 0 ? Math.floor(precision) : 0;
111
+ const factor = 10 ** safePrecision;
112
+ return {
113
+ x: Math.round(point.x * factor) / factor,
114
+ y: Math.round(point.y * factor) / factor,
115
+ };
116
+ }
117
+ export function normalizeEditPosition(point, rect, policy) {
118
+ const bounded = policy.bounds === 'viewBox'
119
+ ? clampPointToRect(point, rect)
120
+ : point;
121
+ return roundPoint(bounded, policy.coordinatePrecision);
122
+ }
123
+ export function normalizeSpatialTransform(transform, policy) {
124
+ const rawScaleX = finiteNumber(transform?.scaleX, 1);
125
+ const rawScaleY = finiteNumber(transform?.scaleY, 1);
126
+ return {
127
+ scaleX: Math.min(policy.maxScale, Math.max(policy.minScale, rawScaleX)),
128
+ scaleY: Math.min(policy.maxScale, Math.max(policy.minScale, rawScaleY)),
129
+ };
130
+ }
131
+ function finitePoint(point) {
132
+ return Boolean(point
133
+ && typeof point === 'object'
134
+ && Number.isFinite(point.x)
135
+ && Number.isFinite(point.y));
136
+ }
137
+ function parsePolygonPoints(points) {
138
+ if (typeof points === 'string') {
139
+ return points
140
+ .trim()
141
+ .split(/\s+/)
142
+ .map((pair) => {
143
+ const [x, y] = pair.split(',').map(Number);
144
+ return { x, y };
145
+ })
146
+ .filter(finitePoint);
147
+ }
148
+ return points
149
+ .map((point) => (Array.isArray(point) ? { x: point[0], y: point[1] } : point))
150
+ .filter(finitePoint);
151
+ }
152
+ function boundsFromPoints(points) {
153
+ if (points.length === 0)
154
+ return null;
155
+ const xs = points.map((point) => point.x);
156
+ const ys = points.map((point) => point.y);
157
+ const minX = Math.min(...xs);
158
+ const maxX = Math.max(...xs);
159
+ const minY = Math.min(...ys);
160
+ const maxY = Math.max(...ys);
161
+ if (!Number.isFinite(minX) || !Number.isFinite(maxX) || !Number.isFinite(minY) || !Number.isFinite(maxY)) {
162
+ return null;
163
+ }
164
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
165
+ }
166
+ export function getSpatialItemLocalBounds(item) {
167
+ if (item.localBounds
168
+ && Number.isFinite(item.localBounds.x)
169
+ && Number.isFinite(item.localBounds.y)
170
+ && Number.isFinite(item.localBounds.width)
171
+ && Number.isFinite(item.localBounds.height)
172
+ && item.localBounds.width > 0
173
+ && item.localBounds.height > 0) {
174
+ return item.localBounds;
175
+ }
176
+ const shape = item.shape;
177
+ if (shape.type === 'rect') {
178
+ return { x: -shape.width / 2, y: -shape.height / 2, width: shape.width, height: shape.height };
179
+ }
180
+ if (shape.type === 'circle') {
181
+ return { x: -shape.radius, y: -shape.radius, width: shape.radius * 2, height: shape.radius * 2 };
182
+ }
183
+ if (shape.type === 'ellipse') {
184
+ return { x: -shape.radiusX, y: -shape.radiusY, width: shape.radiusX * 2, height: shape.radiusY * 2 };
185
+ }
186
+ if (shape.type === 'polygon') {
187
+ return boundsFromPoints(parsePolygonPoints(shape.points));
188
+ }
189
+ return null;
190
+ }
191
+ function validLocalBounds(bounds) {
192
+ return Boolean(bounds
193
+ && Number.isFinite(bounds.x)
194
+ && Number.isFinite(bounds.y)
195
+ && Number.isFinite(bounds.width)
196
+ && Number.isFinite(bounds.height)
197
+ && bounds.width > 0
198
+ && bounds.height > 0);
199
+ }
200
+ export function getSpatialZoneMapBounds(zone) {
201
+ if (validLocalBounds(zone.localBounds))
202
+ return zone.localBounds;
203
+ const shape = zone.shape;
204
+ if (shape.type === 'rect') {
205
+ return { x: shape.x, y: shape.y, width: shape.width, height: shape.height };
206
+ }
207
+ if (shape.type === 'circle') {
208
+ return { x: shape.cx - shape.radius, y: shape.cy - shape.radius, width: shape.radius * 2, height: shape.radius * 2 };
209
+ }
210
+ if (shape.type === 'ellipse') {
211
+ return { x: shape.cx - shape.radiusX, y: shape.cy - shape.radiusY, width: shape.radiusX * 2, height: shape.radiusY * 2 };
212
+ }
213
+ if (shape.type === 'polygon') {
214
+ return boundsFromPoints(parsePolygonPoints(shape.points));
215
+ }
216
+ return null;
217
+ }
218
+ export function getSpatialZonePosition(zone) {
219
+ return finitePoint(zone.position) ? zone.position : { x: 0, y: 0 };
220
+ }
221
+ export function normalizeZonePosition(zone, position, rect, policy) {
222
+ if (policy.bounds !== 'viewBox')
223
+ return roundPoint(position, policy.coordinatePrecision);
224
+ const bounds = getSpatialZoneTransformedBounds({ ...zone, position }, policy);
225
+ if (!bounds)
226
+ return roundPoint(position, policy.coordinatePrecision);
227
+ const minX = position.x + rect.x - bounds.x;
228
+ const maxX = position.x + rect.x + rect.width - (bounds.x + bounds.width);
229
+ const minY = position.y + rect.y - bounds.y;
230
+ const maxY = position.y + rect.y + rect.height - (bounds.y + bounds.height);
231
+ const bounded = {
232
+ x: Math.min(Math.max(minX, maxX), Math.max(Math.min(minX, maxX), position.x)),
233
+ y: Math.min(Math.max(minY, maxY), Math.max(Math.min(minY, maxY), position.y)),
234
+ };
235
+ return roundPoint(bounded, policy.coordinatePrecision);
236
+ }
237
+ function rotatePoint(point, degrees) {
238
+ const radians = degrees * Math.PI / 180;
239
+ const cos = Math.cos(radians);
240
+ const sin = Math.sin(radians);
241
+ return {
242
+ x: point.x * cos - point.y * sin,
243
+ y: point.x * sin + point.y * cos,
244
+ };
245
+ }
246
+ export function mapPointToItemLocal(item, point, policy) {
247
+ const transform = normalizeSpatialTransform(item.transform, policy);
248
+ const translated = {
249
+ x: point.x - item.position.x,
250
+ y: point.y - item.position.y,
251
+ };
252
+ const unrotated = rotatePoint(translated, -(item.rotation ?? 0));
253
+ return {
254
+ x: unrotated.x / transform.scaleX,
255
+ y: unrotated.y / transform.scaleY,
256
+ };
257
+ }
258
+ export function itemLocalPointToMap(item, point, transform) {
259
+ const scaled = { x: point.x * transform.scaleX, y: point.y * transform.scaleY };
260
+ const rotated = rotatePoint(scaled, item.rotation ?? 0);
261
+ return {
262
+ x: item.position.x + rotated.x,
263
+ y: item.position.y + rotated.y,
264
+ };
265
+ }
266
+ export function mapPointToZoneLocal(zone, point, policy) {
267
+ const position = getSpatialZonePosition(zone);
268
+ const transform = normalizeSpatialTransform(zone.transform, policy);
269
+ const translated = {
270
+ x: point.x - position.x,
271
+ y: point.y - position.y,
272
+ };
273
+ const unrotated = rotatePoint(translated, -(zone.rotation ?? 0));
274
+ return {
275
+ x: unrotated.x / transform.scaleX,
276
+ y: unrotated.y / transform.scaleY,
277
+ };
278
+ }
279
+ export function zoneLocalPointToMap(zone, point, transform) {
280
+ const position = getSpatialZonePosition(zone);
281
+ const scaled = { x: point.x * transform.scaleX, y: point.y * transform.scaleY };
282
+ const rotated = rotatePoint(scaled, zone.rotation ?? 0);
283
+ return {
284
+ x: position.x + rotated.x,
285
+ y: position.y + rotated.y,
286
+ };
287
+ }
288
+ function itemLocalDirectionToMap(item, vector) {
289
+ return rotatePoint(vector, item.rotation ?? 0);
290
+ }
291
+ function zoneLocalDirectionToMap(zone, vector) {
292
+ return rotatePoint(vector, zone.rotation ?? 0);
293
+ }
294
+ function handleLocalPoint(bounds, handle) {
295
+ const minX = bounds.x;
296
+ const maxX = bounds.x + bounds.width;
297
+ const midX = bounds.x + bounds.width / 2;
298
+ const minY = bounds.y;
299
+ const maxY = bounds.y + bounds.height;
300
+ const midY = bounds.y + bounds.height / 2;
301
+ return {
302
+ n: { x: midX, y: minY },
303
+ ne: { x: maxX, y: minY },
304
+ e: { x: maxX, y: midY },
305
+ se: { x: maxX, y: maxY },
306
+ s: { x: midX, y: maxY },
307
+ sw: { x: minX, y: maxY },
308
+ w: { x: minX, y: midY },
309
+ nw: { x: minX, y: minY },
310
+ }[handle];
311
+ }
312
+ function oppositeHandle(handle) {
313
+ return {
314
+ n: 's',
315
+ ne: 'sw',
316
+ e: 'w',
317
+ se: 'nw',
318
+ s: 'n',
319
+ sw: 'ne',
320
+ w: 'e',
321
+ nw: 'se',
322
+ }[handle];
323
+ }
324
+ export function getSpatialItemWorldHandlePoints(item, policy) {
325
+ const bounds = getSpatialItemLocalBounds(item);
326
+ if (!bounds)
327
+ return null;
328
+ const transform = normalizeSpatialTransform(item.transform, policy);
329
+ const points = {
330
+ n: itemLocalPointToMap(item, handleLocalPoint(bounds, 'n'), transform),
331
+ ne: itemLocalPointToMap(item, handleLocalPoint(bounds, 'ne'), transform),
332
+ e: itemLocalPointToMap(item, handleLocalPoint(bounds, 'e'), transform),
333
+ se: itemLocalPointToMap(item, handleLocalPoint(bounds, 'se'), transform),
334
+ s: itemLocalPointToMap(item, handleLocalPoint(bounds, 's'), transform),
335
+ sw: itemLocalPointToMap(item, handleLocalPoint(bounds, 'sw'), transform),
336
+ w: itemLocalPointToMap(item, handleLocalPoint(bounds, 'w'), transform),
337
+ nw: itemLocalPointToMap(item, handleLocalPoint(bounds, 'nw'), transform),
338
+ rotate: { x: 0, y: 0 },
339
+ };
340
+ const top = points.n;
341
+ const outward = itemLocalDirectionToMap(item, { x: 0, y: -1 });
342
+ const rotateGap = Math.max(28, Math.min(56, bounds.height * 0.35));
343
+ points.rotate = {
344
+ x: top.x + outward.x * rotateGap,
345
+ y: top.y + outward.y * rotateGap,
346
+ };
347
+ return points;
348
+ }
349
+ export function getSpatialZoneWorldHandlePoints(zone, policy) {
350
+ const bounds = getSpatialZoneMapBounds(zone);
351
+ if (!bounds)
352
+ return null;
353
+ const transform = normalizeSpatialTransform(zone.transform, policy);
354
+ const points = {
355
+ n: zoneLocalPointToMap(zone, handleLocalPoint(bounds, 'n'), transform),
356
+ ne: zoneLocalPointToMap(zone, handleLocalPoint(bounds, 'ne'), transform),
357
+ e: zoneLocalPointToMap(zone, handleLocalPoint(bounds, 'e'), transform),
358
+ se: zoneLocalPointToMap(zone, handleLocalPoint(bounds, 'se'), transform),
359
+ s: zoneLocalPointToMap(zone, handleLocalPoint(bounds, 's'), transform),
360
+ sw: zoneLocalPointToMap(zone, handleLocalPoint(bounds, 'sw'), transform),
361
+ w: zoneLocalPointToMap(zone, handleLocalPoint(bounds, 'w'), transform),
362
+ nw: zoneLocalPointToMap(zone, handleLocalPoint(bounds, 'nw'), transform),
363
+ rotate: { x: 0, y: 0 },
364
+ };
365
+ const top = points.n;
366
+ const outward = zoneLocalDirectionToMap(zone, { x: 0, y: -1 });
367
+ const rotateGap = Math.max(28, Math.min(56, bounds.height * 0.35));
368
+ points.rotate = {
369
+ x: top.x + outward.x * rotateGap,
370
+ y: top.y + outward.y * rotateGap,
371
+ };
372
+ return points;
373
+ }
374
+ export function getSpatialZonePolygonPoints(zone) {
375
+ if (zone.shape.type !== 'polygon')
376
+ return null;
377
+ const points = parsePolygonPoints(zone.shape.points);
378
+ return points.length >= 3 ? points : null;
379
+ }
380
+ export function getSpatialZonePolygonHandles(zone, policy) {
381
+ const points = getSpatialZonePolygonPoints(zone);
382
+ if (!points)
383
+ return null;
384
+ const transform = normalizeSpatialTransform(zone.transform, policy);
385
+ const vertices = points.map((point, index) => ({
386
+ index,
387
+ point: zoneLocalPointToMap(zone, point, transform),
388
+ }));
389
+ const segments = points.map((point, index) => {
390
+ const next = points[(index + 1) % points.length];
391
+ return {
392
+ index,
393
+ point: zoneLocalPointToMap(zone, {
394
+ x: (point.x + next.x) / 2,
395
+ y: (point.y + next.y) / 2,
396
+ }, transform),
397
+ };
398
+ });
399
+ return { vertices, segments };
400
+ }
401
+ function normalizePolygonWorldPoint(zone, point, rect, policy) {
402
+ const bounded = policy.bounds === 'viewBox' ? clampPointToRect(point, rect) : point;
403
+ return roundPoint(mapPointToZoneLocal(zone, bounded, policy), policy.coordinatePrecision);
404
+ }
405
+ export function moveSpatialZonePolygonVertex(zone, args) {
406
+ const points = getSpatialZonePolygonPoints(zone);
407
+ if (!points || args.vertexIndex < 0 || args.vertexIndex >= points.length)
408
+ return zone;
409
+ const nextPoints = points.map((point, index) => (index === args.vertexIndex
410
+ ? normalizePolygonWorldPoint(zone, args.worldPoint, args.rect, args.policy)
411
+ : point));
412
+ return {
413
+ ...zone,
414
+ shape: {
415
+ type: 'polygon',
416
+ points: nextPoints,
417
+ },
418
+ };
419
+ }
420
+ export function insertSpatialZonePolygonVertex(zone, args) {
421
+ const points = getSpatialZonePolygonPoints(zone);
422
+ if (!points || args.segmentIndex < 0 || args.segmentIndex >= points.length)
423
+ return zone;
424
+ const nextPoint = normalizePolygonWorldPoint(zone, args.worldPoint, args.rect, args.policy);
425
+ const nextPoints = [
426
+ ...points.slice(0, args.segmentIndex + 1),
427
+ nextPoint,
428
+ ...points.slice(args.segmentIndex + 1),
429
+ ];
430
+ return {
431
+ ...zone,
432
+ shape: {
433
+ type: 'polygon',
434
+ points: nextPoints,
435
+ },
436
+ };
437
+ }
438
+ export function deleteSpatialZonePolygonVertex(zone, args) {
439
+ const points = getSpatialZonePolygonPoints(zone);
440
+ if (!points || points.length <= 3 || args.vertexIndex < 0 || args.vertexIndex >= points.length)
441
+ return zone;
442
+ return {
443
+ ...zone,
444
+ shape: {
445
+ type: 'polygon',
446
+ points: points.filter((_, index) => index !== args.vertexIndex),
447
+ },
448
+ };
449
+ }
450
+ export function getSpatialZoneTransformedBounds(zone, policy) {
451
+ const bounds = getSpatialZoneMapBounds(zone);
452
+ if (!bounds)
453
+ return null;
454
+ const transform = normalizeSpatialTransform(zone.transform, policy);
455
+ const corners = [
456
+ handleLocalPoint(bounds, 'nw'),
457
+ handleLocalPoint(bounds, 'ne'),
458
+ handleLocalPoint(bounds, 'se'),
459
+ handleLocalPoint(bounds, 'sw'),
460
+ ].map((point) => zoneLocalPointToMap(zone, point, transform));
461
+ const xs = corners.map((point) => point.x);
462
+ const ys = corners.map((point) => point.y);
463
+ const minX = Math.min(...xs);
464
+ const maxX = Math.max(...xs);
465
+ const minY = Math.min(...ys);
466
+ const maxY = Math.max(...ys);
467
+ return {
468
+ x: minX,
469
+ y: minY,
470
+ width: maxX - minX,
471
+ height: maxY - minY,
472
+ };
473
+ }
474
+ export function resizeSpatialItem(item, args) {
475
+ const bounds = getSpatialItemLocalBounds(item);
476
+ if (!bounds)
477
+ return item;
478
+ const current = normalizeSpatialTransform(item.transform, args.policy);
479
+ const anchor = handleLocalPoint(bounds, oppositeHandle(args.handle));
480
+ const active = handleLocalPoint(bounds, args.handle);
481
+ const widthDenominator = active.x - anchor.x;
482
+ const heightDenominator = active.y - anchor.y;
483
+ const scaledLocalPoint = {
484
+ x: args.localPoint.x * current.scaleX,
485
+ y: args.localPoint.y * current.scaleY,
486
+ };
487
+ const scaledAnchor = {
488
+ x: anchor.x * current.scaleX,
489
+ y: anchor.y * current.scaleY,
490
+ };
491
+ let nextScaleX = current.scaleX;
492
+ let nextScaleY = current.scaleY;
493
+ if (widthDenominator !== 0) {
494
+ nextScaleX = Math.abs((scaledLocalPoint.x - scaledAnchor.x) / widthDenominator);
495
+ }
496
+ if (heightDenominator !== 0) {
497
+ nextScaleY = Math.abs((scaledLocalPoint.y - scaledAnchor.y) / heightDenominator);
498
+ }
499
+ if (args.preserveAspectRatio) {
500
+ const dominant = Math.abs(nextScaleX - current.scaleX) >= Math.abs(nextScaleY - current.scaleY)
501
+ ? nextScaleX
502
+ : nextScaleY;
503
+ if (widthDenominator !== 0)
504
+ nextScaleX = dominant;
505
+ if (heightDenominator !== 0)
506
+ nextScaleY = dominant;
507
+ }
508
+ const transform = normalizeSpatialTransform({ scaleX: nextScaleX, scaleY: nextScaleY }, args.policy);
509
+ const anchorBefore = itemLocalPointToMap(item, anchor, current);
510
+ const rotatedAnchorAfter = rotatePoint({ x: anchor.x * transform.scaleX, y: anchor.y * transform.scaleY }, item.rotation ?? 0);
511
+ return {
512
+ ...item,
513
+ position: {
514
+ x: anchorBefore.x - rotatedAnchorAfter.x,
515
+ y: anchorBefore.y - rotatedAnchorAfter.y,
516
+ },
517
+ transform,
518
+ };
519
+ }
520
+ export function resizeSpatialZone(zone, args) {
521
+ const bounds = getSpatialZoneMapBounds(zone);
522
+ if (!bounds)
523
+ return zone;
524
+ const current = normalizeSpatialTransform(zone.transform, args.policy);
525
+ const anchor = handleLocalPoint(bounds, oppositeHandle(args.handle));
526
+ const active = handleLocalPoint(bounds, args.handle);
527
+ const widthDenominator = active.x - anchor.x;
528
+ const heightDenominator = active.y - anchor.y;
529
+ const scaledLocalPoint = {
530
+ x: args.localPoint.x * current.scaleX,
531
+ y: args.localPoint.y * current.scaleY,
532
+ };
533
+ const scaledAnchor = {
534
+ x: anchor.x * current.scaleX,
535
+ y: anchor.y * current.scaleY,
536
+ };
537
+ let nextScaleX = current.scaleX;
538
+ let nextScaleY = current.scaleY;
539
+ if (widthDenominator !== 0) {
540
+ nextScaleX = Math.abs((scaledLocalPoint.x - scaledAnchor.x) / widthDenominator);
541
+ }
542
+ if (heightDenominator !== 0) {
543
+ nextScaleY = Math.abs((scaledLocalPoint.y - scaledAnchor.y) / heightDenominator);
544
+ }
545
+ if (args.preserveAspectRatio) {
546
+ const dominant = Math.abs(nextScaleX - current.scaleX) >= Math.abs(nextScaleY - current.scaleY)
547
+ ? nextScaleX
548
+ : nextScaleY;
549
+ if (widthDenominator !== 0)
550
+ nextScaleX = dominant;
551
+ if (heightDenominator !== 0)
552
+ nextScaleY = dominant;
553
+ }
554
+ const transform = normalizeSpatialTransform({ scaleX: nextScaleX, scaleY: nextScaleY }, args.policy);
555
+ const anchorBefore = zoneLocalPointToMap(zone, anchor, current);
556
+ const rotatedAnchorAfter = rotatePoint({ x: anchor.x * transform.scaleX, y: anchor.y * transform.scaleY }, zone.rotation ?? 0);
557
+ return {
558
+ ...zone,
559
+ position: {
560
+ x: anchorBefore.x - rotatedAnchorAfter.x,
561
+ y: anchorBefore.y - rotatedAnchorAfter.y,
562
+ },
563
+ transform,
564
+ };
565
+ }
566
+ export function normalizeSpatialRotation(rotation) {
567
+ if (!Number.isFinite(rotation))
568
+ return 0;
569
+ let normalized = rotation % 360;
570
+ if (normalized >= 180)
571
+ normalized -= 360;
572
+ if (normalized < -180)
573
+ normalized += 360;
574
+ return normalized;
575
+ }
576
+ export function rotateSpatialItem(item, rotation, policy) {
577
+ const step = policy.rotationStep;
578
+ const snapped = step > 0 ? Math.round(rotation / step) * step : rotation;
579
+ return {
580
+ ...item,
581
+ rotation: normalizeSpatialRotation(snapped),
582
+ };
583
+ }
584
+ function sameSpatialTransform(a, b, policy) {
585
+ const left = normalizeSpatialTransform(a, policy);
586
+ const right = normalizeSpatialTransform(b, policy);
587
+ return left.scaleX === right.scaleX && left.scaleY === right.scaleY;
588
+ }
589
+ function nearestGridValue(value, size, offset) {
590
+ return offset + Math.round((value - offset) / size) * size;
591
+ }
592
+ function keyboardGridValue(origin, delta, size, offset) {
593
+ const direction = delta < 0 ? -1 : 1;
594
+ const steps = Math.max(1, Math.round(Math.abs(delta) / size));
595
+ const originRatio = (origin - offset) / size;
596
+ const originStop = direction > 0
597
+ ? Math.floor(originRatio)
598
+ : Math.ceil(originRatio);
599
+ return offset + (originStop + direction * steps) * size;
600
+ }
601
+ function uniqueSources(guides) {
602
+ const seen = new Set();
603
+ const sources = [];
604
+ for (const guide of guides) {
605
+ if (!seen.has(guide.source)) {
606
+ seen.add(guide.source);
607
+ sources.push(guide.source);
608
+ }
609
+ }
610
+ return sources;
611
+ }
612
+ function candidateWins(candidate, current) {
613
+ if (!current)
614
+ return true;
615
+ if (candidate.distance < current.distance)
616
+ return true;
617
+ return candidate.distance === current.distance && candidate.source === 'item-center';
618
+ }
619
+ function axisValue(point, axis) {
620
+ return axis === 'x' ? point.x : point.y;
621
+ }
622
+ function axisPoint(point, axis, value) {
623
+ return axis === 'x'
624
+ ? { x: value, y: point.y }
625
+ : { x: point.x, y: value };
626
+ }
627
+ export function resolveSpatialEditPoint(args) {
628
+ const boundedRaw = args.policy.bounds === 'viewBox'
629
+ ? clampPointToRect(args.rawPoint, args.rect)
630
+ : args.rawPoint;
631
+ let point = boundedRaw;
632
+ const guidesByAxis = {};
633
+ if (args.policy.snap.enabled) {
634
+ if (args.policy.snap.grid.enabled) {
635
+ for (const axis of ['x', 'y']) {
636
+ const value = axisValue(point, axis);
637
+ const size = args.policy.snap.grid.size[axis];
638
+ const offset = args.policy.snap.grid.offset[axis];
639
+ const keyboardIntent = args.intent?.kind === 'keyboard' ? args.intent : undefined;
640
+ const intentDelta = keyboardIntent ? keyboardIntent.delta[axis] : 0;
641
+ let usesKeyboardGridStep = false;
642
+ let position = nearestGridValue(value, size, offset);
643
+ if (keyboardIntent && intentDelta !== 0) {
644
+ usesKeyboardGridStep = true;
645
+ position = keyboardGridValue(keyboardIntent.origin[axis], intentDelta, size, offset);
646
+ }
647
+ const distance = usesKeyboardGridStep ? 0 : Math.abs(position - value);
648
+ if (usesKeyboardGridStep || distance <= args.policy.snap.grid.threshold) {
649
+ guidesByAxis[axis] = {
650
+ axis,
651
+ source: 'grid',
652
+ value: position,
653
+ distance,
654
+ };
655
+ }
656
+ }
657
+ }
658
+ if (args.policy.snap.itemCenters.enabled) {
659
+ for (const candidateItem of args.items ?? []) {
660
+ if (args.currentItemId !== undefined
661
+ && String(candidateItem.id) === String(args.currentItemId)) {
662
+ continue;
663
+ }
664
+ if (!Number.isFinite(candidateItem.position?.x) || !Number.isFinite(candidateItem.position?.y)) {
665
+ continue;
666
+ }
667
+ for (const axis of ['x', 'y']) {
668
+ const position = candidateItem.position[axis];
669
+ const keyboardIntent = args.intent?.kind === 'keyboard' ? args.intent : undefined;
670
+ const intentDelta = keyboardIntent ? keyboardIntent.delta[axis] : 0;
671
+ const gridGuide = intentDelta !== 0 && guidesByAxis[axis]?.source === 'grid'
672
+ ? guidesByAxis[axis]
673
+ : undefined;
674
+ if (keyboardIntent && intentDelta !== 0 && !gridGuide) {
675
+ const origin = keyboardIntent.origin[axis];
676
+ if ((intentDelta > 0 && position <= origin)
677
+ || (intentDelta < 0 && position >= origin)) {
678
+ continue;
679
+ }
680
+ }
681
+ const comparisonValue = gridGuide ? gridGuide.value : axisValue(point, axis);
682
+ const distance = Math.abs(position - comparisonValue);
683
+ if (distance > args.policy.snap.itemCenters.threshold)
684
+ continue;
685
+ const candidate = {
686
+ axis,
687
+ source: 'item-center',
688
+ value: position,
689
+ distance,
690
+ targetId: candidateItem.id,
691
+ };
692
+ if (candidateWins(candidate, guidesByAxis[axis])) {
693
+ guidesByAxis[axis] = candidate;
694
+ }
695
+ }
696
+ }
697
+ }
698
+ const axisGuides = ['x', 'y']
699
+ .map((axis) => guidesByAxis[axis])
700
+ .filter((guide) => Boolean(guide));
701
+ for (const guide of axisGuides) {
702
+ point = axisPoint(point, guide.axis, guide.value);
703
+ }
704
+ }
705
+ const snapGuides = ['x', 'y']
706
+ .map((axis) => guidesByAxis[axis])
707
+ .filter((guide) => Boolean(guide))
708
+ .map(({ distance: _distance, ...guide }) => guide);
709
+ const snapped = snapGuides.length > 0;
710
+ const boundedSnapped = args.policy.bounds === 'viewBox'
711
+ ? clampPointToRect(point, args.rect)
712
+ : point;
713
+ const roundedPoint = roundPoint(boundedSnapped, args.policy.coordinatePrecision);
714
+ return {
715
+ point: roundedPoint,
716
+ rawPoint: args.rawPoint,
717
+ snapped,
718
+ guides: snapGuides,
719
+ sources: uniqueSources(snapGuides),
720
+ };
721
+ }
722
+ export function isMovableSpatialItem(item) {
723
+ if (item.disabled)
724
+ return false;
725
+ return Number.isFinite(item.position?.x) && Number.isFinite(item.position?.y);
726
+ }
727
+ export function moveSpatialItem(item, position) {
728
+ return {
729
+ ...item,
730
+ position: { x: position.x, y: position.y },
731
+ };
732
+ }
733
+ export function moveSpatialZone(zone, position, rect, policy) {
734
+ return {
735
+ ...zone,
736
+ position: normalizeZonePosition(zone, position, rect, policy),
737
+ };
738
+ }
739
+ export function samePoint(a, b) {
740
+ return a.x === b.x && a.y === b.y;
741
+ }
742
+ export function hasSpatialItemChangedByType(args) {
743
+ const previousPosition = args.rect
744
+ ? normalizeEditPosition(args.previousItem.position, args.rect, args.policy)
745
+ : args.previousItem.position;
746
+ const nextPosition = args.rect
747
+ ? normalizeEditPosition(args.nextItem.position, args.rect, args.policy)
748
+ : args.nextItem.position;
749
+ if (args.changeType === 'move') {
750
+ return !samePoint(previousPosition, nextPosition);
751
+ }
752
+ if (args.changeType === 'resize') {
753
+ return !sameSpatialTransform(args.previousItem.transform, args.nextItem.transform, args.policy)
754
+ || !samePoint(previousPosition, nextPosition);
755
+ }
756
+ if (args.changeType === 'rotate') {
757
+ return normalizeSpatialRotation(args.previousItem.rotation ?? 0) !== normalizeSpatialRotation(args.nextItem.rotation ?? 0);
758
+ }
759
+ return args.previousItem !== args.nextItem;
760
+ }
761
+ export function hasSpatialZoneChangedByType(args) {
762
+ const previousPosition = getSpatialZonePosition(args.previousZone);
763
+ const nextPosition = getSpatialZonePosition(args.nextZone);
764
+ if (args.changeType === 'move') {
765
+ return !samePoint(previousPosition, nextPosition);
766
+ }
767
+ if (args.changeType === 'resize') {
768
+ return !sameSpatialTransform(args.previousZone.transform, args.nextZone.transform, args.policy)
769
+ || !samePoint(previousPosition, nextPosition);
770
+ }
771
+ if (args.changeType === 'rotate') {
772
+ return normalizeSpatialRotation(args.previousZone.rotation ?? 0) !== normalizeSpatialRotation(args.nextZone.rotation ?? 0);
773
+ }
774
+ if (args.changeType === 'shape') {
775
+ const previousPoints = getSpatialZonePolygonPoints(args.previousZone);
776
+ const nextPoints = getSpatialZonePolygonPoints(args.nextZone);
777
+ if (!previousPoints || !nextPoints) {
778
+ return previousPoints !== nextPoints;
779
+ }
780
+ if (previousPoints.length !== nextPoints.length) {
781
+ return true;
782
+ }
783
+ return previousPoints.some((point, index) => !samePoint(point, nextPoints[index]));
784
+ }
785
+ return args.previousZone !== args.nextZone;
786
+ }
787
+ export function buildSpatialItemChangeContext(args) {
788
+ const zone = args.nextItem.zoneId
789
+ ? args.zones.find((candidate) => candidate.id === args.nextItem.zoneId)
790
+ : undefined;
791
+ const previousPosition = args.previousItem?.position;
792
+ const position = args.nextItem.position;
793
+ const previousTransform = args.previousItem
794
+ ? normalizeSpatialTransform(args.previousItem.transform, args.policy)
795
+ : undefined;
796
+ const transform = normalizeSpatialTransform(args.nextItem.transform, args.policy);
797
+ const previousRotation = args.previousItem?.rotation ?? 0;
798
+ const rotation = args.nextItem.rotation ?? 0;
799
+ const previousLocalBounds = args.previousItem
800
+ ? getSpatialItemLocalBounds(args.previousItem) ?? undefined
801
+ : undefined;
802
+ const localBounds = getSpatialItemLocalBounds(args.nextItem) ?? undefined;
803
+ return {
804
+ kind: 'item-change',
805
+ changeType: args.changeType,
806
+ mode: args.mode,
807
+ itemId: args.nextItem.id,
808
+ zoneId: args.nextItem.zoneId,
809
+ previousItem: args.previousItem,
810
+ nextItem: args.nextItem,
811
+ item: args.nextItem,
812
+ ...(previousPosition ? { previousPosition } : {}),
813
+ position,
814
+ ...(previousPosition ? {
815
+ delta: {
816
+ x: position.x - previousPosition.x,
817
+ y: position.y - previousPosition.y,
818
+ },
819
+ } : {}),
820
+ previousTransform,
821
+ transform,
822
+ previousRotation,
823
+ rotation,
824
+ previousLocalBounds,
825
+ localBounds,
826
+ zone,
827
+ };
828
+ }
829
+ export function buildSpatialZoneChangeContext(args) {
830
+ const previousPosition = args.previousZone ? getSpatialZonePosition(args.previousZone) : undefined;
831
+ const position = getSpatialZonePosition(args.nextZone);
832
+ const previousTransform = args.previousZone
833
+ ? normalizeSpatialTransform(args.previousZone.transform, args.policy)
834
+ : undefined;
835
+ const transform = normalizeSpatialTransform(args.nextZone.transform, args.policy);
836
+ const previousRotation = args.previousZone?.rotation ?? 0;
837
+ const rotation = args.nextZone.rotation ?? 0;
838
+ const previousLocalBounds = args.previousZone
839
+ ? getSpatialZoneMapBounds(args.previousZone) ?? undefined
840
+ : undefined;
841
+ const localBounds = getSpatialZoneMapBounds(args.nextZone) ?? undefined;
842
+ const previousPoints = args.previousZone
843
+ ? getSpatialZonePolygonPoints(args.previousZone) ?? undefined
844
+ : undefined;
845
+ const points = getSpatialZonePolygonPoints(args.nextZone) ?? undefined;
846
+ return {
847
+ kind: 'zone-change',
848
+ changeType: args.changeType,
849
+ mode: args.mode,
850
+ zoneId: args.nextZone.id,
851
+ previousZone: args.previousZone,
852
+ nextZone: args.nextZone,
853
+ zone: args.nextZone,
854
+ ...(previousPosition ? { previousPosition } : {}),
855
+ position,
856
+ ...(previousPosition ? {
857
+ delta: {
858
+ x: position.x - previousPosition.x,
859
+ y: position.y - previousPosition.y,
860
+ },
861
+ } : {}),
862
+ previousTransform,
863
+ transform,
864
+ previousRotation,
865
+ rotation,
866
+ previousLocalBounds,
867
+ localBounds,
868
+ ...(args.changeType === 'shape' ? {
869
+ ...(args.previousZone ? { previousShape: args.previousZone.shape } : {}),
870
+ shape: args.nextZone.shape,
871
+ ...(previousPoints ? { previousPoints } : {}),
872
+ ...(points ? { points } : {}),
873
+ ...(typeof args.vertexIndex === 'number' ? { vertexIndex: args.vertexIndex } : {}),
874
+ ...(typeof args.segmentIndex === 'number' ? { segmentIndex: args.segmentIndex } : {}),
875
+ ...(args.shapeAction ? { shapeAction: args.shapeAction } : {}),
876
+ } : {}),
877
+ };
878
+ }
879
+ export function buildItemChangeContext(args) {
880
+ return buildSpatialItemChangeContext({
881
+ ...args,
882
+ changeType: 'move',
883
+ policy: args.policy ?? getEditPolicy(args.mode),
884
+ });
885
+ }
886
+ //# sourceMappingURL=spatial-map-editing.js.map