@archireport/react-native-drawing 0.2.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 (323) hide show
  1. package/README.md +181 -0
  2. package/lib/commonjs/DrawingEditor.js +815 -0
  3. package/lib/commonjs/DrawingEditor.js.map +1 -0
  4. package/lib/commonjs/assets/toolbar-icons/arrow-disabled.png +0 -0
  5. package/lib/commonjs/assets/toolbar-icons/arrow-enabled.png +0 -0
  6. package/lib/commonjs/assets/toolbar-icons/arrow.png +0 -0
  7. package/lib/commonjs/assets/toolbar-icons/circle-disabled.png +0 -0
  8. package/lib/commonjs/assets/toolbar-icons/circle-enabled.png +0 -0
  9. package/lib/commonjs/assets/toolbar-icons/circle.png +0 -0
  10. package/lib/commonjs/assets/toolbar-icons/freehand-disabled.png +0 -0
  11. package/lib/commonjs/assets/toolbar-icons/freehand-enabled.png +0 -0
  12. package/lib/commonjs/assets/toolbar-icons/freehand.png +0 -0
  13. package/lib/commonjs/assets/toolbar-icons/line-disabled.png +0 -0
  14. package/lib/commonjs/assets/toolbar-icons/line-enabled.png +0 -0
  15. package/lib/commonjs/assets/toolbar-icons/line.png +0 -0
  16. package/lib/commonjs/assets/toolbar-icons/measure-disabled.png +0 -0
  17. package/lib/commonjs/assets/toolbar-icons/measure-enabled.png +0 -0
  18. package/lib/commonjs/assets/toolbar-icons/measure.png +0 -0
  19. package/lib/commonjs/assets/toolbar-icons/move-disabled.png +0 -0
  20. package/lib/commonjs/assets/toolbar-icons/move-enabled.png +0 -0
  21. package/lib/commonjs/assets/toolbar-icons/move.png +0 -0
  22. package/lib/commonjs/assets/toolbar-icons/polygon-disabled.png +0 -0
  23. package/lib/commonjs/assets/toolbar-icons/polygon-enabled.png +0 -0
  24. package/lib/commonjs/assets/toolbar-icons/polygon.png +0 -0
  25. package/lib/commonjs/assets/toolbar-icons/rectangle-disabled.png +0 -0
  26. package/lib/commonjs/assets/toolbar-icons/rectangle-enabled.png +0 -0
  27. package/lib/commonjs/assets/toolbar-icons/rectangle.png +0 -0
  28. package/lib/commonjs/assets/toolbar-icons/text-disabled.png +0 -0
  29. package/lib/commonjs/assets/toolbar-icons/text-enabled.png +0 -0
  30. package/lib/commonjs/assets/toolbar-icons/text.png +0 -0
  31. package/lib/commonjs/components/ColorPalette.js +379 -0
  32. package/lib/commonjs/components/ColorPalette.js.map +1 -0
  33. package/lib/commonjs/components/LineWidthSlider.js +70 -0
  34. package/lib/commonjs/components/LineWidthSlider.js.map +1 -0
  35. package/lib/commonjs/components/MeasurementEditModal.js +153 -0
  36. package/lib/commonjs/components/MeasurementEditModal.js.map +1 -0
  37. package/lib/commonjs/components/MiniMap.js +244 -0
  38. package/lib/commonjs/components/MiniMap.js.map +1 -0
  39. package/lib/commonjs/components/TextAnnotation.js +162 -0
  40. package/lib/commonjs/components/TextAnnotation.js.map +1 -0
  41. package/lib/commonjs/components/TextEditModal.js +133 -0
  42. package/lib/commonjs/components/TextEditModal.js.map +1 -0
  43. package/lib/commonjs/components/Toolbar.js +198 -0
  44. package/lib/commonjs/components/Toolbar.js.map +1 -0
  45. package/lib/commonjs/components/ZoomBadge.js +161 -0
  46. package/lib/commonjs/components/ZoomBadge.js.map +1 -0
  47. package/lib/commonjs/hooks/useFreehandGesture.js +173 -0
  48. package/lib/commonjs/hooks/useFreehandGesture.js.map +1 -0
  49. package/lib/commonjs/hooks/usePolygonGesture.js +109 -0
  50. package/lib/commonjs/hooks/usePolygonGesture.js.map +1 -0
  51. package/lib/commonjs/hooks/useSelectionGesture.js +236 -0
  52. package/lib/commonjs/hooks/useSelectionGesture.js.map +1 -0
  53. package/lib/commonjs/hooks/useShapeGesture.js +181 -0
  54. package/lib/commonjs/hooks/useShapeGesture.js.map +1 -0
  55. package/lib/commonjs/hooks/useViewportGesture.js +238 -0
  56. package/lib/commonjs/hooks/useViewportGesture.js.map +1 -0
  57. package/lib/commonjs/index.js +104 -0
  58. package/lib/commonjs/index.js.map +1 -0
  59. package/lib/commonjs/package.json +1 -0
  60. package/lib/commonjs/renderers/ArrowRenderer.js +118 -0
  61. package/lib/commonjs/renderers/ArrowRenderer.js.map +1 -0
  62. package/lib/commonjs/renderers/CircleRenderer.js +51 -0
  63. package/lib/commonjs/renderers/CircleRenderer.js.map +1 -0
  64. package/lib/commonjs/renderers/FreehandRenderer.js +31 -0
  65. package/lib/commonjs/renderers/FreehandRenderer.js.map +1 -0
  66. package/lib/commonjs/renderers/InProgressRenderer.js +174 -0
  67. package/lib/commonjs/renderers/InProgressRenderer.js.map +1 -0
  68. package/lib/commonjs/renderers/LineRenderer.js +27 -0
  69. package/lib/commonjs/renderers/LineRenderer.js.map +1 -0
  70. package/lib/commonjs/renderers/MeasurementRenderer.js +134 -0
  71. package/lib/commonjs/renderers/MeasurementRenderer.js.map +1 -0
  72. package/lib/commonjs/renderers/ObjectRenderer.js +65 -0
  73. package/lib/commonjs/renderers/ObjectRenderer.js.map +1 -0
  74. package/lib/commonjs/renderers/PolygonRenderer.js +46 -0
  75. package/lib/commonjs/renderers/PolygonRenderer.js.map +1 -0
  76. package/lib/commonjs/renderers/RectRenderer.js +51 -0
  77. package/lib/commonjs/renderers/RectRenderer.js.map +1 -0
  78. package/lib/commonjs/renderers/SelectedObjectRenderer.js +592 -0
  79. package/lib/commonjs/renderers/SelectedObjectRenderer.js.map +1 -0
  80. package/lib/commonjs/renderers/SelectionOverlay.js +120 -0
  81. package/lib/commonjs/renderers/SelectionOverlay.js.map +1 -0
  82. package/lib/commonjs/store/useDrawingStore.js +354 -0
  83. package/lib/commonjs/store/useDrawingStore.js.map +1 -0
  84. package/lib/commonjs/types.js +6 -0
  85. package/lib/commonjs/types.js.map +1 -0
  86. package/lib/commonjs/utils/colors.js +44 -0
  87. package/lib/commonjs/utils/colors.js.map +1 -0
  88. package/lib/commonjs/utils/coordinates.js +81 -0
  89. package/lib/commonjs/utils/coordinates.js.map +1 -0
  90. package/lib/commonjs/utils/hitTesting.js +181 -0
  91. package/lib/commonjs/utils/hitTesting.js.map +1 -0
  92. package/lib/commonjs/utils/serialization.js +42 -0
  93. package/lib/commonjs/utils/serialization.js.map +1 -0
  94. package/lib/commonjs/utils/shapeDetection.js +151 -0
  95. package/lib/commonjs/utils/shapeDetection.js.map +1 -0
  96. package/lib/commonjs/utils/smoothing.js +85 -0
  97. package/lib/commonjs/utils/smoothing.js.map +1 -0
  98. package/lib/module/DrawingEditor.js +811 -0
  99. package/lib/module/DrawingEditor.js.map +1 -0
  100. package/lib/module/assets/toolbar-icons/arrow-disabled.png +0 -0
  101. package/lib/module/assets/toolbar-icons/arrow-enabled.png +0 -0
  102. package/lib/module/assets/toolbar-icons/arrow.png +0 -0
  103. package/lib/module/assets/toolbar-icons/circle-disabled.png +0 -0
  104. package/lib/module/assets/toolbar-icons/circle-enabled.png +0 -0
  105. package/lib/module/assets/toolbar-icons/circle.png +0 -0
  106. package/lib/module/assets/toolbar-icons/freehand-disabled.png +0 -0
  107. package/lib/module/assets/toolbar-icons/freehand-enabled.png +0 -0
  108. package/lib/module/assets/toolbar-icons/freehand.png +0 -0
  109. package/lib/module/assets/toolbar-icons/line-disabled.png +0 -0
  110. package/lib/module/assets/toolbar-icons/line-enabled.png +0 -0
  111. package/lib/module/assets/toolbar-icons/line.png +0 -0
  112. package/lib/module/assets/toolbar-icons/measure-disabled.png +0 -0
  113. package/lib/module/assets/toolbar-icons/measure-enabled.png +0 -0
  114. package/lib/module/assets/toolbar-icons/measure.png +0 -0
  115. package/lib/module/assets/toolbar-icons/move-disabled.png +0 -0
  116. package/lib/module/assets/toolbar-icons/move-enabled.png +0 -0
  117. package/lib/module/assets/toolbar-icons/move.png +0 -0
  118. package/lib/module/assets/toolbar-icons/polygon-disabled.png +0 -0
  119. package/lib/module/assets/toolbar-icons/polygon-enabled.png +0 -0
  120. package/lib/module/assets/toolbar-icons/polygon.png +0 -0
  121. package/lib/module/assets/toolbar-icons/rectangle-disabled.png +0 -0
  122. package/lib/module/assets/toolbar-icons/rectangle-enabled.png +0 -0
  123. package/lib/module/assets/toolbar-icons/rectangle.png +0 -0
  124. package/lib/module/assets/toolbar-icons/text-disabled.png +0 -0
  125. package/lib/module/assets/toolbar-icons/text-enabled.png +0 -0
  126. package/lib/module/assets/toolbar-icons/text.png +0 -0
  127. package/lib/module/components/ColorPalette.js +374 -0
  128. package/lib/module/components/ColorPalette.js.map +1 -0
  129. package/lib/module/components/LineWidthSlider.js +64 -0
  130. package/lib/module/components/LineWidthSlider.js.map +1 -0
  131. package/lib/module/components/MeasurementEditModal.js +148 -0
  132. package/lib/module/components/MeasurementEditModal.js.map +1 -0
  133. package/lib/module/components/MiniMap.js +239 -0
  134. package/lib/module/components/MiniMap.js.map +1 -0
  135. package/lib/module/components/TextAnnotation.js +157 -0
  136. package/lib/module/components/TextAnnotation.js.map +1 -0
  137. package/lib/module/components/TextEditModal.js +128 -0
  138. package/lib/module/components/TextEditModal.js.map +1 -0
  139. package/lib/module/components/Toolbar.js +193 -0
  140. package/lib/module/components/Toolbar.js.map +1 -0
  141. package/lib/module/components/ZoomBadge.js +155 -0
  142. package/lib/module/components/ZoomBadge.js.map +1 -0
  143. package/lib/module/hooks/useFreehandGesture.js +169 -0
  144. package/lib/module/hooks/useFreehandGesture.js.map +1 -0
  145. package/lib/module/hooks/usePolygonGesture.js +106 -0
  146. package/lib/module/hooks/usePolygonGesture.js.map +1 -0
  147. package/lib/module/hooks/useSelectionGesture.js +232 -0
  148. package/lib/module/hooks/useSelectionGesture.js.map +1 -0
  149. package/lib/module/hooks/useShapeGesture.js +177 -0
  150. package/lib/module/hooks/useShapeGesture.js.map +1 -0
  151. package/lib/module/hooks/useViewportGesture.js +234 -0
  152. package/lib/module/hooks/useViewportGesture.js.map +1 -0
  153. package/lib/module/index.js +20 -0
  154. package/lib/module/index.js.map +1 -0
  155. package/lib/module/package.json +1 -0
  156. package/lib/module/renderers/ArrowRenderer.js +113 -0
  157. package/lib/module/renderers/ArrowRenderer.js.map +1 -0
  158. package/lib/module/renderers/CircleRenderer.js +46 -0
  159. package/lib/module/renderers/CircleRenderer.js.map +1 -0
  160. package/lib/module/renderers/FreehandRenderer.js +26 -0
  161. package/lib/module/renderers/FreehandRenderer.js.map +1 -0
  162. package/lib/module/renderers/InProgressRenderer.js +169 -0
  163. package/lib/module/renderers/InProgressRenderer.js.map +1 -0
  164. package/lib/module/renderers/LineRenderer.js +22 -0
  165. package/lib/module/renderers/LineRenderer.js.map +1 -0
  166. package/lib/module/renderers/MeasurementRenderer.js +129 -0
  167. package/lib/module/renderers/MeasurementRenderer.js.map +1 -0
  168. package/lib/module/renderers/ObjectRenderer.js +60 -0
  169. package/lib/module/renderers/ObjectRenderer.js.map +1 -0
  170. package/lib/module/renderers/PolygonRenderer.js +41 -0
  171. package/lib/module/renderers/PolygonRenderer.js.map +1 -0
  172. package/lib/module/renderers/RectRenderer.js +46 -0
  173. package/lib/module/renderers/RectRenderer.js.map +1 -0
  174. package/lib/module/renderers/SelectedObjectRenderer.js +587 -0
  175. package/lib/module/renderers/SelectedObjectRenderer.js.map +1 -0
  176. package/lib/module/renderers/SelectionOverlay.js +116 -0
  177. package/lib/module/renderers/SelectionOverlay.js.map +1 -0
  178. package/lib/module/store/useDrawingStore.js +350 -0
  179. package/lib/module/store/useDrawingStore.js.map +1 -0
  180. package/lib/module/types.js +4 -0
  181. package/lib/module/types.js.map +1 -0
  182. package/lib/module/utils/colors.js +40 -0
  183. package/lib/module/utils/colors.js.map +1 -0
  184. package/lib/module/utils/coordinates.js +71 -0
  185. package/lib/module/utils/coordinates.js.map +1 -0
  186. package/lib/module/utils/hitTesting.js +171 -0
  187. package/lib/module/utils/hitTesting.js.map +1 -0
  188. package/lib/module/utils/serialization.js +36 -0
  189. package/lib/module/utils/serialization.js.map +1 -0
  190. package/lib/module/utils/shapeDetection.js +147 -0
  191. package/lib/module/utils/shapeDetection.js.map +1 -0
  192. package/lib/module/utils/smoothing.js +80 -0
  193. package/lib/module/utils/smoothing.js.map +1 -0
  194. package/lib/typescript/DrawingEditor.d.ts +3 -0
  195. package/lib/typescript/DrawingEditor.d.ts.map +1 -0
  196. package/lib/typescript/components/ColorPalette.d.ts +9 -0
  197. package/lib/typescript/components/ColorPalette.d.ts.map +1 -0
  198. package/lib/typescript/components/LineWidthSlider.d.ts +11 -0
  199. package/lib/typescript/components/LineWidthSlider.d.ts.map +1 -0
  200. package/lib/typescript/components/MeasurementEditModal.d.ts +11 -0
  201. package/lib/typescript/components/MeasurementEditModal.d.ts.map +1 -0
  202. package/lib/typescript/components/MiniMap.d.ts +23 -0
  203. package/lib/typescript/components/MiniMap.d.ts.map +1 -0
  204. package/lib/typescript/components/TextAnnotation.d.ts +22 -0
  205. package/lib/typescript/components/TextAnnotation.d.ts.map +1 -0
  206. package/lib/typescript/components/TextEditModal.d.ts +10 -0
  207. package/lib/typescript/components/TextEditModal.d.ts.map +1 -0
  208. package/lib/typescript/components/Toolbar.d.ts +13 -0
  209. package/lib/typescript/components/Toolbar.d.ts.map +1 -0
  210. package/lib/typescript/components/ZoomBadge.d.ts +9 -0
  211. package/lib/typescript/components/ZoomBadge.d.ts.map +1 -0
  212. package/lib/typescript/hooks/useFreehandGesture.d.ts +47 -0
  213. package/lib/typescript/hooks/useFreehandGesture.d.ts.map +1 -0
  214. package/lib/typescript/hooks/usePolygonGesture.d.ts +47 -0
  215. package/lib/typescript/hooks/usePolygonGesture.d.ts.map +1 -0
  216. package/lib/typescript/hooks/useSelectionGesture.d.ts +32 -0
  217. package/lib/typescript/hooks/useSelectionGesture.d.ts.map +1 -0
  218. package/lib/typescript/hooks/useShapeGesture.d.ts +54 -0
  219. package/lib/typescript/hooks/useShapeGesture.d.ts.map +1 -0
  220. package/lib/typescript/hooks/useViewportGesture.d.ts +37 -0
  221. package/lib/typescript/hooks/useViewportGesture.d.ts.map +1 -0
  222. package/lib/typescript/index.d.ts +11 -0
  223. package/lib/typescript/index.d.ts.map +1 -0
  224. package/lib/typescript/renderers/ArrowRenderer.d.ts +9 -0
  225. package/lib/typescript/renderers/ArrowRenderer.d.ts.map +1 -0
  226. package/lib/typescript/renderers/CircleRenderer.d.ts +9 -0
  227. package/lib/typescript/renderers/CircleRenderer.d.ts.map +1 -0
  228. package/lib/typescript/renderers/FreehandRenderer.d.ts +9 -0
  229. package/lib/typescript/renderers/FreehandRenderer.d.ts.map +1 -0
  230. package/lib/typescript/renderers/InProgressRenderer.d.ts +32 -0
  231. package/lib/typescript/renderers/InProgressRenderer.d.ts.map +1 -0
  232. package/lib/typescript/renderers/LineRenderer.d.ts +9 -0
  233. package/lib/typescript/renderers/LineRenderer.d.ts.map +1 -0
  234. package/lib/typescript/renderers/MeasurementRenderer.d.ts +9 -0
  235. package/lib/typescript/renderers/MeasurementRenderer.d.ts.map +1 -0
  236. package/lib/typescript/renderers/ObjectRenderer.d.ts +12 -0
  237. package/lib/typescript/renderers/ObjectRenderer.d.ts.map +1 -0
  238. package/lib/typescript/renderers/PolygonRenderer.d.ts +13 -0
  239. package/lib/typescript/renderers/PolygonRenderer.d.ts.map +1 -0
  240. package/lib/typescript/renderers/RectRenderer.d.ts +9 -0
  241. package/lib/typescript/renderers/RectRenderer.d.ts.map +1 -0
  242. package/lib/typescript/renderers/SelectedObjectRenderer.d.ts +18 -0
  243. package/lib/typescript/renderers/SelectedObjectRenderer.d.ts.map +1 -0
  244. package/lib/typescript/renderers/SelectionOverlay.d.ts +21 -0
  245. package/lib/typescript/renderers/SelectionOverlay.d.ts.map +1 -0
  246. package/lib/typescript/store/useDrawingStore.d.ts +30 -0
  247. package/lib/typescript/store/useDrawingStore.d.ts.map +1 -0
  248. package/lib/typescript/types.d.ts +130 -0
  249. package/lib/typescript/types.d.ts.map +1 -0
  250. package/lib/typescript/utils/colors.d.ts +11 -0
  251. package/lib/typescript/utils/colors.d.ts.map +1 -0
  252. package/lib/typescript/utils/coordinates.d.ts +34 -0
  253. package/lib/typescript/utils/coordinates.d.ts.map +1 -0
  254. package/lib/typescript/utils/hitTesting.d.ts +18 -0
  255. package/lib/typescript/utils/hitTesting.d.ts.map +1 -0
  256. package/lib/typescript/utils/serialization.d.ts +17 -0
  257. package/lib/typescript/utils/serialization.d.ts.map +1 -0
  258. package/lib/typescript/utils/shapeDetection.d.ts +22 -0
  259. package/lib/typescript/utils/shapeDetection.d.ts.map +1 -0
  260. package/lib/typescript/utils/smoothing.d.ts +16 -0
  261. package/lib/typescript/utils/smoothing.d.ts.map +1 -0
  262. package/package.json +108 -0
  263. package/src/DrawingEditor.tsx +1071 -0
  264. package/src/assets/toolbar-icons/arrow-disabled.png +0 -0
  265. package/src/assets/toolbar-icons/arrow-enabled.png +0 -0
  266. package/src/assets/toolbar-icons/arrow.png +0 -0
  267. package/src/assets/toolbar-icons/circle-disabled.png +0 -0
  268. package/src/assets/toolbar-icons/circle-enabled.png +0 -0
  269. package/src/assets/toolbar-icons/circle.png +0 -0
  270. package/src/assets/toolbar-icons/freehand-disabled.png +0 -0
  271. package/src/assets/toolbar-icons/freehand-enabled.png +0 -0
  272. package/src/assets/toolbar-icons/freehand.png +0 -0
  273. package/src/assets/toolbar-icons/line-disabled.png +0 -0
  274. package/src/assets/toolbar-icons/line-enabled.png +0 -0
  275. package/src/assets/toolbar-icons/line.png +0 -0
  276. package/src/assets/toolbar-icons/measure-disabled.png +0 -0
  277. package/src/assets/toolbar-icons/measure-enabled.png +0 -0
  278. package/src/assets/toolbar-icons/measure.png +0 -0
  279. package/src/assets/toolbar-icons/move-disabled.png +0 -0
  280. package/src/assets/toolbar-icons/move-enabled.png +0 -0
  281. package/src/assets/toolbar-icons/move.png +0 -0
  282. package/src/assets/toolbar-icons/polygon-disabled.png +0 -0
  283. package/src/assets/toolbar-icons/polygon-enabled.png +0 -0
  284. package/src/assets/toolbar-icons/polygon.png +0 -0
  285. package/src/assets/toolbar-icons/rectangle-disabled.png +0 -0
  286. package/src/assets/toolbar-icons/rectangle-enabled.png +0 -0
  287. package/src/assets/toolbar-icons/rectangle.png +0 -0
  288. package/src/assets/toolbar-icons/text-disabled.png +0 -0
  289. package/src/assets/toolbar-icons/text-enabled.png +0 -0
  290. package/src/assets/toolbar-icons/text.png +0 -0
  291. package/src/components/ColorPalette.tsx +497 -0
  292. package/src/components/LineWidthSlider.tsx +87 -0
  293. package/src/components/MeasurementEditModal.tsx +163 -0
  294. package/src/components/MiniMap.tsx +275 -0
  295. package/src/components/TextAnnotation.tsx +198 -0
  296. package/src/components/TextEditModal.tsx +139 -0
  297. package/src/components/Toolbar.tsx +254 -0
  298. package/src/components/ZoomBadge.tsx +166 -0
  299. package/src/hooks/useFreehandGesture.ts +249 -0
  300. package/src/hooks/usePolygonGesture.ts +162 -0
  301. package/src/hooks/useSelectionGesture.ts +293 -0
  302. package/src/hooks/useShapeGesture.ts +256 -0
  303. package/src/hooks/useViewportGesture.ts +337 -0
  304. package/src/index.tsx +51 -0
  305. package/src/renderers/ArrowRenderer.tsx +123 -0
  306. package/src/renderers/CircleRenderer.tsx +60 -0
  307. package/src/renderers/FreehandRenderer.tsx +33 -0
  308. package/src/renderers/InProgressRenderer.tsx +217 -0
  309. package/src/renderers/LineRenderer.tsx +34 -0
  310. package/src/renderers/MeasurementRenderer.tsx +179 -0
  311. package/src/renderers/ObjectRenderer.tsx +42 -0
  312. package/src/renderers/PolygonRenderer.tsx +66 -0
  313. package/src/renderers/RectRenderer.tsx +60 -0
  314. package/src/renderers/SelectedObjectRenderer.tsx +738 -0
  315. package/src/renderers/SelectionOverlay.tsx +170 -0
  316. package/src/store/useDrawingStore.ts +357 -0
  317. package/src/types.ts +186 -0
  318. package/src/utils/colors.ts +98 -0
  319. package/src/utils/coordinates.ts +75 -0
  320. package/src/utils/hitTesting.ts +242 -0
  321. package/src/utils/serialization.ts +45 -0
  322. package/src/utils/shapeDetection.ts +160 -0
  323. package/src/utils/smoothing.ts +84 -0
@@ -0,0 +1,249 @@
1
+ import { useCallback } from "react";
2
+ import { Gesture } from "react-native-gesture-handler";
3
+ import {
4
+ useSharedValue,
5
+ useDerivedValue,
6
+ runOnJS,
7
+ } from "react-native-reanimated";
8
+ import { Skia } from "@shopify/react-native-skia";
9
+ import type { Point, Size, FreehandObject, DrawingObject } from "../types";
10
+ import { buildSmoothedPathFromFlat } from "../utils/smoothing";
11
+ import { normalize, screenToCanvas } from "../utils/coordinates";
12
+ import { generateId } from "../utils/serialization";
13
+ import { detectShape } from "../utils/shapeDetection";
14
+ import { useDrawingStore } from "../store/useDrawingStore";
15
+
16
+ const MIN_GESTURE_DURATION_MS = 300;
17
+ const MIN_GESTURE_DISTANCE = 5;
18
+
19
+ interface UseFreehandGestureOptions {
20
+ colorSV: { value: string };
21
+ lineWidthSV: { value: number };
22
+ fillColorSV: { value: string | null };
23
+ fillAlphaSV: { value: number };
24
+ canvasSize: Size;
25
+ enabled: boolean;
26
+ imageOffsetX: { value: number };
27
+ imageOffsetY: { value: number };
28
+ imageWidth: { value: number };
29
+ imageHeight: { value: number };
30
+ viewScale: { value: number };
31
+ viewTranslateX: { value: number };
32
+ viewTranslateY: { value: number };
33
+ }
34
+
35
+ export function useFreehandGesture({
36
+ colorSV,
37
+ lineWidthSV,
38
+ fillColorSV,
39
+ fillAlphaSV,
40
+ canvasSize,
41
+ enabled,
42
+ imageOffsetX,
43
+ imageOffsetY,
44
+ imageWidth,
45
+ imageHeight,
46
+ viewScale,
47
+ viewTranslateX,
48
+ viewTranslateY,
49
+ }: UseFreehandGestureOptions) {
50
+ const rawPoints = useSharedValue<number[]>([]);
51
+ const isDrawing = useSharedValue(false);
52
+ const gestureStartTime = useSharedValue(0);
53
+ const gestureStartPos = useSharedValue<[number, number]>([0, 0]);
54
+
55
+ const addObject = useDrawingStore((s) => s.addObject);
56
+
57
+ // Build the path in the Skia worklet on every points update
58
+ const inProgressPath = useDerivedValue(() => {
59
+ if (!isDrawing.value) return null;
60
+ const svg = buildSmoothedPathFromFlat(rawPoints.value);
61
+ if (!svg) return null;
62
+ const path = Skia.Path.MakeFromSVGString(svg);
63
+ return path;
64
+ });
65
+
66
+ const inProgressColor = useDerivedValue(() => colorSV.value);
67
+ const inProgressWidth = useDerivedValue(() => lineWidthSV.value);
68
+
69
+ const commitPath = useCallback(
70
+ (
71
+ flatPoints: number[],
72
+ durationMs: number,
73
+ color: string,
74
+ lineWidth: number,
75
+ fillColor: string | null,
76
+ fillAlpha: number,
77
+ ) => {
78
+ const n = flatPoints.length / 2;
79
+ if (n < 2) return;
80
+
81
+ // Convert flat array to Point[]
82
+ const pixelPoints: Point[] = [];
83
+ for (let i = 0; i < n; i++) {
84
+ pixelPoints.push({ x: flatPoints[i * 2]!, y: flatPoints[i * 2 + 1]! });
85
+ }
86
+
87
+ // Try auto shape detection
88
+ const detected = detectShape(pixelPoints, durationMs);
89
+
90
+ let obj: DrawingObject;
91
+
92
+ if (detected) {
93
+ const from = normalize(detected.from, canvasSize);
94
+ const to = normalize(detected.to, canvasSize);
95
+
96
+ switch (detected.type) {
97
+ case "circle":
98
+ obj = {
99
+ id: generateId(),
100
+ type: "circle",
101
+ from,
102
+ to,
103
+ color,
104
+ lineWidth,
105
+ backgroundColor: fillColor ?? undefined,
106
+ backgroundAlpha: fillColor ? fillAlpha : undefined,
107
+ };
108
+ break;
109
+ case "rectangle":
110
+ obj = {
111
+ id: generateId(),
112
+ type: "rectangle",
113
+ from,
114
+ to,
115
+ color,
116
+ lineWidth,
117
+ backgroundColor: fillColor ?? undefined,
118
+ backgroundAlpha: fillColor ? fillAlpha : undefined,
119
+ };
120
+ break;
121
+ case "line":
122
+ obj = {
123
+ id: generateId(),
124
+ type: "line",
125
+ from,
126
+ to,
127
+ color,
128
+ lineWidth,
129
+ };
130
+ break;
131
+ }
132
+ } else {
133
+ // Store as freehand with normalized points
134
+ const normalizedPoints = pixelPoints.map((p) =>
135
+ normalize(p, canvasSize),
136
+ );
137
+ obj = {
138
+ id: generateId(),
139
+ type: "freehand",
140
+ points: normalizedPoints,
141
+ color,
142
+ lineWidth,
143
+ } satisfies FreehandObject;
144
+ }
145
+
146
+ addObject(obj);
147
+ },
148
+ [canvasSize, addObject],
149
+ );
150
+
151
+ const gesture = Gesture.Pan()
152
+ .enabled(enabled)
153
+ .maxPointers(1)
154
+ .minDistance(0)
155
+ .onBegin((e) => {
156
+ "worklet";
157
+ const canvas = screenToCanvas(
158
+ e.x,
159
+ e.y,
160
+ viewScale.value,
161
+ viewTranslateX.value,
162
+ viewTranslateY.value,
163
+ );
164
+ const x = Math.max(
165
+ 0,
166
+ Math.min(imageWidth.value, canvas.x - imageOffsetX.value),
167
+ );
168
+ const y = Math.max(
169
+ 0,
170
+ Math.min(imageHeight.value, canvas.y - imageOffsetY.value),
171
+ );
172
+ isDrawing.value = true;
173
+ gestureStartTime.value = Date.now();
174
+ gestureStartPos.value = [x, y];
175
+ rawPoints.value = [x, y];
176
+ })
177
+ .onUpdate((e) => {
178
+ "worklet";
179
+ const canvas = screenToCanvas(
180
+ e.x,
181
+ e.y,
182
+ viewScale.value,
183
+ viewTranslateX.value,
184
+ viewTranslateY.value,
185
+ );
186
+ const x = Math.max(
187
+ 0,
188
+ Math.min(imageWidth.value, canvas.x - imageOffsetX.value),
189
+ );
190
+ const y = Math.max(
191
+ 0,
192
+ Math.min(imageHeight.value, canvas.y - imageOffsetY.value),
193
+ );
194
+ // Append new point
195
+ rawPoints.value = [...rawPoints.value, x, y];
196
+ })
197
+ .onEnd((e) => {
198
+ "worklet";
199
+ const canvas = screenToCanvas(
200
+ e.x,
201
+ e.y,
202
+ viewScale.value,
203
+ viewTranslateX.value,
204
+ viewTranslateY.value,
205
+ );
206
+ const x = Math.max(
207
+ 0,
208
+ Math.min(imageWidth.value, canvas.x - imageOffsetX.value),
209
+ );
210
+ const y = Math.max(
211
+ 0,
212
+ Math.min(imageHeight.value, canvas.y - imageOffsetY.value),
213
+ );
214
+ const duration = Date.now() - gestureStartTime.value;
215
+ const [sx, sy] = gestureStartPos.value;
216
+ const dist = Math.sqrt((x - sx) ** 2 + (y - sy) ** 2);
217
+
218
+ // Cancel accidental taps (< 300ms and < 5px movement)
219
+ if (duration < MIN_GESTURE_DURATION_MS && dist < MIN_GESTURE_DISTANCE) {
220
+ isDrawing.value = false;
221
+ rawPoints.value = [];
222
+ return;
223
+ }
224
+
225
+ const pts = rawPoints.value;
226
+ const color = colorSV.value;
227
+ const lw = lineWidthSV.value;
228
+ const fc = fillColorSV.value;
229
+ const fa = fillAlphaSV.value;
230
+
231
+ isDrawing.value = false;
232
+ rawPoints.value = [];
233
+
234
+ runOnJS(commitPath)(pts, duration, color, lw, fc, fa);
235
+ })
236
+ .onFinalize(() => {
237
+ "worklet";
238
+ isDrawing.value = false;
239
+ rawPoints.value = [];
240
+ });
241
+
242
+ return {
243
+ gesture,
244
+ inProgressPath,
245
+ inProgressColor,
246
+ inProgressWidth,
247
+ isDrawing,
248
+ };
249
+ }
@@ -0,0 +1,162 @@
1
+ import { useCallback } from "react";
2
+ import { Gesture } from "react-native-gesture-handler";
3
+ import { useSharedValue, runOnJS } from "react-native-reanimated";
4
+ import type { PolygonObject, Size, Point } from "../types";
5
+ import { normalize, screenToCanvas } from "../utils/coordinates";
6
+ import { generateId } from "../utils/serialization";
7
+ import { useDrawingStore } from "../store/useDrawingStore";
8
+
9
+ /** Distance in pixels to auto-close the polygon when tapping near the first point */
10
+ const CLOSE_DISTANCE = 30;
11
+
12
+ interface UsePolygonGestureOptions {
13
+ colorSV: { value: string };
14
+ lineWidthSV: { value: number };
15
+ fillColorSV: { value: string | null };
16
+ fillAlphaSV: { value: number };
17
+ canvasSize: Size;
18
+ enabled: boolean;
19
+ imageOffsetX: { value: number };
20
+ imageOffsetY: { value: number };
21
+ imageWidth: { value: number };
22
+ imageHeight: { value: number };
23
+ viewScale: { value: number };
24
+ viewTranslateX: { value: number };
25
+ viewTranslateY: { value: number };
26
+ }
27
+
28
+ export function usePolygonGesture({
29
+ colorSV,
30
+ lineWidthSV,
31
+ fillColorSV,
32
+ fillAlphaSV,
33
+ canvasSize,
34
+ enabled,
35
+ imageOffsetX,
36
+ imageOffsetY,
37
+ imageWidth,
38
+ imageHeight,
39
+ viewScale,
40
+ viewTranslateX,
41
+ viewTranslateY,
42
+ }: UsePolygonGestureOptions) {
43
+ // Flat array of pixel coordinates: [x0, y0, x1, y1, ...]
44
+ const pointsFlat = useSharedValue<number[]>([]);
45
+ const pointCount = useSharedValue(0);
46
+ const isActive = useSharedValue(false);
47
+
48
+ const addObject = useDrawingStore((s) => s.addObject);
49
+
50
+ const commitPolygon = useCallback(
51
+ (
52
+ flatPoints: number[],
53
+ closed: boolean,
54
+ color: string,
55
+ lineWidth: number,
56
+ fillColor: string | null,
57
+ fillAlpha: number,
58
+ ) => {
59
+ const count = flatPoints.length / 2;
60
+ if (count < 3) return; // Need at least 3 points for a polygon
61
+
62
+ const normalizedPoints: Point[] = [];
63
+ for (let i = 0; i < count; i++) {
64
+ normalizedPoints.push(
65
+ normalize(
66
+ { x: flatPoints[i * 2]!, y: flatPoints[i * 2 + 1]! },
67
+ canvasSize,
68
+ ),
69
+ );
70
+ }
71
+
72
+ const obj: PolygonObject = {
73
+ id: generateId(),
74
+ type: "polygon",
75
+ points: normalizedPoints,
76
+ closed,
77
+ color,
78
+ lineWidth,
79
+ backgroundColor: closed && fillColor ? fillColor : undefined,
80
+ backgroundAlpha: closed && fillColor ? fillAlpha : undefined,
81
+ };
82
+
83
+ addObject(obj);
84
+ },
85
+ [canvasSize, addObject],
86
+ );
87
+
88
+ const resetState = useCallback(() => {
89
+ pointsFlat.value = [];
90
+ pointCount.value = 0;
91
+ isActive.value = false;
92
+ }, [pointsFlat, pointCount, isActive]);
93
+
94
+ const gesture = Gesture.Tap()
95
+ .enabled(enabled)
96
+ .maxDuration(500)
97
+ .onEnd((e) => {
98
+ "worklet";
99
+ const canvas = screenToCanvas(
100
+ e.x,
101
+ e.y,
102
+ viewScale.value,
103
+ viewTranslateX.value,
104
+ viewTranslateY.value,
105
+ );
106
+ const x = Math.max(
107
+ 0,
108
+ Math.min(imageWidth.value, canvas.x - imageOffsetX.value),
109
+ );
110
+ const y = Math.max(
111
+ 0,
112
+ Math.min(imageHeight.value, canvas.y - imageOffsetY.value),
113
+ );
114
+
115
+ const flat = pointsFlat.value;
116
+ const count = pointCount.value;
117
+
118
+ // Check if should close
119
+ if (count >= 3) {
120
+ const firstX = flat[0]!;
121
+ const firstY = flat[1]!;
122
+ const dx = x - firstX;
123
+ const dy = y - firstY;
124
+ const dist = Math.sqrt(dx * dx + dy * dy);
125
+ if (dist < CLOSE_DISTANCE) {
126
+ // Close — commit on JS thread
127
+ const snapshot = flat.slice();
128
+ runOnJS(commitPolygon)(
129
+ snapshot,
130
+ true,
131
+ colorSV.value,
132
+ lineWidthSV.value,
133
+ fillColorSV.value,
134
+ fillAlphaSV.value,
135
+ );
136
+ pointsFlat.value = [];
137
+ pointCount.value = 0;
138
+ isActive.value = false;
139
+ return;
140
+ }
141
+ }
142
+
143
+ // Add point
144
+ const newFlat = [...flat, x, y];
145
+ pointsFlat.value = newFlat;
146
+ pointCount.value = count + 1;
147
+ isActive.value = true;
148
+ });
149
+
150
+ /** Cancel the current polygon construction (e.g. on tool switch) — discard points */
151
+ const cancel = useCallback(() => {
152
+ resetState();
153
+ }, [resetState]);
154
+
155
+ return {
156
+ gesture,
157
+ pointsFlat,
158
+ pointCount,
159
+ isActive,
160
+ cancel,
161
+ };
162
+ }
@@ -0,0 +1,293 @@
1
+ import { useCallback, useEffect } from "react";
2
+ import { Gesture } from "react-native-gesture-handler";
3
+ import { runOnJS, useSharedValue } from "react-native-reanimated";
4
+ import type { SharedValue } from "react-native-reanimated";
5
+ import { screenToCanvas } from "../utils/coordinates";
6
+ import type { Size, Point } from "../types";
7
+ import { useDrawingStore } from "../store/useDrawingStore";
8
+ import { hitTestObject } from "../utils/hitTesting";
9
+ import {
10
+ getAnchorPoints,
11
+ ANCHOR_HIT_RADIUS,
12
+ } from "../renderers/SelectionOverlay";
13
+
14
+ interface UseSelectionGestureOptions {
15
+ canvasSize: Size;
16
+ imageOffsetX: { value: number };
17
+ imageOffsetY: { value: number };
18
+ viewScale: { value: number };
19
+ viewTranslateX: { value: number };
20
+ viewTranslateY: { value: number };
21
+ onTapEmpty?: (x: number, y: number) => void;
22
+ }
23
+
24
+ export function useSelectionGesture({
25
+ canvasSize,
26
+ imageOffsetX,
27
+ imageOffsetY,
28
+ viewScale,
29
+ viewTranslateX,
30
+ viewTranslateY,
31
+ onTapEmpty,
32
+ }: UseSelectionGestureOptions) {
33
+ const isDragging = useSharedValue(false);
34
+ const dragStartX = useSharedValue(0);
35
+ const dragStartY = useSharedValue(0);
36
+ // Live pixel offset for visual feedback during drag
37
+ const dragOffsetX: SharedValue<number> = useSharedValue(0);
38
+ const dragOffsetY: SharedValue<number> = useSharedValue(0);
39
+ // Which anchor is being dragged (-1 = none, moving whole object)
40
+ const draggingAnchorIndex = useSharedValue(-1);
41
+ // True between onEnd and the React re-render that commits the new position
42
+ const pendingCommit = useSharedValue(false);
43
+
44
+ // Pre-computed anchor positions in pixel coords (for worklet access)
45
+ const anchor0X = useSharedValue(0);
46
+ const anchor0Y = useSharedValue(0);
47
+ const anchor1X = useSharedValue(0);
48
+ const anchor1Y = useSharedValue(0);
49
+ const hasAnchors = useSharedValue(false);
50
+ // Flat array of all anchor positions [x0, y0, x1, y1, ...] for N-anchor objects
51
+ const allAnchorsFlat = useSharedValue<number[]>([]);
52
+ const anchorCount = useSharedValue(0);
53
+
54
+ const objects = useDrawingStore((s) => s.objects);
55
+ const selectedObjectId = useDrawingStore((s) => s.selectedObjectId);
56
+ const selectObject = useDrawingStore((s) => s.selectObject);
57
+ const moveObject = useDrawingStore((s) => s.moveObject);
58
+ const resizeObject = useDrawingStore((s) => s.resizeObject);
59
+
60
+ // Sync anchor positions to shared values whenever selection changes
61
+ useEffect(() => {
62
+ if (!selectedObjectId) {
63
+ hasAnchors.value = false;
64
+ anchorCount.value = 0;
65
+ allAnchorsFlat.value = [];
66
+ return;
67
+ }
68
+ const obj = objects.find((o) => o.id === selectedObjectId);
69
+ if (!obj) {
70
+ hasAnchors.value = false;
71
+ anchorCount.value = 0;
72
+ allAnchorsFlat.value = [];
73
+ return;
74
+ }
75
+ const anchors = getAnchorPoints(obj, canvasSize);
76
+ if (anchors.length >= 2) {
77
+ anchor0X.value = anchors[0]!.x;
78
+ anchor0Y.value = anchors[0]!.y;
79
+ anchor1X.value = anchors[1]!.x;
80
+ anchor1Y.value = anchors[1]!.y;
81
+ hasAnchors.value = true;
82
+ // Store all anchors for N-vertex objects (polygon)
83
+ const flat: number[] = [];
84
+ for (const a of anchors) {
85
+ flat.push(a.x, a.y);
86
+ }
87
+ allAnchorsFlat.value = flat;
88
+ anchorCount.value = anchors.length;
89
+ } else {
90
+ hasAnchors.value = false;
91
+ anchorCount.value = 0;
92
+ allAnchorsFlat.value = [];
93
+ }
94
+ }, [selectedObjectId, objects, canvasSize]);
95
+
96
+ const handleTap = useCallback(
97
+ (x: number, y: number) => {
98
+ const tapPoint: Point = { x, y };
99
+
100
+ // Find the topmost object that is hit (reverse iteration)
101
+ for (let i = objects.length - 1; i >= 0; i--) {
102
+ const obj = objects[i]!;
103
+ if (hitTestObject(obj, tapPoint, canvasSize)) {
104
+ selectObject(obj.id);
105
+ return;
106
+ }
107
+ }
108
+ // Tap on empty space → delegate or deselect
109
+ if (onTapEmpty) {
110
+ onTapEmpty(x, y);
111
+ } else {
112
+ selectObject(null);
113
+ }
114
+ },
115
+ [objects, canvasSize, selectObject, onTapEmpty],
116
+ );
117
+
118
+ const resetDragState = useCallback(() => {
119
+ isDragging.value = false;
120
+ dragOffsetX.value = 0;
121
+ dragOffsetY.value = 0;
122
+ draggingAnchorIndex.value = -1;
123
+ pendingCommit.value = false;
124
+ }, [
125
+ isDragging,
126
+ dragOffsetX,
127
+ dragOffsetY,
128
+ draggingAnchorIndex,
129
+ pendingCommit,
130
+ ]);
131
+
132
+ const handleDragEnd = useCallback(
133
+ (dx: number, dy: number) => {
134
+ if (!selectedObjectId) {
135
+ // No store update will occur — reset immediately
136
+ resetDragState();
137
+ return;
138
+ }
139
+
140
+ // Convert pixel delta to normalized delta
141
+ const normalizedDx = canvasSize.width > 0 ? dx / canvasSize.width : 0;
142
+ const normalizedDy = canvasSize.height > 0 ? dy / canvasSize.height : 0;
143
+
144
+ moveObject(selectedObjectId, normalizedDx, normalizedDy);
145
+ // Drag state is reset by SelectedObjectRenderer's useEffect
146
+ // when the component re-renders with the updated object.
147
+ },
148
+ [selectedObjectId, canvasSize, moveObject, resetDragState],
149
+ );
150
+
151
+ const handleResizeEnd = useCallback(
152
+ (anchorIndex: number, dx: number, dy: number) => {
153
+ if (!selectedObjectId) {
154
+ resetDragState();
155
+ return;
156
+ }
157
+
158
+ const normalizedDx = canvasSize.width > 0 ? dx / canvasSize.width : 0;
159
+ const normalizedDy = canvasSize.height > 0 ? dy / canvasSize.height : 0;
160
+
161
+ resizeObject(selectedObjectId, anchorIndex, normalizedDx, normalizedDy);
162
+ // Drag state is reset by SelectedObjectRenderer's useEffect
163
+ },
164
+ [selectedObjectId, canvasSize, resizeObject, resetDragState],
165
+ );
166
+
167
+ const tapGesture = Gesture.Tap().onEnd((e) => {
168
+ "worklet";
169
+ const canvas = screenToCanvas(
170
+ e.x,
171
+ e.y,
172
+ viewScale.value,
173
+ viewTranslateX.value,
174
+ viewTranslateY.value,
175
+ );
176
+ const x = canvas.x - imageOffsetX.value;
177
+ const y = canvas.y - imageOffsetY.value;
178
+ runOnJS(handleTap)(x, y);
179
+ });
180
+
181
+ const panGesture = Gesture.Pan()
182
+ .enabled(selectedObjectId !== null)
183
+ .maxPointers(1)
184
+ .minDistance(5)
185
+ .onBegin((e) => {
186
+ "worklet";
187
+ const canvas = screenToCanvas(
188
+ e.x,
189
+ e.y,
190
+ viewScale.value,
191
+ viewTranslateX.value,
192
+ viewTranslateY.value,
193
+ );
194
+ const x = canvas.x - imageOffsetX.value;
195
+ const y = canvas.y - imageOffsetY.value;
196
+
197
+ pendingCommit.value = false;
198
+ isDragging.value = true;
199
+ dragStartX.value = e.x;
200
+ dragStartY.value = e.y;
201
+ dragOffsetX.value = 0;
202
+ dragOffsetY.value = 0;
203
+
204
+ // Check if we hit an anchor handle
205
+ draggingAnchorIndex.value = -1;
206
+ if (hasAnchors.value) {
207
+ const hitRadius = ANCHOR_HIT_RADIUS;
208
+ let bestDist = hitRadius;
209
+ let bestIdx = -1;
210
+ const flat = allAnchorsFlat.value;
211
+ const count = anchorCount.value;
212
+ for (let i = 0; i < count; i++) {
213
+ const ax = flat[i * 2]!;
214
+ const ay = flat[i * 2 + 1]!;
215
+ const d = Math.sqrt((x - ax) ** 2 + (y - ay) ** 2);
216
+ if (d < bestDist) {
217
+ bestDist = d;
218
+ bestIdx = i;
219
+ }
220
+ }
221
+ if (bestIdx >= 0) {
222
+ draggingAnchorIndex.value = bestIdx;
223
+ }
224
+ }
225
+ })
226
+ .onUpdate((e) => {
227
+ "worklet";
228
+ // Scale drag offset by 1/scale so visual feedback matches canvas coordinates
229
+ dragOffsetX.value = (e.x - dragStartX.value) / viewScale.value;
230
+ dragOffsetY.value = (e.y - dragStartY.value) / viewScale.value;
231
+ })
232
+ .onEnd((e) => {
233
+ "worklet";
234
+ // Convert screen-space deltas to canvas-space deltas
235
+ const dx = (e.x - dragStartX.value) / viewScale.value;
236
+ const dy = (e.y - dragStartY.value) / viewScale.value;
237
+
238
+ // Keep isDragging + offsets alive so the object stays at its dragged
239
+ // position until React re-renders with the committed store update.
240
+ // SelectedObjectRenderer's useEffect will reset drag state atomically.
241
+ pendingCommit.value = true;
242
+
243
+ if (draggingAnchorIndex.value >= 0) {
244
+ runOnJS(handleResizeEnd)(draggingAnchorIndex.value, dx, dy);
245
+ } else {
246
+ runOnJS(handleDragEnd)(dx, dy);
247
+ }
248
+ })
249
+ .onFinalize(() => {
250
+ "worklet";
251
+ // Only reset if onEnd didn't fire (gesture was cancelled)
252
+ if (!pendingCommit.value) {
253
+ isDragging.value = false;
254
+ dragOffsetX.value = 0;
255
+ dragOffsetY.value = 0;
256
+ draggingAnchorIndex.value = -1;
257
+ }
258
+ });
259
+
260
+ // Double-tap for editing measurement text or text annotations
261
+ const doubleTapGesture = Gesture.Tap()
262
+ .numberOfTaps(2)
263
+ .minPointers(1)
264
+ .onEnd((e) => {
265
+ "worklet";
266
+ const canvas = screenToCanvas(
267
+ e.x,
268
+ e.y,
269
+ viewScale.value,
270
+ viewTranslateX.value,
271
+ viewTranslateY.value,
272
+ );
273
+ const x = canvas.x - imageOffsetX.value;
274
+ const y = canvas.y - imageOffsetY.value;
275
+ // Will be handled in the DrawingEditor for opening edit modals
276
+ runOnJS(handleDoubleTap)(x, y);
277
+ });
278
+
279
+ const handleDoubleTap = useCallback((_x: number, _y: number) => {
280
+ // Double-tap handling is delegated to DrawingEditor via callbacks
281
+ // This hook just detects the gesture
282
+ }, []);
283
+
284
+ return {
285
+ tapGesture,
286
+ panGesture,
287
+ doubleTapGesture,
288
+ isDragging,
289
+ dragOffsetX,
290
+ dragOffsetY,
291
+ draggingAnchorIndex,
292
+ };
293
+ }