js-draw 1.9.1 → 1.11.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/dist/Editor.css +48 -1
- package/dist/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/Editor.d.ts +41 -0
- package/dist/cjs/Editor.js +9 -0
- package/dist/cjs/Pointer.js +1 -1
- package/dist/cjs/commands/Erase.d.ts +22 -2
- package/dist/cjs/commands/Erase.js +22 -2
- package/dist/cjs/commands/invertCommand.js +5 -0
- package/dist/cjs/commands/uniteCommands.d.ts +36 -0
- package/dist/cjs/commands/uniteCommands.js +36 -0
- package/dist/cjs/components/AbstractComponent.d.ts +8 -0
- package/dist/cjs/components/AbstractComponent.js +28 -8
- package/dist/cjs/components/ImageComponent.d.ts +12 -0
- package/dist/cjs/components/ImageComponent.js +16 -9
- package/dist/cjs/components/Stroke.d.ts +16 -2
- package/dist/cjs/components/Stroke.js +17 -1
- package/dist/cjs/components/builders/ArrowBuilder.js +3 -3
- package/dist/cjs/components/builders/CircleBuilder.js +3 -3
- package/dist/cjs/components/builders/FreehandLineBuilder.js +3 -3
- package/dist/cjs/components/builders/LineBuilder.js +3 -3
- package/dist/cjs/components/builders/PressureSensitiveFreehandLineBuilder.js +3 -3
- package/dist/cjs/components/builders/RectangleBuilder.js +5 -6
- package/dist/cjs/components/builders/autocorrect/makeShapeFitAutocorrect.d.ts +3 -0
- package/dist/cjs/components/builders/autocorrect/makeShapeFitAutocorrect.js +168 -0
- package/dist/cjs/components/builders/autocorrect/makeSnapToGridAutocorrect.d.ts +3 -0
- package/dist/cjs/components/builders/autocorrect/makeSnapToGridAutocorrect.js +46 -0
- package/dist/cjs/components/builders/types.d.ts +12 -0
- package/dist/cjs/image/EditorImage.d.ts +32 -1
- package/dist/cjs/image/EditorImage.js +32 -1
- package/dist/cjs/rendering/RenderablePathSpec.d.ts +5 -1
- package/dist/cjs/rendering/RenderablePathSpec.js +4 -0
- package/dist/cjs/toolbar/AbstractToolbar.d.ts +18 -2
- package/dist/cjs/toolbar/AbstractToolbar.js +46 -30
- package/dist/cjs/toolbar/IconProvider.d.ts +2 -0
- package/dist/cjs/toolbar/IconProvider.js +17 -0
- package/dist/cjs/toolbar/localization.d.ts +3 -0
- package/dist/cjs/toolbar/localization.js +4 -1
- package/dist/cjs/toolbar/widgets/BaseWidget.js +1 -1
- package/dist/cjs/toolbar/widgets/ExitActionWidget.d.ts +12 -0
- package/dist/cjs/toolbar/widgets/ExitActionWidget.js +32 -0
- package/dist/cjs/toolbar/widgets/HandToolWidget.d.ts +4 -3
- package/dist/cjs/toolbar/widgets/HandToolWidget.js +24 -13
- package/dist/cjs/toolbar/widgets/InsertImageWidget.d.ts +2 -1
- package/dist/cjs/toolbar/widgets/InsertImageWidget.js +102 -22
- package/dist/cjs/toolbar/widgets/PenToolWidget.d.ts +1 -2
- package/dist/cjs/toolbar/widgets/PenToolWidget.js +50 -20
- package/dist/cjs/toolbar/widgets/keybindings.d.ts +1 -0
- package/dist/cjs/toolbar/widgets/keybindings.js +4 -1
- package/dist/cjs/toolbar/widgets/layout/types.d.ts +1 -1
- package/dist/cjs/tools/Pen.d.ts +9 -0
- package/dist/cjs/tools/Pen.js +82 -3
- package/dist/cjs/tools/SelectionTool/Selection.d.ts +4 -0
- package/dist/cjs/tools/SelectionTool/Selection.js +56 -12
- package/dist/cjs/tools/SelectionTool/SelectionTool.d.ts +1 -0
- package/dist/cjs/tools/SelectionTool/SelectionTool.js +19 -1
- package/dist/cjs/tools/TextTool.js +5 -1
- package/dist/cjs/tools/ToolSwitcherShortcut.d.ts +0 -1
- package/dist/cjs/tools/ToolSwitcherShortcut.js +0 -1
- package/dist/cjs/tools/keybindings.d.ts +1 -0
- package/dist/cjs/tools/keybindings.js +3 -1
- package/dist/cjs/tools/util/StationaryPenDetector.d.ts +22 -0
- package/dist/cjs/tools/util/StationaryPenDetector.js +95 -0
- package/dist/cjs/util/ReactiveValue.d.ts +2 -0
- package/dist/cjs/util/ReactiveValue.js +2 -0
- package/dist/cjs/util/lib.d.ts +1 -0
- package/dist/cjs/util/lib.js +4 -1
- package/dist/cjs/util/waitForImageLoaded.d.ts +2 -0
- package/dist/cjs/util/waitForImageLoaded.js +12 -0
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/Editor.d.ts +41 -0
- package/dist/mjs/Editor.mjs +9 -0
- package/dist/mjs/Pointer.mjs +1 -1
- package/dist/mjs/commands/Erase.d.ts +22 -2
- package/dist/mjs/commands/Erase.mjs +22 -2
- package/dist/mjs/commands/invertCommand.mjs +5 -0
- package/dist/mjs/commands/uniteCommands.d.ts +36 -0
- package/dist/mjs/commands/uniteCommands.mjs +36 -0
- package/dist/mjs/components/AbstractComponent.d.ts +8 -0
- package/dist/mjs/components/AbstractComponent.mjs +28 -8
- package/dist/mjs/components/ImageComponent.d.ts +12 -0
- package/dist/mjs/components/ImageComponent.mjs +16 -9
- package/dist/mjs/components/Stroke.d.ts +16 -2
- package/dist/mjs/components/Stroke.mjs +17 -1
- package/dist/mjs/components/builders/ArrowBuilder.mjs +3 -2
- package/dist/mjs/components/builders/CircleBuilder.mjs +3 -2
- package/dist/mjs/components/builders/FreehandLineBuilder.mjs +3 -2
- package/dist/mjs/components/builders/LineBuilder.mjs +3 -2
- package/dist/mjs/components/builders/PressureSensitiveFreehandLineBuilder.mjs +3 -2
- package/dist/mjs/components/builders/RectangleBuilder.mjs +5 -4
- package/dist/mjs/components/builders/autocorrect/makeShapeFitAutocorrect.d.ts +3 -0
- package/dist/mjs/components/builders/autocorrect/makeShapeFitAutocorrect.mjs +166 -0
- package/dist/mjs/components/builders/autocorrect/makeSnapToGridAutocorrect.d.ts +3 -0
- package/dist/mjs/components/builders/autocorrect/makeSnapToGridAutocorrect.mjs +44 -0
- package/dist/mjs/components/builders/types.d.ts +12 -0
- package/dist/mjs/image/EditorImage.d.ts +32 -1
- package/dist/mjs/image/EditorImage.mjs +32 -1
- package/dist/mjs/rendering/RenderablePathSpec.d.ts +5 -1
- package/dist/mjs/rendering/RenderablePathSpec.mjs +4 -0
- package/dist/mjs/toolbar/AbstractToolbar.d.ts +18 -2
- package/dist/mjs/toolbar/AbstractToolbar.mjs +46 -30
- package/dist/mjs/toolbar/IconProvider.d.ts +2 -0
- package/dist/mjs/toolbar/IconProvider.mjs +17 -0
- package/dist/mjs/toolbar/localization.d.ts +3 -0
- package/dist/mjs/toolbar/localization.mjs +4 -1
- package/dist/mjs/toolbar/widgets/BaseWidget.mjs +1 -1
- package/dist/mjs/toolbar/widgets/ExitActionWidget.d.ts +12 -0
- package/dist/mjs/toolbar/widgets/ExitActionWidget.mjs +27 -0
- package/dist/mjs/toolbar/widgets/HandToolWidget.d.ts +4 -3
- package/dist/mjs/toolbar/widgets/HandToolWidget.mjs +24 -13
- package/dist/mjs/toolbar/widgets/InsertImageWidget.d.ts +2 -1
- package/dist/mjs/toolbar/widgets/InsertImageWidget.mjs +102 -22
- package/dist/mjs/toolbar/widgets/PenToolWidget.d.ts +1 -2
- package/dist/mjs/toolbar/widgets/PenToolWidget.mjs +50 -20
- package/dist/mjs/toolbar/widgets/keybindings.d.ts +1 -0
- package/dist/mjs/toolbar/widgets/keybindings.mjs +3 -0
- package/dist/mjs/toolbar/widgets/layout/types.d.ts +1 -1
- package/dist/mjs/tools/Pen.d.ts +9 -0
- package/dist/mjs/tools/Pen.mjs +82 -3
- package/dist/mjs/tools/SelectionTool/Selection.d.ts +4 -0
- package/dist/mjs/tools/SelectionTool/Selection.mjs +56 -12
- package/dist/mjs/tools/SelectionTool/SelectionTool.d.ts +1 -0
- package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +20 -2
- package/dist/mjs/tools/TextTool.mjs +5 -1
- package/dist/mjs/tools/ToolSwitcherShortcut.d.ts +0 -1
- package/dist/mjs/tools/ToolSwitcherShortcut.mjs +0 -1
- package/dist/mjs/tools/keybindings.d.ts +1 -0
- package/dist/mjs/tools/keybindings.mjs +2 -0
- package/dist/mjs/tools/util/StationaryPenDetector.d.ts +22 -0
- package/dist/mjs/tools/util/StationaryPenDetector.mjs +92 -0
- package/dist/mjs/util/ReactiveValue.d.ts +2 -0
- package/dist/mjs/util/ReactiveValue.mjs +2 -0
- package/dist/mjs/util/lib.d.ts +1 -0
- package/dist/mjs/util/lib.mjs +1 -0
- package/dist/mjs/util/waitForImageLoaded.d.ts +2 -0
- package/dist/mjs/util/waitForImageLoaded.mjs +10 -0
- package/dist/mjs/version.mjs +1 -1
- package/package.json +3 -3
- package/src/Editor.scss +7 -0
- package/src/toolbar/AbstractToolbar.scss +20 -0
- package/src/toolbar/toolbar.scss +1 -1
- package/src/toolbar/widgets/InsertImageWidget.scss +6 -1
- package/src/toolbar/widgets/PenToolWidget.scss +33 -0
- package/src/tools/SelectionTool/SelectionTool.scss +6 -0
- package/src/toolbar/widgets/PenToolWidget.css +0 -2
@@ -6,7 +6,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const math_1 = require("@js-draw/math");
|
7
7
|
const assertions_1 = require("../util/assertions");
|
8
8
|
const AbstractComponent_1 = __importDefault(require("./AbstractComponent"));
|
9
|
-
|
9
|
+
const waitForImageLoaded_1 = __importDefault(require("../util/waitForImageLoaded"));
|
10
|
+
/**
|
11
|
+
* Represents a raster image.
|
12
|
+
*
|
13
|
+
* **Example: Adding images**:
|
14
|
+
* [[include:doc-pages/inline-examples/adding-an-image-and-data-urls.md]]
|
15
|
+
*/
|
10
16
|
class ImageComponent extends AbstractComponent_1.default {
|
11
17
|
constructor(image) {
|
12
18
|
super('image-component');
|
@@ -29,15 +35,14 @@ class ImageComponent extends AbstractComponent_1.default {
|
|
29
35
|
this.contentBBox = this.getImageRect();
|
30
36
|
this.contentBBox = this.contentBBox.transformedBoundingBox(this.image.transform);
|
31
37
|
}
|
32
|
-
|
38
|
+
/**
|
39
|
+
* Load from an image. Waits for the image to load if incomplete.
|
40
|
+
*
|
41
|
+
* The image, `elem`, must not [taint](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image#security_and_tainted_canvases)
|
42
|
+
* an HTMLCanvasElement when rendered.
|
43
|
+
*/
|
33
44
|
static async fromImage(elem, transform) {
|
34
|
-
|
35
|
-
await new Promise((resolve, reject) => {
|
36
|
-
elem.onload = resolve;
|
37
|
-
elem.onerror = reject;
|
38
|
-
elem.onabort = reject;
|
39
|
-
});
|
40
|
-
}
|
45
|
+
await (0, waitForImageLoaded_1.default)(elem);
|
41
46
|
let width, height;
|
42
47
|
if (typeof elem.width === 'number' && typeof elem.height === 'number'
|
43
48
|
&& elem.width !== 0 && elem.height !== 0) {
|
@@ -79,6 +84,7 @@ class ImageComponent extends AbstractComponent_1.default {
|
|
79
84
|
canvas.drawImage(this.image);
|
80
85
|
canvas.endObject(this.getLoadSaveData());
|
81
86
|
}
|
87
|
+
// A *very* rough estimate of how long it takes to render this component
|
82
88
|
getProportionalRenderingTime() {
|
83
89
|
// Estimate: Equivalent to a stroke with 10 segments.
|
84
90
|
return 10;
|
@@ -103,6 +109,7 @@ class ImageComponent extends AbstractComponent_1.default {
|
|
103
109
|
getAltText() {
|
104
110
|
return this.image.label;
|
105
111
|
}
|
112
|
+
// The base64 image URL of this image.
|
106
113
|
getURL() {
|
107
114
|
return this.image.base64Url;
|
108
115
|
}
|
@@ -5,7 +5,7 @@ import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
|
|
5
5
|
import AbstractComponent from './AbstractComponent';
|
6
6
|
import { ImageComponentLocalization } from './localization';
|
7
7
|
import RestyleableComponent, { ComponentStyle } from './RestylableComponent';
|
8
|
-
import RenderablePathSpec from '../rendering/RenderablePathSpec';
|
8
|
+
import RenderablePathSpec, { RenderablePathSpecWithPath } from '../rendering/RenderablePathSpec';
|
9
9
|
/**
|
10
10
|
* Represents an {@link AbstractComponent} made up of one or more {@link Path}s.
|
11
11
|
*
|
@@ -21,6 +21,9 @@ import RenderablePathSpec from '../rendering/RenderablePathSpec';
|
|
21
21
|
* ```ts
|
22
22
|
* editor.dispatch(stroke.transformBy(Mat33.translation(Vec2.of(10, 0))));
|
23
23
|
* ```
|
24
|
+
*
|
25
|
+
* **Adding**:
|
26
|
+
* [[include:doc-pages/inline-examples/adding-a-stroke.md]]
|
24
27
|
*/
|
25
28
|
export default class Stroke extends AbstractComponent implements RestyleableComponent {
|
26
29
|
private parts;
|
@@ -39,7 +42,7 @@ export default class Stroke extends AbstractComponent implements RestyleableComp
|
|
39
42
|
*
|
40
43
|
* const stroke = new Stroke([
|
41
44
|
* // Fill with red
|
42
|
-
* pathToRenderable({ fill: Color4.red })
|
45
|
+
* pathToRenderable(path, { fill: Color4.red })
|
43
46
|
* ]);
|
44
47
|
* ```
|
45
48
|
*/
|
@@ -57,6 +60,17 @@ export default class Stroke extends AbstractComponent implements RestyleableComp
|
|
57
60
|
private bboxForPart;
|
58
61
|
getExactBBox(): Rect2;
|
59
62
|
protected applyTransformation(affineTransfm: Mat33): void;
|
63
|
+
/**
|
64
|
+
* @returns A list of the parts that make up this path. Many paths only have one part.
|
65
|
+
*
|
66
|
+
* Each part (a {@link RenderablePathSpec}) contains information about the style and geometry
|
67
|
+
* of that part of the stroke. Use the `.path` property to do collision detection and other
|
68
|
+
* operations involving the stroke's geometry.
|
69
|
+
*
|
70
|
+
* Note that many of {@link Path}'s methods (e.g. {@link Path.intersection}) take a
|
71
|
+
* `strokeWidth` parameter that can be gotten from {@link RenderablePathSpec.style} `.stroke.width`.
|
72
|
+
*/
|
73
|
+
getParts(): Readonly<RenderablePathSpecWithPath>[];
|
60
74
|
/**
|
61
75
|
* @returns the {@link Path.union} of all paths that make up this stroke.
|
62
76
|
*/
|
@@ -23,6 +23,9 @@ const RenderablePathSpec_1 = require("../rendering/RenderablePathSpec");
|
|
23
23
|
* ```ts
|
24
24
|
* editor.dispatch(stroke.transformBy(Mat33.translation(Vec2.of(10, 0))));
|
25
25
|
* ```
|
26
|
+
*
|
27
|
+
* **Adding**:
|
28
|
+
* [[include:doc-pages/inline-examples/adding-a-stroke.md]]
|
26
29
|
*/
|
27
30
|
class Stroke extends AbstractComponent_1.default {
|
28
31
|
/**
|
@@ -37,7 +40,7 @@ class Stroke extends AbstractComponent_1.default {
|
|
37
40
|
*
|
38
41
|
* const stroke = new Stroke([
|
39
42
|
* // Fill with red
|
40
|
-
* pathToRenderable({ fill: Color4.red })
|
43
|
+
* pathToRenderable(path, { fill: Color4.red })
|
41
44
|
* ]);
|
42
45
|
* ```
|
43
46
|
*/
|
@@ -295,6 +298,19 @@ class Stroke extends AbstractComponent_1.default {
|
|
295
298
|
};
|
296
299
|
});
|
297
300
|
}
|
301
|
+
/**
|
302
|
+
* @returns A list of the parts that make up this path. Many paths only have one part.
|
303
|
+
*
|
304
|
+
* Each part (a {@link RenderablePathSpec}) contains information about the style and geometry
|
305
|
+
* of that part of the stroke. Use the `.path` property to do collision detection and other
|
306
|
+
* operations involving the stroke's geometry.
|
307
|
+
*
|
308
|
+
* Note that many of {@link Path}'s methods (e.g. {@link Path.intersection}) take a
|
309
|
+
* `strokeWidth` parameter that can be gotten from {@link RenderablePathSpec.style} `.stroke.width`.
|
310
|
+
*/
|
311
|
+
getParts() {
|
312
|
+
return [...this.parts];
|
313
|
+
}
|
298
314
|
/**
|
299
315
|
* @returns the {@link Path.union} of all paths that make up this stroke.
|
300
316
|
*/
|
@@ -6,10 +6,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.makeArrowBuilder = void 0;
|
7
7
|
const math_1 = require("@js-draw/math");
|
8
8
|
const Stroke_1 = __importDefault(require("../Stroke"));
|
9
|
-
const
|
9
|
+
const makeSnapToGridAutocorrect_1 = __importDefault(require("./autocorrect/makeSnapToGridAutocorrect"));
|
10
|
+
exports.makeArrowBuilder = (0, makeSnapToGridAutocorrect_1.default)((initialPoint, viewport) => {
|
10
11
|
return new ArrowBuilder(initialPoint, viewport);
|
11
|
-
};
|
12
|
-
exports.makeArrowBuilder = makeArrowBuilder;
|
12
|
+
});
|
13
13
|
class ArrowBuilder {
|
14
14
|
constructor(startPoint, viewport) {
|
15
15
|
this.startPoint = startPoint;
|
@@ -8,10 +8,10 @@ const math_1 = require("@js-draw/math");
|
|
8
8
|
const RenderablePathSpec_1 = require("../../rendering/RenderablePathSpec");
|
9
9
|
const Viewport_1 = __importDefault(require("../../Viewport"));
|
10
10
|
const Stroke_1 = __importDefault(require("../Stroke"));
|
11
|
-
const
|
11
|
+
const makeSnapToGridAutocorrect_1 = __importDefault(require("./autocorrect/makeSnapToGridAutocorrect"));
|
12
|
+
exports.makeOutlinedCircleBuilder = (0, makeSnapToGridAutocorrect_1.default)((initialPoint, viewport) => {
|
12
13
|
return new CircleBuilder(initialPoint, viewport);
|
13
|
-
};
|
14
|
-
exports.makeOutlinedCircleBuilder = makeOutlinedCircleBuilder;
|
14
|
+
});
|
15
15
|
class CircleBuilder {
|
16
16
|
constructor(startPoint, viewport) {
|
17
17
|
this.startPoint = startPoint;
|
@@ -8,14 +8,14 @@ const math_1 = require("@js-draw/math");
|
|
8
8
|
const Stroke_1 = __importDefault(require("../Stroke"));
|
9
9
|
const Viewport_1 = __importDefault(require("../../Viewport"));
|
10
10
|
const StrokeSmoother_1 = require("../util/StrokeSmoother");
|
11
|
-
const
|
11
|
+
const makeShapeFitAutocorrect_1 = __importDefault(require("./autocorrect/makeShapeFitAutocorrect"));
|
12
|
+
exports.makeFreehandLineBuilder = (0, makeShapeFitAutocorrect_1.default)((initialPoint, viewport) => {
|
12
13
|
// Don't smooth if input is more than ± 3 pixels from the true curve, do smooth if
|
13
14
|
// less than ±1 px from the curve.
|
14
15
|
const maxSmoothingDist = viewport.getSizeOfPixelOnCanvas() * 3;
|
15
16
|
const minSmoothingDist = viewport.getSizeOfPixelOnCanvas();
|
16
17
|
return new FreehandLineBuilder(initialPoint, minSmoothingDist, maxSmoothingDist, viewport);
|
17
|
-
};
|
18
|
-
exports.makeFreehandLineBuilder = makeFreehandLineBuilder;
|
18
|
+
});
|
19
19
|
// Handles stroke smoothing and creates Strokes from user/stylus input.
|
20
20
|
class FreehandLineBuilder {
|
21
21
|
constructor(startPoint, minFitAllowed, maxFitAllowed, viewport) {
|
@@ -7,10 +7,10 @@ exports.makeLineBuilder = void 0;
|
|
7
7
|
const math_1 = require("@js-draw/math");
|
8
8
|
const RenderablePathSpec_1 = require("../../rendering/RenderablePathSpec");
|
9
9
|
const Stroke_1 = __importDefault(require("../Stroke"));
|
10
|
-
const
|
10
|
+
const makeSnapToGridAutocorrect_1 = __importDefault(require("./autocorrect/makeSnapToGridAutocorrect"));
|
11
|
+
exports.makeLineBuilder = (0, makeSnapToGridAutocorrect_1.default)((initialPoint, viewport) => {
|
11
12
|
return new LineBuilder(initialPoint, viewport);
|
12
|
-
};
|
13
|
-
exports.makeLineBuilder = makeLineBuilder;
|
13
|
+
});
|
14
14
|
class LineBuilder {
|
15
15
|
constructor(startPoint, viewport) {
|
16
16
|
this.startPoint = startPoint;
|
@@ -9,14 +9,14 @@ const math_1 = require("@js-draw/math");
|
|
9
9
|
const Stroke_1 = __importDefault(require("../Stroke"));
|
10
10
|
const Viewport_1 = __importDefault(require("../../Viewport"));
|
11
11
|
const StrokeSmoother_1 = require("../util/StrokeSmoother");
|
12
|
-
const
|
12
|
+
const makeShapeFitAutocorrect_1 = __importDefault(require("./autocorrect/makeShapeFitAutocorrect"));
|
13
|
+
exports.makePressureSensitiveFreehandLineBuilder = (0, makeShapeFitAutocorrect_1.default)((initialPoint, viewport) => {
|
13
14
|
// Don't smooth if input is more than ± 3 pixels from the true curve, do smooth if
|
14
15
|
// less than ±1 px from the curve.
|
15
16
|
const maxSmoothingDist = viewport.getSizeOfPixelOnCanvas() * 3;
|
16
17
|
const minSmoothingDist = viewport.getSizeOfPixelOnCanvas();
|
17
18
|
return new PressureSensitiveFreehandLineBuilder(initialPoint, minSmoothingDist, maxSmoothingDist, viewport);
|
18
|
-
};
|
19
|
-
exports.makePressureSensitiveFreehandLineBuilder = makePressureSensitiveFreehandLineBuilder;
|
19
|
+
});
|
20
20
|
// Handles stroke smoothing and creates Strokes from user/stylus input.
|
21
21
|
class PressureSensitiveFreehandLineBuilder {
|
22
22
|
constructor(startPoint,
|
@@ -7,14 +7,13 @@ exports.makeOutlinedRectangleBuilder = exports.makeFilledRectangleBuilder = void
|
|
7
7
|
const math_1 = require("@js-draw/math");
|
8
8
|
const RenderablePathSpec_1 = require("../../rendering/RenderablePathSpec");
|
9
9
|
const Stroke_1 = __importDefault(require("../Stroke"));
|
10
|
-
const
|
10
|
+
const makeSnapToGridAutocorrect_1 = __importDefault(require("./autocorrect/makeSnapToGridAutocorrect"));
|
11
|
+
exports.makeFilledRectangleBuilder = (0, makeSnapToGridAutocorrect_1.default)((initialPoint, viewport) => {
|
11
12
|
return new RectangleBuilder(initialPoint, true, viewport);
|
12
|
-
};
|
13
|
-
exports.
|
14
|
-
const makeOutlinedRectangleBuilder = (initialPoint, viewport) => {
|
13
|
+
});
|
14
|
+
exports.makeOutlinedRectangleBuilder = (0, makeSnapToGridAutocorrect_1.default)((initialPoint, viewport) => {
|
15
15
|
return new RectangleBuilder(initialPoint, false, viewport);
|
16
|
-
};
|
17
|
-
exports.makeOutlinedRectangleBuilder = makeOutlinedRectangleBuilder;
|
16
|
+
});
|
18
17
|
class RectangleBuilder {
|
19
18
|
constructor(startPoint, filled, viewport) {
|
20
19
|
this.startPoint = startPoint;
|
@@ -0,0 +1,168 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
const math_1 = require("@js-draw/math");
|
4
|
+
const makeShapeFitAutocorrect = (sourceFactory) => {
|
5
|
+
return (startPoint, viewport) => {
|
6
|
+
return new ShapeFitBuilder(sourceFactory, startPoint, viewport);
|
7
|
+
};
|
8
|
+
};
|
9
|
+
exports.default = makeShapeFitAutocorrect;
|
10
|
+
const makeLineTemplate = (startPoint, points, _bbox) => {
|
11
|
+
const templatePoints = [
|
12
|
+
startPoint,
|
13
|
+
points[points.length - 1],
|
14
|
+
];
|
15
|
+
return { points: templatePoints, };
|
16
|
+
};
|
17
|
+
const makeRectangleTemplate = (_startPoint, _points, bbox) => {
|
18
|
+
return { points: [...bbox.corners, bbox.corners[0]], };
|
19
|
+
};
|
20
|
+
class ShapeFitBuilder {
|
21
|
+
constructor(sourceFactory, startPoint, viewport) {
|
22
|
+
this.sourceFactory = sourceFactory;
|
23
|
+
this.startPoint = startPoint;
|
24
|
+
this.viewport = viewport;
|
25
|
+
this.builder = sourceFactory(startPoint, viewport);
|
26
|
+
this.points = [startPoint];
|
27
|
+
}
|
28
|
+
getBBox() {
|
29
|
+
return this.builder.getBBox();
|
30
|
+
}
|
31
|
+
build() {
|
32
|
+
return this.builder.build();
|
33
|
+
}
|
34
|
+
preview(renderer) {
|
35
|
+
this.builder.preview(renderer);
|
36
|
+
}
|
37
|
+
addPoint(point) {
|
38
|
+
this.points.push(point);
|
39
|
+
this.builder.addPoint(point);
|
40
|
+
}
|
41
|
+
async autocorrectShape() {
|
42
|
+
// Use screen points so that autocorrected shapes rotate with the screen.
|
43
|
+
const startPoint = this.viewport.canvasToScreen(this.startPoint.pos);
|
44
|
+
const points = this.points.map(point => this.viewport.canvasToScreen(point.pos));
|
45
|
+
const bbox = math_1.Rect2.bboxOf(points);
|
46
|
+
const snappedStartPoint = this.viewport.canvasToScreen(this.viewport.snapToGrid(this.startPoint.pos));
|
47
|
+
const snappedPoints = this.points.map(point => this.viewport.canvasToScreen(this.viewport.snapToGrid(point.pos)));
|
48
|
+
const snappedBBox = math_1.Rect2.bboxOf(snappedPoints);
|
49
|
+
// Only fit larger shapes
|
50
|
+
if (bbox.maxDimension < 32) {
|
51
|
+
return null;
|
52
|
+
}
|
53
|
+
const maxError = Math.min(30, bbox.maxDimension / 4);
|
54
|
+
// Create templates
|
55
|
+
const templates = [
|
56
|
+
{
|
57
|
+
...makeLineTemplate(snappedStartPoint, snappedPoints, snappedBBox),
|
58
|
+
toleranceMultiplier: 0.5,
|
59
|
+
},
|
60
|
+
makeLineTemplate(startPoint, points, bbox),
|
61
|
+
{
|
62
|
+
...makeRectangleTemplate(snappedStartPoint, snappedPoints, snappedBBox),
|
63
|
+
toleranceMultiplier: 0.6,
|
64
|
+
},
|
65
|
+
makeRectangleTemplate(startPoint, points, bbox),
|
66
|
+
];
|
67
|
+
// Find a good fit fit
|
68
|
+
const selectTemplate = (maximumAllowedError) => {
|
69
|
+
for (const template of templates) {
|
70
|
+
const templatePoints = template.points;
|
71
|
+
// Maximum square error to accept the template
|
72
|
+
const acceptMaximumSquareError = maximumAllowedError * maximumAllowedError * (template.toleranceMultiplier ?? 1);
|
73
|
+
// Gets the point at index, wrapping the the start of the template if
|
74
|
+
// outside the array of points.
|
75
|
+
const templateAt = (index) => {
|
76
|
+
while (index < 0) {
|
77
|
+
index += templatePoints.length;
|
78
|
+
}
|
79
|
+
index %= templatePoints.length;
|
80
|
+
return templatePoints[index];
|
81
|
+
};
|
82
|
+
let closestToFirst = null;
|
83
|
+
let closestToFirstSqrDist = Infinity;
|
84
|
+
let templateStartIndex = 0;
|
85
|
+
// Find the closest point to the startPoint
|
86
|
+
for (let i = 0; i < templatePoints.length; i++) {
|
87
|
+
const current = templatePoints[i];
|
88
|
+
const currentSqrDist = current.minus(startPoint).magnitudeSquared();
|
89
|
+
if (!closestToFirst || currentSqrDist < closestToFirstSqrDist) {
|
90
|
+
closestToFirstSqrDist = currentSqrDist;
|
91
|
+
closestToFirst = current;
|
92
|
+
templateStartIndex = i;
|
93
|
+
}
|
94
|
+
}
|
95
|
+
// Walk through the points and find the maximum error
|
96
|
+
let maximumSqrError = 0;
|
97
|
+
let templateIndex = templateStartIndex;
|
98
|
+
for (const point of points) {
|
99
|
+
let minimumCurrentSqrError = Infinity;
|
100
|
+
let minimumErrorAtIndex = templateIndex;
|
101
|
+
const windowRadius = 6;
|
102
|
+
for (let i = -windowRadius; i <= windowRadius; i++) {
|
103
|
+
const index = templateIndex + i;
|
104
|
+
const prevTemplatePoint = templateAt(index - 1);
|
105
|
+
const currentTemplatePoint = templateAt(index);
|
106
|
+
const nextTemplatePoint = templateAt(index + 1);
|
107
|
+
const prevToCurrent = new math_1.LineSegment2(prevTemplatePoint, currentTemplatePoint);
|
108
|
+
const currentToNext = new math_1.LineSegment2(currentTemplatePoint, nextTemplatePoint);
|
109
|
+
const prevToCurrentDist = prevToCurrent.distance(point);
|
110
|
+
const nextToCurrentDist = currentToNext.distance(point);
|
111
|
+
const error = Math.min(prevToCurrentDist, nextToCurrentDist);
|
112
|
+
const squareError = error * error;
|
113
|
+
if (squareError < minimumCurrentSqrError) {
|
114
|
+
minimumCurrentSqrError = squareError;
|
115
|
+
minimumErrorAtIndex = index;
|
116
|
+
}
|
117
|
+
}
|
118
|
+
templateIndex = minimumErrorAtIndex;
|
119
|
+
maximumSqrError = Math.max(minimumCurrentSqrError, maximumSqrError);
|
120
|
+
if (maximumSqrError > acceptMaximumSquareError) {
|
121
|
+
break;
|
122
|
+
}
|
123
|
+
}
|
124
|
+
if (maximumSqrError < acceptMaximumSquareError) {
|
125
|
+
return templatePoints;
|
126
|
+
}
|
127
|
+
}
|
128
|
+
return null;
|
129
|
+
};
|
130
|
+
const template = selectTemplate(maxError);
|
131
|
+
if (!template) {
|
132
|
+
return null;
|
133
|
+
}
|
134
|
+
const lastDataPoint = this.points[this.points.length - 1];
|
135
|
+
const startWidth = this.startPoint.width;
|
136
|
+
const endWidth = lastDataPoint.width;
|
137
|
+
const startColor = this.startPoint.color;
|
138
|
+
const endColor = lastDataPoint.color;
|
139
|
+
const startTime = this.startPoint.time;
|
140
|
+
const endTime = lastDataPoint.time;
|
141
|
+
const templateIndexToStrokeDataPoint = (index) => {
|
142
|
+
const prevPoint = template[Math.max(0, Math.floor(index))];
|
143
|
+
const nextPoint = template[Math.min(Math.ceil(index), template.length - 1)];
|
144
|
+
const point = prevPoint.lerp(nextPoint, index - Math.floor(index));
|
145
|
+
const fractionToEnd = index / template.length;
|
146
|
+
return {
|
147
|
+
pos: this.viewport.screenToCanvas(point),
|
148
|
+
width: startWidth * (1 - fractionToEnd) + endWidth * fractionToEnd,
|
149
|
+
color: startColor.mix(endColor, fractionToEnd),
|
150
|
+
time: startTime * (1 - fractionToEnd) + endTime * fractionToEnd,
|
151
|
+
};
|
152
|
+
};
|
153
|
+
const builder = this.sourceFactory(templateIndexToStrokeDataPoint(0), this.viewport);
|
154
|
+
// Prevent the original builder from doing stroke smoothing if the template is short
|
155
|
+
// enough to likely have sharp corners.
|
156
|
+
const preventSmoothing = template.length < 10;
|
157
|
+
for (let i = 0; i < template.length; i++) {
|
158
|
+
if (preventSmoothing) {
|
159
|
+
builder.addPoint(templateIndexToStrokeDataPoint(i - 0.001));
|
160
|
+
}
|
161
|
+
builder.addPoint(templateIndexToStrokeDataPoint(i));
|
162
|
+
if (preventSmoothing) {
|
163
|
+
builder.addPoint(templateIndexToStrokeDataPoint(i + 0.001));
|
164
|
+
}
|
165
|
+
}
|
166
|
+
return builder.build();
|
167
|
+
}
|
168
|
+
}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
const makeSnapToGridAutocorrect = (sourceFactory) => {
|
4
|
+
return (startPoint, viewport) => {
|
5
|
+
return new SnapToGridAutocompleteBuilder(sourceFactory, startPoint, viewport);
|
6
|
+
};
|
7
|
+
};
|
8
|
+
exports.default = makeSnapToGridAutocorrect;
|
9
|
+
class SnapToGridAutocompleteBuilder {
|
10
|
+
constructor(sourceFactory, startPoint, viewport) {
|
11
|
+
this.sourceFactory = sourceFactory;
|
12
|
+
this.startPoint = startPoint;
|
13
|
+
this.viewport = viewport;
|
14
|
+
this.builder = sourceFactory(startPoint, viewport);
|
15
|
+
this.points = [startPoint];
|
16
|
+
}
|
17
|
+
getBBox() {
|
18
|
+
return this.builder.getBBox();
|
19
|
+
}
|
20
|
+
build() {
|
21
|
+
return this.builder.build();
|
22
|
+
}
|
23
|
+
preview(renderer) {
|
24
|
+
this.builder.preview(renderer);
|
25
|
+
}
|
26
|
+
addPoint(point) {
|
27
|
+
this.points.push(point);
|
28
|
+
this.builder.addPoint(point);
|
29
|
+
}
|
30
|
+
async autocorrectShape() {
|
31
|
+
const snapToGrid = (point) => {
|
32
|
+
return {
|
33
|
+
...point,
|
34
|
+
pos: this.viewport.snapToGrid(point.pos),
|
35
|
+
};
|
36
|
+
};
|
37
|
+
// Use screen points so that snapped shapes rotate with the screen.
|
38
|
+
const startPoint = snapToGrid(this.startPoint);
|
39
|
+
const builder = this.sourceFactory(startPoint, this.viewport);
|
40
|
+
const points = this.points.map(point => snapToGrid(point));
|
41
|
+
for (const point of points) {
|
42
|
+
builder.addPoint(point);
|
43
|
+
}
|
44
|
+
return builder.build();
|
45
|
+
}
|
46
|
+
}
|
@@ -7,6 +7,18 @@ export interface ComponentBuilder {
|
|
7
7
|
getBBox(): Rect2;
|
8
8
|
build(): AbstractComponent;
|
9
9
|
preview(renderer: AbstractRenderer): void;
|
10
|
+
/**
|
11
|
+
* Called when the pen is stationary (or the user otherwise
|
12
|
+
* activates autocomplete). This might attempt to fit the user's
|
13
|
+
* drawing to a particular shape.
|
14
|
+
*
|
15
|
+
* The shape returned by this function may be ignored if it has
|
16
|
+
* an empty bounding box.
|
17
|
+
*
|
18
|
+
* Although this returns a Promise, it should return *as fast as
|
19
|
+
* possible*.
|
20
|
+
*/
|
21
|
+
autocorrectShape?: () => Promise<AbstractComponent | null>;
|
10
22
|
addPoint(point: StrokeDataPoint): void;
|
11
23
|
}
|
12
24
|
export type ComponentBuilderFactory = (startPoint: StrokeDataPoint, viewport: Viewport) => ComponentBuilder;
|
@@ -87,6 +87,9 @@ export default class EditorImage {
|
|
87
87
|
* rendered onto the main rendering canvas instead of doing a full re-render.
|
88
88
|
*
|
89
89
|
* @see {@link Display.flatten}
|
90
|
+
*
|
91
|
+
* @example
|
92
|
+
* [[include:doc-pages/inline-examples/adding-a-stroke.md]]
|
90
93
|
*/
|
91
94
|
static addElement(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand;
|
92
95
|
/** @see EditorImage.addElement */
|
@@ -105,8 +108,36 @@ export default class EditorImage {
|
|
105
108
|
* autoresize (if it was previously enabled).
|
106
109
|
*/
|
107
110
|
setImportExportRect(imageRect: Rect2): SerializableCommand;
|
111
|
+
/** @see {@link setAutoresizeEnabled} */
|
108
112
|
getAutoresizeEnabled(): boolean;
|
109
|
-
/**
|
113
|
+
/**
|
114
|
+
* Returns a `Command` that sets whether the image should autoresize when
|
115
|
+
* {@link AbstractComponent}s are added/removed.
|
116
|
+
*
|
117
|
+
* @example
|
118
|
+
*
|
119
|
+
* ```ts,runnable
|
120
|
+
* import { Editor } from 'js-draw';
|
121
|
+
*
|
122
|
+
* const editor = new Editor(document.body);
|
123
|
+
* const toolbar = editor.addToolbar();
|
124
|
+
*
|
125
|
+
* // Add a save button to demonstrate what the output looks like
|
126
|
+
* // (it should change size to fit whatever was drawn)
|
127
|
+
* toolbar.addSaveButton(() => {
|
128
|
+
* document.body.replaceChildren(editor.toSVG({ sanitize: true }));
|
129
|
+
* });
|
130
|
+
*
|
131
|
+
* // Actually using setAutoresizeEnabled:
|
132
|
+
* //
|
133
|
+
* // To set autoresize without announcing for accessibility/making undoable
|
134
|
+
* const addToHistory = false;
|
135
|
+
* editor.dispatchNoAnnounce(editor.image.setAutoresizeEnabled(true), addToHistory);
|
136
|
+
*
|
137
|
+
* // Add to undo history **and** announce for accessibility
|
138
|
+
* //editor.dispatch(editor.image.setAutoresizeEnabled(true), true);
|
139
|
+
* ```
|
140
|
+
*/
|
110
141
|
setAutoresizeEnabled(autoresize: boolean): Command;
|
111
142
|
private setAutoresizeEnabledDirectly;
|
112
143
|
/** Updates the size/position of the viewport */
|
@@ -210,6 +210,9 @@ class EditorImage {
|
|
210
210
|
* rendered onto the main rendering canvas instead of doing a full re-render.
|
211
211
|
*
|
212
212
|
* @see {@link Display.flatten}
|
213
|
+
*
|
214
|
+
* @example
|
215
|
+
* [[include:doc-pages/inline-examples/adding-a-stroke.md]]
|
213
216
|
*/
|
214
217
|
static addElement(elem, applyByFlattening = false) {
|
215
218
|
return new _a.AddElementCommand(elem, applyByFlattening);
|
@@ -237,10 +240,38 @@ class EditorImage {
|
|
237
240
|
setImportExportRect(imageRect) {
|
238
241
|
return _a.SetImportExportRectCommand.of(this, imageRect, false);
|
239
242
|
}
|
243
|
+
/** @see {@link setAutoresizeEnabled} */
|
240
244
|
getAutoresizeEnabled() {
|
241
245
|
return this.shouldAutoresizeExportViewport;
|
242
246
|
}
|
243
|
-
/**
|
247
|
+
/**
|
248
|
+
* Returns a `Command` that sets whether the image should autoresize when
|
249
|
+
* {@link AbstractComponent}s are added/removed.
|
250
|
+
*
|
251
|
+
* @example
|
252
|
+
*
|
253
|
+
* ```ts,runnable
|
254
|
+
* import { Editor } from 'js-draw';
|
255
|
+
*
|
256
|
+
* const editor = new Editor(document.body);
|
257
|
+
* const toolbar = editor.addToolbar();
|
258
|
+
*
|
259
|
+
* // Add a save button to demonstrate what the output looks like
|
260
|
+
* // (it should change size to fit whatever was drawn)
|
261
|
+
* toolbar.addSaveButton(() => {
|
262
|
+
* document.body.replaceChildren(editor.toSVG({ sanitize: true }));
|
263
|
+
* });
|
264
|
+
*
|
265
|
+
* // Actually using setAutoresizeEnabled:
|
266
|
+
* //
|
267
|
+
* // To set autoresize without announcing for accessibility/making undoable
|
268
|
+
* const addToHistory = false;
|
269
|
+
* editor.dispatchNoAnnounce(editor.image.setAutoresizeEnabled(true), addToHistory);
|
270
|
+
*
|
271
|
+
* // Add to undo history **and** announce for accessibility
|
272
|
+
* //editor.dispatch(editor.image.setAutoresizeEnabled(true), true);
|
273
|
+
* ```
|
274
|
+
*/
|
244
275
|
setAutoresizeEnabled(autoresize) {
|
245
276
|
if (autoresize === this.shouldAutoresizeExportViewport) {
|
246
277
|
return Command_1.default.empty;
|
@@ -6,11 +6,15 @@ interface RenderablePathSpec {
|
|
6
6
|
style: RenderingStyle;
|
7
7
|
path?: Path;
|
8
8
|
}
|
9
|
-
interface RenderablePathSpecWithPath extends RenderablePathSpec {
|
9
|
+
export interface RenderablePathSpecWithPath extends RenderablePathSpec {
|
10
10
|
path: Path;
|
11
11
|
}
|
12
12
|
/** Converts a renderable path (a path with a `startPoint`, `commands`, and `style`). */
|
13
13
|
export declare const pathFromRenderable: (renderable: RenderablePathSpec) => Path;
|
14
|
+
/**
|
15
|
+
* Converts `path` into a format that can be rendered (by passing to a {@link Stroke} constructor
|
16
|
+
* or directly to an {@link AbstractRenderer.drawPath}).
|
17
|
+
*/
|
14
18
|
export declare const pathToRenderable: (path: Path, style: RenderingStyle) => RenderablePathSpecWithPath;
|
15
19
|
interface RectangleSimplificationResult {
|
16
20
|
rectangle: Rect2;
|
@@ -10,6 +10,10 @@ const pathFromRenderable = (renderable) => {
|
|
10
10
|
return new math_1.Path(renderable.startPoint, renderable.commands);
|
11
11
|
};
|
12
12
|
exports.pathFromRenderable = pathFromRenderable;
|
13
|
+
/**
|
14
|
+
* Converts `path` into a format that can be rendered (by passing to a {@link Stroke} constructor
|
15
|
+
* or directly to an {@link AbstractRenderer.drawPath}).
|
16
|
+
*/
|
13
17
|
const pathToRenderable = (path, style) => {
|
14
18
|
return {
|
15
19
|
startPoint: path.startPoint,
|
@@ -3,6 +3,7 @@ import { ToolbarLocalization } from './localization';
|
|
3
3
|
import { ActionButtonIcon } from './types';
|
4
4
|
import BaseWidget, { ToolbarWidgetTag } from './widgets/BaseWidget';
|
5
5
|
import { DispatcherEventListener } from '../EventDispatcher';
|
6
|
+
import { BaseTool } from '../lib';
|
6
7
|
export interface SpacerOptions {
|
7
8
|
grow: number;
|
8
9
|
minSize: string;
|
@@ -131,7 +132,7 @@ export default abstract class AbstractToolbar {
|
|
131
132
|
/**
|
132
133
|
* Adds an "Exit" button that, when clicked, calls `exitCallback`.
|
133
134
|
*
|
134
|
-
* **Note**: This is roughly equivalent to
|
135
|
+
* **Note**: This is *roughly* equivalent to
|
135
136
|
* ```ts
|
136
137
|
* toolbar.addTaggedActionButton([ ToolbarWidgetTag.Exit ], {
|
137
138
|
* label: this.editor.localization.exit,
|
@@ -154,9 +155,24 @@ export default abstract class AbstractToolbar {
|
|
154
155
|
*/
|
155
156
|
addUndoRedoButtons(undoFirst?: boolean): void;
|
156
157
|
/**
|
157
|
-
* Adds
|
158
|
+
* Adds widgets for pen/eraser/selection/text/pan-zoom primary tools.
|
159
|
+
*
|
160
|
+
* If `filter` returns `false` for a tool, no widget is added for that tool.
|
161
|
+
* See {@link addDefaultToolWidgets}
|
162
|
+
*/
|
163
|
+
addWidgetsForPrimaryTools(filter?: (tool: BaseTool) => boolean): void;
|
164
|
+
/**
|
165
|
+
* Adds toolbar widgets based on the enabled tools, and additional tool-like
|
166
|
+
* buttons (e.g. {@link DocumentPropertiesWidget} and {@link InsertImageWidget}).
|
158
167
|
*/
|
159
168
|
addDefaultToolWidgets(): void;
|
169
|
+
/**
|
170
|
+
* Adds widgets that don't correspond to tools, but do allow the user to control
|
171
|
+
* the editor in some way.
|
172
|
+
*
|
173
|
+
* By default, this includes {@link DocumentPropertiesWidget} and {@link InsertImageWidget}.
|
174
|
+
*/
|
175
|
+
addDefaultEditorControlWidgets(): void;
|
160
176
|
addDefaultActionButtons(): void;
|
161
177
|
/**
|
162
178
|
* Adds both the default tool widgets and action buttons.
|