js-draw 0.1.12 → 0.2.1
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/ISSUE_TEMPLATE/bug_report.md +34 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/.github/ISSUE_TEMPLATE/translation.md +96 -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 +9 -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 +129 -2
- package/dist/src/Editor.js +94 -17
- package/dist/src/EditorImage.d.ts +7 -2
- package/dist/src/EditorImage.js +41 -25
- package/dist/src/EventDispatcher.d.ts +18 -0
- package/dist/src/EventDispatcher.js +19 -4
- package/dist/src/Pointer.js +3 -2
- package/dist/src/UndoRedoHistory.js +15 -2
- package/dist/src/Viewport.js +4 -1
- package/dist/src/bundle/bundled.d.ts +1 -2
- package/dist/src/bundle/bundled.js +1 -2
- package/dist/src/commands/Duplicate.d.ts +1 -1
- package/dist/src/commands/Duplicate.js +3 -4
- package/dist/src/commands/Erase.d.ts +1 -1
- package/dist/src/commands/Erase.js +6 -5
- 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 +1 -0
- package/dist/src/commands/localization.js +1 -0
- package/dist/src/components/AbstractComponent.d.ts +13 -8
- package/dist/src/components/AbstractComponent.js +26 -15
- package/dist/src/components/SVGGlobalAttributesObject.d.ts +1 -1
- package/dist/src/components/SVGGlobalAttributesObject.js +7 -1
- package/dist/src/components/Stroke.d.ts +12 -2
- package/dist/src/components/Stroke.js +10 -7
- package/dist/src/components/Text.d.ts +2 -2
- package/dist/src/components/Text.js +6 -6
- package/dist/src/components/UnknownSVGObject.d.ts +1 -1
- package/dist/src/components/UnknownSVGObject.js +6 -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/localizations/de.d.ts +3 -0
- package/dist/src/localizations/de.js +4 -0
- package/dist/src/localizations/getLocalizationTable.js +2 -0
- package/dist/src/math/Mat33.d.ts +47 -1
- package/dist/src/math/Mat33.js +48 -20
- package/dist/src/math/Path.js +3 -3
- package/dist/src/math/Rect2.d.ts +2 -2
- package/dist/src/math/Vec3.d.ts +62 -0
- package/dist/src/math/Vec3.js +62 -14
- package/dist/src/math/lib.d.ts +7 -0
- package/dist/src/math/lib.js +7 -0
- package/dist/src/math/rounding.js +1 -0
- package/dist/src/rendering/Display.d.ts +44 -0
- package/dist/src/rendering/Display.js +45 -6
- package/dist/src/rendering/caching/CacheRecord.d.ts +1 -0
- package/dist/src/rendering/caching/CacheRecord.js +3 -0
- package/dist/src/rendering/caching/CacheRecordManager.d.ts +4 -3
- 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 +9 -10
- package/dist/src/rendering/caching/types.d.ts +1 -3
- package/dist/src/rendering/renderers/CanvasRenderer.js +1 -1
- package/dist/src/toolbar/HTMLToolbar.js +1 -0
- package/dist/src/toolbar/makeColorInput.js +1 -1
- package/dist/src/toolbar/widgets/PenWidget.js +1 -0
- 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.js +45 -22
- package/dist/src/types.d.ts +17 -6
- 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 +147 -25
- package/src/EditorImage.ts +45 -27
- package/src/EventDispatcher.ts +21 -6
- package/src/Pointer.ts +3 -2
- package/src/UndoRedoHistory.ts +18 -2
- package/src/Viewport.ts +5 -2
- package/src/bundle/bundled.ts +1 -2
- package/src/commands/Duplicate.ts +3 -4
- package/src/commands/Erase.ts +6 -5
- 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 +2 -0
- package/src/components/AbstractComponent.ts +31 -20
- package/src/components/SVGGlobalAttributesObject.ts +8 -1
- package/src/components/Stroke.test.ts +1 -1
- package/src/components/Stroke.ts +11 -7
- package/src/components/Text.ts +6 -7
- package/src/components/UnknownSVGObject.ts +7 -1
- package/src/components/lib.ts +9 -0
- package/src/lib.ts +28 -0
- package/src/localizations/de.ts +98 -0
- package/src/localizations/getLocalizationTable.ts +2 -0
- package/src/math/Mat33.ts +48 -20
- package/src/math/Path.ts +3 -3
- package/src/math/Rect2.ts +2 -2
- package/src/math/Vec3.ts +62 -14
- package/src/math/lib.ts +15 -0
- package/src/math/rounding.ts +2 -0
- package/src/rendering/Display.ts +46 -6
- package/src/rendering/caching/CacheRecord.test.ts +1 -1
- package/src/rendering/caching/CacheRecord.ts +4 -0
- package/src/rendering/caching/CacheRecordManager.ts +33 -7
- package/src/rendering/caching/RenderingCache.ts +10 -15
- package/src/rendering/caching/types.ts +1 -6
- package/src/rendering/renderers/CanvasRenderer.ts +1 -1
- package/src/styles.js +4 -0
- package/src/toolbar/HTMLToolbar.ts +1 -0
- package/src/toolbar/makeColorInput.ts +1 -1
- package/src/toolbar/toolbar.css +8 -2
- package/src/toolbar/widgets/PenWidget.ts +2 -0
- package/src/tools/PanZoom.ts +0 -1
- package/src/tools/Pen.ts +11 -2
- package/src/tools/PipetteTool.ts +2 -0
- package/src/tools/SelectionTool.ts +46 -18
- package/src/types.ts +19 -3
- package/tsconfig.json +4 -1
- package/typedoc.json +20 -0
package/src/rendering/Display.ts
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
/**
|
2
|
+
* Handles `HTMLCanvasElement`s (or other drawing surfaces if being used) used to display the editor's contents.
|
3
|
+
*
|
4
|
+
* @example
|
5
|
+
* ```
|
6
|
+
* const editor = new Editor(document.body);
|
7
|
+
* const w = editor.display.width;
|
8
|
+
* const h = editor.display.height;
|
9
|
+
* const center = Vec2.of(w / 2, h / 2);
|
10
|
+
* const colorAtCenter = editor.display.getColorAt(center);
|
11
|
+
* ```
|
12
|
+
*
|
13
|
+
* @packageDocumentation
|
14
|
+
*/
|
15
|
+
|
1
16
|
import AbstractRenderer from './renderers/AbstractRenderer';
|
2
17
|
import CanvasRenderer from './renderers/CanvasRenderer';
|
3
18
|
import { Editor } from '../Editor';
|
@@ -23,6 +38,7 @@ export default class Display {
|
|
23
38
|
private resizeSurfacesCallback?: ()=> void;
|
24
39
|
private flattenCallback?: ()=> void;
|
25
40
|
|
41
|
+
/** @internal */
|
26
42
|
public constructor(
|
27
43
|
private editor: Editor, mode: RenderingMode, private parent: HTMLElement|null
|
28
44
|
) {
|
@@ -60,7 +76,7 @@ export default class Display {
|
|
60
76
|
return this.dryInkRenderer.canRenderFromWithoutDataLoss(renderer);
|
61
77
|
},
|
62
78
|
blockResolution: cacheBlockResolution,
|
63
|
-
cacheSize: 500 * 500 * 4 *
|
79
|
+
cacheSize: 500 * 500 * 4 * 150,
|
64
80
|
maxScale: 1.5,
|
65
81
|
minComponentsPerCache: 45,
|
66
82
|
minComponentsToUseCache: 105,
|
@@ -75,9 +91,11 @@ export default class Display {
|
|
75
91
|
});
|
76
92
|
}
|
77
93
|
|
78
|
-
|
79
|
-
|
80
|
-
|
94
|
+
/**
|
95
|
+
* @returns the visible width of the display (e.g. how much
|
96
|
+
* space the display's element takes up in the x direction
|
97
|
+
* in the DOM).
|
98
|
+
*/
|
81
99
|
public get width(): number {
|
82
100
|
return this.dryInkRenderer.displaySize().x;
|
83
101
|
}
|
@@ -86,10 +104,15 @@ export default class Display {
|
|
86
104
|
return this.dryInkRenderer.displaySize().y;
|
87
105
|
}
|
88
106
|
|
107
|
+
/** @internal */
|
89
108
|
public getCache() {
|
90
109
|
return this.cache;
|
91
110
|
}
|
92
111
|
|
112
|
+
/**
|
113
|
+
* @returns the color at the given point on the dry ink renderer, or `null` if `screenPos`
|
114
|
+
* is not on the display.
|
115
|
+
*/
|
93
116
|
public getColorAt = (_screenPos: Point2): Color4|null => {
|
94
117
|
return null;
|
95
118
|
};
|
@@ -170,6 +193,10 @@ export default class Display {
|
|
170
193
|
this.editor.createHTMLOverlay(textRendererOutputContainer);
|
171
194
|
}
|
172
195
|
|
196
|
+
/**
|
197
|
+
* Rerenders the text-based display.
|
198
|
+
* The text-based display is intended for screen readers and can be navigated to by pressing `tab`.
|
199
|
+
*/
|
173
200
|
public rerenderAsText() {
|
174
201
|
this.textRenderer.clear();
|
175
202
|
this.editor.image.render(this.textRenderer, this.editor.viewport);
|
@@ -179,7 +206,11 @@ export default class Display {
|
|
179
206
|
}
|
180
207
|
}
|
181
208
|
|
182
|
-
|
209
|
+
/**
|
210
|
+
* Clears the drawing surfaces and otherwise prepares for a rerender.
|
211
|
+
*
|
212
|
+
* @returns the dry ink renderer.
|
213
|
+
*/
|
183
214
|
public startRerender(): AbstractRenderer {
|
184
215
|
this.resizeSurfacesCallback?.();
|
185
216
|
this.wetInkRenderer.clear();
|
@@ -188,19 +219,28 @@ export default class Display {
|
|
188
219
|
return this.dryInkRenderer;
|
189
220
|
}
|
190
221
|
|
222
|
+
/**
|
223
|
+
* If `draftMode`, the dry ink renderer is configured to render
|
224
|
+
* low-quality output.
|
225
|
+
*/
|
191
226
|
public setDraftMode(draftMode: boolean) {
|
192
227
|
this.dryInkRenderer.setDraftMode(draftMode);
|
193
228
|
}
|
194
229
|
|
230
|
+
/** @internal */
|
195
231
|
public getDryInkRenderer(): AbstractRenderer {
|
196
232
|
return this.dryInkRenderer;
|
197
233
|
}
|
198
234
|
|
235
|
+
/**
|
236
|
+
* @returns The renderer used for showing action previews (e.g. an unfinished stroke).
|
237
|
+
* The `wetInkRenderer`'s surface is stacked above the `dryInkRenderer`'s.
|
238
|
+
*/
|
199
239
|
public getWetInkRenderer(): AbstractRenderer {
|
200
240
|
return this.wetInkRenderer;
|
201
241
|
}
|
202
242
|
|
203
|
-
|
243
|
+
/** Re-renders the contents of the wetInkRenderer onto the dryInkRenderer. */
|
204
244
|
public flatten() {
|
205
245
|
this.flattenCallback?.();
|
206
246
|
}
|
@@ -11,6 +11,9 @@ export default class CacheRecord {
|
|
11
11
|
private lastUsedCycle: number;
|
12
12
|
private allocd: boolean = false;
|
13
13
|
|
14
|
+
// For debugging
|
15
|
+
public allocCount: number = 0;
|
16
|
+
|
14
17
|
public constructor(
|
15
18
|
private onBeforeDeallocCallback: BeforeDeallocCallback|null,
|
16
19
|
private cacheState: CacheState,
|
@@ -46,6 +49,7 @@ export default class CacheRecord {
|
|
46
49
|
this.allocd = true;
|
47
50
|
this.onBeforeDeallocCallback = newDeallocCallback;
|
48
51
|
this.lastUsedCycle = this.cacheState.currentRenderingCycle;
|
52
|
+
this.allocCount ++;
|
49
53
|
}
|
50
54
|
|
51
55
|
public getLastUsedCycle(): number {
|
@@ -1,38 +1,64 @@
|
|
1
|
-
import { BeforeDeallocCallback,
|
1
|
+
import { BeforeDeallocCallback, CacheProps, CacheState } from './types';
|
2
2
|
import CacheRecord from './CacheRecord';
|
3
3
|
import Rect2 from '../../math/Rect2';
|
4
4
|
|
5
|
+
const debugMode = false;
|
5
6
|
|
6
7
|
export class CacheRecordManager {
|
7
8
|
// Fixed-size array: Cache blocks are assigned indicies into [cachedCanvases].
|
8
9
|
private cacheRecords: CacheRecord[] = [];
|
9
10
|
private maxCanvases: number;
|
11
|
+
private cacheState: CacheState;
|
10
12
|
|
11
|
-
public constructor(
|
12
|
-
const cacheProps = cacheState.props;
|
13
|
+
public constructor(cacheProps: CacheProps) {
|
13
14
|
this.maxCanvases = Math.ceil(
|
14
15
|
// Assuming four components per pixel:
|
15
16
|
cacheProps.cacheSize / 4 / cacheProps.blockResolution.x / cacheProps.blockResolution.y
|
16
17
|
);
|
17
18
|
}
|
18
19
|
|
20
|
+
public setSharedState(state: CacheState) {
|
21
|
+
this.cacheState = state;
|
22
|
+
}
|
23
|
+
|
19
24
|
public allocCanvas(drawTo: Rect2, onDealloc: BeforeDeallocCallback): CacheRecord {
|
20
25
|
if (this.cacheRecords.length < this.maxCanvases) {
|
21
26
|
const record: CacheRecord = new CacheRecord(
|
22
27
|
onDealloc,
|
23
|
-
|
24
|
-
...this.cacheState,
|
25
|
-
recordManager: this,
|
26
|
-
},
|
28
|
+
this.cacheState,
|
27
29
|
);
|
28
30
|
record.setRenderingRegion(drawTo);
|
29
31
|
this.cacheRecords.push(record);
|
30
32
|
|
33
|
+
if (debugMode) {
|
34
|
+
console.log('[Cache] Cache spaces used: ', this.cacheRecords.length, ' of ', this.maxCanvases);
|
35
|
+
}
|
36
|
+
|
31
37
|
return record;
|
32
38
|
} else {
|
33
39
|
const lru = this.getLeastRecentlyUsedRecord()!;
|
40
|
+
|
41
|
+
if (debugMode) {
|
42
|
+
console.log(
|
43
|
+
'[Cache] Re-alloc. Times allocated: ', lru.allocCount,
|
44
|
+
'\nLast used cycle: ', lru.getLastUsedCycle(),
|
45
|
+
'\nCurrent cycle: ', this.cacheState.currentRenderingCycle
|
46
|
+
);
|
47
|
+
}
|
48
|
+
|
34
49
|
lru.realloc(onDealloc);
|
35
50
|
lru.setRenderingRegion(drawTo);
|
51
|
+
|
52
|
+
if (debugMode) {
|
53
|
+
console.log(
|
54
|
+
'[Cache] Now re-alloc\'d. Last used cycle: ', lru.getLastUsedCycle()
|
55
|
+
);
|
56
|
+
console.assert(
|
57
|
+
lru['cacheState'] === this.cacheState,
|
58
|
+
'[Cache] Unequal cache states! cacheState should be a shared object!'
|
59
|
+
);
|
60
|
+
}
|
61
|
+
|
36
62
|
return lru;
|
37
63
|
}
|
38
64
|
}
|
@@ -4,46 +4,41 @@ import Viewport from '../../Viewport';
|
|
4
4
|
import AbstractRenderer from '../renderers/AbstractRenderer';
|
5
5
|
import RenderingCacheNode from './RenderingCacheNode';
|
6
6
|
import { CacheRecordManager } from './CacheRecordManager';
|
7
|
-
import { CacheProps, CacheState
|
7
|
+
import { CacheProps, CacheState } from './types';
|
8
8
|
|
9
9
|
export default class RenderingCache {
|
10
|
-
private
|
10
|
+
private sharedState: CacheState;
|
11
11
|
private recordManager: CacheRecordManager;
|
12
12
|
private rootNode: RenderingCacheNode|null;
|
13
13
|
|
14
14
|
public constructor(cacheProps: CacheProps) {
|
15
|
-
this.
|
15
|
+
this.recordManager = new CacheRecordManager(cacheProps);
|
16
|
+
this.sharedState = {
|
16
17
|
props: cacheProps,
|
17
18
|
currentRenderingCycle: 0,
|
18
|
-
};
|
19
|
-
this.recordManager = new CacheRecordManager(this.partialSharedState);
|
20
|
-
}
|
21
|
-
|
22
|
-
public getSharedState(): CacheState {
|
23
|
-
return {
|
24
|
-
...this.partialSharedState,
|
25
19
|
recordManager: this.recordManager,
|
26
20
|
};
|
21
|
+
this.recordManager.setSharedState(this.sharedState);
|
27
22
|
}
|
28
23
|
|
29
24
|
public render(screenRenderer: AbstractRenderer, image: ImageNode, viewport: Viewport) {
|
30
25
|
const visibleRect = viewport.visibleRect;
|
31
|
-
this.
|
26
|
+
this.sharedState.currentRenderingCycle ++;
|
32
27
|
|
33
28
|
// If we can't use the cache,
|
34
|
-
if (!this.
|
29
|
+
if (!this.sharedState.props.isOfCorrectType(screenRenderer)) {
|
35
30
|
image.render(screenRenderer, visibleRect);
|
36
31
|
return;
|
37
32
|
}
|
38
33
|
|
39
34
|
if (!this.rootNode) {
|
40
35
|
// Adjust the node so that it has the correct aspect ratio
|
41
|
-
const res = this.
|
36
|
+
const res = this.sharedState.props.blockResolution;
|
42
37
|
|
43
38
|
const topLeft = visibleRect.topLeft;
|
44
39
|
this.rootNode = new RenderingCacheNode(
|
45
40
|
new Rect2(topLeft.x, topLeft.y, res.x, res.y),
|
46
|
-
this.
|
41
|
+
this.sharedState
|
47
42
|
);
|
48
43
|
}
|
49
44
|
|
@@ -54,7 +49,7 @@ export default class RenderingCache {
|
|
54
49
|
this.rootNode = this.rootNode!.smallestChildContaining(visibleRect) ?? this.rootNode;
|
55
50
|
|
56
51
|
const visibleLeaves = image.getLeavesIntersectingRegion(viewport.visibleRect, rect => screenRenderer.isTooSmallToRender(rect));
|
57
|
-
if (visibleLeaves.length > this.
|
52
|
+
if (visibleLeaves.length > this.sharedState.props.minComponentsToUseCache) {
|
58
53
|
this.rootNode!.renderItems(screenRenderer, [ image ], viewport);
|
59
54
|
} else {
|
60
55
|
image.render(screenRenderer, visibleRect);
|
@@ -27,13 +27,8 @@ export interface CacheProps {
|
|
27
27
|
minComponentsToUseCache: number;
|
28
28
|
}
|
29
29
|
|
30
|
-
|
31
|
-
// we need to separate partial/non-partial state.
|
32
|
-
export interface PartialCacheState {
|
30
|
+
export interface CacheState {
|
33
31
|
currentRenderingCycle: number;
|
34
32
|
props: CacheProps;
|
35
|
-
}
|
36
|
-
|
37
|
-
export interface CacheState extends PartialCacheState {
|
38
33
|
recordManager: CacheRecordManager;
|
39
34
|
}
|
@@ -64,7 +64,7 @@ export default class CanvasRenderer extends AbstractRenderer {
|
|
64
64
|
this.minRenderSizeAnyDimen = 2;
|
65
65
|
} else {
|
66
66
|
this.minSquareCurveApproxDist = 1;
|
67
|
-
this.minRenderSizeBothDimens =
|
67
|
+
this.minRenderSizeBothDimens = 0.5;
|
68
68
|
this.minRenderSizeAnyDimen = 0;
|
69
69
|
}
|
70
70
|
}
|
package/src/styles.js
CHANGED
@@ -8,7 +8,7 @@ import { makePipetteIcon } from './icons';
|
|
8
8
|
type OnColorChangeListener = (color: Color4)=>void;
|
9
9
|
|
10
10
|
|
11
|
-
// Returns [ input, container ].
|
11
|
+
// Returns [ color input, input container ].
|
12
12
|
export const makeColorInput = (editor: Editor, onColorChange: OnColorChangeListener): [ HTMLInputElement, HTMLElement ] => {
|
13
13
|
const colorInputContainer = document.createElement('span');
|
14
14
|
const colorInput = document.createElement('input');
|
package/src/toolbar/toolbar.css
CHANGED
@@ -24,6 +24,7 @@
|
|
24
24
|
.toolbar-root > .toolbar-toolContainer > * > button,
|
25
25
|
.toolbar-root > .toolbar-buttonGroup > button {
|
26
26
|
width: min-content;
|
27
|
+
white-space: pre;
|
27
28
|
height: min(20vh, 60px);
|
28
29
|
}
|
29
30
|
|
@@ -64,8 +65,13 @@
|
|
64
65
|
margin-right: 3px;
|
65
66
|
|
66
67
|
min-width: 40px;
|
67
|
-
|
68
|
-
font-size:
|
68
|
+
width: min-content;
|
69
|
+
font-size: 1em;
|
70
|
+
}
|
71
|
+
|
72
|
+
.toolbar-dropdown > .toolbar-toolContainer > button,
|
73
|
+
.toolbar-dropdown > .toolbar-toolContainer > .toolbar-button {
|
74
|
+
width: 6em;
|
69
75
|
}
|
70
76
|
|
71
77
|
.toolbar-button:hover, .toolbar-root button:not(:disabled):hover {
|
@@ -1,3 +1,5 @@
|
|
1
|
+
// @internal @packageDocumentation
|
2
|
+
|
1
3
|
import { makeArrowBuilder } from '../../components/builders/ArrowBuilder';
|
2
4
|
import { makeFreehandLineBuilder } from '../../components/builders/FreehandLineBuilder';
|
3
5
|
import { makeLineBuilder } from '../../components/builders/LineBuilder';
|
package/src/tools/PanZoom.ts
CHANGED
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
@@ -12,6 +12,7 @@ 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,39 +277,65 @@ 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
341
|
public description(_editor: Editor, localizationTable: EditorLocalization) {
|
package/src/types.ts
CHANGED
@@ -9,6 +9,7 @@ import AbstractComponent from './components/AbstractComponent';
|
|
9
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
|
+
}
|