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.
Files changed (70) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/bundle.js +1 -1
  3. package/dist/src/Color4.d.ts +12 -0
  4. package/dist/src/Color4.js +16 -0
  5. package/dist/src/Editor.d.ts +33 -18
  6. package/dist/src/Editor.js +22 -19
  7. package/dist/src/EditorImage.d.ts +12 -0
  8. package/dist/src/EditorImage.js +12 -0
  9. package/dist/src/Pointer.d.ts +1 -0
  10. package/dist/src/Pointer.js +8 -0
  11. package/dist/src/SVGLoader.d.ts +5 -0
  12. package/dist/src/SVGLoader.js +6 -1
  13. package/dist/src/Viewport.d.ts +30 -1
  14. package/dist/src/Viewport.js +39 -9
  15. package/dist/src/commands/invertCommand.js +1 -1
  16. package/dist/src/components/AbstractComponent.d.ts +19 -0
  17. package/dist/src/components/AbstractComponent.js +17 -2
  18. package/dist/src/lib.d.ts +6 -3
  19. package/dist/src/lib.js +4 -1
  20. package/dist/src/math/Mat33.d.ts +1 -1
  21. package/dist/src/math/Mat33.js +1 -1
  22. package/dist/src/rendering/Display.d.ts +9 -11
  23. package/dist/src/rendering/Display.js +12 -14
  24. package/dist/src/rendering/lib.d.ts +3 -0
  25. package/dist/src/rendering/lib.js +3 -0
  26. package/dist/src/rendering/renderers/DummyRenderer.js +2 -2
  27. package/dist/src/rendering/renderers/SVGRenderer.js +4 -0
  28. package/dist/src/toolbar/IconProvider.d.ts +1 -1
  29. package/dist/src/toolbar/IconProvider.js +90 -29
  30. package/dist/src/tools/PanZoom.js +1 -1
  31. package/dist/src/tools/PasteHandler.d.ts +11 -4
  32. package/dist/src/tools/PasteHandler.js +12 -5
  33. package/dist/src/tools/Pen.d.ts +7 -2
  34. package/dist/src/tools/Pen.js +39 -6
  35. package/dist/src/tools/SelectionTool/SelectionHandle.d.ts +3 -0
  36. package/dist/src/tools/SelectionTool/SelectionHandle.js +6 -0
  37. package/dist/src/tools/SelectionTool/SelectionTool.d.ts +3 -1
  38. package/dist/src/tools/SelectionTool/SelectionTool.js +53 -15
  39. package/dist/src/tools/ToolSwitcherShortcut.d.ts +8 -0
  40. package/dist/src/tools/ToolSwitcherShortcut.js +9 -3
  41. package/dist/src/tools/UndoRedoShortcut.js +2 -4
  42. package/package.json +2 -2
  43. package/src/Color4.test.ts +11 -0
  44. package/src/Color4.ts +22 -0
  45. package/src/Editor.ts +36 -22
  46. package/src/EditorImage.ts +12 -0
  47. package/src/Pointer.ts +19 -0
  48. package/src/SVGLoader.ts +6 -1
  49. package/src/Viewport.ts +50 -11
  50. package/src/commands/invertCommand.ts +1 -1
  51. package/src/components/AbstractComponent.ts +33 -2
  52. package/src/lib.ts +6 -3
  53. package/src/math/Mat33.ts +1 -1
  54. package/src/rendering/Display.ts +12 -15
  55. package/src/rendering/RenderingStyle.ts +1 -1
  56. package/src/rendering/lib.ts +4 -0
  57. package/src/rendering/renderers/DummyRenderer.ts +2 -3
  58. package/src/rendering/renderers/SVGRenderer.ts +4 -0
  59. package/src/rendering/renderers/TextOnlyRenderer.ts +0 -1
  60. package/src/toolbar/HTMLToolbar.ts +1 -1
  61. package/src/toolbar/IconProvider.ts +98 -31
  62. package/src/tools/PanZoom.ts +1 -1
  63. package/src/tools/PasteHandler.ts +12 -6
  64. package/src/tools/Pen.test.ts +44 -1
  65. package/src/tools/Pen.ts +53 -8
  66. package/src/tools/SelectionTool/SelectionHandle.ts +9 -0
  67. package/src/tools/SelectionTool/SelectionTool.ts +67 -15
  68. package/src/tools/ToolSwitcherShortcut.ts +10 -5
  69. package/src/tools/UndoRedoShortcut.ts +2 -5
  70. package/typedoc.json +2 -2
@@ -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`.
@@ -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
  *
@@ -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
- readonly viewport: Viewport;
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): {
@@ -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
- // { @inheritDoc Editor! }
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.getResolution();
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
  }
@@ -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
  }
@@ -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
  }
@@ -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) {
@@ -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
  }
@@ -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
- // @param sanitize - if `true`, don't store unknown attributes.
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');
@@ -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
- getResolution(): Vec2;
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;
@@ -29,22 +29,26 @@ export class Viewport {
29
29
  updateScreenSize(screenSize) {
30
30
  this.screenRect = this.screenRect.resizedTo(screenSize);
31
31
  }
32
- // Get the screen's visible region transformed into canvas space.
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
- // the given point, but in canvas coordinates
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
- // Updates the transformation directly. Using `transformBy` is preferred.
47
- // [newTransform] should map from canvas coordinates to screen coordinates.
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
- getResolution() {
68
+ /** @returns the size of the visible region in pixels. */
69
+ getScreenRectSize() {
65
70
  return this.screenRect.size;
66
71
  }
67
- // Returns the amount a vector on the canvas is scaled to become a vector on the screen.
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
- // Returns the size of one screen pixel in canvas units.
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
- // Returns the angle of the canvas in radians.
77
- // This is the angle by which the canvas is rotated relative to the screen.
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 taht does the opposite of the given command --- `result.apply()` calls
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
- // Get and manage data attached by a loader.
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!default.lookupElement }
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 };
@@ -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 !fromCSSMatrix} */
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.
@@ -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 !fromCSSMatrix} */
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;