@yuzhouu/canvas-kit 0.1.2

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 (263) hide show
  1. package/README.md +145 -0
  2. package/_vendor/canvas-core/bindings/BindingUtil.ts +92 -0
  3. package/_vendor/canvas-core/bindings/BindingUtilRegistry.ts +22 -0
  4. package/_vendor/canvas-core/bindings/arrow/arrowBindingHelpers.ts +52 -0
  5. package/_vendor/canvas-core/contextMenu.ts +85 -0
  6. package/_vendor/canvas-core/editor/Editor.ts +2614 -0
  7. package/_vendor/canvas-core/editor/HistoryManager.ts +83 -0
  8. package/_vendor/canvas-core/editor/StateNode.ts +162 -0
  9. package/_vendor/canvas-core/editor/camera.ts +6 -0
  10. package/_vendor/canvas-core/editor/clipboard.ts +478 -0
  11. package/_vendor/canvas-core/editor/commands.ts +833 -0
  12. package/_vendor/canvas-core/editor/edgeScroll.ts +37 -0
  13. package/_vendor/canvas-core/editor/editorComputedHelpers.ts +257 -0
  14. package/_vendor/canvas-core/editor/editorSelectionTransforms.ts +116 -0
  15. package/_vendor/canvas-core/editor/events.ts +93 -0
  16. package/_vendor/canvas-core/editor/index.ts +12 -0
  17. package/_vendor/canvas-core/editor/interactions.ts +101 -0
  18. package/_vendor/canvas-core/editor/internal.ts +22 -0
  19. package/_vendor/canvas-core/editor/managers/BindingManager.ts +262 -0
  20. package/_vendor/canvas-core/editor/managers/CameraManager.ts +147 -0
  21. package/_vendor/canvas-core/editor/managers/ClipboardManager.ts +32 -0
  22. package/_vendor/canvas-core/editor/managers/DocumentSnapshotManager.ts +256 -0
  23. package/_vendor/canvas-core/editor/managers/InputsManager.ts +319 -0
  24. package/_vendor/canvas-core/editor/managers/InteractionTransientManager.ts +218 -0
  25. package/_vendor/canvas-core/editor/managers/QueryManager.ts +837 -0
  26. package/_vendor/canvas-core/editor/managers/RenderWindowManager.ts +347 -0
  27. package/_vendor/canvas-core/editor/managers/SelectionLayoutManager.ts +196 -0
  28. package/_vendor/canvas-core/editor/managers/SelectionManager.ts +98 -0
  29. package/_vendor/canvas-core/editor/managers/SelectionOverlayManager.ts +308 -0
  30. package/_vendor/canvas-core/editor/managers/ShapeEditingManager.ts +226 -0
  31. package/_vendor/canvas-core/editor/managers/ShapeGeometryManager.ts +162 -0
  32. package/_vendor/canvas-core/editor/managers/ShapeHierarchyManager.ts +797 -0
  33. package/_vendor/canvas-core/editor/managers/SideEffectManager.ts +83 -0
  34. package/_vendor/canvas-core/editor/managers/SnapManager.ts +433 -0
  35. package/_vendor/canvas-core/editor/managers/SnapSnapshotBuilder.ts +430 -0
  36. package/_vendor/canvas-core/editor/managers/SpatialIndexManager.ts +354 -0
  37. package/_vendor/canvas-core/editor/managers/ToolManager.ts +81 -0
  38. package/_vendor/canvas-core/editor/managers/queryState.ts +583 -0
  39. package/_vendor/canvas-core/editor/managers/selectionGeometry.ts +287 -0
  40. package/_vendor/canvas-core/editor/managers/shapeOrder.ts +350 -0
  41. package/_vendor/canvas-core/editor/managers/shapeReparenting.ts +160 -0
  42. package/_vendor/canvas-core/editor/managers/snapCalculations.ts +412 -0
  43. package/_vendor/canvas-core/editor/managers/snapGeometry.ts +202 -0
  44. package/_vendor/canvas-core/editor/managers/snapIndicators.ts +403 -0
  45. package/_vendor/canvas-core/editor/managers/snapTypes.ts +122 -0
  46. package/_vendor/canvas-core/editor/queries/shapeTransforms.ts +171 -0
  47. package/_vendor/canvas-core/editor/renderWindow.ts +439 -0
  48. package/_vendor/canvas-core/editor/resizeGeometry.ts +654 -0
  49. package/_vendor/canvas-core/editor/selection.ts +489 -0
  50. package/_vendor/canvas-core/editor/selectionHandleInteractions.ts +43 -0
  51. package/_vendor/canvas-core/editor/selectionOverlay.ts +567 -0
  52. package/_vendor/canvas-core/exports/integrity.ts +174 -0
  53. package/_vendor/canvas-core/exports/svg.ts +494 -0
  54. package/_vendor/canvas-core/geometry/Box.ts +80 -0
  55. package/_vendor/canvas-core/geometry/Ellipse2d.ts +67 -0
  56. package/_vendor/canvas-core/geometry/Geometry2d.ts +52 -0
  57. package/_vendor/canvas-core/geometry/Mat.ts +92 -0
  58. package/_vendor/canvas-core/geometry/Polyline2d.ts +63 -0
  59. package/_vendor/canvas-core/geometry/Rectangle2d.ts +64 -0
  60. package/_vendor/canvas-core/geometry/Vec.ts +123 -0
  61. package/_vendor/canvas-core/geometry/index.ts +13 -0
  62. package/_vendor/canvas-core/geometry/segments.ts +65 -0
  63. package/_vendor/canvas-core/index.ts +391 -0
  64. package/_vendor/canvas-core/plugins/index.ts +5 -0
  65. package/_vendor/canvas-core/plugins/types.ts +53 -0
  66. package/_vendor/canvas-core/reactivity.ts +16 -0
  67. package/_vendor/canvas-core/schema/records.ts +221 -0
  68. package/_vendor/canvas-core/schema/snapshots.ts +527 -0
  69. package/_vendor/canvas-core/shapes/ShapeUtil.ts +516 -0
  70. package/_vendor/canvas-core/shapes/ShapeUtilRegistry.ts +26 -0
  71. package/_vendor/canvas-core/shapes/capabilities.ts +67 -0
  72. package/_vendor/canvas-core/shapes/types.ts +20 -0
  73. package/_vendor/canvas-core/toolbar.ts +148 -0
  74. package/_vendor/canvas-core/tools/hand/HandTool.ts +75 -0
  75. package/_vendor/canvas-core/tools/index.ts +2 -0
  76. package/_vendor/canvas-core/tools/select/SelectTool.ts +33 -0
  77. package/_vendor/canvas-core/tools/select/selectContainment.ts +104 -0
  78. package/_vendor/canvas-core/tools/select/selectToolHelpers.ts +801 -0
  79. package/_vendor/canvas-core/tools/select/states/BrushingState.ts +145 -0
  80. package/_vendor/canvas-core/tools/select/states/DraggingSelectionHandleInteractionState.ts +116 -0
  81. package/_vendor/canvas-core/tools/select/states/IdleState.ts +159 -0
  82. package/_vendor/canvas-core/tools/select/states/PointingCanvasState.ts +41 -0
  83. package/_vendor/canvas-core/tools/select/states/PointingResizeHandleState.ts +78 -0
  84. package/_vendor/canvas-core/tools/select/states/PointingRotateHandleState.ts +64 -0
  85. package/_vendor/canvas-core/tools/select/states/PointingSelectionHandleInteractionState.ts +34 -0
  86. package/_vendor/canvas-core/tools/select/states/PointingShapeState.ts +140 -0
  87. package/_vendor/canvas-core/tools/select/states/ResizingState.ts +171 -0
  88. package/_vendor/canvas-core/tools/select/states/RotatingState.ts +124 -0
  89. package/_vendor/canvas-core/tools/select/states/SelectStateNode.ts +128 -0
  90. package/_vendor/canvas-core/tools/select/states/TranslatingState.ts +139 -0
  91. package/_vendor/canvas-core/tools/select/states/index.ts +11 -0
  92. package/_vendor/canvas-plugin-api/feature.ts +204 -0
  93. package/_vendor/canvas-plugin-api/index.ts +142 -0
  94. package/_vendor/canvas-product-react/api/apiRegistry.ts +35 -0
  95. package/_vendor/canvas-product-react/api/canvasProductApi.ts +422 -0
  96. package/_vendor/canvas-product-react/api/index.ts +31 -0
  97. package/_vendor/canvas-product-react/capabilities/clipboard/clipboardService.ts +36 -0
  98. package/_vendor/canvas-product-react/capabilities/clipboard/systemClipboard.ts +245 -0
  99. package/_vendor/canvas-product-react/capabilities/context-menu/systemContextMenu.ts +396 -0
  100. package/_vendor/canvas-product-react/capabilities/context-menu/useContextMenuSurface.ts +59 -0
  101. package/_vendor/canvas-product-react/capabilities/export/useSvgExport.ts +156 -0
  102. package/_vendor/canvas-product-react/capabilities/history/useHistoryControls.ts +23 -0
  103. package/_vendor/canvas-product-react/capabilities/pages/usePageControls.ts +41 -0
  104. package/_vendor/canvas-product-react/capabilities/toolbar/systemToolbar.ts +490 -0
  105. package/_vendor/canvas-product-react/capabilities/toolbar/useToolbarSurfaces.ts +92 -0
  106. package/_vendor/canvas-product-react/capabilities/zoom/useZoomControls.ts +27 -0
  107. package/_vendor/canvas-product-react/index.ts +84 -0
  108. package/_vendor/canvas-product-react/persistence/index.ts +18 -0
  109. package/_vendor/canvas-product-react/persistence/localSnapshotPersistence.ts +294 -0
  110. package/_vendor/canvas-product-react/runtime/CanvasProductRoot.tsx +250 -0
  111. package/_vendor/canvas-product-react/runtime/productDefinition.ts +72 -0
  112. package/_vendor/canvas-product-react/runtime/productServices.ts +96 -0
  113. package/_vendor/canvas-react/components/CanvasView.tsx +796 -0
  114. package/_vendor/canvas-react/components/OverlayLayer.tsx +879 -0
  115. package/_vendor/canvas-react/components/PageSpace.tsx +34 -0
  116. package/_vendor/canvas-react/components/ShapeLayer.tsx +428 -0
  117. package/_vendor/canvas-react/components/index.ts +4 -0
  118. package/_vendor/canvas-react/hooks/useEditor.ts +11 -0
  119. package/_vendor/canvas-react/hooks/useValue.ts +14 -0
  120. package/_vendor/canvas-react/host/CanvasRendererRegistryContext.tsx +14 -0
  121. package/_vendor/canvas-react/host/contextMenuRegistry.ts +61 -0
  122. package/_vendor/canvas-react/host/domEventGuards.ts +61 -0
  123. package/_vendor/canvas-react/host/index.ts +38 -0
  124. package/_vendor/canvas-react/host/pluginHost.ts +635 -0
  125. package/_vendor/canvas-react/host/rendererRegistry.tsx +58 -0
  126. package/_vendor/canvas-react/host/toolbarRegistry.ts +100 -0
  127. package/_vendor/canvas-react/index.ts +3 -0
  128. package/_vendor/plugin-arrow/ArrowBindingUtil.ts +239 -0
  129. package/_vendor/plugin-arrow/ArrowShapeUtil.ts +311 -0
  130. package/_vendor/plugin-arrow/ArrowTool.ts +208 -0
  131. package/_vendor/plugin-arrow/arrowGeometry.ts +441 -0
  132. package/_vendor/plugin-arrow/arrowTerminalBindingResolver.ts +253 -0
  133. package/_vendor/plugin-arrow/arrowTerminalCommands.ts +230 -0
  134. package/_vendor/plugin-arrow/canvas.tsx +92 -0
  135. package/_vendor/plugin-arrow/core.ts +409 -0
  136. package/_vendor/plugin-arrow/feature.ts +20 -0
  137. package/_vendor/plugin-arrow/index.ts +28 -0
  138. package/_vendor/plugin-arrow/selectionHandleInteractions.ts +154 -0
  139. package/_vendor/plugin-arrow/types.ts +63 -0
  140. package/_vendor/plugin-draw/DrawShapeRenderer.tsx +60 -0
  141. package/_vendor/plugin-draw/DrawShapeUtil.ts +372 -0
  142. package/_vendor/plugin-draw/DrawTool.ts +444 -0
  143. package/_vendor/plugin-draw/canvas.tsx +24 -0
  144. package/_vendor/plugin-draw/core.ts +515 -0
  145. package/_vendor/plugin-draw/drawGeometry.ts +491 -0
  146. package/_vendor/plugin-draw/drawHitTest.ts +271 -0
  147. package/_vendor/plugin-draw/drawInterpolation.ts +250 -0
  148. package/_vendor/plugin-draw/drawPath.ts +550 -0
  149. package/_vendor/plugin-draw/drawSegments.ts +106 -0
  150. package/_vendor/plugin-draw/drawSelectionTransform.ts +67 -0
  151. package/_vendor/plugin-draw/drawShapeGeometry.ts +307 -0
  152. package/_vendor/plugin-draw/drawStrokeGeometry.ts +454 -0
  153. package/_vendor/plugin-draw/feature.ts +20 -0
  154. package/_vendor/plugin-draw/index.ts +31 -0
  155. package/_vendor/plugin-draw/types.ts +36 -0
  156. package/_vendor/plugin-eraser/EraserTool.ts +297 -0
  157. package/_vendor/plugin-eraser/canvas.tsx +616 -0
  158. package/_vendor/plugin-eraser/core.ts +49 -0
  159. package/_vendor/plugin-eraser/feature.ts +20 -0
  160. package/_vendor/plugin-eraser/index.ts +4 -0
  161. package/_vendor/plugin-frame/FrameShapeUtil.ts +173 -0
  162. package/_vendor/plugin-frame/FrameTool.ts +225 -0
  163. package/_vendor/plugin-frame/canvas.tsx +75 -0
  164. package/_vendor/plugin-frame/core.ts +534 -0
  165. package/_vendor/plugin-frame/feature.ts +20 -0
  166. package/_vendor/plugin-frame/index.ts +6 -0
  167. package/_vendor/plugin-geo/GeoShapeUtil.ts +463 -0
  168. package/_vendor/plugin-geo/GeoTools.ts +247 -0
  169. package/_vendor/plugin-geo/canvas.tsx +90 -0
  170. package/_vendor/plugin-geo/core.ts +495 -0
  171. package/_vendor/plugin-geo/feature.ts +20 -0
  172. package/_vendor/plugin-geo/index.ts +7 -0
  173. package/_vendor/plugin-geo/types.ts +27 -0
  174. package/_vendor/plugin-group/GroupShapeUtil.ts +59 -0
  175. package/_vendor/plugin-group/canvas.tsx +59 -0
  176. package/_vendor/plugin-group/core.ts +40 -0
  177. package/_vendor/plugin-group/feature.ts +20 -0
  178. package/_vendor/plugin-group/index.ts +5 -0
  179. package/_vendor/plugin-image/ImageShapeUtil.ts +95 -0
  180. package/_vendor/plugin-image/canvas.tsx +212 -0
  181. package/_vendor/plugin-image/core.ts +542 -0
  182. package/_vendor/plugin-image/crop.ts +770 -0
  183. package/_vendor/plugin-image/cropInteractions.ts +350 -0
  184. package/_vendor/plugin-image/cropSession.ts +38 -0
  185. package/_vendor/plugin-image/feature.ts +20 -0
  186. package/_vendor/plugin-image/index.ts +29 -0
  187. package/_vendor/plugin-image/types.ts +55 -0
  188. package/_vendor/plugin-image-product-react/ImageImportController.tsx +143 -0
  189. package/_vendor/plugin-image-product-react/imageAssetService.ts +52 -0
  190. package/_vendor/plugin-image-product-react/imageBlobStore.ts +315 -0
  191. package/_vendor/plugin-image-product-react/imageHostActions.ts +438 -0
  192. package/_vendor/plugin-image-product-react/imageProductApi.ts +88 -0
  193. package/_vendor/plugin-image-product-react/imageToolbarHostBridge.ts +48 -0
  194. package/_vendor/plugin-image-product-react/imageToolbarRuntime.ts +21 -0
  195. package/_vendor/plugin-image-product-react/index.ts +60 -0
  196. package/_vendor/plugin-image-product-react/snapshotGuards.ts +33 -0
  197. package/_vendor/plugin-image-product-react/svgImageAssetInliner.ts +54 -0
  198. package/_vendor/plugin-image-product-react/useImageImport.ts +64 -0
  199. package/_vendor/plugin-laser/LaserTool.ts +154 -0
  200. package/_vendor/plugin-laser/canvas.tsx +645 -0
  201. package/_vendor/plugin-laser/core.ts +49 -0
  202. package/_vendor/plugin-laser/feature.ts +20 -0
  203. package/_vendor/plugin-laser/index.ts +4 -0
  204. package/_vendor/plugin-text/ArrowLabelBindingUtil.ts +336 -0
  205. package/_vendor/plugin-text/GeoLabelBindingUtil.ts +391 -0
  206. package/_vendor/plugin-text/TextShapeSvgExporter.ts +101 -0
  207. package/_vendor/plugin-text/TextShapeUtil.ts +284 -0
  208. package/_vendor/plugin-text/TextTool.ts +228 -0
  209. package/_vendor/plugin-text/canvas.tsx +115 -0
  210. package/_vendor/plugin-text/core.ts +422 -0
  211. package/_vendor/plugin-text/feature.ts +20 -0
  212. package/_vendor/plugin-text/host/RichTextEditingBox.tsx +144 -0
  213. package/_vendor/plugin-text/host/RichTextToolbar.tsx +266 -0
  214. package/_vendor/plugin-text/host/TiptapTextEditor.tsx +191 -0
  215. package/_vendor/plugin-text/host/richTextRendering.tsx +49 -0
  216. package/_vendor/plugin-text/host/textDomSelection.ts +93 -0
  217. package/_vendor/plugin-text/host/textEditingBoxStyle.ts +36 -0
  218. package/_vendor/plugin-text/host/textEntryFocus.ts +307 -0
  219. package/_vendor/plugin-text/host/textFontLoader.ts +149 -0
  220. package/_vendor/plugin-text/host/textMeasurement.ts +258 -0
  221. package/_vendor/plugin-text/host/textMeasurementSync.ts +77 -0
  222. package/_vendor/plugin-text/host/textRangeSelection.ts +134 -0
  223. package/_vendor/plugin-text/host/textShapeRendererRuntime.tsx +89 -0
  224. package/_vendor/plugin-text/host/textShapeSync.ts +173 -0
  225. package/_vendor/plugin-text/host/tiptapTextEditorController.ts +148 -0
  226. package/_vendor/plugin-text/index.ts +18 -0
  227. package/_vendor/plugin-text/richTextUtils.ts +259 -0
  228. package/_vendor/plugin-text/textInteractions.ts +366 -0
  229. package/_vendor/plugin-text/textLabelHelpers.ts +235 -0
  230. package/_vendor/plugin-text/textShapeHelpers.ts +123 -0
  231. package/_vendor/plugin-text/textStyle.ts +96 -0
  232. package/_vendor/plugin-text/textTypes.ts +48 -0
  233. package/_vendor/signals/atom.ts +74 -0
  234. package/_vendor/signals/computed.ts +132 -0
  235. package/_vendor/signals/core.ts +130 -0
  236. package/_vendor/signals/effect.ts +117 -0
  237. package/_vendor/signals/index.ts +9 -0
  238. package/_vendor/signals/types.ts +17 -0
  239. package/_vendor/store/Store.ts +171 -0
  240. package/_vendor/store/diff.ts +229 -0
  241. package/_vendor/store/history.ts +177 -0
  242. package/_vendor/store/index.ts +32 -0
  243. package/_vendor/store/types.ts +37 -0
  244. package/api/canvasKitApi.d.ts +29 -0
  245. package/index.css +3 -0
  246. package/index.d.ts +16 -0
  247. package/index.mjs +1 -0
  248. package/internal-modules.d.ts +187 -0
  249. package/package.json +48 -0
  250. package/product/Shell.d.ts +19 -0
  251. package/product/chromeContext.d.ts +70 -0
  252. package/product/createCanvasKitEditor.d.ts +6 -0
  253. package/product/index.d.ts +7 -0
  254. package/product/persistence.d.ts +3 -0
  255. package/product/product.d.ts +22 -0
  256. package/product/useCanvasClipboardShortcuts.d.ts +6 -0
  257. package/product-iyBbEPIs.mjs +4 -0
  258. package/product.mjs +1 -0
  259. package/standard/extensions.d.ts +1 -0
  260. package/standard/index.d.ts +3 -0
  261. package/standard/standardApi.d.ts +91 -0
  262. package/standard-BmrxwqpJ.mjs +44 -0
  263. package/standard.mjs +1 -0
@@ -0,0 +1,550 @@
1
+ import { Vec } from "@land/canvas-core";
2
+ import type { DrawShapePoint, DrawShapeSegment } from "./types";
3
+
4
+ export interface DrawPointSamplingOptions {
5
+ minPointDistance: number;
6
+ positionBlend: number;
7
+ pressureBlend: number;
8
+ maxPointCount: number;
9
+ }
10
+
11
+ export interface DrawInputPointGuardOptions {
12
+ minPointDistance: number;
13
+ pressureDeltaThreshold: number;
14
+ softMaxPointCount: number;
15
+ hardMaxPointCount: number;
16
+ maxDistanceMultiplier: number;
17
+ }
18
+
19
+ export interface DrawPointSamplingResult {
20
+ points: DrawShapePoint[];
21
+ rawPointCount: number;
22
+ sampledPointCount: number;
23
+ maxPointCount: number;
24
+ reduced: boolean;
25
+ capped: boolean;
26
+ }
27
+
28
+ function formatNumber(value: number): string {
29
+ return Number.isInteger(value) ? `${value}` : `${Number(value.toFixed(2))}`;
30
+ }
31
+
32
+ function clamp(value: number, min: number, max: number): number {
33
+ return Math.max(min, Math.min(max, value));
34
+ }
35
+
36
+ export function getDrawPointSamplingLimit(isPen: boolean): number {
37
+ return isPen ? 640 : 480;
38
+ }
39
+
40
+ export function getDrawSegmentPointLimit(isPen: boolean): number {
41
+ return getDrawPointSamplingLimit(isPen);
42
+ }
43
+
44
+ function getAdaptiveMinPointDistance(
45
+ pointCount: number,
46
+ options: DrawInputPointGuardOptions,
47
+ ): number {
48
+ if (pointCount <= options.softMaxPointCount) {
49
+ return options.minPointDistance;
50
+ }
51
+
52
+ const overflowRatio = clamp(
53
+ (pointCount - options.softMaxPointCount) /
54
+ Math.max(options.hardMaxPointCount - options.softMaxPointCount, 1),
55
+ 0,
56
+ 1,
57
+ );
58
+
59
+ return options.minPointDistance * (1 + overflowRatio * options.maxDistanceMultiplier);
60
+ }
61
+
62
+ function areDrawPointsEqual(a: DrawShapePoint, b: DrawShapePoint): boolean {
63
+ return (
64
+ a.x === b.x &&
65
+ a.y === b.y &&
66
+ a.pressure === b.pressure &&
67
+ a.segment === b.segment
68
+ );
69
+ }
70
+
71
+ export function drawPointToVec(point: DrawShapePoint): Vec {
72
+ return new Vec(point.x, point.y);
73
+ }
74
+
75
+ export function cloneDrawPoint(point: DrawShapePoint): DrawShapePoint {
76
+ return {
77
+ x: point.x,
78
+ y: point.y,
79
+ ...(point.pressure === undefined ? null : { pressure: point.pressure }),
80
+ ...(point.segment === undefined ? null : { segment: point.segment }),
81
+ };
82
+ }
83
+
84
+ export function cloneDrawSegment(segment: DrawShapeSegment): DrawShapeSegment {
85
+ return {
86
+ points: segment.points.map(cloneDrawPoint),
87
+ };
88
+ }
89
+
90
+ export function getFlattenedDrawSegmentPoints(
91
+ segments: readonly DrawShapeSegment[],
92
+ ): DrawShapePoint[] {
93
+ const points: DrawShapePoint[] = [];
94
+ for (const segment of segments) {
95
+ points.push(...segment.points.map(cloneDrawPoint));
96
+ }
97
+ return points;
98
+ }
99
+
100
+ export function getDrawSegmentsFromPoints(
101
+ points: readonly DrawShapePoint[],
102
+ ): DrawShapeSegment[] {
103
+ return [{ points: points.map(cloneDrawPoint) }];
104
+ }
105
+
106
+ function normalizePressure(pressure: number | undefined): number | undefined {
107
+ if (pressure === undefined || !Number.isFinite(pressure)) {
108
+ return undefined;
109
+ }
110
+
111
+ return clamp(pressure, 0.05, 1);
112
+ }
113
+
114
+ export function getDrawInputPointGuardOptions(
115
+ strokeWidth: number,
116
+ isPen: boolean,
117
+ ): DrawInputPointGuardOptions {
118
+ return isPen
119
+ ? {
120
+ minPointDistance: Math.max(0.12, strokeWidth * 0.08),
121
+ pressureDeltaThreshold: 0.035,
122
+ softMaxPointCount: 1600,
123
+ hardMaxPointCount: 3200,
124
+ maxDistanceMultiplier: 2.5,
125
+ }
126
+ : {
127
+ minPointDistance: Math.max(0.3, strokeWidth * 0.14),
128
+ pressureDeltaThreshold: 0.08,
129
+ softMaxPointCount: 900,
130
+ hardMaxPointCount: 2200,
131
+ maxDistanceMultiplier: 6,
132
+ };
133
+ }
134
+
135
+ export function appendGuardedDrawPoint(
136
+ points: DrawShapePoint[],
137
+ point: DrawShapePoint,
138
+ options: DrawInputPointGuardOptions,
139
+ ): void {
140
+ const candidate = cloneDrawPoint(point);
141
+ const previous = points[points.length - 1];
142
+
143
+ if (!previous) {
144
+ points.push(candidate);
145
+ return;
146
+ }
147
+
148
+ if (areDrawPointsEqual(previous, candidate)) {
149
+ return;
150
+ }
151
+
152
+ const minDistance = getAdaptiveMinPointDistance(points.length, options);
153
+ const dx = candidate.x - previous.x;
154
+ const dy = candidate.y - previous.y;
155
+ const pressureDelta = Math.abs(
156
+ (candidate.pressure ?? 0) - (previous.pressure ?? 0),
157
+ );
158
+
159
+ if (
160
+ dx * dx + dy * dy < minDistance * minDistance &&
161
+ pressureDelta < options.pressureDeltaThreshold
162
+ ) {
163
+ points[points.length - 1] = candidate;
164
+ return;
165
+ }
166
+
167
+ points.push(candidate);
168
+ }
169
+
170
+ function getDrawPointsWithinStableLimit(
171
+ points: readonly DrawShapePoint[],
172
+ maxPointCount: number,
173
+ ): DrawShapePoint[] {
174
+ const limit = Math.max(2, Math.floor(maxPointCount));
175
+ if (points.length <= limit) {
176
+ return points.map(cloneDrawPoint);
177
+ }
178
+
179
+ const removalCount = points.length - limit;
180
+ const removalIndices = getStableDrawPointRemovalIndices(
181
+ points,
182
+ removalCount,
183
+ );
184
+
185
+ return points
186
+ .filter((_point, index) => !removalIndices.has(index))
187
+ .map(cloneDrawPoint);
188
+ }
189
+
190
+ function getStableDrawPointRemovalIndices(
191
+ points: readonly DrawShapePoint[],
192
+ removalCount: number,
193
+ ): Set<number> {
194
+ let candidates = getStableDrawPointRemovalCandidates(points, true);
195
+ if (candidates.length < removalCount) {
196
+ candidates = getStableDrawPointRemovalCandidates(points, false);
197
+ }
198
+
199
+ return new Set(
200
+ candidates
201
+ .slice(0, removalCount)
202
+ .map((candidate) => candidate.index),
203
+ );
204
+ }
205
+
206
+ function getStableDrawPointRemovalCandidates(
207
+ points: readonly DrawShapePoint[],
208
+ preserveStraightSegmentPoints: boolean,
209
+ ): { index: number; score: number }[] {
210
+ const candidates: { index: number; score: number }[] = [];
211
+
212
+ for (let index = 1; index < points.length - 1; index += 1) {
213
+ if (
214
+ preserveStraightSegmentPoints &&
215
+ isStraightSegmentControlPoint(points, index)
216
+ ) {
217
+ continue;
218
+ }
219
+
220
+ const point = points[index];
221
+ const previous = points[index - 1];
222
+ const next = points[index + 1];
223
+ if (!point || !previous || !next) {
224
+ continue;
225
+ }
226
+
227
+ candidates.push({
228
+ index,
229
+ score: getDrawPointRemovalCost(point, previous, next),
230
+ });
231
+ }
232
+
233
+ candidates.sort(
234
+ (a, b) =>
235
+ a.score - b.score ||
236
+ getDrawPointRemovalTieBreakScore(a.index) -
237
+ getDrawPointRemovalTieBreakScore(b.index),
238
+ );
239
+
240
+ return candidates;
241
+ }
242
+
243
+ function getDrawPointRemovalTieBreakScore(index: number): number {
244
+ return index;
245
+ }
246
+
247
+ function isStraightSegmentControlPoint(
248
+ points: readonly DrawShapePoint[],
249
+ index: number,
250
+ ): boolean {
251
+ return (
252
+ points[index]?.segment === "straight" ||
253
+ points[index + 1]?.segment === "straight"
254
+ );
255
+ }
256
+
257
+ function getDrawPointRemovalCost(
258
+ point: DrawShapePoint,
259
+ previous: DrawShapePoint,
260
+ next: DrawShapePoint,
261
+ ): number {
262
+ const positionCost = getPointToSegmentDistanceSquared(point, previous, next);
263
+ const pressureCost = getPressureRemovalCost(point, previous, next);
264
+ return positionCost + pressureCost * pressureCost;
265
+ }
266
+
267
+ function getPointToSegmentDistanceSquared(
268
+ point: DrawShapePoint,
269
+ start: DrawShapePoint,
270
+ end: DrawShapePoint,
271
+ ): number {
272
+ const dx = end.x - start.x;
273
+ const dy = end.y - start.y;
274
+ const lengthSquared = dx * dx + dy * dy;
275
+ if (lengthSquared === 0) {
276
+ const pointDx = point.x - start.x;
277
+ const pointDy = point.y - start.y;
278
+ return pointDx * pointDx + pointDy * pointDy;
279
+ }
280
+
281
+ const t = clamp(
282
+ ((point.x - start.x) * dx + (point.y - start.y) * dy) / lengthSquared,
283
+ 0,
284
+ 1,
285
+ );
286
+ const projectedX = start.x + dx * t;
287
+ const projectedY = start.y + dy * t;
288
+ const pointDx = point.x - projectedX;
289
+ const pointDy = point.y - projectedY;
290
+ return pointDx * pointDx + pointDy * pointDy;
291
+ }
292
+
293
+ function getPressureRemovalCost(
294
+ point: DrawShapePoint,
295
+ previous: DrawShapePoint,
296
+ next: DrawShapePoint,
297
+ ): number {
298
+ if (point.pressure === undefined) {
299
+ return 0;
300
+ }
301
+
302
+ const previousPressure = previous.pressure ?? point.pressure;
303
+ const nextPressure = next.pressure ?? point.pressure;
304
+ return Math.abs(point.pressure - (previousPressure + nextPressure) / 2) * 8;
305
+ }
306
+
307
+ export function sampleDrawPoints(
308
+ points: readonly DrawShapePoint[],
309
+ options: DrawPointSamplingOptions,
310
+ ): DrawPointSamplingResult {
311
+ if (points.length <= 1) {
312
+ return {
313
+ points: points.map(cloneDrawPoint),
314
+ rawPointCount: points.length,
315
+ sampledPointCount: points.length,
316
+ maxPointCount: options.maxPointCount,
317
+ reduced: false,
318
+ capped: false,
319
+ };
320
+ }
321
+
322
+ const sampled: DrawShapePoint[] = [cloneDrawPoint(points[0]!)];
323
+ const minDistanceSquared =
324
+ options.minPointDistance * options.minPointDistance;
325
+ let previousSmoothedPoint = drawPointToVec(points[0]!);
326
+ let previousSmoothedPressure = normalizePressure(points[0]!.pressure);
327
+
328
+ for (let index = 1; index < points.length; index += 1) {
329
+ const point = points[index];
330
+ if (!point) {
331
+ continue;
332
+ }
333
+
334
+ const rawPoint = drawPointToVec(point);
335
+ const rawPressure = normalizePressure(point.pressure);
336
+ const isLastPoint = index === points.length - 1;
337
+ const isStraightSegment = point.segment === "straight";
338
+ const smoothedPoint =
339
+ isLastPoint || isStraightSegment
340
+ ? rawPoint
341
+ : Vec.Lrp(previousSmoothedPoint, rawPoint, options.positionBlend);
342
+ const smoothedPressure =
343
+ isStraightSegment
344
+ ? rawPressure
345
+ : rawPressure === undefined
346
+ ? undefined
347
+ : previousSmoothedPressure === undefined
348
+ ? rawPressure
349
+ : previousSmoothedPressure +
350
+ (rawPressure - previousSmoothedPressure) * options.pressureBlend;
351
+
352
+ previousSmoothedPoint = smoothedPoint;
353
+ previousSmoothedPressure = smoothedPressure;
354
+
355
+ const candidate: DrawShapePoint =
356
+ smoothedPressure === undefined
357
+ ? {
358
+ x: smoothedPoint.x,
359
+ y: smoothedPoint.y,
360
+ ...(point.segment === undefined ? null : { segment: point.segment }),
361
+ }
362
+ : {
363
+ x: smoothedPoint.x,
364
+ y: smoothedPoint.y,
365
+ pressure: smoothedPressure,
366
+ ...(point.segment === undefined ? null : { segment: point.segment }),
367
+ };
368
+
369
+ const previousAccepted = sampled[sampled.length - 1];
370
+ if (!previousAccepted) {
371
+ sampled.push(candidate);
372
+ continue;
373
+ }
374
+
375
+ const dx = candidate.x - previousAccepted.x;
376
+ const dy = candidate.y - previousAccepted.y;
377
+ const pressureDelta = Math.abs(
378
+ (candidate.pressure ?? 0) - (previousAccepted.pressure ?? 0),
379
+ );
380
+
381
+ if (dx * dx + dy * dy < minDistanceSquared) {
382
+ if (isStraightSegment) {
383
+ if (areDrawPointsEqual(previousAccepted, candidate)) {
384
+ continue;
385
+ }
386
+ sampled.push(candidate);
387
+ continue;
388
+ }
389
+
390
+ if (pressureDelta > 0.08 || isLastPoint) {
391
+ sampled[sampled.length - 1] = candidate;
392
+ }
393
+ continue;
394
+ }
395
+
396
+ sampled.push(candidate);
397
+ }
398
+
399
+ const wasOverLimit = sampled.length > options.maxPointCount;
400
+ const clamped = getDrawPointsWithinStableLimit(
401
+ sampled,
402
+ options.maxPointCount,
403
+ );
404
+
405
+ return {
406
+ points: clamped,
407
+ rawPointCount: points.length,
408
+ sampledPointCount: clamped.length,
409
+ maxPointCount: options.maxPointCount,
410
+ reduced: clamped.length < points.length,
411
+ capped: wasOverLimit,
412
+ };
413
+ }
414
+
415
+ export function getDrawCenterlinePathFromPoints(
416
+ points: readonly DrawShapePoint[],
417
+ ): string | null {
418
+ if (points.length === 0) {
419
+ return null;
420
+ }
421
+
422
+ if (points.length === 1) {
423
+ const point = points[0]!;
424
+ return `M ${formatNumber(point.x)} ${formatNumber(point.y)}`;
425
+ }
426
+
427
+ if (points.length === 2) {
428
+ const start = points[0]!;
429
+ const end = points[1]!;
430
+ return [
431
+ `M ${formatNumber(start.x)} ${formatNumber(start.y)}`,
432
+ `L ${formatNumber(end.x)} ${formatNumber(end.y)}`,
433
+ ].join(" ");
434
+ }
435
+
436
+ const commands: string[] = [];
437
+ const first = points[0]!;
438
+ commands.push(`M ${formatNumber(first.x)} ${formatNumber(first.y)}`);
439
+
440
+ for (let index = 1; index < points.length; index += 1) {
441
+ const current = points[index];
442
+ if (!current) {
443
+ continue;
444
+ }
445
+
446
+ if (current.segment === "straight") {
447
+ commands.push(`L ${formatNumber(current.x)} ${formatNumber(current.y)}`);
448
+ continue;
449
+ }
450
+
451
+ const next = points[index + 1];
452
+ if (!next || next.segment === "straight") {
453
+ if (canSmoothFreeRunEnd(points, index)) {
454
+ const previous = points[index - 1]!;
455
+ commands.push(
456
+ `Q ${formatNumber(previous.x)} ${formatNumber(previous.y)} ${formatNumber(current.x)} ${formatNumber(current.y)}`,
457
+ );
458
+ } else {
459
+ commands.push(`L ${formatNumber(current.x)} ${formatNumber(current.y)}`);
460
+ }
461
+ continue;
462
+ }
463
+
464
+ const midpoint = Vec.Med(drawPointToVec(current), drawPointToVec(next));
465
+ commands.push(
466
+ `Q ${formatNumber(current.x)} ${formatNumber(current.y)} ${formatNumber(midpoint.x)} ${formatNumber(midpoint.y)}`,
467
+ );
468
+ }
469
+
470
+ return commands.join(" ");
471
+ }
472
+
473
+ export function getClosedDrawCenterlinePathFromPoints(
474
+ points: readonly DrawShapePoint[],
475
+ ): string | null {
476
+ if (points.length < 3) {
477
+ return null;
478
+ }
479
+
480
+ const centerlinePath = getDrawCenterlinePathFromPoints(points);
481
+ if (!centerlinePath) {
482
+ return null;
483
+ }
484
+
485
+ return `${centerlinePath} Z`;
486
+ }
487
+
488
+ function canSmoothFreeRunEnd(
489
+ points: readonly DrawShapePoint[],
490
+ index: number,
491
+ ): boolean {
492
+ return (
493
+ index >= 2 &&
494
+ points[index - 1]?.segment !== "straight" &&
495
+ points[index - 2]?.segment !== "straight"
496
+ );
497
+ }
498
+
499
+ export function getClosedSvgPathFromVecs(points: readonly Vec[]): string | null {
500
+ if (points.length === 0) {
501
+ return null;
502
+ }
503
+
504
+ const [first, ...rest] = points;
505
+ if (!first) {
506
+ return null;
507
+ }
508
+
509
+ return [
510
+ `M ${formatNumber(first.x)} ${formatNumber(first.y)}`,
511
+ ...rest.map(
512
+ (point) => `L ${formatNumber(point.x)} ${formatNumber(point.y)}`,
513
+ ),
514
+ "Z",
515
+ ].join(" ");
516
+ }
517
+
518
+ export function getSmoothClosedSvgPathFromVecs(
519
+ points: readonly Vec[],
520
+ ): string | null {
521
+ if (points.length < 3) {
522
+ return getClosedSvgPathFromVecs(points);
523
+ }
524
+
525
+ const first = points[0];
526
+ const last = points[points.length - 1];
527
+ if (!first || !last) {
528
+ return null;
529
+ }
530
+
531
+ const start = Vec.Med(last, first);
532
+ const commands: string[] = [
533
+ `M ${formatNumber(start.x)} ${formatNumber(start.y)}`,
534
+ ];
535
+ for (let index = 0; index < points.length; index += 1) {
536
+ const current = points[index];
537
+ const next = points[(index + 1) % points.length];
538
+ if (!current || !next) {
539
+ continue;
540
+ }
541
+
542
+ const midpoint = Vec.Med(current, next);
543
+ commands.push(
544
+ `Q ${formatNumber(current.x)} ${formatNumber(current.y)} ${formatNumber(midpoint.x)} ${formatNumber(midpoint.y)}`,
545
+ );
546
+ }
547
+
548
+ commands.push("Z");
549
+ return commands.join(" ");
550
+ }
@@ -0,0 +1,106 @@
1
+ import { Box, Vec } from "@land/canvas-core";
2
+ import {
3
+ cloneDrawPoint,
4
+ cloneDrawSegment as cloneDrawSegmentImpl,
5
+ drawPointToVec,
6
+ } from "./drawPath";
7
+ import type { DrawShapePoint, DrawShapeSegment } from "./types";
8
+
9
+ export function cloneDrawPoints(
10
+ points: readonly DrawShapePoint[],
11
+ ): DrawShapePoint[] {
12
+ return points.map(cloneDrawPoint);
13
+ }
14
+
15
+ export function cloneDrawSegments(
16
+ segments: readonly DrawShapeSegment[],
17
+ ): DrawShapeSegment[] {
18
+ return segments.map(cloneDrawSegment);
19
+ }
20
+
21
+ export function cloneDrawSegment(segment: DrawShapeSegment): DrawShapeSegment {
22
+ return cloneDrawSegmentImpl(segment);
23
+ }
24
+
25
+ export function translateDrawPoints(
26
+ points: readonly DrawShapePoint[],
27
+ dx: number,
28
+ dy: number,
29
+ ): DrawShapePoint[] {
30
+ return points.map((point) => ({
31
+ x: point.x + dx,
32
+ y: point.y + dy,
33
+ ...(point.pressure === undefined ? null : { pressure: point.pressure }),
34
+ ...(point.segment === undefined ? null : { segment: point.segment }),
35
+ }));
36
+ }
37
+
38
+ export function translateDrawSegments(
39
+ segments: readonly DrawShapeSegment[],
40
+ dx: number,
41
+ dy: number,
42
+ ): DrawShapeSegment[] {
43
+ return segments.map((segment) => ({
44
+ points: translateDrawPoints(segment.points, dx, dy),
45
+ }));
46
+ }
47
+
48
+ export function mapDrawPointCoordinates(
49
+ points: readonly DrawShapePoint[],
50
+ mapper: (point: Vec, index: number) => Vec,
51
+ ): DrawShapePoint[] {
52
+ return points.map((point, index) => {
53
+ const mapped = mapper(drawPointToVec(point), index);
54
+ return {
55
+ x: mapped.x,
56
+ y: mapped.y,
57
+ ...(point.pressure === undefined ? null : { pressure: point.pressure }),
58
+ ...(point.segment === undefined ? null : { segment: point.segment }),
59
+ };
60
+ });
61
+ }
62
+
63
+ export function mapDrawSegmentCoordinates(
64
+ segments: readonly DrawShapeSegment[],
65
+ mapper: (point: Vec, segmentIndex: number, pointIndex: number) => Vec,
66
+ ): DrawShapeSegment[] {
67
+ return segments.map((segment, segmentIndex) => ({
68
+ points: segment.points.map((point, pointIndex) => {
69
+ const mapped = mapper(drawPointToVec(point), segmentIndex, pointIndex);
70
+ return {
71
+ x: mapped.x,
72
+ y: mapped.y,
73
+ ...(point.pressure === undefined ? null : { pressure: point.pressure }),
74
+ ...(point.segment === undefined ? null : { segment: point.segment }),
75
+ };
76
+ }),
77
+ }));
78
+ }
79
+
80
+ export function mapDrawShapePointsToBounds(
81
+ points: readonly DrawShapePoint[],
82
+ fromBounds: Box,
83
+ toBounds: Box,
84
+ ): DrawShapePoint[] {
85
+ const fromWidth = fromBounds.w === 0 ? 1 : fromBounds.w;
86
+ const fromHeight = fromBounds.h === 0 ? 1 : fromBounds.h;
87
+ const toWidth = toBounds.w === 0 ? 1 : toBounds.w;
88
+ const toHeight = toBounds.h === 0 ? 1 : toBounds.h;
89
+
90
+ return points.map((point) => ({
91
+ x: toBounds.minX + ((point.x - fromBounds.minX) / fromWidth) * toWidth,
92
+ y: toBounds.minY + ((point.y - fromBounds.minY) / fromHeight) * toHeight,
93
+ ...(point.pressure === undefined ? null : { pressure: point.pressure }),
94
+ ...(point.segment === undefined ? null : { segment: point.segment }),
95
+ }));
96
+ }
97
+
98
+ export function mapDrawShapeSegmentsToBounds(
99
+ segments: readonly DrawShapeSegment[],
100
+ fromBounds: Box,
101
+ toBounds: Box,
102
+ ): DrawShapeSegment[] {
103
+ return segments.map((segment) => ({
104
+ points: mapDrawShapePointsToBounds(segment.points, fromBounds, toBounds),
105
+ }));
106
+ }
@@ -0,0 +1,67 @@
1
+ import {
2
+ Box,
3
+ Vec,
4
+ mirrorSelectionLocalPoint,
5
+ selectionLocalPointToPagePoint,
6
+ selectionPagePointToLocalPoint,
7
+ type SelectionMirrorAxis,
8
+ } from "@land/canvas-core";
9
+ import { mapDrawSegmentCoordinates } from "./drawSegments";
10
+ import type { DrawShapeSegment } from "./types";
11
+
12
+ export interface DrawSelectionRotationTransform {
13
+ selectionRotation: number;
14
+ selectionRotationCenter: Vec;
15
+ }
16
+
17
+ export interface DrawSelectionResizeInput
18
+ extends DrawSelectionRotationTransform {
19
+ targetSelectionStrokeBounds: Box;
20
+ scaleX: number;
21
+ scaleY: number;
22
+ }
23
+
24
+ export interface DrawSelectionFlipInput extends DrawSelectionRotationTransform {
25
+ axis: SelectionMirrorAxis;
26
+ selectionMirrorBounds: Box;
27
+ }
28
+
29
+ function getSelectionTransform(input: DrawSelectionRotationTransform) {
30
+ return {
31
+ rotation: input.selectionRotation,
32
+ rotationCenter: input.selectionRotationCenter,
33
+ };
34
+ }
35
+
36
+ export function mapPageDrawSegmentsToSelectionLocal(
37
+ segments: readonly DrawShapeSegment[],
38
+ input: DrawSelectionRotationTransform,
39
+ ): DrawShapeSegment[] {
40
+ const transform = getSelectionTransform(input);
41
+ return mapDrawSegmentCoordinates(segments, (point) =>
42
+ selectionPagePointToLocalPoint(point, transform),
43
+ );
44
+ }
45
+
46
+ export function mapSelectionLocalDrawSegmentsToPage(
47
+ segments: readonly DrawShapeSegment[],
48
+ input: DrawSelectionRotationTransform,
49
+ ): DrawShapeSegment[] {
50
+ const transform = getSelectionTransform(input);
51
+ return mapDrawSegmentCoordinates(segments, (point) =>
52
+ selectionLocalPointToPagePoint(point, transform),
53
+ );
54
+ }
55
+
56
+ export function mirrorSelectionLocalDrawSegments(
57
+ segments: readonly DrawShapeSegment[],
58
+ input: DrawSelectionFlipInput,
59
+ ): DrawShapeSegment[] {
60
+ return mapDrawSegmentCoordinates(segments, (point) =>
61
+ mirrorSelectionLocalPoint({
62
+ axis: input.axis,
63
+ bounds: input.selectionMirrorBounds,
64
+ point,
65
+ }),
66
+ );
67
+ }