js-draw 0.12.0 → 0.13.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/CHANGELOG.md +9 -0
- package/dist/bundle.js +1 -1
- package/dist/src/Color4.d.ts +12 -0
- package/dist/src/Color4.js +16 -0
- package/dist/src/Editor.d.ts +33 -18
- package/dist/src/Editor.js +22 -19
- package/dist/src/EditorImage.d.ts +12 -0
- package/dist/src/EditorImage.js +12 -0
- package/dist/src/Pointer.d.ts +1 -0
- package/dist/src/Pointer.js +8 -0
- package/dist/src/SVGLoader.d.ts +5 -0
- package/dist/src/SVGLoader.js +6 -1
- package/dist/src/Viewport.d.ts +30 -1
- package/dist/src/Viewport.js +39 -9
- package/dist/src/commands/invertCommand.js +1 -1
- package/dist/src/components/AbstractComponent.d.ts +19 -0
- package/dist/src/components/AbstractComponent.js +17 -2
- package/dist/src/lib.d.ts +6 -3
- package/dist/src/lib.js +4 -1
- package/dist/src/math/Mat33.d.ts +1 -1
- package/dist/src/math/Mat33.js +1 -1
- package/dist/src/rendering/Display.d.ts +9 -11
- package/dist/src/rendering/Display.js +12 -14
- package/dist/src/rendering/lib.d.ts +3 -0
- package/dist/src/rendering/lib.js +3 -0
- package/dist/src/rendering/renderers/DummyRenderer.js +2 -2
- package/dist/src/rendering/renderers/SVGRenderer.js +4 -0
- package/dist/src/toolbar/IconProvider.d.ts +1 -1
- package/dist/src/toolbar/IconProvider.js +90 -29
- package/dist/src/tools/PanZoom.js +1 -1
- package/dist/src/tools/PasteHandler.d.ts +11 -4
- package/dist/src/tools/PasteHandler.js +12 -5
- package/dist/src/tools/Pen.d.ts +7 -2
- package/dist/src/tools/Pen.js +39 -6
- package/dist/src/tools/SelectionTool/SelectionHandle.d.ts +3 -0
- package/dist/src/tools/SelectionTool/SelectionHandle.js +6 -0
- package/dist/src/tools/SelectionTool/SelectionTool.d.ts +3 -1
- package/dist/src/tools/SelectionTool/SelectionTool.js +53 -15
- package/dist/src/tools/ToolSwitcherShortcut.d.ts +8 -0
- package/dist/src/tools/ToolSwitcherShortcut.js +9 -3
- package/dist/src/tools/UndoRedoShortcut.js +2 -4
- package/package.json +2 -2
- package/src/Color4.test.ts +11 -0
- package/src/Color4.ts +22 -0
- package/src/Editor.ts +36 -22
- package/src/EditorImage.ts +12 -0
- package/src/Pointer.ts +19 -0
- package/src/SVGLoader.ts +6 -1
- package/src/Viewport.ts +50 -11
- package/src/commands/invertCommand.ts +1 -1
- package/src/components/AbstractComponent.ts +33 -2
- package/src/lib.ts +6 -3
- package/src/math/Mat33.ts +1 -1
- package/src/rendering/Display.ts +12 -15
- package/src/rendering/RenderingStyle.ts +1 -1
- package/src/rendering/lib.ts +4 -0
- package/src/rendering/renderers/DummyRenderer.ts +2 -3
- package/src/rendering/renderers/SVGRenderer.ts +4 -0
- package/src/rendering/renderers/TextOnlyRenderer.ts +0 -1
- package/src/toolbar/HTMLToolbar.ts +1 -1
- package/src/toolbar/IconProvider.ts +98 -31
- package/src/tools/PanZoom.ts +1 -1
- package/src/tools/PasteHandler.ts +12 -6
- package/src/tools/Pen.test.ts +44 -1
- package/src/tools/Pen.ts +53 -8
- package/src/tools/SelectionTool/SelectionHandle.ts +9 -0
- package/src/tools/SelectionTool/SelectionTool.ts +67 -15
- package/src/tools/ToolSwitcherShortcut.ts +10 -5
- package/src/tools/UndoRedoShortcut.ts +2 -5
- package/typedoc.json +2 -2
package/dist/src/Color4.d.ts
CHANGED
@@ -20,6 +20,18 @@ export default class Color4 {
|
|
20
20
|
static fromString(text: string): Color4;
|
21
21
|
/** @returns true if `this` and `other` are approximately equal. */
|
22
22
|
eq(other: Color4 | null | undefined): boolean;
|
23
|
+
/**
|
24
|
+
* If `fractionTo` is not in the range [0, 1], it will be clamped to the nearest number
|
25
|
+
* in that range. For example, `a.mix(b, -1)` is equivalent to `a.mix(b, 0)`.
|
26
|
+
*
|
27
|
+
* @returns a color `fractionTo` of the way from this color to `other`.
|
28
|
+
*
|
29
|
+
* @example
|
30
|
+
* ```ts
|
31
|
+
* Color4.ofRGB(1, 0, 0).mix(Color4.ofRGB(0, 1, 0), 0.1) // -> Color4(0.9, 0.1, 0)
|
32
|
+
* ```
|
33
|
+
*/
|
34
|
+
mix(other: Color4, fractionTo: number): Color4;
|
23
35
|
private hexString;
|
24
36
|
/**
|
25
37
|
* @returns a hexadecimal color string representation of `this`, in the form `#rrggbbaa`.
|
package/dist/src/Color4.js
CHANGED
@@ -105,6 +105,22 @@ export default class Color4 {
|
|
105
105
|
}
|
106
106
|
return this.toHexString() === other.toHexString();
|
107
107
|
}
|
108
|
+
/**
|
109
|
+
* If `fractionTo` is not in the range [0, 1], it will be clamped to the nearest number
|
110
|
+
* in that range. For example, `a.mix(b, -1)` is equivalent to `a.mix(b, 0)`.
|
111
|
+
*
|
112
|
+
* @returns a color `fractionTo` of the way from this color to `other`.
|
113
|
+
*
|
114
|
+
* @example
|
115
|
+
* ```ts
|
116
|
+
* Color4.ofRGB(1, 0, 0).mix(Color4.ofRGB(0, 1, 0), 0.1) // -> Color4(0.9, 0.1, 0)
|
117
|
+
* ```
|
118
|
+
*/
|
119
|
+
mix(other, fractionTo) {
|
120
|
+
fractionTo = Math.min(Math.max(fractionTo, 0), 1);
|
121
|
+
const fractionOfThis = 1 - fractionTo;
|
122
|
+
return new Color4(this.r * fractionOfThis + other.r * fractionTo, this.g * fractionOfThis + other.g * fractionTo, this.b * fractionOfThis + other.b * fractionTo, this.a * fractionOfThis + other.a * fractionTo);
|
123
|
+
}
|
108
124
|
/**
|
109
125
|
* @returns a hexadecimal color string representation of `this`, in the form `#rrggbbaa`.
|
110
126
|
*
|
package/dist/src/Editor.d.ts
CHANGED
@@ -1,20 +1,3 @@
|
|
1
|
-
/**
|
2
|
-
* The main entrypoint for the full editor.
|
3
|
-
*
|
4
|
-
* @example
|
5
|
-
* To create an editor with a toolbar,
|
6
|
-
* ```
|
7
|
-
* const editor = new Editor(document.body);
|
8
|
-
*
|
9
|
-
* const toolbar = editor.addToolbar();
|
10
|
-
* toolbar.addActionButton('Save', () => {
|
11
|
-
* const saveData = editor.toSVG().outerHTML;
|
12
|
-
* // Do something with saveData...
|
13
|
-
* });
|
14
|
-
* ```
|
15
|
-
*
|
16
|
-
* @packageDocumentation
|
17
|
-
*/
|
18
1
|
import EditorImage from './EditorImage';
|
19
2
|
import ToolController from './tools/ToolController';
|
20
3
|
import { InputEvtType, EditorNotifier, ImageLoader } from './types';
|
@@ -48,9 +31,25 @@ export interface EditorSettings {
|
|
48
31
|
maxZoom: number;
|
49
32
|
iconProvider: IconProvider;
|
50
33
|
}
|
34
|
+
/**
|
35
|
+
* The main entrypoint for the full editor.
|
36
|
+
*
|
37
|
+
* @example
|
38
|
+
* To create an editor with a toolbar,
|
39
|
+
* ```
|
40
|
+
* const editor = new Editor(document.body);
|
41
|
+
*
|
42
|
+
* const toolbar = editor.addToolbar();
|
43
|
+
* toolbar.addActionButton('Save', () => {
|
44
|
+
* const saveData = editor.toSVG().outerHTML;
|
45
|
+
* // Do something with saveData...
|
46
|
+
* });
|
47
|
+
* ```
|
48
|
+
*/
|
51
49
|
export declare class Editor {
|
52
50
|
private container;
|
53
51
|
private renderingRegion;
|
52
|
+
/** Manages drawing surfaces/{@link lib!AbstractRenderer}s. */
|
54
53
|
display: Display;
|
55
54
|
/**
|
56
55
|
* Handles undo/redo.
|
@@ -87,10 +86,20 @@ export declare class Editor {
|
|
87
86
|
readonly image: EditorImage;
|
88
87
|
/** Viewport for the exported/imported image. */
|
89
88
|
private importExportViewport;
|
89
|
+
/**
|
90
|
+
* Allows transforming the view and querying information about
|
91
|
+
* what is currently visible.
|
92
|
+
*/
|
93
|
+
readonly viewport: Viewport;
|
90
94
|
/** @internal */
|
91
95
|
readonly localization: EditorLocalization;
|
96
|
+
/** {@link lib!EditorSettings.iconProvider} */
|
92
97
|
readonly icons: IconProvider;
|
93
|
-
|
98
|
+
/**
|
99
|
+
* Controls the list of tools. See
|
100
|
+
* [the custom tool example](https://github.com/personalizedrefrigerator/js-draw/tree/main/docs/example-custom-tools)
|
101
|
+
* for more.
|
102
|
+
*/
|
94
103
|
readonly toolController: ToolController;
|
95
104
|
/**
|
96
105
|
* Global event dispatcher/subscriber.
|
@@ -196,7 +205,13 @@ export declare class Editor {
|
|
196
205
|
*/
|
197
206
|
queueRerender(): Promise<void>;
|
198
207
|
rerender(showImageBounds?: boolean): void;
|
208
|
+
/**
|
209
|
+
* @see {@link Display.getWetInkRenderer} {@link Display.flatten}
|
210
|
+
*/
|
199
211
|
drawWetInk(...path: RenderablePathSpec[]): void;
|
212
|
+
/**
|
213
|
+
* @see {@link Display.getWetInkRenderer}
|
214
|
+
*/
|
200
215
|
clearWetInk(): void;
|
201
216
|
focus(): void;
|
202
217
|
createHTMLOverlay(overlay: HTMLElement): {
|
package/dist/src/Editor.js
CHANGED
@@ -1,20 +1,3 @@
|
|
1
|
-
/**
|
2
|
-
* The main entrypoint for the full editor.
|
3
|
-
*
|
4
|
-
* @example
|
5
|
-
* To create an editor with a toolbar,
|
6
|
-
* ```
|
7
|
-
* const editor = new Editor(document.body);
|
8
|
-
*
|
9
|
-
* const toolbar = editor.addToolbar();
|
10
|
-
* toolbar.addActionButton('Save', () => {
|
11
|
-
* const saveData = editor.toSVG().outerHTML;
|
12
|
-
* // Do something with saveData...
|
13
|
-
* });
|
14
|
-
* ```
|
15
|
-
*
|
16
|
-
* @packageDocumentation
|
17
|
-
*/
|
18
1
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
19
2
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
20
3
|
return new (P || (P = Promise))(function (resolve, reject) {
|
@@ -48,7 +31,21 @@ import untilNextAnimationFrame from './util/untilNextAnimationFrame';
|
|
48
31
|
import fileToBase64 from './util/fileToBase64';
|
49
32
|
import uniteCommands from './commands/uniteCommands';
|
50
33
|
import SelectionTool from './tools/SelectionTool/SelectionTool';
|
51
|
-
|
34
|
+
/**
|
35
|
+
* The main entrypoint for the full editor.
|
36
|
+
*
|
37
|
+
* @example
|
38
|
+
* To create an editor with a toolbar,
|
39
|
+
* ```
|
40
|
+
* const editor = new Editor(document.body);
|
41
|
+
*
|
42
|
+
* const toolbar = editor.addToolbar();
|
43
|
+
* toolbar.addActionButton('Save', () => {
|
44
|
+
* const saveData = editor.toSVG().outerHTML;
|
45
|
+
* // Do something with saveData...
|
46
|
+
* });
|
47
|
+
* ```
|
48
|
+
*/
|
52
49
|
export class Editor {
|
53
50
|
/**
|
54
51
|
* @example
|
@@ -582,11 +579,17 @@ export class Editor {
|
|
582
579
|
this.nextRerenderListeners.forEach(listener => listener());
|
583
580
|
this.nextRerenderListeners = [];
|
584
581
|
}
|
582
|
+
/**
|
583
|
+
* @see {@link Display.getWetInkRenderer} {@link Display.flatten}
|
584
|
+
*/
|
585
585
|
drawWetInk(...path) {
|
586
586
|
for (const part of path) {
|
587
587
|
this.display.getWetInkRenderer().drawPath(part);
|
588
588
|
}
|
589
589
|
}
|
590
|
+
/**
|
591
|
+
* @see {@link Display.getWetInkRenderer}
|
592
|
+
*/
|
590
593
|
clearWetInk() {
|
591
594
|
this.display.getWetInkRenderer().clear();
|
592
595
|
}
|
@@ -681,7 +684,7 @@ export class Editor {
|
|
681
684
|
// The export resolution is the same as the size of the drawing canvas.
|
682
685
|
toDataURL(format = 'image/png') {
|
683
686
|
const canvas = document.createElement('canvas');
|
684
|
-
const resolution = this.importExportViewport.
|
687
|
+
const resolution = this.importExportViewport.getScreenRectSize();
|
685
688
|
canvas.width = resolution.x;
|
686
689
|
canvas.height = resolution.y;
|
687
690
|
const ctx = canvas.getContext('2d');
|
@@ -22,8 +22,20 @@ export default class EditorImage {
|
|
22
22
|
getElementsIntersectingRegion(region: Rect2): AbstractComponent[];
|
23
23
|
/** @internal */
|
24
24
|
onDestroyElement(elem: AbstractComponent): void;
|
25
|
+
/**
|
26
|
+
* @returns the AbstractComponent with `id`, if it exists.
|
27
|
+
*
|
28
|
+
* @see {@link AbstractComponent.getId}
|
29
|
+
*/
|
25
30
|
lookupElement(id: string): AbstractComponent | null;
|
26
31
|
private addElementDirectly;
|
32
|
+
/**
|
33
|
+
* Returns a command that adds the given element to the `EditorImage`.
|
34
|
+
* If `applyByFlattening` is true, the content of the wet ink renderer is
|
35
|
+
* rendered onto the main rendering canvas instead of doing a full re-render.
|
36
|
+
*
|
37
|
+
* @see {@link Display.flatten}
|
38
|
+
*/
|
27
39
|
static addElement(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand;
|
28
40
|
private static AddElementCommand;
|
29
41
|
}
|
package/dist/src/EditorImage.js
CHANGED
@@ -55,6 +55,11 @@ export default class EditorImage {
|
|
55
55
|
onDestroyElement(elem) {
|
56
56
|
delete this.componentsById[elem.getId()];
|
57
57
|
}
|
58
|
+
/**
|
59
|
+
* @returns the AbstractComponent with `id`, if it exists.
|
60
|
+
*
|
61
|
+
* @see {@link AbstractComponent.getId}
|
62
|
+
*/
|
58
63
|
lookupElement(id) {
|
59
64
|
var _a;
|
60
65
|
return (_a = this.componentsById[id]) !== null && _a !== void 0 ? _a : null;
|
@@ -63,6 +68,13 @@ export default class EditorImage {
|
|
63
68
|
this.componentsById[elem.getId()] = elem;
|
64
69
|
return this.root.addLeaf(elem);
|
65
70
|
}
|
71
|
+
/**
|
72
|
+
* Returns a command that adds the given element to the `EditorImage`.
|
73
|
+
* If `applyByFlattening` is true, the content of the wet ink renderer is
|
74
|
+
* rendered onto the main rendering canvas instead of doing a full re-render.
|
75
|
+
*
|
76
|
+
* @see {@link Display.flatten}
|
77
|
+
*/
|
66
78
|
static addElement(elem, applyByFlattening = false) {
|
67
79
|
return new EditorImage.AddElementCommand(elem, applyByFlattening);
|
68
80
|
}
|
package/dist/src/Pointer.d.ts
CHANGED
@@ -18,6 +18,7 @@ export default class Pointer {
|
|
18
18
|
readonly id: number;
|
19
19
|
readonly timeStamp: number;
|
20
20
|
private constructor();
|
21
|
+
snappedToGrid(viewport: Viewport): Pointer;
|
21
22
|
static ofEvent(evt: PointerEvent, isDown: boolean, viewport: Viewport, relativeTo?: HTMLElement): Pointer;
|
22
23
|
static ofCanvasPoint(canvasPos: Point2, isDown: boolean, viewport: Viewport, id?: number, device?: PointerDevice, isPrimary?: boolean, pressure?: number | null): Pointer;
|
23
24
|
}
|
package/dist/src/Pointer.js
CHANGED
@@ -31,6 +31,14 @@ export default class Pointer {
|
|
31
31
|
this.id = id;
|
32
32
|
this.timeStamp = timeStamp;
|
33
33
|
}
|
34
|
+
// Snaps this pointer to the nearest grid point (rounds the coordinates of this
|
35
|
+
// pointer based on the current zoom). Returns a new Pointer and does not modify
|
36
|
+
// this.
|
37
|
+
snappedToGrid(viewport) {
|
38
|
+
const snappedCanvasPos = viewport.snapToGrid(this.canvasPos);
|
39
|
+
const snappedScreenPos = viewport.canvasToScreen(snappedCanvasPos);
|
40
|
+
return new Pointer(snappedScreenPos, snappedCanvasPos, this.pressure, this.isPrimary, this.down, this.device, this.id, this.timeStamp);
|
41
|
+
}
|
34
42
|
// Creates a Pointer from a DOM event. If `relativeTo` is given, (0, 0) in screen coordinates is
|
35
43
|
// considered the top left of `relativeTo`.
|
36
44
|
static ofEvent(evt, isDown, viewport, relativeTo) {
|
package/dist/src/SVGLoader.d.ts
CHANGED
@@ -34,5 +34,10 @@ export default class SVGLoader implements ImageLoader {
|
|
34
34
|
private visit;
|
35
35
|
private getSourceAttrs;
|
36
36
|
start(onAddComponent: ComponentAddedListener, onProgress: OnProgressListener, onDetermineExportRect?: OnDetermineExportRectListener | null): Promise<void>;
|
37
|
+
/**
|
38
|
+
* @see {@link Editor.loadFrom}
|
39
|
+
* @param text - Textual representation of the SVG (e.g. `<svg viewbox='...'>...</svg>`).
|
40
|
+
* @param sanitize - if `true`, don't store unknown attributes.
|
41
|
+
*/
|
37
42
|
static fromString(text: string, sanitize?: boolean): SVGLoader;
|
38
43
|
}
|
package/dist/src/SVGLoader.js
CHANGED
@@ -23,6 +23,7 @@ export const defaultSVGViewRect = new Rect2(0, 0, 500, 500);
|
|
23
23
|
export const svgAttributesDataKey = 'svgAttrs';
|
24
24
|
export const svgStyleAttributesDataKey = 'svgStyleAttrs';
|
25
25
|
const supportedStrokeFillStyleAttrs = ['stroke', 'fill', 'stroke-width'];
|
26
|
+
// Handles loading images from SVG.
|
26
27
|
export default class SVGLoader {
|
27
28
|
constructor(source, onFinish, storeUnknown = true) {
|
28
29
|
this.source = source;
|
@@ -359,7 +360,11 @@ export default class SVGLoader {
|
|
359
360
|
(_b = this.onFinish) === null || _b === void 0 ? void 0 : _b.call(this);
|
360
361
|
});
|
361
362
|
}
|
362
|
-
|
363
|
+
/**
|
364
|
+
* @see {@link Editor.loadFrom}
|
365
|
+
* @param text - Textual representation of the SVG (e.g. `<svg viewbox='...'>...</svg>`).
|
366
|
+
* @param sanitize - if `true`, don't store unknown attributes.
|
367
|
+
*/
|
363
368
|
static fromString(text, sanitize = false) {
|
364
369
|
var _a, _b;
|
365
370
|
const sandbox = document.createElement('iframe');
|
package/dist/src/Viewport.d.ts
CHANGED
@@ -2,6 +2,7 @@ import Command from './commands/Command';
|
|
2
2
|
import Mat33 from './math/Mat33';
|
3
3
|
import Rect2 from './math/Rect2';
|
4
4
|
import { Point2, Vec2 } from './math/Vec2';
|
5
|
+
import Vec3 from './math/Vec3';
|
5
6
|
import { StrokeDataPoint } from './types';
|
6
7
|
import { EditorNotifier } from './types';
|
7
8
|
type PointDataType<T extends Point2 | StrokeDataPoint | number> = T extends Point2 ? Point2 : number;
|
@@ -16,17 +17,45 @@ export declare class Viewport {
|
|
16
17
|
private screenRect;
|
17
18
|
constructor(notifier: EditorNotifier);
|
18
19
|
updateScreenSize(screenSize: Vec2): void;
|
20
|
+
/** Get the screen's visible region transformed into canvas space. */
|
19
21
|
get visibleRect(): Rect2;
|
22
|
+
/** @returns the given point, but in canvas coordinates */
|
20
23
|
screenToCanvas(screenPoint: Point2): Point2;
|
24
|
+
/** @returns the given point transformed into screen coordinates. */
|
21
25
|
canvasToScreen(canvasPoint: Point2): Point2;
|
26
|
+
/** @returns a command that transforms the canvas by `transform`. */
|
22
27
|
static transformBy(transform: Mat33): ViewportTransform;
|
28
|
+
/**
|
29
|
+
* Updates the transformation directly. Using `transformBy` is preferred.
|
30
|
+
* @param newTransform - should map from canvas coordinates to screen coordinates.
|
31
|
+
*/
|
23
32
|
resetTransform(newTransform?: Mat33): void;
|
24
33
|
get screenToCanvasTransform(): Mat33;
|
25
34
|
get canvasToScreenTransform(): Mat33;
|
26
|
-
|
35
|
+
/** @returns the size of the visible region in pixels. */
|
36
|
+
getScreenRectSize(): Vec2;
|
37
|
+
/** Alias for `getScreenRectSize`. @deprecated */
|
38
|
+
getResolution(): Vec3;
|
39
|
+
/** @returns the amount a vector on the canvas is scaled to become a vector on the screen. */
|
27
40
|
getScaleFactor(): number;
|
41
|
+
/**
|
42
|
+
* @returns `getScaleFactor()` rounded to the nearest power of 10.
|
43
|
+
* For example, if `getScaleFactor()` returns 101, `getScaleFactorToNearestPowerOfTen()`
|
44
|
+
* should return `100` because `100` is the nearest power of 10 to 101.
|
45
|
+
*/
|
46
|
+
getScaleFactorToNearestPowerOfTen(): number;
|
47
|
+
snapToGrid(canvasPos: Point2): Vec3;
|
48
|
+
/** Returns the size of one screen pixel in canvas units. */
|
28
49
|
getSizeOfPixelOnCanvas(): number;
|
50
|
+
/**
|
51
|
+
* @returns the angle of the canvas in radians.
|
52
|
+
* This is the angle by which the canvas is rotated relative to the screen.
|
53
|
+
*/
|
29
54
|
getRotationAngle(): number;
|
55
|
+
/**
|
56
|
+
* Rounds the given `point` to a multiple of 10 such that it is within `tolerance` of
|
57
|
+
* its original location. This is useful for preparing data for base-10 conversion.
|
58
|
+
*/
|
30
59
|
static roundPoint<T extends Point2 | number>(point: T, tolerance: number): PointDataType<T>;
|
31
60
|
roundPoint(point: Point2): Point2;
|
32
61
|
static roundScaleRatio(scaleRatio: number, roundAmount?: number): number;
|
package/dist/src/Viewport.js
CHANGED
@@ -29,22 +29,26 @@ export class Viewport {
|
|
29
29
|
updateScreenSize(screenSize) {
|
30
30
|
this.screenRect = this.screenRect.resizedTo(screenSize);
|
31
31
|
}
|
32
|
-
|
32
|
+
/** Get the screen's visible region transformed into canvas space. */
|
33
33
|
get visibleRect() {
|
34
34
|
return this.screenRect.transformedBoundingBox(this.inverseTransform);
|
35
35
|
}
|
36
|
-
|
36
|
+
/** @returns the given point, but in canvas coordinates */
|
37
37
|
screenToCanvas(screenPoint) {
|
38
38
|
return this.inverseTransform.transformVec2(screenPoint);
|
39
39
|
}
|
40
|
+
/** @returns the given point transformed into screen coordinates. */
|
40
41
|
canvasToScreen(canvasPoint) {
|
41
42
|
return this.transform.transformVec2(canvasPoint);
|
42
43
|
}
|
44
|
+
/** @returns a command that transforms the canvas by `transform`. */
|
43
45
|
static transformBy(transform) {
|
44
46
|
return new Viewport.ViewportTransform(transform);
|
45
47
|
}
|
46
|
-
|
47
|
-
|
48
|
+
/**
|
49
|
+
* Updates the transformation directly. Using `transformBy` is preferred.
|
50
|
+
* @param newTransform - should map from canvas coordinates to screen coordinates.
|
51
|
+
*/
|
48
52
|
resetTransform(newTransform = Mat33.identity) {
|
49
53
|
const oldTransform = this.transform;
|
50
54
|
this.transform = newTransform;
|
@@ -61,20 +65,46 @@ export class Viewport {
|
|
61
65
|
get canvasToScreenTransform() {
|
62
66
|
return this.transform;
|
63
67
|
}
|
64
|
-
|
68
|
+
/** @returns the size of the visible region in pixels. */
|
69
|
+
getScreenRectSize() {
|
65
70
|
return this.screenRect.size;
|
66
71
|
}
|
67
|
-
|
72
|
+
/** Alias for `getScreenRectSize`. @deprecated */
|
73
|
+
getResolution() {
|
74
|
+
return this.getScreenRectSize();
|
75
|
+
}
|
76
|
+
/** @returns the amount a vector on the canvas is scaled to become a vector on the screen. */
|
68
77
|
getScaleFactor() {
|
69
78
|
// Use transformVec3 to avoid translating the vector
|
70
79
|
return this.transform.transformVec3(Vec3.unitX).magnitude();
|
71
80
|
}
|
72
|
-
|
81
|
+
/**
|
82
|
+
* @returns `getScaleFactor()` rounded to the nearest power of 10.
|
83
|
+
* For example, if `getScaleFactor()` returns 101, `getScaleFactorToNearestPowerOfTen()`
|
84
|
+
* should return `100` because `100` is the nearest power of 10 to 101.
|
85
|
+
*/
|
86
|
+
getScaleFactorToNearestPowerOfTen() {
|
87
|
+
const scaleFactor = this.getScaleFactor();
|
88
|
+
return Math.pow(10, Math.round(Math.log10(scaleFactor)));
|
89
|
+
}
|
90
|
+
snapToGrid(canvasPos) {
|
91
|
+
const snapCoordinate = (coordinate) => {
|
92
|
+
const scaleFactor = this.getScaleFactorToNearestPowerOfTen();
|
93
|
+
const roundFactor = scaleFactor / 100;
|
94
|
+
const snapped = Math.round(coordinate * roundFactor) / roundFactor;
|
95
|
+
return snapped;
|
96
|
+
};
|
97
|
+
const snappedCanvasPos = Vec2.of(snapCoordinate(canvasPos.x), snapCoordinate(canvasPos.y));
|
98
|
+
return snappedCanvasPos;
|
99
|
+
}
|
100
|
+
/** Returns the size of one screen pixel in canvas units. */
|
73
101
|
getSizeOfPixelOnCanvas() {
|
74
102
|
return 1 / this.getScaleFactor();
|
75
103
|
}
|
76
|
-
|
77
|
-
|
104
|
+
/**
|
105
|
+
* @returns the angle of the canvas in radians.
|
106
|
+
* This is the angle by which the canvas is rotated relative to the screen.
|
107
|
+
*/
|
78
108
|
getRotationAngle() {
|
79
109
|
return this.transform.transformVec3(Vec3.unitX).angle();
|
80
110
|
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import Command from './Command';
|
2
2
|
import SerializableCommand from './SerializableCommand';
|
3
|
-
// Returns a command
|
3
|
+
// Returns a command that does the opposite of the given command --- `result.apply()` calls
|
4
4
|
// `command.unapply()` and `result.unapply()` calls `command.apply()`.
|
5
5
|
const invertCommand = (command) => {
|
6
6
|
if (command instanceof SerializableCommand) {
|
@@ -7,6 +7,9 @@ import { ImageComponentLocalization } from './localization';
|
|
7
7
|
export type LoadSaveData = (string[] | Record<symbol, string | number>);
|
8
8
|
export type LoadSaveDataTable = Record<string, Array<LoadSaveData>>;
|
9
9
|
export type DeserializeCallback = (data: string) => AbstractComponent;
|
10
|
+
/**
|
11
|
+
* A base class for everything that can be added to an {@link EditorImage}.
|
12
|
+
*/
|
10
13
|
export default abstract class AbstractComponent {
|
11
14
|
private readonly componentKind;
|
12
15
|
protected lastChangedTime: number;
|
@@ -19,12 +22,24 @@ export default abstract class AbstractComponent {
|
|
19
22
|
private static deserializationCallbacks;
|
20
23
|
static registerComponent(componentKind: string, deserialize: DeserializeCallback | null): void;
|
21
24
|
private loadSaveData;
|
25
|
+
/**
|
26
|
+
* Attach data that can be used while exporting the component (e.g. to SVG).
|
27
|
+
*
|
28
|
+
* This is intended for use by a {@link ImageLoader}.
|
29
|
+
*/
|
22
30
|
attachLoadSaveData(key: string, data: LoadSaveData): void;
|
31
|
+
/** See {@link attachLoadSaveData} */
|
23
32
|
getLoadSaveData(): LoadSaveDataTable;
|
24
33
|
getZIndex(): number;
|
34
|
+
/** @returns the bounding box of */
|
25
35
|
getBBox(): Rect2;
|
26
36
|
abstract render(canvas: AbstractRenderer, visibleRect?: Rect2): void;
|
37
|
+
/** @return true if `lineSegment` intersects this component. */
|
27
38
|
abstract intersects(lineSegment: LineSegment2): boolean;
|
39
|
+
/**
|
40
|
+
* @returns true if this component intersects `rect` -- it is entirely contained
|
41
|
+
* within the rectangle or one of the rectangle's edges intersects this component.
|
42
|
+
*/
|
28
43
|
intersectsRect(rect: Rect2): boolean;
|
29
44
|
protected abstract serializeToJSON(): any[] | Record<string, any> | number | string | null;
|
30
45
|
protected abstract applyTransformation(affineTransfm: Mat33): void;
|
@@ -35,6 +50,10 @@ export default abstract class AbstractComponent {
|
|
35
50
|
private static transformElementCommandId;
|
36
51
|
private static UnresolvedTransformElementCommand;
|
37
52
|
private static TransformElementCommand;
|
53
|
+
/**
|
54
|
+
* @return a description that could be read by a screen reader
|
55
|
+
* (e.g. when adding/erasing the component)
|
56
|
+
*/
|
38
57
|
abstract description(localizationTable: ImageComponentLocalization): string;
|
39
58
|
protected abstract createClone(): AbstractComponent;
|
40
59
|
clone(): AbstractComponent;
|
@@ -2,12 +2,15 @@ var _a;
|
|
2
2
|
import SerializableCommand from '../commands/SerializableCommand';
|
3
3
|
import EditorImage from '../EditorImage';
|
4
4
|
import Mat33 from '../math/Mat33';
|
5
|
+
/**
|
6
|
+
* A base class for everything that can be added to an {@link EditorImage}.
|
7
|
+
*/
|
5
8
|
export default class AbstractComponent {
|
6
9
|
constructor(
|
7
10
|
// A unique identifier for the type of component
|
8
11
|
componentKind) {
|
9
12
|
this.componentKind = componentKind;
|
10
|
-
//
|
13
|
+
// Stores data attached by a loader.
|
11
14
|
this.loadSaveData = {};
|
12
15
|
this.lastChangedTime = (new Date()).getTime();
|
13
16
|
this.zIndex = AbstractComponent.zIndexCounter++;
|
@@ -18,7 +21,7 @@ export default class AbstractComponent {
|
|
18
21
|
}
|
19
22
|
}
|
20
23
|
// Returns a unique ID for this element.
|
21
|
-
// @see { @link EditorImage
|
24
|
+
// @see { @link lib!EditorImage.lookupElement }
|
22
25
|
getId() {
|
23
26
|
return this.id;
|
24
27
|
}
|
@@ -28,21 +31,32 @@ export default class AbstractComponent {
|
|
28
31
|
static registerComponent(componentKind, deserialize) {
|
29
32
|
this.deserializationCallbacks[componentKind] = deserialize !== null && deserialize !== void 0 ? deserialize : null;
|
30
33
|
}
|
34
|
+
/**
|
35
|
+
* Attach data that can be used while exporting the component (e.g. to SVG).
|
36
|
+
*
|
37
|
+
* This is intended for use by a {@link ImageLoader}.
|
38
|
+
*/
|
31
39
|
attachLoadSaveData(key, data) {
|
32
40
|
if (!this.loadSaveData[key]) {
|
33
41
|
this.loadSaveData[key] = [];
|
34
42
|
}
|
35
43
|
this.loadSaveData[key].push(data);
|
36
44
|
}
|
45
|
+
/** See {@link attachLoadSaveData} */
|
37
46
|
getLoadSaveData() {
|
38
47
|
return this.loadSaveData;
|
39
48
|
}
|
40
49
|
getZIndex() {
|
41
50
|
return this.zIndex;
|
42
51
|
}
|
52
|
+
/** @returns the bounding box of */
|
43
53
|
getBBox() {
|
44
54
|
return this.contentBBox;
|
45
55
|
}
|
56
|
+
/**
|
57
|
+
* @returns true if this component intersects `rect` -- it is entirely contained
|
58
|
+
* within the rectangle or one of the rectangle's edges intersects this component.
|
59
|
+
*/
|
46
60
|
intersectsRect(rect) {
|
47
61
|
// If this component intersects rect,
|
48
62
|
// it is either contained entirely within rect or intersects one of rect's edges.
|
@@ -198,6 +212,7 @@ AbstractComponent.TransformElementCommand = (_a = class extends SerializableComm
|
|
198
212
|
hadParent = true;
|
199
213
|
}
|
200
214
|
this.component.applyTransformation(newTransfm);
|
215
|
+
this.component.lastChangedTime = (new Date()).getTime();
|
201
216
|
// Add the element back to the document.
|
202
217
|
if (hadParent) {
|
203
218
|
EditorImage.addElement(this.component).apply(editor);
|
package/dist/src/lib.d.ts
CHANGED
@@ -8,22 +8,25 @@
|
|
8
8
|
* ```
|
9
9
|
*
|
10
10
|
* @see
|
11
|
-
* {@link Editor
|
11
|
+
* {@link Editor}
|
12
12
|
*
|
13
13
|
* @packageDocumentation
|
14
14
|
*/
|
15
|
-
import Editor from './Editor';
|
15
|
+
import Editor, { EditorSettings } from './Editor';
|
16
16
|
export { default as EditorImage } from './EditorImage';
|
17
17
|
export * from './types';
|
18
18
|
export { default as getLocalizationTable } from './localizations/getLocalizationTable';
|
19
19
|
export * from './localization';
|
20
20
|
export { default as Color4 } from './Color4';
|
21
|
+
export { default as SVGLoader } from './SVGLoader';
|
22
|
+
export { default as Viewport } from './Viewport';
|
21
23
|
export * from './math/lib';
|
22
24
|
export * from './components/lib';
|
23
25
|
export * from './commands/lib';
|
24
26
|
export * from './tools/lib';
|
25
27
|
export * from './toolbar/lib';
|
28
|
+
export * from './rendering/lib';
|
26
29
|
export { default as Pointer, PointerDevice } from './Pointer';
|
27
30
|
export { default as HTMLToolbar } from './toolbar/HTMLToolbar';
|
28
|
-
export { Editor };
|
31
|
+
export { Editor, EditorSettings };
|
29
32
|
export default Editor;
|
package/dist/src/lib.js
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
* ```
|
9
9
|
*
|
10
10
|
* @see
|
11
|
-
* {@link Editor
|
11
|
+
* {@link Editor}
|
12
12
|
*
|
13
13
|
* @packageDocumentation
|
14
14
|
*/
|
@@ -18,11 +18,14 @@ export * from './types';
|
|
18
18
|
export { default as getLocalizationTable } from './localizations/getLocalizationTable';
|
19
19
|
export * from './localization';
|
20
20
|
export { default as Color4 } from './Color4';
|
21
|
+
export { default as SVGLoader } from './SVGLoader';
|
22
|
+
export { default as Viewport } from './Viewport';
|
21
23
|
export * from './math/lib';
|
22
24
|
export * from './components/lib';
|
23
25
|
export * from './commands/lib';
|
24
26
|
export * from './tools/lib';
|
25
27
|
export * from './toolbar/lib';
|
28
|
+
export * from './rendering/lib';
|
26
29
|
export { default as Pointer, PointerDevice } from './Pointer';
|
27
30
|
export { default as HTMLToolbar } from './toolbar/HTMLToolbar';
|
28
31
|
export { Editor };
|
package/dist/src/math/Mat33.d.ts
CHANGED
@@ -102,7 +102,7 @@ export default class Mat33 {
|
|
102
102
|
static translation(amount: Vec2): Mat33;
|
103
103
|
static zRotation(radians: number, center?: Point2): Mat33;
|
104
104
|
static scaling2D(amount: number | Vec2, center?: Point2): Mat33;
|
105
|
-
/** @see {@link
|
105
|
+
/** @see {@link fromCSSMatrix} */
|
106
106
|
toCSSMatrix(): string;
|
107
107
|
/**
|
108
108
|
* Converts a CSS-form `matrix(a, b, c, d, e, f)` to a Mat33.
|
package/dist/src/math/Mat33.js
CHANGED
@@ -241,7 +241,7 @@ export default class Mat33 {
|
|
241
241
|
// Translate such that [center] goes to (0, 0)
|
242
242
|
return result.rightMul(Mat33.translation(center.times(-1)));
|
243
243
|
}
|
244
|
-
/** @see {@link
|
244
|
+
/** @see {@link fromCSSMatrix} */
|
245
245
|
toCSSMatrix() {
|
246
246
|
return `matrix(${this.a1},${this.b1},${this.a2},${this.b2},${this.a3},${this.b3})`;
|
247
247
|
}
|
@@ -1,3 +1,12 @@
|
|
1
|
+
import AbstractRenderer from './renderers/AbstractRenderer';
|
2
|
+
import { Editor } from '../Editor';
|
3
|
+
import { Point2 } from '../math/Vec2';
|
4
|
+
import RenderingCache from './caching/RenderingCache';
|
5
|
+
import Color4 from '../Color4';
|
6
|
+
export declare enum RenderingMode {
|
7
|
+
DummyRenderer = 0,
|
8
|
+
CanvasRenderer = 1
|
9
|
+
}
|
1
10
|
/**
|
2
11
|
* Handles `HTMLCanvasElement`s (or other drawing surfaces if being used) used to display the editor's contents.
|
3
12
|
*
|
@@ -9,18 +18,7 @@
|
|
9
18
|
* const center = Vec2.of(w / 2, h / 2);
|
10
19
|
* const colorAtCenter = editor.display.getColorAt(center);
|
11
20
|
* ```
|
12
|
-
*
|
13
|
-
* @packageDocumentation
|
14
21
|
*/
|
15
|
-
import AbstractRenderer from './renderers/AbstractRenderer';
|
16
|
-
import { Editor } from '../Editor';
|
17
|
-
import { Point2 } from '../math/Vec2';
|
18
|
-
import RenderingCache from './caching/RenderingCache';
|
19
|
-
import Color4 from '../Color4';
|
20
|
-
export declare enum RenderingMode {
|
21
|
-
DummyRenderer = 0,
|
22
|
-
CanvasRenderer = 1
|
23
|
-
}
|
24
22
|
export default class Display {
|
25
23
|
private editor;
|
26
24
|
private parent;
|