js-draw 1.9.1 → 1.10.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 (103) hide show
  1. package/dist/Editor.css +48 -1
  2. package/dist/bundle.js +2 -2
  3. package/dist/bundledStyles.js +1 -1
  4. package/dist/cjs/Editor.d.ts +41 -0
  5. package/dist/cjs/Editor.js +9 -0
  6. package/dist/cjs/Pointer.js +1 -1
  7. package/dist/cjs/commands/Erase.d.ts +22 -2
  8. package/dist/cjs/commands/Erase.js +22 -2
  9. package/dist/cjs/commands/uniteCommands.d.ts +36 -0
  10. package/dist/cjs/commands/uniteCommands.js +36 -0
  11. package/dist/cjs/components/ImageComponent.d.ts +12 -0
  12. package/dist/cjs/components/ImageComponent.js +16 -9
  13. package/dist/cjs/components/Stroke.d.ts +16 -2
  14. package/dist/cjs/components/Stroke.js +17 -1
  15. package/dist/cjs/components/builders/ArrowBuilder.js +3 -3
  16. package/dist/cjs/components/builders/CircleBuilder.js +3 -3
  17. package/dist/cjs/components/builders/FreehandLineBuilder.js +3 -3
  18. package/dist/cjs/components/builders/LineBuilder.js +3 -3
  19. package/dist/cjs/components/builders/PressureSensitiveFreehandLineBuilder.js +3 -3
  20. package/dist/cjs/components/builders/RectangleBuilder.js +5 -6
  21. package/dist/cjs/components/builders/autocorrect/makeShapeFitAutocorrect.d.ts +3 -0
  22. package/dist/cjs/components/builders/autocorrect/makeShapeFitAutocorrect.js +168 -0
  23. package/dist/cjs/components/builders/autocorrect/makeSnapToGridAutocorrect.d.ts +3 -0
  24. package/dist/cjs/components/builders/autocorrect/makeSnapToGridAutocorrect.js +46 -0
  25. package/dist/cjs/components/builders/types.d.ts +1 -0
  26. package/dist/cjs/image/EditorImage.d.ts +32 -1
  27. package/dist/cjs/image/EditorImage.js +32 -1
  28. package/dist/cjs/rendering/RenderablePathSpec.d.ts +5 -1
  29. package/dist/cjs/rendering/RenderablePathSpec.js +4 -0
  30. package/dist/cjs/toolbar/IconProvider.d.ts +2 -0
  31. package/dist/cjs/toolbar/IconProvider.js +17 -0
  32. package/dist/cjs/toolbar/localization.d.ts +3 -0
  33. package/dist/cjs/toolbar/localization.js +4 -1
  34. package/dist/cjs/toolbar/widgets/InsertImageWidget.d.ts +2 -1
  35. package/dist/cjs/toolbar/widgets/InsertImageWidget.js +102 -22
  36. package/dist/cjs/toolbar/widgets/PenToolWidget.d.ts +1 -2
  37. package/dist/cjs/toolbar/widgets/PenToolWidget.js +50 -20
  38. package/dist/cjs/tools/Pen.d.ts +9 -0
  39. package/dist/cjs/tools/Pen.js +77 -3
  40. package/dist/cjs/tools/TextTool.js +5 -1
  41. package/dist/cjs/tools/util/StationaryPenDetector.d.ts +22 -0
  42. package/dist/cjs/tools/util/StationaryPenDetector.js +95 -0
  43. package/dist/cjs/util/ReactiveValue.d.ts +2 -0
  44. package/dist/cjs/util/ReactiveValue.js +2 -0
  45. package/dist/cjs/util/lib.d.ts +1 -0
  46. package/dist/cjs/util/lib.js +4 -1
  47. package/dist/cjs/util/waitForImageLoaded.d.ts +2 -0
  48. package/dist/cjs/util/waitForImageLoaded.js +12 -0
  49. package/dist/cjs/version.js +1 -1
  50. package/dist/mjs/Editor.d.ts +41 -0
  51. package/dist/mjs/Editor.mjs +9 -0
  52. package/dist/mjs/Pointer.mjs +1 -1
  53. package/dist/mjs/commands/Erase.d.ts +22 -2
  54. package/dist/mjs/commands/Erase.mjs +22 -2
  55. package/dist/mjs/commands/uniteCommands.d.ts +36 -0
  56. package/dist/mjs/commands/uniteCommands.mjs +36 -0
  57. package/dist/mjs/components/ImageComponent.d.ts +12 -0
  58. package/dist/mjs/components/ImageComponent.mjs +16 -9
  59. package/dist/mjs/components/Stroke.d.ts +16 -2
  60. package/dist/mjs/components/Stroke.mjs +17 -1
  61. package/dist/mjs/components/builders/ArrowBuilder.mjs +3 -2
  62. package/dist/mjs/components/builders/CircleBuilder.mjs +3 -2
  63. package/dist/mjs/components/builders/FreehandLineBuilder.mjs +3 -2
  64. package/dist/mjs/components/builders/LineBuilder.mjs +3 -2
  65. package/dist/mjs/components/builders/PressureSensitiveFreehandLineBuilder.mjs +3 -2
  66. package/dist/mjs/components/builders/RectangleBuilder.mjs +5 -4
  67. package/dist/mjs/components/builders/autocorrect/makeShapeFitAutocorrect.d.ts +3 -0
  68. package/dist/mjs/components/builders/autocorrect/makeShapeFitAutocorrect.mjs +166 -0
  69. package/dist/mjs/components/builders/autocorrect/makeSnapToGridAutocorrect.d.ts +3 -0
  70. package/dist/mjs/components/builders/autocorrect/makeSnapToGridAutocorrect.mjs +44 -0
  71. package/dist/mjs/components/builders/types.d.ts +1 -0
  72. package/dist/mjs/image/EditorImage.d.ts +32 -1
  73. package/dist/mjs/image/EditorImage.mjs +32 -1
  74. package/dist/mjs/rendering/RenderablePathSpec.d.ts +5 -1
  75. package/dist/mjs/rendering/RenderablePathSpec.mjs +4 -0
  76. package/dist/mjs/toolbar/IconProvider.d.ts +2 -0
  77. package/dist/mjs/toolbar/IconProvider.mjs +17 -0
  78. package/dist/mjs/toolbar/localization.d.ts +3 -0
  79. package/dist/mjs/toolbar/localization.mjs +4 -1
  80. package/dist/mjs/toolbar/widgets/InsertImageWidget.d.ts +2 -1
  81. package/dist/mjs/toolbar/widgets/InsertImageWidget.mjs +102 -22
  82. package/dist/mjs/toolbar/widgets/PenToolWidget.d.ts +1 -2
  83. package/dist/mjs/toolbar/widgets/PenToolWidget.mjs +50 -20
  84. package/dist/mjs/tools/Pen.d.ts +9 -0
  85. package/dist/mjs/tools/Pen.mjs +77 -3
  86. package/dist/mjs/tools/TextTool.mjs +5 -1
  87. package/dist/mjs/tools/util/StationaryPenDetector.d.ts +22 -0
  88. package/dist/mjs/tools/util/StationaryPenDetector.mjs +92 -0
  89. package/dist/mjs/util/ReactiveValue.d.ts +2 -0
  90. package/dist/mjs/util/ReactiveValue.mjs +2 -0
  91. package/dist/mjs/util/lib.d.ts +1 -0
  92. package/dist/mjs/util/lib.mjs +1 -0
  93. package/dist/mjs/util/waitForImageLoaded.d.ts +2 -0
  94. package/dist/mjs/util/waitForImageLoaded.mjs +10 -0
  95. package/dist/mjs/version.mjs +1 -1
  96. package/package.json +3 -3
  97. package/src/Editor.scss +7 -0
  98. package/src/toolbar/AbstractToolbar.scss +20 -0
  99. package/src/toolbar/toolbar.scss +1 -1
  100. package/src/toolbar/widgets/InsertImageWidget.scss +6 -1
  101. package/src/toolbar/widgets/PenToolWidget.scss +33 -0
  102. package/src/tools/SelectionTool/SelectionTool.scss +6 -0
  103. package/src/toolbar/widgets/PenToolWidget.css +0 -2
@@ -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 makeArrowBuilder = (initialPoint, viewport) => {
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 makeOutlinedCircleBuilder = (initialPoint, viewport) => {
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 makeFreehandLineBuilder = (initialPoint, viewport) => {
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 makeLineBuilder = (initialPoint, viewport) => {
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 makePressureSensitiveFreehandLineBuilder = (initialPoint, viewport) => {
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 makeFilledRectangleBuilder = (initialPoint, viewport) => {
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.makeFilledRectangleBuilder = makeFilledRectangleBuilder;
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,3 @@
1
+ import { ComponentBuilderFactory } from '../types';
2
+ declare const makeShapeFitAutocorrect: (sourceFactory: ComponentBuilderFactory) => ComponentBuilderFactory;
3
+ export default makeShapeFitAutocorrect;
@@ -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,3 @@
1
+ import { ComponentBuilderFactory } from '../types';
2
+ declare const makeSnapToGridAutocorrect: (sourceFactory: ComponentBuilderFactory) => ComponentBuilderFactory;
3
+ export default makeSnapToGridAutocorrect;
@@ -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,7 @@ export interface ComponentBuilder {
7
7
  getBBox(): Rect2;
8
8
  build(): AbstractComponent;
9
9
  preview(renderer: AbstractRenderer): void;
10
+ autocorrectShape?: () => Promise<AbstractComponent | null>;
10
11
  addPoint(point: StrokeDataPoint): void;
11
12
  }
12
13
  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
- /** Returns a `Command` that sets whether the image should autoresize. */
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
- /** Returns a `Command` that sets whether the image should autoresize. */
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,
@@ -51,6 +51,8 @@ export default class IconProvider {
51
51
  makePenIcon(penStyle: PenStyle): IconElemType;
52
52
  makeIconFromFactory(penStyle: PenStyle): IconElemType;
53
53
  makePipetteIcon(color?: Color4): IconElemType;
54
+ makeShapeAutocorrectIcon(): IconElemType;
55
+ makeStrokeSmoothingIcon(): IconElemType;
54
56
  /** Unused. @deprecated */
55
57
  makeFormatSelectionIcon(): IconElemType;
56
58
  makeResizeImageToSelectionIcon(): IconElemType;
@@ -649,6 +649,23 @@ class IconProvider {
649
649
  icon.setAttribute('viewBox', '5 -40 140 140');
650
650
  return icon;
651
651
  }
652
+ makeShapeAutocorrectIcon() {
653
+ const fill = 'none';
654
+ const strokeColor = 'var(--icon-color)';
655
+ return this.makeIconFromPath(`
656
+ m 79.129476,33.847107 9.967823,-0.03218 v 55 h -55 l 0.03218,-9.96782
657
+ M 71.1,40.8 a 30,30 0 0 1 -30,30 30,30 0 0 1 -30,-30 30,30 0 0 1 30,-30 30,30 0 0 1 30,30 L 71.1,40.8
658
+ M 34.1,58.8 v -25 h 25 v 0
659
+ `, fill, strokeColor, '7px');
660
+ }
661
+ makeStrokeSmoothingIcon() {
662
+ const fill = 'none';
663
+ const strokeColor = 'var(--icon-color)';
664
+ return this.makeIconFromPath(`
665
+ m 31,83.2 c -50,0 30,-65 -20,-65
666
+ M 75,17.3 40,59.7 38.2,77.6 55.5,72.4 90.5,30 Z
667
+ `, fill, strokeColor, '7px');
668
+ }
652
669
  /** Unused. @deprecated */
653
670
  makeFormatSelectionIcon() {
654
671
  return this.makeIconFromPath(`
@@ -10,6 +10,8 @@ export interface ToolbarLocalization {
10
10
  arrowPen: string;
11
11
  image: string;
12
12
  inputAltText: string;
13
+ decreaseImageSize: string;
14
+ resetImage: string;
13
15
  chooseFile: string;
14
16
  dragAndDropHereOrBrowse: string;
15
17
  cancel: string;
@@ -48,6 +50,7 @@ export interface ToolbarLocalization {
48
50
  toggleOverflow: string;
49
51
  about: string;
50
52
  inputStabilization: string;
53
+ strokeAutocorrect: string;
51
54
  errorImageHasZeroSize: string;
52
55
  closeSidebar: (toolName: string) => string;
53
56
  dropdownShown: (toolName: string) => string;
@@ -10,6 +10,8 @@ exports.defaultToolbarLocalization = {
10
10
  image: 'Image',
11
11
  reformatSelection: 'Format selection',
12
12
  inputAltText: 'Alt text',
13
+ decreaseImageSize: 'Decrease size',
14
+ resetImage: 'Reset',
13
15
  chooseFile: 'Choose file',
14
16
  dragAndDropHereOrBrowse: 'Drag and drop here\nor\n{{browse}}',
15
17
  submit: 'Submit',
@@ -40,7 +42,8 @@ exports.defaultToolbarLocalization = {
40
42
  enableAutoresizeOption: 'Auto-resize',
41
43
  toggleOverflow: 'More',
42
44
  about: 'About',
43
- inputStabilization: 'Input stabilization',
45
+ inputStabilization: 'Stabilization',
46
+ strokeAutocorrect: 'Autocorrect',
44
47
  touchPanning: 'Touchscreen panning',
45
48
  roundedTipPen: 'Round',
46
49
  flatTipPen: 'Flat',
@@ -3,10 +3,10 @@ import { ToolbarLocalization } from '../localization';
3
3
  import BaseWidget from './BaseWidget';
4
4
  export default class InsertImageWidget extends BaseWidget {
5
5
  private imagePreview;
6
+ private image;
6
7
  private selectedFiles;
7
8
  private imageAltTextInput;
8
9
  private statusView;
9
- private imageBase64URL;
10
10
  private submitButton;
11
11
  constructor(editor: Editor, localization?: ToolbarLocalization);
12
12
  protected getTitle(): string;
@@ -15,6 +15,7 @@ export default class InsertImageWidget extends BaseWidget {
15
15
  protected handleClick(): void;
16
16
  private static nextInputId;
17
17
  protected fillDropdown(dropdown: HTMLElement): boolean;
18
+ private onImageDataUpdate;
18
19
  private hideDialog;
19
20
  private updateImageSizeDisplay;
20
21
  private updateInputs;