js-draw 1.0.1 → 1.0.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 (182) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bundle.js +1 -1
  3. package/dist/cjs/version.js +1 -1
  4. package/dist/mjs/version.mjs +1 -1
  5. package/docs/img/readme-images/js-draw.jpg +0 -0
  6. package/docs/img/readme-images/unsupported-elements--in-editor.png +0 -0
  7. package/package.json +5 -4
  8. package/dist-test/test_imports/package-lock.json +0 -13
  9. package/dist-test/test_imports/package.json +0 -12
  10. package/dist-test/test_imports/test-imports.js +0 -11
  11. package/dist-test/test_imports/test-require.cjs +0 -14
  12. package/src/Editor.loadFrom.test.ts +0 -24
  13. package/src/Editor.test.ts +0 -107
  14. package/src/Editor.toSVG.test.ts +0 -294
  15. package/src/Editor.ts +0 -1443
  16. package/src/EditorImage.test.ts +0 -117
  17. package/src/EditorImage.ts +0 -609
  18. package/src/EventDispatcher.test.ts +0 -123
  19. package/src/EventDispatcher.ts +0 -72
  20. package/src/Pointer.ts +0 -183
  21. package/src/SVGLoader.test.ts +0 -114
  22. package/src/SVGLoader.ts +0 -672
  23. package/src/UndoRedoHistory.test.ts +0 -34
  24. package/src/UndoRedoHistory.ts +0 -102
  25. package/src/Viewport.ts +0 -322
  26. package/src/bundle/bundled.ts +0 -7
  27. package/src/commands/Command.ts +0 -45
  28. package/src/commands/Duplicate.ts +0 -75
  29. package/src/commands/Erase.ts +0 -95
  30. package/src/commands/SerializableCommand.ts +0 -49
  31. package/src/commands/UnresolvedCommand.ts +0 -37
  32. package/src/commands/invertCommand.ts +0 -58
  33. package/src/commands/lib.ts +0 -16
  34. package/src/commands/localization.ts +0 -47
  35. package/src/commands/uniteCommands.test.ts +0 -23
  36. package/src/commands/uniteCommands.ts +0 -140
  37. package/src/components/AbstractComponent.transformBy.test.ts +0 -23
  38. package/src/components/AbstractComponent.ts +0 -383
  39. package/src/components/BackgroundComponent.test.ts +0 -44
  40. package/src/components/BackgroundComponent.ts +0 -348
  41. package/src/components/ImageComponent.ts +0 -176
  42. package/src/components/RestylableComponent.ts +0 -161
  43. package/src/components/SVGGlobalAttributesObject.ts +0 -79
  44. package/src/components/Stroke.test.ts +0 -137
  45. package/src/components/Stroke.ts +0 -294
  46. package/src/components/TextComponent.test.ts +0 -202
  47. package/src/components/TextComponent.ts +0 -429
  48. package/src/components/UnknownSVGObject.test.ts +0 -10
  49. package/src/components/UnknownSVGObject.ts +0 -60
  50. package/src/components/builders/ArrowBuilder.ts +0 -106
  51. package/src/components/builders/CircleBuilder.ts +0 -100
  52. package/src/components/builders/FreehandLineBuilder.test.ts +0 -24
  53. package/src/components/builders/FreehandLineBuilder.ts +0 -210
  54. package/src/components/builders/LineBuilder.ts +0 -77
  55. package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +0 -453
  56. package/src/components/builders/RectangleBuilder.ts +0 -73
  57. package/src/components/builders/types.ts +0 -15
  58. package/src/components/lib.ts +0 -31
  59. package/src/components/localization.ts +0 -24
  60. package/src/components/util/StrokeSmoother.ts +0 -302
  61. package/src/components/util/describeComponentList.ts +0 -18
  62. package/src/dialogs/makeAboutDialog.ts +0 -82
  63. package/src/inputEvents.ts +0 -143
  64. package/src/lib.ts +0 -91
  65. package/src/localization.ts +0 -34
  66. package/src/localizations/de.ts +0 -146
  67. package/src/localizations/en.ts +0 -8
  68. package/src/localizations/es.ts +0 -74
  69. package/src/localizations/getLocalizationTable.test.ts +0 -27
  70. package/src/localizations/getLocalizationTable.ts +0 -74
  71. package/src/rendering/Display.ts +0 -247
  72. package/src/rendering/RenderablePathSpec.ts +0 -88
  73. package/src/rendering/RenderingStyle.test.ts +0 -68
  74. package/src/rendering/RenderingStyle.ts +0 -55
  75. package/src/rendering/TextRenderingStyle.ts +0 -55
  76. package/src/rendering/caching/CacheRecord.test.ts +0 -48
  77. package/src/rendering/caching/CacheRecord.ts +0 -76
  78. package/src/rendering/caching/CacheRecordManager.ts +0 -71
  79. package/src/rendering/caching/RenderingCache.test.ts +0 -43
  80. package/src/rendering/caching/RenderingCache.ts +0 -66
  81. package/src/rendering/caching/RenderingCacheNode.ts +0 -404
  82. package/src/rendering/caching/testUtils.ts +0 -35
  83. package/src/rendering/caching/types.ts +0 -34
  84. package/src/rendering/lib.ts +0 -8
  85. package/src/rendering/localization.ts +0 -20
  86. package/src/rendering/renderers/AbstractRenderer.ts +0 -232
  87. package/src/rendering/renderers/CanvasRenderer.ts +0 -312
  88. package/src/rendering/renderers/DummyRenderer.test.ts +0 -41
  89. package/src/rendering/renderers/DummyRenderer.ts +0 -142
  90. package/src/rendering/renderers/SVGRenderer.ts +0 -434
  91. package/src/rendering/renderers/TextOnlyRenderer.test.ts +0 -34
  92. package/src/rendering/renderers/TextOnlyRenderer.ts +0 -68
  93. package/src/shortcuts/KeyBinding.test.ts +0 -61
  94. package/src/shortcuts/KeyBinding.ts +0 -257
  95. package/src/shortcuts/KeyboardShortcutManager.test.ts +0 -95
  96. package/src/shortcuts/KeyboardShortcutManager.ts +0 -163
  97. package/src/shortcuts/lib.ts +0 -3
  98. package/src/testing/createEditor.ts +0 -11
  99. package/src/testing/getUniquePointerId.ts +0 -18
  100. package/src/testing/lib.ts +0 -3
  101. package/src/testing/sendPenEvent.ts +0 -36
  102. package/src/testing/sendTouchEvent.ts +0 -71
  103. package/src/toolbar/AbstractToolbar.ts +0 -542
  104. package/src/toolbar/DropdownToolbar.ts +0 -220
  105. package/src/toolbar/EdgeToolbar.test.ts +0 -54
  106. package/src/toolbar/EdgeToolbar.ts +0 -543
  107. package/src/toolbar/IconProvider.ts +0 -861
  108. package/src/toolbar/constants.ts +0 -1
  109. package/src/toolbar/lib.ts +0 -6
  110. package/src/toolbar/localization.ts +0 -136
  111. package/src/toolbar/types.ts +0 -13
  112. package/src/toolbar/widgets/ActionButtonWidget.ts +0 -39
  113. package/src/toolbar/widgets/BaseToolWidget.ts +0 -81
  114. package/src/toolbar/widgets/BaseWidget.ts +0 -495
  115. package/src/toolbar/widgets/DocumentPropertiesWidget.ts +0 -250
  116. package/src/toolbar/widgets/EraserToolWidget.ts +0 -84
  117. package/src/toolbar/widgets/HandToolWidget.ts +0 -239
  118. package/src/toolbar/widgets/InsertImageWidget.ts +0 -248
  119. package/src/toolbar/widgets/OverflowWidget.ts +0 -92
  120. package/src/toolbar/widgets/PenToolWidget.ts +0 -369
  121. package/src/toolbar/widgets/SelectionToolWidget.ts +0 -195
  122. package/src/toolbar/widgets/TextToolWidget.ts +0 -149
  123. package/src/toolbar/widgets/components/makeColorInput.ts +0 -184
  124. package/src/toolbar/widgets/components/makeFileInput.ts +0 -128
  125. package/src/toolbar/widgets/components/makeGridSelector.ts +0 -179
  126. package/src/toolbar/widgets/components/makeSeparator.ts +0 -17
  127. package/src/toolbar/widgets/components/makeThicknessSlider.ts +0 -62
  128. package/src/toolbar/widgets/keybindings.ts +0 -19
  129. package/src/toolbar/widgets/layout/DropdownLayoutManager.ts +0 -262
  130. package/src/toolbar/widgets/layout/EdgeToolbarLayoutManager.ts +0 -71
  131. package/src/toolbar/widgets/layout/types.ts +0 -74
  132. package/src/toolbar/widgets/lib.ts +0 -13
  133. package/src/tools/BaseTool.ts +0 -169
  134. package/src/tools/Eraser.test.ts +0 -103
  135. package/src/tools/Eraser.ts +0 -173
  136. package/src/tools/FindTool.test.ts +0 -67
  137. package/src/tools/FindTool.ts +0 -153
  138. package/src/tools/InputFilter/FunctionMapper.ts +0 -17
  139. package/src/tools/InputFilter/InputMapper.ts +0 -41
  140. package/src/tools/InputFilter/InputPipeline.test.ts +0 -41
  141. package/src/tools/InputFilter/InputPipeline.ts +0 -34
  142. package/src/tools/InputFilter/InputStabilizer.ts +0 -254
  143. package/src/tools/InputFilter/StrokeKeyboardControl.ts +0 -104
  144. package/src/tools/PanZoom.test.ts +0 -339
  145. package/src/tools/PanZoom.ts +0 -525
  146. package/src/tools/PasteHandler.ts +0 -94
  147. package/src/tools/Pen.test.ts +0 -260
  148. package/src/tools/Pen.ts +0 -284
  149. package/src/tools/PipetteTool.ts +0 -84
  150. package/src/tools/SelectionTool/SelectAllShortcutHandler.ts +0 -29
  151. package/src/tools/SelectionTool/Selection.ts +0 -647
  152. package/src/tools/SelectionTool/SelectionHandle.ts +0 -142
  153. package/src/tools/SelectionTool/SelectionTool.test.ts +0 -370
  154. package/src/tools/SelectionTool/SelectionTool.ts +0 -510
  155. package/src/tools/SelectionTool/TransformMode.ts +0 -112
  156. package/src/tools/SelectionTool/types.ts +0 -11
  157. package/src/tools/SoundUITool.ts +0 -221
  158. package/src/tools/TextTool.ts +0 -339
  159. package/src/tools/ToolController.ts +0 -224
  160. package/src/tools/ToolEnabledGroup.ts +0 -14
  161. package/src/tools/ToolSwitcherShortcut.ts +0 -39
  162. package/src/tools/ToolbarShortcutHandler.ts +0 -39
  163. package/src/tools/UndoRedoShortcut.test.ts +0 -62
  164. package/src/tools/UndoRedoShortcut.ts +0 -24
  165. package/src/tools/keybindings.ts +0 -85
  166. package/src/tools/lib.ts +0 -22
  167. package/src/tools/localization.ts +0 -76
  168. package/src/types.ts +0 -151
  169. package/src/util/ReactiveValue.test.ts +0 -168
  170. package/src/util/ReactiveValue.ts +0 -241
  171. package/src/util/assertions.ts +0 -55
  172. package/src/util/fileToBase64.ts +0 -18
  173. package/src/util/guessKeyCodeFromKey.ts +0 -36
  174. package/src/util/listPrefixMatch.ts +0 -19
  175. package/src/util/stopPropagationOfScrollingWheelEvents.ts +0 -20
  176. package/src/util/untilNextAnimationFrame.ts +0 -9
  177. package/src/util/waitForAll.ts +0 -18
  178. package/src/util/waitForTimeout.ts +0 -9
  179. package/src/version.test.ts +0 -12
  180. package/src/version.ts +0 -3
  181. package/tools/allLocales.js +0 -4
  182. package/tools/copyREADME.ts +0 -62
@@ -1,260 +0,0 @@
1
-
2
- import PenTool from './Pen';
3
- import { Mat33, Rect2, Vec2 } from '@js-draw/math';
4
- import createEditor from '../testing/createEditor';
5
- import { InputEvtType } from '../inputEvents';
6
- import StrokeComponent from '../components/Stroke';
7
- import { makeFreehandLineBuilder } from '../components/builders/FreehandLineBuilder';
8
- import sendPenEvent from '../testing/sendPenEvent';
9
- import sendTouchEvent from '../testing/sendTouchEvent';
10
-
11
- describe('Pen', () => {
12
- it('should draw horizontal lines', () => {
13
- const editor = createEditor();
14
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0, 0));
15
- for (let i = 0; i < 10; i++) {
16
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(i, 0));
17
- jest.advanceTimersByTime(200);
18
- }
19
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(200, 0));
20
-
21
- const elems = editor.image.getElementsIntersectingRegion(new Rect2(0, 10, 10, -10));
22
- expect(elems).toHaveLength(1);
23
-
24
- // Account for stroke width
25
- const tolerableError = 8;
26
- expect(elems[0].getBBox().topLeft).objEq(Vec2.of(0, 0), tolerableError);
27
- expect(elems[0].getBBox().bottomRight).objEq(Vec2.of(200, 0), tolerableError);
28
- });
29
-
30
- it('should draw vertical line', () => {
31
- const editor = createEditor();
32
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0, 0));
33
- for (let i = 0; i < 10; i++) {
34
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(0, i * 20));
35
- jest.advanceTimersByTime(200);
36
- }
37
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(0, 150));
38
-
39
- const elems = editor.image.getElementsIntersectingRegion(Rect2.unitSquare);
40
- expect(elems).toHaveLength(1);
41
-
42
- expect(elems[0].getBBox().topLeft).objEq(Vec2.of(0, 0), 8); // ± 8
43
- expect(elems[0].getBBox().bottomRight).objEq(Vec2.of(0, 175), 25); // ± 25
44
- });
45
-
46
- it('should draw vertical line with slight bend', () => {
47
- const editor = createEditor();
48
-
49
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(417, 24));
50
- jest.advanceTimersByTime(245);
51
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 197));
52
- jest.advanceTimersByTime(20);
53
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 199));
54
- jest.advanceTimersByTime(12);
55
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 201));
56
- jest.advanceTimersByTime(40);
57
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 203));
58
- jest.advanceTimersByTime(14);
59
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 206));
60
- jest.advanceTimersByTime(35);
61
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 208));
62
- jest.advanceTimersByTime(16);
63
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 211));
64
- jest.advanceTimersByTime(51);
65
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 215));
66
- jest.advanceTimersByTime(32);
67
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 218));
68
- jest.advanceTimersByTime(30);
69
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 220));
70
- jest.advanceTimersByTime(24);
71
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 222));
72
- jest.advanceTimersByTime(14);
73
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 224));
74
- jest.advanceTimersByTime(32);
75
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 227));
76
- jest.advanceTimersByTime(17);
77
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 229));
78
- jest.advanceTimersByTime(53);
79
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 234));
80
- jest.advanceTimersByTime(34);
81
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 236));
82
- jest.advanceTimersByTime(17);
83
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 238));
84
- jest.advanceTimersByTime(39);
85
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 240));
86
- jest.advanceTimersByTime(10);
87
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 243));
88
- jest.advanceTimersByTime(34);
89
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 250));
90
- jest.advanceTimersByTime(57);
91
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 252));
92
- jest.advanceTimersByTime(8);
93
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(422, 256));
94
- jest.advanceTimersByTime(28);
95
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(422, 258));
96
- jest.advanceTimersByTime(21);
97
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(421, 262));
98
- jest.advanceTimersByTime(34);
99
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 264));
100
- jest.advanceTimersByTime(5);
101
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 266));
102
- jest.advanceTimersByTime(22);
103
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 268));
104
- jest.advanceTimersByTime(22);
105
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 271));
106
- jest.advanceTimersByTime(18);
107
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 274));
108
- jest.advanceTimersByTime(33);
109
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 277));
110
- jest.advanceTimersByTime(16);
111
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 279));
112
- jest.advanceTimersByTime(36);
113
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 282));
114
- jest.advanceTimersByTime(15);
115
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 284));
116
- jest.advanceTimersByTime(48);
117
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 289));
118
- jest.advanceTimersByTime(16);
119
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 291));
120
- jest.advanceTimersByTime(31);
121
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 295));
122
- jest.advanceTimersByTime(23);
123
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 301));
124
- jest.advanceTimersByTime(31);
125
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 306));
126
- jest.advanceTimersByTime(18);
127
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 308));
128
- jest.advanceTimersByTime(20);
129
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 310));
130
- jest.advanceTimersByTime(13);
131
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 313));
132
- jest.advanceTimersByTime(17);
133
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 317));
134
- jest.advanceTimersByTime(33);
135
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 321));
136
- jest.advanceTimersByTime(15);
137
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 324));
138
- jest.advanceTimersByTime(23);
139
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 326));
140
- jest.advanceTimersByTime(14);
141
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(419, 329));
142
- jest.advanceTimersByTime(36);
143
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 333));
144
- jest.advanceTimersByTime(8);
145
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 340));
146
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(420, 340));
147
-
148
- const elems = editor.image.getElementsIntersectingRegion(new Rect2(0, 0, 1000, 1000));
149
- expect(elems).toHaveLength(1);
150
-
151
- expect(elems[0].getBBox().topLeft).objEq(Vec2.of(420, 24), 32); // ± 32
152
- expect(elems[0].getBBox().bottomRight).objEq(Vec2.of(420, 340), 25); // ± 25
153
- });
154
-
155
- // if `mainEventIsPen` is false, tests with touch events.
156
- const testEventCancelation = (mainEventIsPen: boolean) => {
157
- const editor = createEditor();
158
-
159
- expect(editor.image.getElementsIntersectingRegion(new Rect2(0, 0, 1000, 1000))).toHaveLength(0);
160
-
161
- const sendMainEvent = mainEventIsPen ? sendPenEvent : sendTouchEvent;
162
-
163
- // Start the drawing
164
- const mainPointer = sendMainEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(417, 24));
165
- jest.advanceTimersByTime(245);
166
- sendMainEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 197));
167
- jest.advanceTimersByTime(20);
168
- sendMainEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 199));
169
- jest.advanceTimersByTime(12);
170
- sendMainEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 201));
171
- jest.advanceTimersByTime(40);
172
- sendMainEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(423, 203));
173
- jest.advanceTimersByTime(14);
174
-
175
- // Attempt to cancel the drawing
176
- let firstPointer = sendTouchEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0, 0), [ mainPointer ]);
177
- let secondPointer = sendTouchEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(100, 0), [ firstPointer, mainPointer ]);
178
-
179
- const maxIterations = 10;
180
- for (let i = 0; i < maxIterations; i++) {
181
- jest.advanceTimersByTime(100);
182
-
183
- const point1 = Vec2.of(-i * 5, 0);
184
- const point2 = Vec2.of(i * 5 + 100, 0);
185
-
186
- const eventType = InputEvtType.PointerMoveEvt;
187
- firstPointer = sendTouchEvent(editor, eventType, point1, [ secondPointer, mainPointer ]);
188
- secondPointer = sendTouchEvent(editor, eventType, point2, [ firstPointer, mainPointer ]);
189
-
190
- if (i === maxIterations - 1) {
191
- jest.advanceTimersByTime(10);
192
-
193
- sendTouchEvent(editor, InputEvtType.PointerUpEvt, point1, [ secondPointer, mainPointer ]);
194
- sendTouchEvent(editor, InputEvtType.PointerUpEvt, point2, [ mainPointer ]);
195
- }
196
-
197
- jest.advanceTimersByTime(100);
198
- }
199
-
200
- // Finish the drawing
201
- sendMainEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 333), [firstPointer, secondPointer]);
202
- jest.advanceTimersByTime(8);
203
- sendMainEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(420, 340), [firstPointer, secondPointer]);
204
- sendMainEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(420, 340), [firstPointer, secondPointer]);
205
-
206
- const elementsInDrawingArea = editor.image.getElementsIntersectingRegion(new Rect2(0, 0, 1000, 1000));
207
- if (mainEventIsPen) {
208
- expect(elementsInDrawingArea).toHaveLength(1);
209
- } else {
210
- expect(elementsInDrawingArea).toHaveLength(0);
211
- }
212
- };
213
-
214
- it('pen events should not be cancelable by touch events', () => {
215
- testEventCancelation(true);
216
- });
217
-
218
- it('touch events should be cancelable by touch events', () => {
219
- testEventCancelation(false);
220
- });
221
-
222
- it('ctrl+z should finalize then undo the current stroke', async () => {
223
- const editor = createEditor();
224
-
225
- expect(editor.history.undoStackSize).toBe(0);
226
-
227
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(10, 10));
228
- jest.advanceTimersByTime(100);
229
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(20, 10));
230
-
231
- const ctrlKeyDown = true;
232
- editor.sendKeyboardEvent(InputEvtType.KeyPressEvent, 'z', ctrlKeyDown);
233
-
234
- // Stroke should have been undone
235
- expect(editor.history.redoStackSize).toBe(1);
236
-
237
- // Lifting the pointer up shouldn't clear the redo stack.
238
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(420, 340));
239
- expect(editor.history.redoStackSize).toBe(1);
240
- });
241
-
242
- it('holding ctrl should snap the stroke to grid', () => {
243
- const editor = createEditor();
244
- editor.viewport.resetTransform(Mat33.identity);
245
-
246
- const penTool = editor.toolController.getMatchingTools(PenTool)[0];
247
- penTool.setStrokeFactory(makeFreehandLineBuilder);
248
-
249
- sendPenEvent(editor, InputEvtType.PointerDownEvt, Vec2.of(0.1, 0.1));
250
- jest.advanceTimersByTime(100);
251
- sendPenEvent(editor, InputEvtType.PointerMoveEvt, Vec2.of(10.1, 10.1));
252
- sendPenEvent(editor, InputEvtType.PointerUpEvt, Vec2.of(10.1, 10.1));
253
-
254
- const allElems = editor.image.getAllElements();
255
- expect(allElems).toHaveLength(1);
256
-
257
- const firstStroke = allElems[0] as StrokeComponent;
258
- expect(firstStroke.getPath().bbox).objEq(new Rect2(0, 0, 10, 10));
259
- });
260
- });
package/src/tools/Pen.ts DELETED
@@ -1,284 +0,0 @@
1
- import { Color4 } from '@js-draw/math';
2
- import Editor from '../Editor';
3
- import EditorImage from '../EditorImage';
4
- import Pointer, { PointerDevice } from '../Pointer';
5
- import { makeFreehandLineBuilder } from '../components/builders/FreehandLineBuilder';
6
- import { EditorEventType, StrokeDataPoint } from '../types';
7
- import { KeyPressEvent, PointerEvt } from '../inputEvents';
8
- import BaseTool from './BaseTool';
9
- import { ComponentBuilder, ComponentBuilderFactory } from '../components/builders/types';
10
- import { undoKeyboardShortcutId } from './keybindings';
11
- import { decreaseSizeKeyboardShortcutId, increaseSizeKeyboardShortcutId } from './keybindings';
12
- import InputStabilizer from './InputFilter/InputStabilizer';
13
- import { MutableReactiveValue, ReactiveValue } from '../util/ReactiveValue';
14
-
15
- export interface PenStyle {
16
- readonly color: Color4;
17
- readonly thickness: number;
18
- readonly factory: ComponentBuilderFactory;
19
- }
20
-
21
- export default class Pen extends BaseTool {
22
- protected builder: ComponentBuilder|null = null;
23
- private lastPoint: StrokeDataPoint|null = null;
24
- private startPoint: StrokeDataPoint|null = null;
25
- private currentDeviceType: PointerDevice|null = null;
26
- private styleValue: MutableReactiveValue<PenStyle>;
27
- private style: PenStyle;
28
-
29
- public constructor(
30
- private editor: Editor,
31
- description: string,
32
- style: Partial<PenStyle>,
33
- ) {
34
- super(editor.notifier, description);
35
-
36
- this.styleValue = ReactiveValue.fromInitialValue<PenStyle>({
37
- factory: makeFreehandLineBuilder,
38
- color: Color4.blue,
39
- thickness: 4,
40
- ...style,
41
- });
42
-
43
- this.styleValue.onUpdateAndNow(newValue => {
44
- this.style = newValue;
45
- this.noteUpdated();
46
- });
47
- }
48
-
49
- private getPressureMultiplier() {
50
- const thickness = this.style.thickness;
51
- return 1 / this.editor.viewport.getScaleFactor() * thickness;
52
- }
53
-
54
- // Converts a `pointer` to a `StrokeDataPoint`.
55
- protected toStrokePoint(pointer: Pointer): StrokeDataPoint {
56
- const minPressure = 0.3;
57
- let pressure = Math.max(pointer.pressure ?? 1.0, minPressure);
58
-
59
- if (!isFinite(pressure)) {
60
- console.warn('Non-finite pressure!', pointer);
61
- pressure = minPressure;
62
- }
63
- console.assert(isFinite(pointer.canvasPos.length()), 'Non-finite canvas position!');
64
- console.assert(isFinite(pointer.screenPos.length()), 'Non-finite screen position!');
65
- console.assert(isFinite(pointer.timeStamp), 'Non-finite timeStamp on pointer!');
66
-
67
- const pos = pointer.canvasPos;
68
-
69
- return {
70
- pos,
71
- width: pressure * this.getPressureMultiplier(),
72
- color: this.style.color,
73
- time: pointer.timeStamp,
74
- };
75
- }
76
-
77
- // Displays the stroke that is currently being built with the display's `wetInkRenderer`.
78
- protected previewStroke() {
79
- this.editor.clearWetInk();
80
- this.builder?.preview(this.editor.display.getWetInkRenderer());
81
- }
82
-
83
- // Throws if no stroke builder exists.
84
- protected addPointToStroke(point: StrokeDataPoint) {
85
- if (!this.builder) {
86
- throw new Error('No stroke is currently being generated.');
87
- }
88
- this.builder.addPoint(point);
89
- this.lastPoint = point;
90
- this.previewStroke();
91
- }
92
-
93
- public override onPointerDown(event: PointerEvt): boolean {
94
- const { current, allPointers } = event;
95
- const isEraser = current.device === PointerDevice.Eraser;
96
-
97
- let anyDeviceIsStylus = false;
98
- for (const pointer of allPointers) {
99
- if (pointer.device === PointerDevice.Pen) {
100
- anyDeviceIsStylus = true;
101
- break;
102
- }
103
- }
104
-
105
- // Avoid canceling an existing stroke
106
- if (this.builder && !this.eventCanCancelStroke(event)) {
107
- return true;
108
- }
109
-
110
- if ((allPointers.length === 1 && !isEraser) || anyDeviceIsStylus) {
111
- this.startPoint = this.toStrokePoint(current);
112
- this.builder = this.style.factory(this.startPoint, this.editor.viewport);
113
- this.currentDeviceType = current.device;
114
- return true;
115
- }
116
-
117
- return false;
118
- }
119
-
120
- private eventCanCancelStroke(event: PointerEvt) {
121
- // If there has been a delay since the last input event,
122
- // it's always okay to cancel
123
- const lastInputTime = this.lastPoint?.time ?? 0;
124
- if (event.current.timeStamp - lastInputTime > 1000) {
125
- return true;
126
- }
127
-
128
- const isPenStroke = this.currentDeviceType === PointerDevice.Pen;
129
- const isTouchEvent = event.current.device === PointerDevice.Touch;
130
-
131
- // Don't allow pen strokes to be cancelled by touch events.
132
- if (isPenStroke && isTouchEvent) {
133
- return false;
134
- }
135
-
136
- return true;
137
- }
138
-
139
- public override eventCanBeDeliveredToNonActiveTool(event: PointerEvt) {
140
- return this.eventCanCancelStroke(event);
141
- }
142
-
143
- public override onPointerMove({ current }: PointerEvt): void {
144
- if (!this.builder) return;
145
- if (current.device !== this.currentDeviceType) return;
146
-
147
- this.addPointToStroke(this.toStrokePoint(current));
148
- }
149
-
150
- public override onPointerUp({ current }: PointerEvt) {
151
- if (!this.builder) return false;
152
- if (current.device !== this.currentDeviceType) {
153
- // this.builder still exists, so we're handling events from another
154
- // device type.
155
- return true;
156
- }
157
-
158
- // onPointerUp events can have zero pressure. Use the last pressure instead.
159
- const currentPoint = this.toStrokePoint(current);
160
- const strokePoint = {
161
- ...currentPoint,
162
- width: this.lastPoint?.width ?? currentPoint.width,
163
- };
164
-
165
- this.addPointToStroke(strokePoint);
166
-
167
- if (current.isPrimary) {
168
- this.finalizeStroke();
169
- }
170
-
171
- return false;
172
- }
173
-
174
- public override onGestureCancel() {
175
- this.builder = null;
176
- this.editor.clearWetInk();
177
- }
178
-
179
- private finalizeStroke() {
180
- if (this.builder) {
181
- const stroke = this.builder.build();
182
- this.previewStroke();
183
-
184
- if (stroke.getBBox().area > 0) {
185
- const canFlatten = true;
186
- const action = EditorImage.addElement(stroke, canFlatten);
187
- this.editor.dispatch(action);
188
- } else {
189
- console.warn('Pen: Not adding empty stroke', stroke, 'to the canvas.');
190
- }
191
- }
192
- this.builder = null;
193
- this.lastPoint = null;
194
- this.editor.clearWetInk();
195
- }
196
-
197
- private noteUpdated() {
198
- this.editor.notifier.dispatch(EditorEventType.ToolUpdated, {
199
- kind: EditorEventType.ToolUpdated,
200
- tool: this,
201
- });
202
- }
203
-
204
- public setColor(color: Color4): void {
205
- if (color.toHexString() !== this.style.color.toHexString()) {
206
- this.styleValue.set({
207
- ...this.style,
208
- color,
209
- });
210
- }
211
- }
212
-
213
- public setThickness(thickness: number) {
214
- if (thickness !== this.style.thickness) {
215
- this.styleValue.set({
216
- ...this.style,
217
- thickness,
218
- });
219
- }
220
- }
221
-
222
- public setStrokeFactory(factory: ComponentBuilderFactory) {
223
- if (factory !== this.style.factory) {
224
- this.styleValue.set({
225
- ...this.style,
226
- factory,
227
- });
228
- }
229
- }
230
-
231
- public setHasStabilization(hasStabilization: boolean) {
232
- const hasInputMapper = !!this.getInputMapper();
233
-
234
- // TODO: Currently, this assumes that there is no other input mapper.
235
- if (hasStabilization === hasInputMapper) {
236
- return;
237
- }
238
-
239
- if (hasInputMapper) {
240
- this.setInputMapper(null);
241
- } else {
242
- this.setInputMapper(new InputStabilizer(this.editor.viewport));
243
- }
244
- this.noteUpdated();
245
- }
246
-
247
- public getThickness() { return this.style.thickness; }
248
- public getColor() { return this.style.color; }
249
- public getStrokeFactory() { return this.style.factory; }
250
- public getStyleValue() { return this.styleValue; }
251
-
252
- public override setEnabled(enabled: boolean): void {
253
- super.setEnabled(enabled);
254
- }
255
-
256
- public override onKeyPress(event: KeyPressEvent): boolean {
257
- const shortcuts = this.editor.shortcuts;
258
-
259
- // Ctrl+Z: End the stroke so that it can be undone/redone.
260
- const isCtrlZ = shortcuts.matchesShortcut(undoKeyboardShortcutId, event);
261
- if (this.builder && isCtrlZ) {
262
- this.finalizeStroke();
263
-
264
- // Return false: Allow other listeners to handle the event (e.g.
265
- // undo/redo).
266
- return false;
267
- }
268
-
269
- let newThickness: number|undefined;
270
- if (shortcuts.matchesShortcut(decreaseSizeKeyboardShortcutId, event)) {
271
- newThickness = this.getThickness() * 2/3;
272
- } else if (shortcuts.matchesShortcut(increaseSizeKeyboardShortcutId, event)) {
273
- newThickness = this.getThickness() * 3/2;
274
- }
275
-
276
- if (newThickness !== undefined) {
277
- newThickness = Math.min(Math.max(1, newThickness), 256);
278
- this.setThickness(newThickness);
279
- return true;
280
- }
281
-
282
- return false;
283
- }
284
- }
@@ -1,84 +0,0 @@
1
- // @internal @packageDocumentation
2
-
3
- import { Color4 } from '@js-draw/math';
4
- import Editor from '../Editor';
5
- import { PointerEvt } from '../inputEvents';
6
- import BaseTool from './BaseTool';
7
-
8
- type ColorListener = (color: Color4|null)=>void;
9
-
10
- /**
11
- * A tool used internally to pick colors from the canvas.
12
- *
13
- * When color selection is in progress, the `pipette--color-selection-in-progress` class
14
- * is added to the root element. This can be used by themes.
15
- *
16
- * @internal
17
- */
18
- export default class PipetteTool extends BaseTool {
19
- private colorPreviewListener: ColorListener|null = null;
20
- private colorSelectListener: ColorListener|null = null;
21
-
22
- public constructor(
23
- private editor: Editor,
24
- description: string,
25
- ) {
26
- super(editor.notifier, description);
27
-
28
- this.enabledValue().onUpdateAndNow(() => {
29
- this.updateSelectingStatus();
30
- });
31
- }
32
-
33
- // Ensures that the root editor element correctly reflects whether color selection
34
- // is in progress.
35
- private updateSelectingStatus() {
36
- const className = 'pipette--color-selection-in-progress';
37
-
38
- if (this.isEnabled() && this.colorSelectListener && this.colorPreviewListener) {
39
- this.editor.getRootElement().classList.add(className);
40
- }
41
- else {
42
- this.editor.getRootElement().classList.remove(className);
43
- }
44
- }
45
-
46
- public setColorListener(
47
- colorPreviewListener: ColorListener,
48
-
49
- // Called when the gesture ends -- when the user has selected a color.
50
- colorSelectListener: ColorListener,
51
- ) {
52
- this.colorPreviewListener = colorPreviewListener;
53
- this.colorSelectListener = colorSelectListener;
54
-
55
- this.updateSelectingStatus();
56
- }
57
-
58
- public clearColorListener() {
59
- this.colorPreviewListener = null;
60
- this.colorSelectListener = null;
61
-
62
- this.updateSelectingStatus();
63
- }
64
-
65
- public override onPointerDown({ current, allPointers }: PointerEvt): boolean {
66
- if (this.colorPreviewListener && allPointers.length === 1) {
67
- this.colorPreviewListener(this.editor.display.getColorAt(current.screenPos));
68
- return true;
69
- }
70
- return false;
71
- }
72
-
73
- public override onPointerMove({ current }: PointerEvt): void {
74
- this.colorPreviewListener?.(this.editor.display.getColorAt(current.screenPos));
75
- }
76
-
77
- public override onPointerUp({ current }: PointerEvt): void {
78
- this.colorSelectListener?.(this.editor.display.getColorAt(current.screenPos));
79
- }
80
-
81
- public override onGestureCancel(): void {
82
- this.colorSelectListener?.(null);
83
- }
84
- }
@@ -1,29 +0,0 @@
1
- import Editor from '../../Editor';
2
- import { KeyPressEvent } from '../../inputEvents';
3
- import BaseTool from '../BaseTool';
4
- import { selectAllKeyboardShortcut } from '../keybindings';
5
- import SelectionTool from './SelectionTool';
6
-
7
- // Handles ctrl+a: Select all
8
- export default class SelectAllShortcutHandler extends BaseTool {
9
- public constructor(private editor: Editor) {
10
- super(editor.notifier, editor.localization.selectAllTool);
11
- }
12
-
13
- // @internal
14
- public override onKeyPress(event: KeyPressEvent): boolean {
15
- if (this.editor.shortcuts.matchesShortcut(selectAllKeyboardShortcut, event)) {
16
- const selectionTools = this.editor.toolController.getMatchingTools(SelectionTool);
17
-
18
- if (selectionTools.length > 0) {
19
- const selectionTool = selectionTools[0];
20
- selectionTool.setEnabled(true);
21
- selectionTool.setSelection(this.editor.image.getAllElements());
22
-
23
- return true;
24
- }
25
- }
26
-
27
- return false;
28
- }
29
- }