js-draw 1.16.1 → 1.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. package/README.md +70 -10
  2. package/dist/bundle.js +2 -2
  3. package/dist/bundledStyles.js +1 -1
  4. package/dist/cjs/Editor.d.ts +75 -7
  5. package/dist/cjs/Editor.js +93 -90
  6. package/dist/cjs/Pointer.d.ts +2 -1
  7. package/dist/cjs/Pointer.js +9 -2
  8. package/dist/cjs/commands/localization.d.ts +1 -0
  9. package/dist/cjs/commands/localization.js +1 -0
  10. package/dist/cjs/commands/uniteCommands.d.ts +5 -1
  11. package/dist/cjs/commands/uniteCommands.js +33 -7
  12. package/dist/cjs/components/AbstractComponent.d.ts +17 -5
  13. package/dist/cjs/components/AbstractComponent.js +15 -15
  14. package/dist/cjs/components/Stroke.d.ts +4 -1
  15. package/dist/cjs/components/Stroke.js +158 -2
  16. package/dist/cjs/components/TextComponent.d.ts +36 -1
  17. package/dist/cjs/components/TextComponent.js +39 -1
  18. package/dist/cjs/components/builders/ArrowBuilder.js +1 -1
  19. package/dist/cjs/components/builders/PolylineBuilder.d.ts +35 -0
  20. package/dist/cjs/components/builders/PolylineBuilder.js +122 -0
  21. package/dist/cjs/components/builders/PressureSensitiveFreehandLineBuilder.d.ts +1 -1
  22. package/dist/cjs/components/builders/PressureSensitiveFreehandLineBuilder.js +44 -51
  23. package/dist/cjs/components/builders/autocorrect/makeShapeFitAutocorrect.js +1 -1
  24. package/dist/cjs/components/lib.d.ts +1 -0
  25. package/dist/cjs/components/lib.js +3 -1
  26. package/dist/cjs/components/util/StrokeSmoother.js +4 -4
  27. package/dist/cjs/image/EditorImage.d.ts +4 -1
  28. package/dist/cjs/image/EditorImage.js +5 -2
  29. package/dist/cjs/inputEvents.d.ts +11 -1
  30. package/dist/cjs/localizations/comments.d.ts +3 -0
  31. package/dist/cjs/localizations/comments.js +3 -0
  32. package/dist/cjs/localizations/de.js +1 -3
  33. package/dist/cjs/localizations/es.js +3 -3
  34. package/dist/cjs/rendering/renderers/CanvasRenderer.d.ts +7 -0
  35. package/dist/cjs/rendering/renderers/CanvasRenderer.js +16 -0
  36. package/dist/cjs/rendering/renderers/SVGRenderer.js +1 -1
  37. package/dist/cjs/testing/createEditor.d.ts +2 -2
  38. package/dist/cjs/testing/createEditor.js +2 -2
  39. package/dist/cjs/toolbar/IconProvider.d.ts +9 -4
  40. package/dist/cjs/toolbar/IconProvider.js +21 -7
  41. package/dist/cjs/toolbar/localization.d.ts +6 -1
  42. package/dist/cjs/toolbar/localization.js +7 -2
  43. package/dist/cjs/toolbar/widgets/DocumentPropertiesWidget.js +24 -1
  44. package/dist/cjs/toolbar/widgets/EraserToolWidget.d.ts +6 -1
  45. package/dist/cjs/toolbar/widgets/EraserToolWidget.js +45 -5
  46. package/dist/cjs/toolbar/widgets/PenToolWidget.d.ts +1 -1
  47. package/dist/cjs/toolbar/widgets/PenToolWidget.js +17 -4
  48. package/dist/cjs/toolbar/widgets/PenToolWidget.test.d.ts +1 -0
  49. package/dist/cjs/toolbar/widgets/keybindings.js +1 -1
  50. package/dist/cjs/tools/Eraser.d.ts +24 -4
  51. package/dist/cjs/tools/Eraser.js +108 -21
  52. package/dist/cjs/tools/InputFilter/InputStabilizer.js +3 -3
  53. package/dist/cjs/tools/PasteHandler.js +35 -10
  54. package/dist/cjs/tools/Pen.js +2 -2
  55. package/dist/cjs/tools/SelectionTool/SelectionTool.js +23 -4
  56. package/dist/cjs/tools/SelectionTool/ToPointerAutoscroller.js +1 -1
  57. package/dist/cjs/tools/ToolController.d.ts +17 -1
  58. package/dist/cjs/tools/ToolController.js +21 -8
  59. package/dist/cjs/tools/lib.d.ts +1 -4
  60. package/dist/cjs/tools/lib.js +2 -4
  61. package/dist/cjs/tools/localization.d.ts +2 -2
  62. package/dist/cjs/tools/localization.js +2 -2
  63. package/dist/cjs/util/ClipboardHandler.d.ts +27 -0
  64. package/dist/cjs/util/ClipboardHandler.js +205 -0
  65. package/dist/cjs/util/ClipboardHandler.test.d.ts +1 -0
  66. package/dist/cjs/version.d.ts +5 -0
  67. package/dist/cjs/version.js +6 -1
  68. package/dist/mjs/Editor.d.ts +75 -7
  69. package/dist/mjs/Editor.mjs +93 -90
  70. package/dist/mjs/Pointer.d.ts +2 -1
  71. package/dist/mjs/Pointer.mjs +9 -2
  72. package/dist/mjs/commands/localization.d.ts +1 -0
  73. package/dist/mjs/commands/localization.mjs +1 -0
  74. package/dist/mjs/commands/uniteCommands.d.ts +5 -1
  75. package/dist/mjs/commands/uniteCommands.mjs +33 -7
  76. package/dist/mjs/components/AbstractComponent.d.ts +17 -5
  77. package/dist/mjs/components/AbstractComponent.mjs +15 -15
  78. package/dist/mjs/components/Stroke.d.ts +4 -1
  79. package/dist/mjs/components/Stroke.mjs +159 -3
  80. package/dist/mjs/components/TextComponent.d.ts +36 -1
  81. package/dist/mjs/components/TextComponent.mjs +40 -2
  82. package/dist/mjs/components/builders/ArrowBuilder.mjs +1 -1
  83. package/dist/mjs/components/builders/PolylineBuilder.d.ts +35 -0
  84. package/dist/mjs/components/builders/PolylineBuilder.mjs +115 -0
  85. package/dist/mjs/components/builders/PressureSensitiveFreehandLineBuilder.d.ts +1 -1
  86. package/dist/mjs/components/builders/PressureSensitiveFreehandLineBuilder.mjs +45 -52
  87. package/dist/mjs/components/builders/autocorrect/makeShapeFitAutocorrect.mjs +1 -1
  88. package/dist/mjs/components/lib.d.ts +1 -0
  89. package/dist/mjs/components/lib.mjs +1 -0
  90. package/dist/mjs/components/util/StrokeSmoother.mjs +4 -4
  91. package/dist/mjs/image/EditorImage.d.ts +4 -1
  92. package/dist/mjs/image/EditorImage.mjs +5 -2
  93. package/dist/mjs/inputEvents.d.ts +11 -1
  94. package/dist/mjs/localizations/comments.d.ts +3 -0
  95. package/dist/mjs/localizations/comments.mjs +3 -0
  96. package/dist/mjs/localizations/de.mjs +1 -3
  97. package/dist/mjs/localizations/es.mjs +3 -3
  98. package/dist/mjs/rendering/renderers/CanvasRenderer.d.ts +7 -0
  99. package/dist/mjs/rendering/renderers/CanvasRenderer.mjs +16 -0
  100. package/dist/mjs/rendering/renderers/SVGRenderer.mjs +1 -1
  101. package/dist/mjs/testing/createEditor.d.ts +2 -2
  102. package/dist/mjs/testing/createEditor.mjs +2 -2
  103. package/dist/mjs/toolbar/IconProvider.d.ts +9 -4
  104. package/dist/mjs/toolbar/IconProvider.mjs +21 -7
  105. package/dist/mjs/toolbar/localization.d.ts +6 -1
  106. package/dist/mjs/toolbar/localization.mjs +7 -2
  107. package/dist/mjs/toolbar/widgets/DocumentPropertiesWidget.mjs +24 -1
  108. package/dist/mjs/toolbar/widgets/EraserToolWidget.d.ts +6 -1
  109. package/dist/mjs/toolbar/widgets/EraserToolWidget.mjs +47 -6
  110. package/dist/mjs/toolbar/widgets/PenToolWidget.d.ts +1 -1
  111. package/dist/mjs/toolbar/widgets/PenToolWidget.mjs +17 -4
  112. package/dist/mjs/toolbar/widgets/PenToolWidget.test.d.ts +1 -0
  113. package/dist/mjs/toolbar/widgets/keybindings.mjs +1 -1
  114. package/dist/mjs/tools/Eraser.d.ts +24 -4
  115. package/dist/mjs/tools/Eraser.mjs +108 -22
  116. package/dist/mjs/tools/InputFilter/InputStabilizer.mjs +3 -3
  117. package/dist/mjs/tools/PasteHandler.mjs +35 -10
  118. package/dist/mjs/tools/Pen.mjs +2 -2
  119. package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +23 -4
  120. package/dist/mjs/tools/SelectionTool/ToPointerAutoscroller.mjs +1 -1
  121. package/dist/mjs/tools/ToolController.d.ts +17 -1
  122. package/dist/mjs/tools/ToolController.mjs +21 -8
  123. package/dist/mjs/tools/lib.d.ts +1 -4
  124. package/dist/mjs/tools/lib.mjs +1 -4
  125. package/dist/mjs/tools/localization.d.ts +2 -2
  126. package/dist/mjs/tools/localization.mjs +2 -2
  127. package/dist/mjs/util/ClipboardHandler.d.ts +27 -0
  128. package/dist/mjs/util/ClipboardHandler.mjs +200 -0
  129. package/dist/mjs/util/ClipboardHandler.test.d.ts +1 -0
  130. package/dist/mjs/version.d.ts +5 -0
  131. package/dist/mjs/version.mjs +6 -1
  132. package/package.json +6 -6
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.makePressureSensitiveFreehandLineBuilder = void 0;
7
- const bezier_js_1 = require("bezier-js");
8
7
  const math_1 = require("@js-draw/math");
9
8
  const Stroke_1 = __importDefault(require("../Stroke"));
10
9
  const Viewport_1 = __importDefault(require("../../Viewport"));
@@ -31,6 +30,7 @@ class PressureSensitiveFreehandLineBuilder {
31
30
  this.isFirstSegment = true;
32
31
  this.pathStartConnector = null;
33
32
  this.mostRecentConnector = null;
33
+ this.nextCurveStartConnector = null;
34
34
  this.lastUpperBezier = null;
35
35
  this.lastLowerBezier = null;
36
36
  this.parts = [];
@@ -48,18 +48,18 @@ class PressureSensitiveFreehandLineBuilder {
48
48
  fill: this.startPoint.color ?? null,
49
49
  };
50
50
  }
51
- previewCurrentPath() {
51
+ previewCurrentPath(extendWithLatest = true) {
52
52
  const upperPath = this.upperSegments.slice();
53
53
  const lowerPath = this.lowerSegments.slice();
54
54
  let lowerToUpperCap;
55
55
  let pathStartConnector;
56
56
  const currentCurve = this.curveFitter.preview();
57
- if (currentCurve) {
57
+ if (currentCurve && extendWithLatest) {
58
58
  const { upperCurveCommand, lowerToUpperConnector, upperToLowerConnector, lowerCurveCommand } = this.segmentToPath(currentCurve);
59
59
  upperPath.push(upperCurveCommand);
60
60
  lowerPath.push(lowerCurveCommand);
61
61
  lowerToUpperCap = lowerToUpperConnector;
62
- pathStartConnector = this.pathStartConnector ?? upperToLowerConnector;
62
+ pathStartConnector = this.pathStartConnector ?? [upperToLowerConnector];
63
63
  }
64
64
  else {
65
65
  if (this.mostRecentConnector === null || this.pathStartConnector === null) {
@@ -100,7 +100,7 @@ class PressureSensitiveFreehandLineBuilder {
100
100
  // __/ __/
101
101
  // /___ /
102
102
  // •
103
- pathStartConnector,
103
+ ...pathStartConnector,
104
104
  // Move back to the start point:
105
105
  // •
106
106
  // __/ __/
@@ -117,13 +117,6 @@ class PressureSensitiveFreehandLineBuilder {
117
117
  }
118
118
  return null;
119
119
  }
120
- previewStroke() {
121
- const pathPreview = this.previewFullPath();
122
- if (pathPreview) {
123
- return new Stroke_1.default(pathPreview);
124
- }
125
- return null;
126
- }
127
120
  preview(renderer) {
128
121
  const paths = this.previewFullPath();
129
122
  if (paths) {
@@ -141,7 +134,7 @@ class PressureSensitiveFreehandLineBuilder {
141
134
  // Ensure we have something.
142
135
  this.addCurve(null);
143
136
  }
144
- return this.previewStroke();
137
+ return new Stroke_1.default(this.previewFullPath());
145
138
  }
146
139
  roundPoint(point) {
147
140
  let minFit = Math.min(this.minFitAllowed, this.curveStartWidth / 3);
@@ -156,25 +149,16 @@ class PressureSensitiveFreehandLineBuilder {
156
149
  return false;
157
150
  }
158
151
  const getIntersection = (curve1, curve2) => {
159
- const intersection = curve1.intersects(curve2);
160
- if (!intersection || intersection.length === 0) {
152
+ const intersections = curve1.intersectsBezier(curve2);
153
+ if (!intersections.length)
161
154
  return null;
162
- }
163
- // From http://pomax.github.io/bezierjs/#intersect-curve,
164
- // .intersects returns an array of 't1/t2' pairs, where curve1.at(t1) gives the point.
165
- const firstTPair = intersection[0];
166
- const match = /^([-0-9.eE]+)\/([-0-9.eE]+)$/.exec(firstTPair);
167
- if (!match) {
168
- throw new Error(`Incorrect format returned by .intersects: ${intersection} should be array of "number/number"!`);
169
- }
170
- const t = parseFloat(match[1]);
171
- return math_1.Vec2.ofXY(curve1.get(t));
155
+ return intersections[0].point;
172
156
  };
173
157
  const getExitDirection = (curve) => {
174
- return math_1.Vec2.ofXY(curve.points[2]).minus(math_1.Vec2.ofXY(curve.points[1])).normalized();
158
+ return curve.p2.minus(curve.p1).normalized();
175
159
  };
176
160
  const getEnterDirection = (curve) => {
177
- return math_1.Vec2.ofXY(curve.points[1]).minus(math_1.Vec2.ofXY(curve.points[0])).normalized();
161
+ return curve.p1.minus(curve.p0).normalized();
178
162
  };
179
163
  // Prevent
180
164
  // /
@@ -185,8 +169,8 @@ class PressureSensitiveFreehandLineBuilder {
185
169
  // where the next stroke and the previous stroke are in different directions.
186
170
  //
187
171
  // Are the exit/enter directions of the previous and current curves in different enough directions?
188
- if (getEnterDirection(upperCurve).dot(getExitDirection(this.lastUpperBezier)) < 0.3
189
- || getEnterDirection(lowerCurve).dot(getExitDirection(this.lastLowerBezier)) < 0.3
172
+ if (getEnterDirection(upperCurve).dot(getExitDirection(this.lastUpperBezier)) < 0.35
173
+ || getEnterDirection(lowerCurve).dot(getExitDirection(this.lastLowerBezier)) < 0.35
190
174
  // Also handle if the curves exit/enter directions differ
191
175
  || getEnterDirection(upperCurve).dot(getExitDirection(upperCurve)) < 0
192
176
  || getEnterDirection(lowerCurve).dot(getExitDirection(lowerCurve)) < 0) {
@@ -242,32 +226,37 @@ class PressureSensitiveFreehandLineBuilder {
242
226
  controlPoint: center.plus(math_1.Vec2.of(width, -width)),
243
227
  endPoint: center.plus(math_1.Vec2.of(width, 0)),
244
228
  });
245
- this.pathStartConnector = {
229
+ const connector = {
246
230
  kind: math_1.PathCommandType.LineTo,
247
231
  point: startPoint,
248
232
  };
249
- this.mostRecentConnector = this.pathStartConnector;
233
+ this.pathStartConnector = [connector];
234
+ this.mostRecentConnector = connector;
250
235
  return;
251
236
  }
252
- const { upperCurveCommand, lowerToUpperConnector, upperToLowerConnector, lowerCurveCommand, lowerCurve, upperCurve, } = this.segmentToPath(curve);
253
- const shouldStartNew = this.shouldStartNewSegment(lowerCurve, upperCurve);
237
+ const { upperCurveCommand, lowerToUpperConnector, upperToLowerConnector, lowerCurveCommand, lowerCurve, upperCurve, nextCurveStartConnector, } = this.segmentToPath(curve);
238
+ let shouldStartNew = this.shouldStartNewSegment(lowerCurve, upperCurve);
254
239
  if (shouldStartNew) {
255
- const part = this.previewCurrentPath();
240
+ const part = this.previewCurrentPath(false);
256
241
  if (part) {
257
242
  this.parts.push(part);
258
243
  this.upperSegments = [];
259
244
  this.lowerSegments = [];
260
245
  }
246
+ else {
247
+ shouldStartNew = false;
248
+ }
261
249
  }
262
250
  if (this.isFirstSegment || shouldStartNew) {
263
251
  // We draw the upper path (reversed), then the lower path, so we need the
264
252
  // upperToLowerConnector to join the two paths.
265
- this.pathStartConnector = upperToLowerConnector;
253
+ this.pathStartConnector = this.nextCurveStartConnector ?? [upperToLowerConnector];
266
254
  this.isFirstSegment = false;
267
255
  }
268
256
  // With the most recent connector, we're joining the end of the lowerPath to the most recent
269
257
  // upperPath:
270
258
  this.mostRecentConnector = lowerToUpperConnector;
259
+ this.nextCurveStartConnector = nextCurveStartConnector;
271
260
  this.lowerSegments.push(lowerCurveCommand);
272
261
  this.upperSegments.push(upperCurveCommand);
273
262
  this.lastLowerBezier = lowerCurve;
@@ -276,9 +265,9 @@ class PressureSensitiveFreehandLineBuilder {
276
265
  }
277
266
  // Returns [upper curve, connector, lower curve]
278
267
  segmentToPath(curve) {
279
- const bezier = new bezier_js_1.Bezier(curve.startPoint.xy, curve.controlPoint.xy, curve.endPoint.xy);
280
- let startVec = math_1.Vec2.ofXY(bezier.normal(0)).normalized();
281
- let endVec = math_1.Vec2.ofXY(bezier.normal(1)).normalized();
268
+ const bezier = new math_1.QuadraticBezier(curve.startPoint, curve.controlPoint, curve.endPoint);
269
+ let startVec = bezier.normal(0);
270
+ let endVec = bezier.normal(1);
282
271
  startVec = startVec.times(curve.startWidth / 2);
283
272
  endVec = endVec.times(curve.endWidth / 2);
284
273
  if (!isFinite(startVec.magnitude())) {
@@ -289,18 +278,9 @@ class PressureSensitiveFreehandLineBuilder {
289
278
  const endPt = curve.endPoint;
290
279
  const controlPoint = curve.controlPoint;
291
280
  // Approximate the normal at the location of the control point
292
- let projectionT = bezier.project(controlPoint.xy).t;
293
- if (!projectionT) {
294
- if (startPt.minus(controlPoint).magnitudeSquared() < endPt.minus(controlPoint).magnitudeSquared()) {
295
- projectionT = 0.1;
296
- }
297
- else {
298
- projectionT = 0.9;
299
- }
300
- }
281
+ const projectionT = bezier.nearestPointTo(controlPoint).parameterValue;
301
282
  const halfVecT = projectionT;
302
- const halfVec = math_1.Vec2.ofXY(bezier.normal(halfVecT))
303
- .normalized().times(curve.startWidth / 2 * halfVecT
283
+ const halfVec = bezier.normal(halfVecT).times(curve.startWidth / 2 * halfVecT
304
284
  + curve.endWidth / 2 * (1 - halfVecT));
305
285
  // Each starts at startPt ± startVec
306
286
  const lowerCurveStartPoint = this.roundPoint(startPt.plus(startVec));
@@ -324,16 +304,29 @@ class PressureSensitiveFreehandLineBuilder {
324
304
  kind: math_1.PathCommandType.LineTo,
325
305
  point: upperCurveStartPoint,
326
306
  };
307
+ // The segment to be used to start the next path (to insert to connect the start of its
308
+ // lower and the end of its upper).
309
+ const nextCurveStartConnector = [
310
+ {
311
+ kind: math_1.PathCommandType.LineTo,
312
+ point: upperCurveStartPoint,
313
+ },
314
+ {
315
+ kind: math_1.PathCommandType.LineTo,
316
+ point: lowerCurveEndPoint,
317
+ },
318
+ ];
327
319
  const upperCurveCommand = {
328
320
  kind: math_1.PathCommandType.QuadraticBezierTo,
329
321
  controlPoint: upperCurveControlPoint,
330
322
  endPoint: upperCurveEndPoint,
331
323
  };
332
- const upperCurve = new bezier_js_1.Bezier(upperCurveStartPoint, upperCurveControlPoint, upperCurveEndPoint);
333
- const lowerCurve = new bezier_js_1.Bezier(lowerCurveStartPoint, lowerCurveControlPoint, lowerCurveEndPoint);
324
+ const upperCurve = new math_1.QuadraticBezier(upperCurveStartPoint, upperCurveControlPoint, upperCurveEndPoint);
325
+ const lowerCurve = new math_1.QuadraticBezier(lowerCurveStartPoint, lowerCurveControlPoint, lowerCurveEndPoint);
334
326
  return {
335
327
  upperCurveCommand, upperToLowerConnector, lowerToUpperConnector, lowerCurveCommand,
336
328
  upperCurve, lowerCurve,
329
+ nextCurveStartConnector,
337
330
  };
338
331
  }
339
332
  addPoint(newPoint) {
@@ -85,7 +85,7 @@ class ShapeFitBuilder {
85
85
  // Find the closest point to the startPoint
86
86
  for (let i = 0; i < templatePoints.length; i++) {
87
87
  const current = templatePoints[i];
88
- const currentSqrDist = current.minus(startPoint).magnitudeSquared();
88
+ const currentSqrDist = current.squareDistanceTo(startPoint);
89
89
  if (!closestToFirst || currentSqrDist < closestToFirstSqrDist) {
90
90
  closestToFirstSqrDist = currentSqrDist;
91
91
  closestToFirst = current;
@@ -1,5 +1,6 @@
1
1
  export * from './builders/types';
2
2
  export { makeFreehandLineBuilder } from './builders/FreehandLineBuilder';
3
+ export { makePolylineBuilder } from './builders/PolylineBuilder';
3
4
  export { makePressureSensitiveFreehandLineBuilder } from './builders/PressureSensitiveFreehandLineBuilder';
4
5
  export { makeOutlinedCircleBuilder } from './builders/CircleBuilder';
5
6
  export { default as StrokeSmoother, Curve as StrokeSmootherCurve } from './util/StrokeSmoother';
@@ -29,10 +29,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
29
29
  return (mod && mod.__esModule) ? mod : { "default": mod };
30
30
  };
31
31
  Object.defineProperty(exports, "__esModule", { value: true });
32
- exports.ImageComponent = exports.BackgroundComponentBackgroundType = exports.BackgroundComponent = exports.StrokeComponent = exports.Text = exports.TextComponent = exports.isRestylableComponent = exports.createRestyleComponentCommand = exports.Stroke = exports.AbstractComponent = exports.StrokeSmoother = exports.makeOutlinedCircleBuilder = exports.makePressureSensitiveFreehandLineBuilder = exports.makeFreehandLineBuilder = void 0;
32
+ exports.ImageComponent = exports.BackgroundComponentBackgroundType = exports.BackgroundComponent = exports.StrokeComponent = exports.Text = exports.TextComponent = exports.isRestylableComponent = exports.createRestyleComponentCommand = exports.Stroke = exports.AbstractComponent = exports.StrokeSmoother = exports.makeOutlinedCircleBuilder = exports.makePressureSensitiveFreehandLineBuilder = exports.makePolylineBuilder = exports.makeFreehandLineBuilder = void 0;
33
33
  __exportStar(require("./builders/types"), exports);
34
34
  var FreehandLineBuilder_1 = require("./builders/FreehandLineBuilder");
35
35
  Object.defineProperty(exports, "makeFreehandLineBuilder", { enumerable: true, get: function () { return FreehandLineBuilder_1.makeFreehandLineBuilder; } });
36
+ var PolylineBuilder_1 = require("./builders/PolylineBuilder");
37
+ Object.defineProperty(exports, "makePolylineBuilder", { enumerable: true, get: function () { return PolylineBuilder_1.makePolylineBuilder; } });
36
38
  var PressureSensitiveFreehandLineBuilder_1 = require("./builders/PressureSensitiveFreehandLineBuilder");
37
39
  Object.defineProperty(exports, "makePressureSensitiveFreehandLineBuilder", { enumerable: true, get: function () { return PressureSensitiveFreehandLineBuilder_1.makePressureSensitiveFreehandLineBuilder; } });
38
40
  var CircleBuilder_1 = require("./builders/CircleBuilder");
@@ -41,8 +41,8 @@ class StrokeSmoother {
41
41
  const startPt = this.currentCurve.p0;
42
42
  const controlPt = this.currentCurve.p1;
43
43
  const endPt = this.currentCurve.p2;
44
- const toControlDist = startPt.minus(controlPt).length();
45
- const toEndDist = endPt.minus(controlPt).length();
44
+ const toControlDist = startPt.distanceTo(controlPt);
45
+ const toEndDist = endPt.distanceTo(controlPt);
46
46
  return toControlDist + toEndDist;
47
47
  }
48
48
  finalizeCurrentCurve() {
@@ -99,7 +99,7 @@ class StrokeSmoother {
99
99
  return;
100
100
  }
101
101
  const threshold = Math.min(this.lastPoint.width, newPoint.width) / 3;
102
- const shouldSnapToInitial = this.startPoint.pos.minus(newPoint.pos).magnitude() < threshold
102
+ const shouldSnapToInitial = this.startPoint.pos.distanceTo(newPoint.pos) < threshold
103
103
  && this.isFirstSegment;
104
104
  // Snap to the starting point if the stroke is contained within a small ball centered
105
105
  // at the starting point.
@@ -150,7 +150,7 @@ class StrokeSmoother {
150
150
  const maxRelativeLength = 1.7;
151
151
  const segmentStart = this.buffer[0];
152
152
  const segmentEnd = newPoint.pos;
153
- const startEndDist = segmentEnd.minus(segmentStart).magnitude();
153
+ const startEndDist = segmentEnd.distanceTo(segmentStart);
154
154
  const maxControlPointDist = maxRelativeLength * startEndDist;
155
155
  // Exit in cases where we would divide by zero
156
156
  if (maxControlPointDist === 0 || exitingVec.magnitude() === 0 || !isFinite(exitingVec.magnitude())) {
@@ -105,7 +105,10 @@ export default class EditorImage {
105
105
  getImportExportRect(): Rect2;
106
106
  /**
107
107
  * Sets the import/export rectangle to the given `imageRect`. Disables
108
- * autoresize (if it was previously enabled).
108
+ * autoresize if it was previously enabled.
109
+ *
110
+ * **Note**: The import/export rectangle is the same as the size of any
111
+ * {@link BackgroundComponent}s (and other components that auto-resize).
109
112
  */
110
113
  setImportExportRect(imageRect: Rect2): SerializableCommand;
111
114
  /** @see {@link setAutoresizeEnabled} */
@@ -235,7 +235,10 @@ class EditorImage {
235
235
  }
236
236
  /**
237
237
  * Sets the import/export rectangle to the given `imageRect`. Disables
238
- * autoresize (if it was previously enabled).
238
+ * autoresize if it was previously enabled.
239
+ *
240
+ * **Note**: The import/export rectangle is the same as the size of any
241
+ * {@link BackgroundComponent}s (and other components that auto-resize).
239
242
  */
240
243
  setImportExportRect(imageRect) {
241
244
  return _a.SetImportExportRectCommand.of(this, imageRect, false);
@@ -657,7 +660,7 @@ class ImageNode {
657
660
  this.bbox = math_1.Rect2.union(...this.children.map(child => child.getBBox()));
658
661
  }
659
662
  if (bubbleUp && !oldBBox.eq(this.bbox)) {
660
- if (!this.bbox.containsRect(oldBBox)) {
663
+ if (this.bbox.containsRect(oldBBox)) {
661
664
  this.parent?.unionBBoxWith(this.bbox);
662
665
  }
663
666
  else {
@@ -53,7 +53,7 @@ export interface KeyUpEvent extends BaseKeyEvent {
53
53
  }
54
54
  export interface CopyEvent {
55
55
  readonly kind: InputEvtType.CopyEvent;
56
- setData(mime: string, data: string): void;
56
+ setData(mime: string, data: string | Promise<Blob>): void;
57
57
  }
58
58
  export interface PasteEvent {
59
59
  readonly kind: InputEvtType.PasteEvent;
@@ -76,7 +76,17 @@ export interface PointerMoveEvt extends PointerEvtBase {
76
76
  export interface PointerUpEvt extends PointerEvtBase {
77
77
  readonly kind: InputEvtType.PointerUpEvt;
78
78
  }
79
+ /**
80
+ * An internal `js-draw` pointer event type.
81
+ *
82
+ * This **is not** the same as a DOM pointer event.
83
+ */
79
84
  export type PointerEvt = PointerDownEvt | PointerMoveEvt | PointerUpEvt;
85
+ /**
86
+ * An internal `js-draw` input event type.
87
+ *
88
+ * These are not DOM events.
89
+ */
80
90
  export type InputEvt = KeyPressEvent | KeyUpEvent | WheelEvt | GestureCancelEvt | PointerEvt | CopyEvent | PasteEvent;
81
91
  export declare const keyUpEventFromHTMLEvent: (event: KeyboardEvent) => KeyUpEvent;
82
92
  export declare const keyPressEventFromHTMLEvent: (event: KeyboardEvent) => KeyPressEvent;
@@ -1,6 +1,9 @@
1
1
  import { EditorLocalization } from '../localization';
2
2
  /**
3
3
  * Comments to help translators create translations.
4
+ *
5
+ * The key for each comment should be the same as is used in the
6
+ * translation and original source records.
4
7
  */
5
8
  declare const comments: Partial<Record<keyof EditorLocalization, string>>;
6
9
  export default comments;
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  /**
4
4
  * Comments to help translators create translations.
5
+ *
6
+ * The key for each comment should be the same as is used in the
7
+ * translation and original source records.
5
8
  */
6
9
  const comments = {
7
10
  pen: 'Likely unused',
@@ -29,7 +29,7 @@ const localization = {
29
29
  selectionToolKeyboardShortcuts: 'Auswahl-Werkzeug: Verwende die Pfeiltasten, um ausgewählte Elemente zu verschieben und ‚i‘ und ‚o‘, um ihre Größe zu ändern.',
30
30
  touchPanning: 'Ansicht mit Touchscreen verschieben',
31
31
  anyDevicePanning: 'Ansicht mit jedem Eingabegerät verschieben',
32
- selectPenTip: 'Objekt-Typ: ',
32
+ selectPenType: 'Objekt-Typ: ',
33
33
  roundedTipPen: 'Freihand',
34
34
  flatTipPen: 'Stift (druckempfindlich)',
35
35
  arrowPen: 'Pfeil',
@@ -111,8 +111,6 @@ const localization = {
111
111
  soundExplorer: 'Klangbasierte Bilderkundung',
112
112
  disableAccessibilityExploreTool: 'Deaktiviere klangbasierte Erkundung',
113
113
  enableAccessibilityExploreTool: 'Aktiviere klangbasierte Erkundung',
114
- copied: (count, description) => `${count} ${description} kopiert`,
115
- pasted: (count, description) => `${count} ${description} eingefügt`,
116
114
  unionOf: (actionDescription, actionCount) => `Vereinigung: ${actionCount} ${actionDescription}`,
117
115
  emptyBackground: 'Leerer Hintergrund',
118
116
  filledBackgroundWithColor: (color) => `Gefüllter Hintergrund (${color})`,
@@ -24,7 +24,7 @@ const localization = {
24
24
  save: 'Guardar',
25
25
  undo: 'Deshace',
26
26
  redo: 'Rehace',
27
- selectPenTip: 'Punta',
27
+ selectPenType: 'Punta',
28
28
  selectShape: 'Forma',
29
29
  pickColorFromScreen: 'Selecciona un color de la pantalla',
30
30
  clickToPickColorAnnouncement: 'Haga un clic en la pantalla para seleccionar un color',
@@ -63,8 +63,8 @@ const localization = {
63
63
  toNextMatch: 'Próxima',
64
64
  closeDialog: 'Cerrar',
65
65
  anyDevicePanning: 'Mover la pantalla con todo dispotivo',
66
- copied: (count, description) => `Copied ${count} ${description}`,
67
- pasted: (count, description) => `Pasted ${count} ${description}`,
66
+ copied: (count) => `${count} cosas fueron copiados`,
67
+ pasted: (count) => count === 1 ? 'Pegado' : `${count} cosas fueron pegados`,
68
68
  toolEnabledAnnouncement: (toolName) => `${toolName} fue activado`,
69
69
  toolDisabledAnnouncement: (toolName) => `${toolName} fue desactivado`,
70
70
  resizeOutputCommand: (newSize) => `Tamaño de imagen fue cambiado a ${newSize.w}x${newSize.h}`,
@@ -58,4 +58,11 @@ export default class CanvasRenderer extends AbstractRenderer {
58
58
  endObject(): void;
59
59
  drawPoints(...points: Point2[]): void;
60
60
  isTooSmallToRender(rect: Rect2): boolean;
61
+ static fromViewport(exportViewport: Viewport, options?: {
62
+ canvasSize?: Vec2;
63
+ maxCanvasDimen?: number;
64
+ }): {
65
+ renderer: CanvasRenderer;
66
+ element: HTMLCanvasElement;
67
+ };
61
68
  }
@@ -250,5 +250,21 @@ class CanvasRenderer extends AbstractRenderer_1.default {
250
250
  const anyTooSmall = Math.abs(diagonal.x) < anyDimenMinSize || Math.abs(diagonal.y) < anyDimenMinSize;
251
251
  return bothTooSmall || anyTooSmall;
252
252
  }
253
+ // @internal
254
+ static fromViewport(exportViewport, options = {}) {
255
+ const canvas = document.createElement('canvas');
256
+ const exportRectSize = exportViewport.getScreenRectSize();
257
+ let canvasSize = options.canvasSize ?? exportRectSize;
258
+ if (options.maxCanvasDimen && canvasSize.maximumEntryMagnitude() > options.maxCanvasDimen) {
259
+ canvasSize = canvasSize.times(options.maxCanvasDimen / canvasSize.maximumEntryMagnitude());
260
+ }
261
+ canvas.width = canvasSize.x;
262
+ canvas.height = canvasSize.y;
263
+ const ctx = canvas.getContext('2d');
264
+ // Scale to ensure that the entire output is visible.
265
+ const scaleFactor = Math.min(canvasSize.x / exportRectSize.x, canvasSize.y / exportRectSize.y);
266
+ ctx.scale(scaleFactor, scaleFactor);
267
+ return { renderer: new CanvasRenderer(ctx, exportViewport), element: canvas };
268
+ }
253
269
  }
254
270
  exports.default = CanvasRenderer;
@@ -116,7 +116,7 @@ class SVGRenderer extends AbstractRenderer_1.default {
116
116
  }
117
117
  if (style.stroke) {
118
118
  pathElem.setAttribute('stroke', style.stroke.color.toHexString());
119
- pathElem.setAttribute('stroke-width', (0, math_1.toRoundedString)(style.stroke.width));
119
+ pathElem.setAttribute('stroke-width', (0, math_1.toRoundedString)(style.stroke.width * this.getSizeOfCanvasPixelOnScreen()));
120
120
  }
121
121
  this.elem.appendChild(pathElem);
122
122
  this.objectElems?.push(pathElem);
@@ -1,4 +1,4 @@
1
- import Editor from '../Editor';
1
+ import Editor, { EditorSettings } from '../Editor';
2
2
  /** Creates an editor. Should only be used in test files. */
3
- declare const _default: () => Editor;
3
+ declare const _default: (settings?: Partial<EditorSettings>) => Editor;
4
4
  export default _default;
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const Display_1 = require("../rendering/Display");
7
7
  const Editor_1 = __importDefault(require("../Editor"));
8
8
  /** Creates an editor. Should only be used in test files. */
9
- exports.default = () => {
9
+ exports.default = (settings) => {
10
10
  if (jest === undefined) {
11
11
  throw new Error('Files in the testing/ folder should only be used in tests!');
12
12
  }
13
- return new Editor_1.default(document.body, { renderingMode: Display_1.RenderingMode.DummyRenderer });
13
+ return new Editor_1.default(document.body, { renderingMode: Display_1.RenderingMode.DummyRenderer, ...settings });
14
14
  };
@@ -1,10 +1,12 @@
1
1
  import { Color4 } from '@js-draw/math';
2
2
  import TextRenderingStyle from '../rendering/TextRenderingStyle';
3
3
  import { PenStyle } from '../tools/Pen';
4
+ import { EraserMode } from '../tools/Eraser';
4
5
  export type IconElemType = HTMLImageElement | SVGElement;
5
6
  /**
6
- * Provides icons that can be used in the toolbar, etc.
7
- * Extend this class and override methods to customize icons.
7
+ * Provides icons that can be used in the toolbar and other locations.
8
+ *
9
+ * To customize the icons used by the editor, extend this class and override methods.
8
10
  *
9
11
  * @example
10
12
  * ```ts,runnable
@@ -12,7 +14,7 @@ export type IconElemType = HTMLImageElement | SVGElement;
12
14
  *
13
15
  * class CustomIconProvider extends jsdraw.IconProvider {
14
16
  * // Use '☺' instead of the default dropdown symbol.
15
- * public makeDropdownIcon() {
17
+ * public override makeDropdownIcon() {
16
18
  * const icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
17
19
  * icon.innerHTML = `
18
20
  * <text x='5' y='55' style='fill: var(--icon-color); font-size: 50pt;'>☺</text>
@@ -24,6 +26,8 @@ export type IconElemType = HTMLImageElement | SVGElement;
24
26
  *
25
27
  * const icons = new CustomIconProvider();
26
28
  * const editor = new jsdraw.Editor(document.body, {
29
+ * // The icon pack to use is specified through the editor's initial
30
+ * // configuration object:
27
31
  * iconProvider: icons,
28
32
  * });
29
33
  *
@@ -36,7 +40,7 @@ export default class IconProvider {
36
40
  makeUndoIcon(): IconElemType;
37
41
  makeRedoIcon(): IconElemType;
38
42
  makeDropdownIcon(): IconElemType;
39
- makeEraserIcon(eraserSize?: number): IconElemType;
43
+ makeEraserIcon(eraserSize?: number, mode?: EraserMode): IconElemType;
40
44
  makeSelectionIcon(): IconElemType;
41
45
  makeRotateIcon(): IconElemType;
42
46
  makeHandToolIcon(): IconElemType;
@@ -85,6 +89,7 @@ export default class IconProvider {
85
89
  * @returns true if the given `penStyle` is known to match a rounded tip type of pen.
86
90
  */
87
91
  protected isRoundedTipPen(penStyle: PenStyle): boolean;
92
+ protected isPolylinePen(penStyle: PenStyle): boolean;
88
93
  /** Must be overridden by icon packs that need attribution. */
89
94
  licenseInfo(): string | null;
90
95
  }
@@ -13,6 +13,8 @@ const math_1 = require("@js-draw/math");
13
13
  const SVGRenderer_1 = __importDefault(require("../rendering/renderers/SVGRenderer"));
14
14
  const Viewport_1 = __importDefault(require("../Viewport"));
15
15
  const FreehandLineBuilder_1 = require("../components/builders/FreehandLineBuilder");
16
+ const PolylineBuilder_1 = require("../components/builders/PolylineBuilder");
17
+ const Eraser_1 = require("../tools/Eraser");
16
18
  const svgNamespace = 'http://www.w3.org/2000/svg';
17
19
  const iconColorFill = `
18
20
  style='fill: var(--icon-color);'
@@ -62,8 +64,9 @@ const makeRedoIcon = (mirror) => {
62
64
  return icon;
63
65
  };
64
66
  /**
65
- * Provides icons that can be used in the toolbar, etc.
66
- * Extend this class and override methods to customize icons.
67
+ * Provides icons that can be used in the toolbar and other locations.
68
+ *
69
+ * To customize the icons used by the editor, extend this class and override methods.
67
70
  *
68
71
  * @example
69
72
  * ```ts,runnable
@@ -71,7 +74,7 @@ const makeRedoIcon = (mirror) => {
71
74
  *
72
75
  * class CustomIconProvider extends jsdraw.IconProvider {
73
76
  * // Use '☺' instead of the default dropdown symbol.
74
- * public makeDropdownIcon() {
77
+ * public override makeDropdownIcon() {
75
78
  * const icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
76
79
  * icon.innerHTML = `
77
80
  * <text x='5' y='55' style='fill: var(--icon-color); font-size: 50pt;'>☺</text>
@@ -83,6 +86,8 @@ const makeRedoIcon = (mirror) => {
83
86
  *
84
87
  * const icons = new CustomIconProvider();
85
88
  * const editor = new jsdraw.Editor(document.body, {
89
+ * // The icon pack to use is specified through the editor's initial
90
+ * // configuration object:
86
91
  * iconProvider: icons,
87
92
  * });
88
93
  *
@@ -97,7 +102,6 @@ class IconProvider {
97
102
  makeUndoIcon() {
98
103
  return makeRedoIcon(true);
99
104
  }
100
- // @param mirror - reflect across the x-axis. This parameter is internal.
101
105
  // @returns a redo icon.
102
106
  makeRedoIcon() {
103
107
  return makeRedoIcon(false);
@@ -115,16 +119,23 @@ class IconProvider {
115
119
  icon.setAttribute('viewBox', '-10 -10 110 110');
116
120
  return icon;
117
121
  }
118
- makeEraserIcon(eraserSize) {
122
+ makeEraserIcon(eraserSize, mode) {
119
123
  const icon = document.createElementNS(svgNamespace, 'svg');
120
124
  eraserSize ??= 10;
121
125
  const scaledSize = eraserSize / 4;
122
126
  const eraserColor = '#ff70af';
123
127
  // Draw an eraser-like shape. Created with Inkscape
124
128
  icon.innerHTML = `
129
+ <defs>
130
+ <linearGradient id="dash-pattern">
131
+ <stop offset="80%" stop-color="${eraserColor}"/>
132
+ <stop offset="85%" stop-color="white"/>
133
+ <stop offset="90%" stop-color="${eraserColor}"/>
134
+ </linearGradient>
135
+ </defs>
125
136
  <g>
126
137
  <path
127
- style="fill:${eraserColor}"
138
+ style="fill:${mode === Eraser_1.EraserMode.PartialStroke ? 'url(#dash-pattern)' : eraserColor}"
128
139
  stroke="black"
129
140
  transform="rotate(41.35)"
130
141
  d="M 52.5 27
@@ -838,7 +849,10 @@ class IconProvider {
838
849
  * @returns true if the given `penStyle` is known to match a rounded tip type of pen.
839
850
  */
840
851
  isRoundedTipPen(penStyle) {
841
- return penStyle.factory === FreehandLineBuilder_1.makeFreehandLineBuilder;
852
+ return penStyle.factory === FreehandLineBuilder_1.makeFreehandLineBuilder || penStyle.factory === PolylineBuilder_1.makePolylineBuilder;
853
+ }
854
+ isPolylinePen(penStyle) {
855
+ return penStyle.factory === PolylineBuilder_1.makePolylineBuilder;
842
856
  }
843
857
  /** Must be overridden by icon packs that need attribution. */
844
858
  licenseInfo() { return null; }
@@ -18,8 +18,9 @@ export interface ToolbarLocalization extends ToolbarUtilsLocalization {
18
18
  cancel: string;
19
19
  submit: string;
20
20
  roundedTipPen: string;
21
+ roundedTipPen2: string;
21
22
  flatTipPen: string;
22
- selectPenTip: string;
23
+ selectPenType: string;
23
24
  selectShape: string;
24
25
  colorLabel: string;
25
26
  pen: string;
@@ -30,6 +31,7 @@ export interface ToolbarLocalization extends ToolbarUtilsLocalization {
30
31
  resizeImageToSelection: string;
31
32
  deleteSelection: string;
32
33
  duplicateSelection: string;
34
+ fullStrokeEraser: string;
33
35
  pickColorFromScreen: string;
34
36
  clickToPickColorAnnouncement: string;
35
37
  colorSelectionCanceledAnnouncement: string;
@@ -66,7 +68,10 @@ export interface ToolbarLocalization extends ToolbarUtilsLocalization {
66
68
  handDropdown__zoomOutHelpText: string;
67
69
  handDropdown__resetViewHelpText: string;
68
70
  handDropdown__touchPanningHelpText: string;
71
+ eraserDropdown__baseHelpText: string;
72
+ eraserDropdown__fullStrokeEraserHelpText: string;
69
73
  handDropdown__lockRotationHelpText: string;
74
+ eraserDropdown__thicknessHelpText: string;
70
75
  selectionDropdown__baseHelpText: string;
71
76
  selectionDropdown__resizeToHelpText: string;
72
77
  selectionDropdown__deleteHelpText: string;