js-draw 0.1.11 → 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.
- package/.eslintrc.js +1 -0
- package/.firebaserc +5 -0
- package/.github/workflows/firebase-hosting-merge.yml +25 -0
- package/.github/workflows/firebase-hosting-pull-request.yml +22 -0
- package/.github/workflows/github-pages.yml +52 -0
- package/CHANGELOG.md +13 -0
- package/README.md +11 -6
- package/dist/bundle.js +1 -1
- package/dist/src/Color4.d.ts +19 -0
- package/dist/src/Color4.js +24 -3
- package/dist/src/Editor.d.ts +133 -4
- package/dist/src/Editor.js +124 -27
- package/dist/src/EditorImage.d.ts +8 -3
- package/dist/src/EditorImage.js +42 -26
- package/dist/src/EventDispatcher.d.ts +18 -0
- package/dist/src/EventDispatcher.js +19 -4
- package/dist/src/Pointer.d.ts +1 -1
- package/dist/src/Pointer.js +4 -3
- package/dist/src/SVGLoader.d.ts +1 -1
- package/dist/src/SVGLoader.js +14 -6
- package/dist/src/UndoRedoHistory.js +15 -2
- package/dist/src/Viewport.d.ts +8 -25
- package/dist/src/Viewport.js +18 -10
- package/dist/src/bundle/bundled.d.ts +1 -2
- package/dist/src/bundle/bundled.js +1 -2
- package/dist/src/commands/Command.d.ts +2 -2
- package/dist/src/commands/Command.js +4 -4
- package/dist/src/commands/Duplicate.d.ts +2 -2
- package/dist/src/commands/Duplicate.js +4 -5
- package/dist/src/commands/Erase.d.ts +2 -2
- package/dist/src/commands/Erase.js +7 -6
- package/dist/src/commands/SerializableCommand.d.ts +4 -5
- package/dist/src/commands/SerializableCommand.js +12 -4
- package/dist/src/commands/invertCommand.d.ts +4 -0
- package/dist/src/commands/invertCommand.js +44 -0
- package/dist/src/commands/lib.d.ts +6 -0
- package/dist/src/commands/lib.js +6 -0
- package/dist/src/commands/localization.d.ts +2 -1
- package/dist/src/commands/localization.js +1 -0
- package/dist/src/components/AbstractComponent.d.ts +16 -11
- package/dist/src/components/AbstractComponent.js +28 -17
- package/dist/src/components/SVGGlobalAttributesObject.d.ts +4 -4
- package/dist/src/components/SVGGlobalAttributesObject.js +8 -2
- package/dist/src/components/Stroke.d.ts +16 -6
- package/dist/src/components/Stroke.js +12 -9
- package/dist/src/components/Text.d.ts +5 -5
- package/dist/src/components/Text.js +9 -9
- package/dist/src/components/UnknownSVGObject.d.ts +4 -4
- package/dist/src/components/UnknownSVGObject.js +7 -2
- package/dist/src/components/builders/ArrowBuilder.d.ts +1 -1
- package/dist/src/components/builders/ArrowBuilder.js +1 -1
- package/dist/src/components/builders/FreehandLineBuilder.d.ts +8 -3
- package/dist/src/components/builders/FreehandLineBuilder.js +142 -71
- package/dist/src/components/builders/LineBuilder.d.ts +1 -1
- package/dist/src/components/builders/LineBuilder.js +1 -1
- package/dist/src/components/builders/RectangleBuilder.d.ts +1 -1
- package/dist/src/components/builders/RectangleBuilder.js +3 -3
- package/dist/src/components/builders/types.d.ts +1 -1
- package/dist/src/components/lib.d.ts +4 -0
- package/dist/src/components/lib.js +4 -0
- package/dist/src/lib.d.ts +25 -0
- package/dist/src/lib.js +25 -0
- package/dist/src/localization.d.ts +1 -0
- package/dist/src/localization.js +5 -1
- package/dist/src/localizations/es.js +1 -1
- package/dist/src/{geometry → math}/LineSegment2.d.ts +0 -0
- package/dist/src/{geometry → math}/LineSegment2.js +0 -0
- package/dist/src/math/Mat33.d.ts +78 -0
- package/dist/src/{geometry → math}/Mat33.js +48 -20
- package/dist/src/{geometry → math}/Path.d.ts +2 -1
- package/dist/src/{geometry → math}/Path.js +59 -52
- package/dist/src/{geometry → math}/Rect2.d.ts +2 -2
- package/dist/src/{geometry → math}/Rect2.js +0 -0
- package/dist/src/{geometry → math}/Vec2.d.ts +0 -0
- package/dist/src/{geometry → math}/Vec2.js +0 -0
- package/dist/src/math/Vec3.d.ts +96 -0
- package/dist/src/{geometry → math}/Vec3.js +63 -15
- package/dist/src/math/lib.d.ts +7 -0
- package/dist/src/math/lib.js +7 -0
- package/dist/src/math/rounding.d.ts +3 -0
- package/dist/src/math/rounding.js +121 -0
- package/dist/src/rendering/Display.d.ts +47 -1
- package/dist/src/rendering/Display.js +60 -15
- package/dist/src/rendering/caching/CacheRecord.d.ts +3 -2
- package/dist/src/rendering/caching/CacheRecord.js +4 -1
- package/dist/src/rendering/caching/CacheRecordManager.d.ts +5 -4
- package/dist/src/rendering/caching/CacheRecordManager.js +16 -4
- package/dist/src/rendering/caching/RenderingCache.d.ts +2 -3
- package/dist/src/rendering/caching/RenderingCache.js +10 -11
- package/dist/src/rendering/caching/RenderingCacheNode.d.ts +2 -1
- package/dist/src/rendering/caching/RenderingCacheNode.js +18 -7
- package/dist/src/rendering/caching/testUtils.js +1 -1
- package/dist/src/rendering/caching/types.d.ts +2 -4
- package/dist/src/rendering/localization.d.ts +2 -0
- package/dist/src/rendering/localization.js +2 -0
- package/dist/src/rendering/renderers/AbstractRenderer.d.ts +4 -4
- package/dist/src/rendering/renderers/AbstractRenderer.js +2 -2
- package/dist/src/rendering/renderers/CanvasRenderer.d.ts +4 -4
- package/dist/src/rendering/renderers/CanvasRenderer.js +2 -2
- package/dist/src/rendering/renderers/DummyRenderer.d.ts +4 -4
- package/dist/src/rendering/renderers/DummyRenderer.js +1 -1
- package/dist/src/rendering/renderers/SVGRenderer.d.ts +3 -3
- package/dist/src/rendering/renderers/SVGRenderer.js +8 -2
- package/dist/src/rendering/renderers/TextOnlyRenderer.d.ts +5 -3
- package/dist/src/rendering/renderers/TextOnlyRenderer.js +13 -3
- package/dist/src/toolbar/HTMLToolbar.js +1 -0
- package/dist/src/toolbar/icons.d.ts +3 -0
- package/dist/src/toolbar/icons.js +142 -132
- package/dist/src/toolbar/localization.d.ts +2 -1
- package/dist/src/toolbar/localization.js +2 -1
- package/dist/src/toolbar/makeColorInput.js +3 -2
- package/dist/src/toolbar/widgets/ActionButtonWidget.d.ts +13 -0
- package/dist/src/toolbar/widgets/ActionButtonWidget.js +21 -0
- package/dist/src/toolbar/widgets/BaseWidget.js +2 -0
- package/dist/src/toolbar/widgets/HandToolWidget.js +3 -3
- package/dist/src/toolbar/widgets/PenWidget.js +1 -0
- package/dist/src/toolbar/widgets/SelectionWidget.d.ts +0 -1
- package/dist/src/toolbar/widgets/SelectionWidget.js +23 -30
- package/dist/src/tools/Eraser.js +1 -1
- package/dist/src/tools/PanZoom.d.ts +1 -1
- package/dist/src/tools/PanZoom.js +24 -14
- package/dist/src/tools/Pen.d.ts +1 -2
- package/dist/src/tools/Pen.js +8 -1
- package/dist/src/tools/PipetteTool.js +1 -0
- package/dist/src/tools/SelectionTool.d.ts +3 -3
- package/dist/src/tools/SelectionTool.js +51 -28
- package/dist/src/tools/TextTool.js +1 -1
- package/dist/src/types.d.ts +21 -10
- package/dist/src/types.js +7 -5
- package/firebase.json +16 -0
- package/package.json +118 -101
- package/src/Color4.ts +23 -2
- package/src/Editor.ts +181 -37
- package/src/EditorImage.test.ts +2 -4
- package/src/EditorImage.ts +46 -28
- package/src/EventDispatcher.ts +21 -6
- package/src/Pointer.ts +4 -3
- package/src/SVGLoader.ts +14 -6
- package/src/UndoRedoHistory.ts +18 -2
- package/src/Viewport.ts +23 -18
- package/src/bundle/bundled.ts +1 -2
- package/src/commands/Command.ts +5 -5
- package/src/commands/Duplicate.ts +4 -5
- package/src/commands/Erase.ts +7 -6
- package/src/commands/SerializableCommand.ts +17 -9
- package/src/commands/invertCommand.ts +51 -0
- package/src/commands/lib.ts +14 -0
- package/src/commands/localization.ts +3 -1
- package/src/components/AbstractComponent.ts +35 -24
- package/src/components/SVGGlobalAttributesObject.ts +11 -4
- package/src/components/Stroke.test.ts +4 -6
- package/src/components/Stroke.ts +15 -11
- package/src/components/Text.test.ts +2 -2
- package/src/components/Text.ts +9 -10
- package/src/components/UnknownSVGObject.ts +10 -4
- package/src/components/builders/ArrowBuilder.ts +2 -2
- package/src/components/builders/FreehandLineBuilder.ts +190 -80
- package/src/components/builders/LineBuilder.ts +2 -2
- package/src/components/builders/RectangleBuilder.ts +3 -3
- package/src/components/builders/types.ts +1 -1
- package/src/components/lib.ts +9 -0
- package/src/lib.ts +28 -0
- package/src/localization.ts +6 -0
- package/src/localizations/es.ts +2 -1
- package/src/{geometry → math}/LineSegment2.test.ts +0 -0
- package/src/{geometry → math}/LineSegment2.ts +0 -0
- package/src/{geometry → math}/Mat33.test.ts +0 -0
- package/src/{geometry → math}/Mat33.ts +48 -20
- package/src/{geometry → math}/Path.fromString.test.ts +0 -0
- package/src/{geometry → math}/Path.test.ts +0 -0
- package/src/{geometry → math}/Path.toString.test.ts +11 -2
- package/src/{geometry → math}/Path.ts +61 -58
- package/src/{geometry → math}/Rect2.test.ts +0 -0
- package/src/{geometry → math}/Rect2.ts +2 -2
- package/src/{geometry → math}/Vec2.test.ts +0 -0
- package/src/{geometry → math}/Vec2.ts +0 -0
- package/src/{geometry → math}/Vec3.test.ts +0 -0
- package/src/{geometry → math}/Vec3.ts +64 -16
- package/src/math/lib.ts +15 -0
- package/src/math/rounding.test.ts +40 -0
- package/src/math/rounding.ts +147 -0
- package/src/rendering/Display.ts +63 -15
- package/src/rendering/caching/CacheRecord.test.ts +3 -3
- package/src/rendering/caching/CacheRecord.ts +6 -2
- package/src/rendering/caching/CacheRecordManager.ts +34 -8
- package/src/rendering/caching/RenderingCache.test.ts +3 -3
- package/src/rendering/caching/RenderingCache.ts +11 -16
- package/src/rendering/caching/RenderingCacheNode.ts +23 -7
- package/src/rendering/caching/testUtils.ts +1 -1
- package/src/rendering/caching/types.ts +2 -7
- package/src/rendering/localization.ts +4 -0
- package/src/rendering/renderers/AbstractRenderer.ts +4 -4
- package/src/rendering/renderers/CanvasRenderer.ts +5 -5
- package/src/rendering/renderers/DummyRenderer.test.ts +2 -2
- package/src/rendering/renderers/DummyRenderer.ts +4 -4
- package/src/rendering/renderers/SVGRenderer.ts +10 -4
- package/src/rendering/renderers/TextOnlyRenderer.ts +17 -6
- package/src/toolbar/HTMLToolbar.ts +1 -0
- package/src/toolbar/icons.ts +157 -137
- package/src/toolbar/localization.ts +4 -2
- package/src/toolbar/makeColorInput.ts +3 -2
- package/src/toolbar/toolbar.css +1 -1
- package/src/toolbar/widgets/ActionButtonWidget.ts +31 -0
- package/src/toolbar/widgets/BaseWidget.ts +2 -0
- package/src/toolbar/widgets/HandToolWidget.ts +3 -3
- package/src/toolbar/widgets/PenWidget.ts +2 -0
- package/src/toolbar/widgets/SelectionWidget.ts +46 -41
- package/src/tools/Eraser.ts +2 -2
- package/src/tools/PanZoom.ts +28 -17
- package/src/tools/Pen.ts +11 -2
- package/src/tools/PipetteTool.ts +2 -0
- package/src/tools/SelectionTool.test.ts +2 -4
- package/src/tools/SelectionTool.ts +52 -24
- package/src/tools/TextTool.ts +2 -2
- package/src/tools/UndoRedoShortcut.test.ts +1 -1
- package/src/types.ts +23 -7
- package/tsconfig.json +4 -1
- package/typedoc.json +20 -0
- package/dist/src/geometry/Mat33.d.ts +0 -32
- package/dist/src/geometry/Vec3.d.ts +0 -34
@@ -1,8 +1,9 @@
|
|
1
1
|
import Editor from '../../Editor';
|
2
2
|
import SelectionTool from '../../tools/SelectionTool';
|
3
3
|
import { EditorEventType } from '../../types';
|
4
|
-
import { makeSelectionIcon } from '../icons';
|
4
|
+
import { makeDeleteSelectionIcon, makeDuplicateSelectionIcon, makeResizeViewportIcon, makeSelectionIcon } from '../icons';
|
5
5
|
import { ToolbarLocalization } from '../localization';
|
6
|
+
import ActionButtonWidget from './ActionButtonWidget';
|
6
7
|
import BaseToolWidget from './BaseToolWidget';
|
7
8
|
|
8
9
|
export class SelectionWidget extends BaseToolWidget {
|
@@ -10,44 +11,46 @@ export class SelectionWidget extends BaseToolWidget {
|
|
10
11
|
editor: Editor, private tool: SelectionTool, localization: ToolbarLocalization
|
11
12
|
) {
|
12
13
|
super(editor, tool, localization);
|
13
|
-
}
|
14
|
-
|
15
|
-
protected getTitle(): string {
|
16
|
-
return this.localizationTable.select;
|
17
|
-
}
|
18
|
-
|
19
|
-
protected createIcon(): Element {
|
20
|
-
return makeSelectionIcon();
|
21
|
-
}
|
22
14
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
deleteButton
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
15
|
+
const resizeButton = new ActionButtonWidget(
|
16
|
+
editor, localization,
|
17
|
+
makeResizeViewportIcon,
|
18
|
+
this.localizationTable.resizeImageToSelection,
|
19
|
+
() => {
|
20
|
+
const selection = this.tool.getSelection();
|
21
|
+
this.editor.dispatch(this.editor.setImportExportRect(selection!.region));
|
22
|
+
},
|
23
|
+
);
|
24
|
+
const deleteButton = new ActionButtonWidget(
|
25
|
+
editor, localization,
|
26
|
+
makeDeleteSelectionIcon,
|
27
|
+
this.localizationTable.deleteSelection,
|
28
|
+
() => {
|
29
|
+
const selection = this.tool.getSelection();
|
30
|
+
this.editor.dispatch(selection!.deleteSelectedObjects());
|
31
|
+
this.tool.clearSelection();
|
32
|
+
},
|
33
|
+
);
|
34
|
+
const duplicateButton = new ActionButtonWidget(
|
35
|
+
editor, localization,
|
36
|
+
makeDuplicateSelectionIcon,
|
37
|
+
this.localizationTable.duplicateSelection,
|
38
|
+
() => {
|
39
|
+
const selection = this.tool.getSelection();
|
40
|
+
this.editor.dispatch(selection!.duplicateSelectedObjects());
|
41
|
+
},
|
42
|
+
);
|
40
43
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
this.tool.clearSelection();
|
45
|
-
};
|
44
|
+
this.addSubWidget(resizeButton);
|
45
|
+
this.addSubWidget(deleteButton);
|
46
|
+
this.addSubWidget(duplicateButton);
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
48
|
+
const updateDisabled = (disabled: boolean) => {
|
49
|
+
resizeButton.setDisabled(disabled);
|
50
|
+
deleteButton.setDisabled(disabled);
|
51
|
+
duplicateButton.setDisabled(disabled);
|
50
52
|
};
|
53
|
+
updateDisabled(true);
|
51
54
|
|
52
55
|
// Enable/disable actions based on whether items are selected
|
53
56
|
this.editor.notifier.on(EditorEventType.ToolUpdated, toolEvt => {
|
@@ -59,14 +62,16 @@ export class SelectionWidget extends BaseToolWidget {
|
|
59
62
|
const selection = this.tool.getSelection();
|
60
63
|
const hasSelection = selection && selection.region.area > 0;
|
61
64
|
|
62
|
-
|
63
|
-
deleteButton.disabled = resizeButton.disabled;
|
64
|
-
duplicateButton.disabled = resizeButton.disabled;
|
65
|
+
updateDisabled(!hasSelection);
|
65
66
|
}
|
66
67
|
});
|
68
|
+
}
|
67
69
|
|
68
|
-
|
69
|
-
|
70
|
-
|
70
|
+
protected getTitle(): string {
|
71
|
+
return this.localizationTable.select;
|
72
|
+
}
|
73
|
+
|
74
|
+
protected createIcon(): Element {
|
75
|
+
return makeSelectionIcon();
|
71
76
|
}
|
72
77
|
}
|
package/src/tools/Eraser.ts
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
import { PointerEvt } from '../types';
|
2
2
|
import BaseTool from './BaseTool';
|
3
3
|
import Editor from '../Editor';
|
4
|
-
import { Point2 } from '../
|
5
|
-
import LineSegment2 from '../
|
4
|
+
import { Point2 } from '../math/Vec2';
|
5
|
+
import LineSegment2 from '../math/LineSegment2';
|
6
6
|
import Erase from '../commands/Erase';
|
7
7
|
import { ToolType } from './ToolController';
|
8
8
|
import AbstractComponent from '../components/AbstractComponent';
|
package/src/tools/PanZoom.ts
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
|
2
2
|
import { Editor } from '../Editor';
|
3
|
-
import Mat33 from '../
|
4
|
-
import { Point2, Vec2 } from '../
|
5
|
-
import Vec3 from '../
|
3
|
+
import Mat33 from '../math/Mat33';
|
4
|
+
import { Point2, Vec2 } from '../math/Vec2';
|
5
|
+
import Vec3 from '../math/Vec3';
|
6
6
|
import Pointer, { PointerDevice } from '../Pointer';
|
7
7
|
import { EditorEventType, KeyPressEvent, PointerEvt, WheelEvt } from '../types';
|
8
|
-
import { Viewport } from '../Viewport';
|
8
|
+
import { Viewport, ViewportTransform } from '../Viewport';
|
9
9
|
import BaseTool from './BaseTool';
|
10
10
|
import { ToolType } from './ToolController';
|
11
11
|
|
@@ -16,7 +16,6 @@ interface PinchData {
|
|
16
16
|
dist: number;
|
17
17
|
}
|
18
18
|
|
19
|
-
|
20
19
|
export enum PanZoomMode {
|
21
20
|
OneFingerTouchGestures = 0x1,
|
22
21
|
TwoFingerTouchGestures = 0x1 << 1,
|
@@ -27,7 +26,7 @@ export enum PanZoomMode {
|
|
27
26
|
|
28
27
|
export default class PanZoom extends BaseTool {
|
29
28
|
public readonly kind: ToolType.PanZoom = ToolType.PanZoom;
|
30
|
-
private transform:
|
29
|
+
private transform: ViewportTransform|null = null;
|
31
30
|
|
32
31
|
private lastAngle: number;
|
33
32
|
private lastDist: number;
|
@@ -74,7 +73,7 @@ export default class PanZoom extends BaseTool {
|
|
74
73
|
}
|
75
74
|
|
76
75
|
if (handlingGesture) {
|
77
|
-
this.transform ??=
|
76
|
+
this.transform ??= Viewport.transformBy(Mat33.identity);
|
78
77
|
this.editor.display.setDraftMode(true);
|
79
78
|
}
|
80
79
|
|
@@ -100,14 +99,14 @@ export default class PanZoom extends BaseTool {
|
|
100
99
|
this.lastScreenCenter = screenCenter;
|
101
100
|
this.lastDist = dist;
|
102
101
|
this.lastAngle = angle;
|
103
|
-
this.transform =
|
102
|
+
this.transform = Viewport.transformBy(
|
104
103
|
this.transform!.transform.rightMul(transformUpdate)
|
105
104
|
);
|
106
105
|
}
|
107
106
|
|
108
107
|
private handleOneFingerMove(pointer: Pointer) {
|
109
108
|
const delta = this.getCenterDelta(pointer.screenPos);
|
110
|
-
this.transform =
|
109
|
+
this.transform = Viewport.transformBy(
|
111
110
|
this.transform!.transform.rightMul(
|
112
111
|
Mat33.translation(delta)
|
113
112
|
)
|
@@ -116,7 +115,7 @@ export default class PanZoom extends BaseTool {
|
|
116
115
|
}
|
117
116
|
|
118
117
|
public onPointerMove({ allPointers }: PointerEvt): void {
|
119
|
-
this.transform ??=
|
118
|
+
this.transform ??= Viewport.transformBy(Mat33.identity);
|
120
119
|
|
121
120
|
const lastTransform = this.transform;
|
122
121
|
if (allPointers.length === 2) {
|
@@ -146,21 +145,25 @@ export default class PanZoom extends BaseTool {
|
|
146
145
|
|
147
146
|
// Applies [transformUpdate] to the editor. This stacks on top of the
|
148
147
|
// current transformation, if it exists.
|
149
|
-
private updateTransform(transformUpdate: Mat33) {
|
148
|
+
private updateTransform(transformUpdate: Mat33, announce: boolean = false) {
|
150
149
|
let newTransform = transformUpdate;
|
151
150
|
if (this.transform) {
|
152
151
|
newTransform = this.transform.transform.rightMul(transformUpdate);
|
153
152
|
}
|
154
153
|
|
155
154
|
this.transform?.unapply(this.editor);
|
156
|
-
this.transform =
|
155
|
+
this.transform = Viewport.transformBy(newTransform);
|
157
156
|
this.transform.apply(this.editor);
|
157
|
+
|
158
|
+
if (announce) {
|
159
|
+
this.editor.announceForAccessibility(this.transform.description(this.editor, this.editor.localization));
|
160
|
+
}
|
158
161
|
}
|
159
162
|
|
160
163
|
public onWheel({ delta, screenPos }: WheelEvt): boolean {
|
161
164
|
// Reset the transformation -- wheel events are individual events, so we don't
|
162
165
|
// need to unapply/reapply.
|
163
|
-
this.transform =
|
166
|
+
this.transform = Viewport.transformBy(Mat33.identity);
|
164
167
|
|
165
168
|
const canvasPos = this.editor.viewport.screenToCanvas(screenPos);
|
166
169
|
const toCanvas = this.editor.viewport.screenToCanvasTransform;
|
@@ -176,7 +179,7 @@ export default class PanZoom extends BaseTool {
|
|
176
179
|
).rightMul(
|
177
180
|
Mat33.translation(translation)
|
178
181
|
);
|
179
|
-
this.updateTransform(transformUpdate);
|
182
|
+
this.updateTransform(transformUpdate, true);
|
180
183
|
|
181
184
|
return true;
|
182
185
|
}
|
@@ -187,7 +190,7 @@ export default class PanZoom extends BaseTool {
|
|
187
190
|
}
|
188
191
|
|
189
192
|
// No need to keep the same the transform for keyboard events.
|
190
|
-
this.transform =
|
193
|
+
this.transform = Viewport.transformBy(Mat33.identity);
|
191
194
|
|
192
195
|
let translation = Vec2.zero;
|
193
196
|
let scale = 1;
|
@@ -205,10 +208,12 @@ export default class PanZoom extends BaseTool {
|
|
205
208
|
case 'ArrowRight':
|
206
209
|
translation = Vec2.of(1, 0);
|
207
210
|
break;
|
211
|
+
case 'q':
|
208
212
|
case 'k':
|
209
213
|
case 'ArrowUp':
|
210
214
|
translation = Vec2.of(0, -1);
|
211
215
|
break;
|
216
|
+
case 'e':
|
212
217
|
case 'j':
|
213
218
|
case 'ArrowDown':
|
214
219
|
translation = Vec2.of(0, 1);
|
@@ -231,13 +236,19 @@ export default class PanZoom extends BaseTool {
|
|
231
236
|
|
232
237
|
// For each keypress,
|
233
238
|
translation = translation.times(30); // Move at most 30 units
|
234
|
-
rotation *= Math.PI / 8; // Rotate at
|
239
|
+
rotation *= Math.PI / 8; // Rotate at least a sixteenth of a rotation
|
235
240
|
|
236
241
|
// Transform the canvas, not the viewport:
|
237
242
|
translation = translation.times(-1);
|
238
243
|
rotation = rotation * -1;
|
239
244
|
scale = 1 / scale;
|
240
245
|
|
246
|
+
// Work around an issue that seems to be related to rotation matricies losing precision on inversion.
|
247
|
+
// TODO: Figure out why and implement a better solution.
|
248
|
+
if (rotation !== 0) {
|
249
|
+
rotation += 0.0001;
|
250
|
+
}
|
251
|
+
|
241
252
|
const toCanvas = this.editor.viewport.screenToCanvasTransform;
|
242
253
|
|
243
254
|
// Transform without translating (treat toCanvas as a linear instead of
|
@@ -253,7 +264,7 @@ export default class PanZoom extends BaseTool {
|
|
253
264
|
)).rightMul(Mat33.translation(
|
254
265
|
translation
|
255
266
|
));
|
256
|
-
this.updateTransform(transformUpdate);
|
267
|
+
this.updateTransform(transformUpdate, true);
|
257
268
|
|
258
269
|
return true;
|
259
270
|
}
|
package/src/tools/Pen.ts
CHANGED
@@ -8,7 +8,7 @@ import BaseTool from './BaseTool';
|
|
8
8
|
import { ToolType } from './ToolController';
|
9
9
|
import { ComponentBuilder, ComponentBuilderFactory } from '../components/builders/types';
|
10
10
|
|
11
|
-
interface PenStyle {
|
11
|
+
export interface PenStyle {
|
12
12
|
color: Color4;
|
13
13
|
thickness: number;
|
14
14
|
}
|
@@ -34,7 +34,16 @@ export default class Pen extends BaseTool {
|
|
34
34
|
|
35
35
|
private getStrokePoint(pointer: Pointer): StrokeDataPoint {
|
36
36
|
const minPressure = 0.3;
|
37
|
-
|
37
|
+
let pressure = Math.max(pointer.pressure ?? 1.0, minPressure);
|
38
|
+
|
39
|
+
if (!isFinite(pressure)) {
|
40
|
+
console.warn('Non-finite pressure!', pointer);
|
41
|
+
pressure = minPressure;
|
42
|
+
}
|
43
|
+
console.assert(isFinite(pointer.canvasPos.length()), 'Non-finite canvas position!');
|
44
|
+
console.assert(isFinite(pointer.screenPos.length()), 'Non-finite screen position!');
|
45
|
+
console.assert(isFinite(pointer.timeStamp), 'Non-finite timeStamp on pointer!');
|
46
|
+
|
38
47
|
return {
|
39
48
|
pos: pointer.canvasPos,
|
40
49
|
width: pressure * this.getPressureMultiplier(),
|
package/src/tools/PipetteTool.ts
CHANGED
@@ -1,11 +1,9 @@
|
|
1
|
-
/* @jest-environment jsdom */
|
2
|
-
|
3
1
|
import Color4 from '../Color4';
|
4
2
|
import Stroke from '../components/Stroke';
|
5
3
|
import Editor from '../Editor';
|
6
4
|
import EditorImage from '../EditorImage';
|
7
|
-
import Path from '../
|
8
|
-
import { Vec2 } from '../
|
5
|
+
import Path from '../math/Path';
|
6
|
+
import { Vec2 } from '../math/Vec2';
|
9
7
|
import { InputEvtType } from '../types';
|
10
8
|
import SelectionTool from './SelectionTool';
|
11
9
|
import { ToolType } from './ToolController';
|
@@ -3,15 +3,16 @@ import Duplicate from '../commands/Duplicate';
|
|
3
3
|
import Erase from '../commands/Erase';
|
4
4
|
import AbstractComponent from '../components/AbstractComponent';
|
5
5
|
import Editor from '../Editor';
|
6
|
-
import Mat33 from '../
|
6
|
+
import Mat33 from '../math/Mat33';
|
7
7
|
// import Mat33 from "../geometry/Mat33";
|
8
|
-
import Rect2 from '../
|
9
|
-
import { Point2, Vec2 } from '../
|
8
|
+
import Rect2 from '../math/Rect2';
|
9
|
+
import { Point2, Vec2 } from '../math/Vec2';
|
10
10
|
import { EditorLocalization } from '../localization';
|
11
11
|
import { EditorEventType, KeyPressEvent, KeyUpEvent, PointerEvt } from '../types';
|
12
12
|
import Viewport from '../Viewport';
|
13
13
|
import BaseTool from './BaseTool';
|
14
14
|
import { ToolType } from './ToolController';
|
15
|
+
import SerializableCommand from '../commands/SerializableCommand';
|
15
16
|
|
16
17
|
const handleScreenSize = 30;
|
17
18
|
const styles = `
|
@@ -124,6 +125,7 @@ const makeDraggable = (element: HTMLElement, onDrag: DragCallback, onDragEnd: Dr
|
|
124
125
|
// Maximum number of strokes to transform without a re-render.
|
125
126
|
const updateChunkSize = 100;
|
126
127
|
|
128
|
+
// @internal
|
127
129
|
class Selection {
|
128
130
|
public region: Rect2;
|
129
131
|
private boxRotation: number;
|
@@ -131,7 +133,7 @@ class Selection {
|
|
131
133
|
private rotateCircle: HTMLElement;
|
132
134
|
private selectedElems: AbstractComponent[];
|
133
135
|
private transform: Mat33;
|
134
|
-
private transformationCommands:
|
136
|
+
private transformationCommands: SerializableCommand[];
|
135
137
|
|
136
138
|
public constructor(
|
137
139
|
public startPoint: Point2, private editor: Editor
|
@@ -230,7 +232,7 @@ class Selection {
|
|
230
232
|
this.transformPreview(Mat33.zRotation(deltaRotation, this.region.center));
|
231
233
|
}
|
232
234
|
|
233
|
-
private computeTransformCommands() {
|
235
|
+
private computeTransformCommands(): SerializableCommand[] {
|
234
236
|
return this.selectedElems.map(elem => {
|
235
237
|
return elem.transformBy(this.transform);
|
236
238
|
});
|
@@ -275,42 +277,68 @@ class Selection {
|
|
275
277
|
|
276
278
|
// Make the commands undo-able
|
277
279
|
this.editor.dispatch(new Selection.ApplyTransformationCommand(
|
278
|
-
this, currentTransfmCommands, fullTransform,
|
280
|
+
this, currentTransfmCommands, fullTransform, deltaBoxRotation
|
279
281
|
));
|
280
282
|
}
|
281
283
|
|
282
|
-
|
284
|
+
static {
|
285
|
+
SerializableCommand.register('selection-tool-transform', (json: any, editor) => {
|
286
|
+
// The selection box is lost when serializing/deserializing. No need to store box rotation
|
287
|
+
const guiBoxRotation = 0;
|
288
|
+
const fullTransform: Mat33 = new Mat33(...(json.transform as [
|
289
|
+
number, number, number,
|
290
|
+
number, number, number,
|
291
|
+
number, number, number,
|
292
|
+
]));
|
293
|
+
const commands = (json.commands as any[]).map(data => SerializableCommand.deserialize(data, editor));
|
294
|
+
|
295
|
+
return new this.ApplyTransformationCommand(null, commands, fullTransform, guiBoxRotation);
|
296
|
+
});
|
297
|
+
}
|
298
|
+
|
299
|
+
private static ApplyTransformationCommand = class extends SerializableCommand {
|
283
300
|
public constructor(
|
284
|
-
private selection: Selection,
|
285
|
-
private currentTransfmCommands:
|
286
|
-
private fullTransform: Mat33,
|
301
|
+
private selection: Selection|null,
|
302
|
+
private currentTransfmCommands: SerializableCommand[],
|
303
|
+
private fullTransform: Mat33,
|
287
304
|
private deltaBoxRotation: number,
|
288
305
|
) {
|
289
|
-
super();
|
306
|
+
super('selection-tool-transform');
|
290
307
|
}
|
291
308
|
|
292
309
|
public async apply(editor: Editor) {
|
293
310
|
// Approximate the new selection
|
294
|
-
|
295
|
-
|
296
|
-
|
311
|
+
if (this.selection) {
|
312
|
+
this.selection.region = this.selection.region.transformedBoundingBox(this.fullTransform);
|
313
|
+
this.selection.boxRotation += this.deltaBoxRotation;
|
314
|
+
this.selection.updateUI();
|
315
|
+
}
|
297
316
|
|
298
317
|
await editor.asyncApplyCommands(this.currentTransfmCommands, updateChunkSize);
|
299
|
-
this.selection
|
300
|
-
this.selection
|
318
|
+
this.selection?.recomputeRegion();
|
319
|
+
this.selection?.updateUI();
|
301
320
|
}
|
302
321
|
|
303
322
|
public async unapply(editor: Editor) {
|
304
|
-
|
305
|
-
|
306
|
-
|
323
|
+
if (this.selection) {
|
324
|
+
this.selection.region = this.selection.region.transformedBoundingBox(this.fullTransform.inverse());
|
325
|
+
this.selection.boxRotation -= this.deltaBoxRotation;
|
326
|
+
this.selection.updateUI();
|
327
|
+
}
|
307
328
|
|
308
329
|
await editor.asyncUnapplyCommands(this.currentTransfmCommands, updateChunkSize);
|
309
|
-
this.selection
|
310
|
-
this.selection
|
330
|
+
this.selection?.recomputeRegion();
|
331
|
+
this.selection?.updateUI();
|
332
|
+
}
|
333
|
+
|
334
|
+
protected serializeToJSON() {
|
335
|
+
return {
|
336
|
+
commands: this.currentTransfmCommands.map(command => command.serialize()),
|
337
|
+
transform: this.fullTransform.toArray(),
|
338
|
+
};
|
311
339
|
}
|
312
340
|
|
313
|
-
public description(localizationTable: EditorLocalization) {
|
341
|
+
public description(_editor: Editor, localizationTable: EditorLocalization) {
|
314
342
|
return localizationTable.transformedElements(this.currentTransfmCommands.length);
|
315
343
|
}
|
316
344
|
};
|
@@ -460,7 +488,7 @@ class Selection {
|
|
460
488
|
const closestPoint = visibleRect.getClosestPointOnBoundaryTo(this.region.center);
|
461
489
|
const delta = this.region.center.minus(closestPoint);
|
462
490
|
this.editor.dispatchNoAnnounce(
|
463
|
-
|
491
|
+
Viewport.transformBy(Mat33.translation(delta.times(-1))), false
|
464
492
|
);
|
465
493
|
}
|
466
494
|
}
|
@@ -671,7 +699,7 @@ export default class SelectionTool extends BaseTool {
|
|
671
699
|
|
672
700
|
if (enabled) {
|
673
701
|
this.handleOverlay.tabIndex = 0;
|
674
|
-
this.handleOverlay.
|
702
|
+
this.handleOverlay.setAttribute('aria-label', this.editor.localization.selectionToolKeyboardShortcuts);
|
675
703
|
} else {
|
676
704
|
this.handleOverlay.tabIndex = -1;
|
677
705
|
}
|
package/src/tools/TextTool.ts
CHANGED
@@ -2,8 +2,8 @@ import Color4 from '../Color4';
|
|
2
2
|
import Text, { TextStyle } from '../components/Text';
|
3
3
|
import Editor from '../Editor';
|
4
4
|
import EditorImage from '../EditorImage';
|
5
|
-
import Mat33 from '../
|
6
|
-
import { Vec2 } from '../
|
5
|
+
import Mat33 from '../math/Mat33';
|
6
|
+
import { Vec2 } from '../math/Vec2';
|
7
7
|
import { PointerDevice } from '../Pointer';
|
8
8
|
import { EditorEventType, PointerEvt } from '../types';
|
9
9
|
import BaseTool from './BaseTool';
|
@@ -3,7 +3,7 @@
|
|
3
3
|
import Color4 from '../Color4';
|
4
4
|
import Stroke from '../components/Stroke';
|
5
5
|
import EditorImage from '../EditorImage';
|
6
|
-
import Path from '../
|
6
|
+
import Path from '../math/Path';
|
7
7
|
import createEditor from '../testing/createEditor';
|
8
8
|
import { InputEvtType } from '../types';
|
9
9
|
|
package/src/types.ts
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
// Types related to the image editor
|
2
2
|
|
3
3
|
import EventDispatcher from './EventDispatcher';
|
4
|
-
import Mat33 from './
|
5
|
-
import { Point2, Vec2 } from './
|
6
|
-
import Vec3 from './
|
4
|
+
import Mat33 from './math/Mat33';
|
5
|
+
import { Point2, Vec2 } from './math/Vec2';
|
6
|
+
import Vec3 from './math/Vec3';
|
7
7
|
import BaseTool from './tools/BaseTool';
|
8
8
|
import AbstractComponent from './components/AbstractComponent';
|
9
|
-
import Rect2 from './
|
9
|
+
import Rect2 from './math/Rect2';
|
10
10
|
import Pointer from './Pointer';
|
11
11
|
import Color4 from './Color4';
|
12
|
+
import Command from './commands/Command';
|
12
13
|
|
13
14
|
|
14
15
|
export interface PointerEvtListener {
|
@@ -91,12 +92,17 @@ export enum EditorEventType {
|
|
91
92
|
ToolEnabled,
|
92
93
|
ToolDisabled,
|
93
94
|
ToolUpdated,
|
95
|
+
|
94
96
|
UndoRedoStackUpdated,
|
97
|
+
CommandDone,
|
98
|
+
CommandUndone,
|
95
99
|
ObjectAdded,
|
100
|
+
|
96
101
|
ViewportChanged,
|
97
102
|
DisplayResized,
|
103
|
+
|
98
104
|
ColorPickerToggled,
|
99
|
-
ColorPickerColorSelected
|
105
|
+
ColorPickerColorSelected,
|
100
106
|
}
|
101
107
|
|
102
108
|
type EditorToolEventType = EditorEventType.ToolEnabled
|
@@ -131,6 +137,16 @@ export interface EditorUndoStackUpdated {
|
|
131
137
|
readonly redoStackSize: number;
|
132
138
|
}
|
133
139
|
|
140
|
+
export interface CommandDoneEvent {
|
141
|
+
readonly kind: EditorEventType.CommandDone;
|
142
|
+
readonly command: Command;
|
143
|
+
}
|
144
|
+
|
145
|
+
export interface CommandUndoneEvent {
|
146
|
+
readonly kind: EditorEventType.CommandUndone;
|
147
|
+
readonly command: Command;
|
148
|
+
}
|
149
|
+
|
134
150
|
export interface ColorPickerToggled {
|
135
151
|
readonly kind: EditorEventType.ColorPickerToggled;
|
136
152
|
readonly open: boolean;
|
@@ -143,8 +159,8 @@ export interface ColorPickerColorSelected {
|
|
143
159
|
|
144
160
|
export type EditorEventDataType = EditorToolEvent | EditorObjectEvent
|
145
161
|
| EditorViewportChangedEvent | DisplayResizedEvent
|
146
|
-
| EditorUndoStackUpdated
|
147
|
-
| ColorPickerToggled| ColorPickerColorSelected;
|
162
|
+
| EditorUndoStackUpdated | CommandDoneEvent | CommandUndoneEvent
|
163
|
+
| ColorPickerToggled | ColorPickerColorSelected;
|
148
164
|
|
149
165
|
|
150
166
|
// Returns a Promise to indicate that the event source should pause until the Promise resolves.
|
package/tsconfig.json
CHANGED
package/typedoc.json
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
{
|
2
|
+
"entryPoints": [
|
3
|
+
"./src/"
|
4
|
+
],
|
5
|
+
"exclude": [
|
6
|
+
"**/*.test.ts"
|
7
|
+
],
|
8
|
+
"excludePrivate": true,
|
9
|
+
"excludeInternal": true,
|
10
|
+
"commentStyle": "all",
|
11
|
+
"readme": "README.md",
|
12
|
+
"entryPointStrategy": "expand",
|
13
|
+
"out": "docs/typedoc/",
|
14
|
+
|
15
|
+
"validation": {
|
16
|
+
"notExported": false,
|
17
|
+
"invalidLink": true,
|
18
|
+
"notDocumented": false
|
19
|
+
}
|
20
|
+
}
|
@@ -1,32 +0,0 @@
|
|
1
|
-
import { Point2, Vec2 } from './Vec2';
|
2
|
-
import Vec3 from './Vec3';
|
3
|
-
export default class Mat33 {
|
4
|
-
readonly a1: number;
|
5
|
-
readonly a2: number;
|
6
|
-
readonly a3: number;
|
7
|
-
readonly b1: number;
|
8
|
-
readonly b2: number;
|
9
|
-
readonly b3: number;
|
10
|
-
readonly c1: number;
|
11
|
-
readonly c2: number;
|
12
|
-
readonly c3: number;
|
13
|
-
private readonly rows;
|
14
|
-
constructor(a1: number, a2: number, a3: number, b1: number, b2: number, b3: number, c1: number, c2: number, c3: number);
|
15
|
-
static ofRows(r1: Vec3, r2: Vec3, r3: Vec3): Mat33;
|
16
|
-
static identity: Mat33;
|
17
|
-
inverse(): Mat33;
|
18
|
-
invertable(): boolean;
|
19
|
-
private cachedInverse;
|
20
|
-
private computeInverse;
|
21
|
-
transposed(): Mat33;
|
22
|
-
rightMul(other: Mat33): Mat33;
|
23
|
-
transformVec2(other: Vec3): Vec2;
|
24
|
-
transformVec3(other: Vec3): Vec3;
|
25
|
-
eq(other: Mat33, fuzz?: number): boolean;
|
26
|
-
toString(): string;
|
27
|
-
toArray(): number[];
|
28
|
-
static translation(amount: Vec2): Mat33;
|
29
|
-
static zRotation(radians: number, center?: Point2): Mat33;
|
30
|
-
static scaling2D(amount: number | Vec2, center?: Point2): Mat33;
|
31
|
-
static fromCSSMatrix(cssString: string): Mat33;
|
32
|
-
}
|
@@ -1,34 +0,0 @@
|
|
1
|
-
export default class Vec3 {
|
2
|
-
readonly x: number;
|
3
|
-
readonly y: number;
|
4
|
-
readonly z: number;
|
5
|
-
private constructor();
|
6
|
-
get xy(): {
|
7
|
-
x: number;
|
8
|
-
y: number;
|
9
|
-
};
|
10
|
-
static of(x: number, y: number, z: number): Vec3;
|
11
|
-
at(idx: number): number;
|
12
|
-
length(): number;
|
13
|
-
magnitude(): number;
|
14
|
-
magnitudeSquared(): number;
|
15
|
-
angle(): number;
|
16
|
-
normalized(): Vec3;
|
17
|
-
times(c: number): Vec3;
|
18
|
-
plus(v: Vec3): Vec3;
|
19
|
-
minus(v: Vec3): Vec3;
|
20
|
-
dot(other: Vec3): number;
|
21
|
-
cross(other: Vec3): Vec3;
|
22
|
-
orthog(): Vec3;
|
23
|
-
extend(distance: number, direction: Vec3): Vec3;
|
24
|
-
lerp(target: Vec3, fractionTo: number): Vec3;
|
25
|
-
zip(other: Vec3, zip: (componentInThis: number, componentInOther: number) => number): Vec3;
|
26
|
-
map(fn: (component: number) => number): Vec3;
|
27
|
-
asArray(): number[];
|
28
|
-
eq(other: Vec3, fuzz: number): boolean;
|
29
|
-
toString(): string;
|
30
|
-
static unitX: Vec3;
|
31
|
-
static unitY: Vec3;
|
32
|
-
static unitZ: Vec3;
|
33
|
-
static zero: Vec3;
|
34
|
-
}
|