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.
- package/README.md +70 -10
- package/dist/bundle.js +2 -2
- package/dist/bundledStyles.js +1 -1
- package/dist/cjs/Editor.d.ts +75 -7
- package/dist/cjs/Editor.js +93 -90
- package/dist/cjs/Pointer.d.ts +2 -1
- package/dist/cjs/Pointer.js +9 -2
- package/dist/cjs/commands/localization.d.ts +1 -0
- package/dist/cjs/commands/localization.js +1 -0
- package/dist/cjs/commands/uniteCommands.d.ts +5 -1
- package/dist/cjs/commands/uniteCommands.js +33 -7
- package/dist/cjs/components/AbstractComponent.d.ts +17 -5
- package/dist/cjs/components/AbstractComponent.js +15 -15
- package/dist/cjs/components/Stroke.d.ts +4 -1
- package/dist/cjs/components/Stroke.js +158 -2
- package/dist/cjs/components/TextComponent.d.ts +36 -1
- package/dist/cjs/components/TextComponent.js +39 -1
- package/dist/cjs/components/builders/ArrowBuilder.js +1 -1
- package/dist/cjs/components/builders/PolylineBuilder.d.ts +35 -0
- package/dist/cjs/components/builders/PolylineBuilder.js +122 -0
- package/dist/cjs/components/builders/PressureSensitiveFreehandLineBuilder.d.ts +1 -1
- package/dist/cjs/components/builders/PressureSensitiveFreehandLineBuilder.js +44 -51
- package/dist/cjs/components/builders/autocorrect/makeShapeFitAutocorrect.js +1 -1
- package/dist/cjs/components/lib.d.ts +1 -0
- package/dist/cjs/components/lib.js +3 -1
- package/dist/cjs/components/util/StrokeSmoother.js +4 -4
- package/dist/cjs/image/EditorImage.d.ts +4 -1
- package/dist/cjs/image/EditorImage.js +5 -2
- package/dist/cjs/inputEvents.d.ts +11 -1
- package/dist/cjs/localizations/comments.d.ts +3 -0
- package/dist/cjs/localizations/comments.js +3 -0
- package/dist/cjs/localizations/de.js +1 -3
- package/dist/cjs/localizations/es.js +3 -3
- package/dist/cjs/rendering/renderers/CanvasRenderer.d.ts +7 -0
- package/dist/cjs/rendering/renderers/CanvasRenderer.js +16 -0
- package/dist/cjs/rendering/renderers/SVGRenderer.js +1 -1
- package/dist/cjs/testing/createEditor.d.ts +2 -2
- package/dist/cjs/testing/createEditor.js +2 -2
- package/dist/cjs/toolbar/IconProvider.d.ts +9 -4
- package/dist/cjs/toolbar/IconProvider.js +21 -7
- package/dist/cjs/toolbar/localization.d.ts +6 -1
- package/dist/cjs/toolbar/localization.js +7 -2
- package/dist/cjs/toolbar/widgets/DocumentPropertiesWidget.js +24 -1
- package/dist/cjs/toolbar/widgets/EraserToolWidget.d.ts +6 -1
- package/dist/cjs/toolbar/widgets/EraserToolWidget.js +45 -5
- package/dist/cjs/toolbar/widgets/PenToolWidget.d.ts +1 -1
- package/dist/cjs/toolbar/widgets/PenToolWidget.js +17 -4
- package/dist/cjs/toolbar/widgets/PenToolWidget.test.d.ts +1 -0
- package/dist/cjs/toolbar/widgets/keybindings.js +1 -1
- package/dist/cjs/tools/Eraser.d.ts +24 -4
- package/dist/cjs/tools/Eraser.js +108 -21
- package/dist/cjs/tools/InputFilter/InputStabilizer.js +3 -3
- package/dist/cjs/tools/PasteHandler.js +35 -10
- package/dist/cjs/tools/Pen.js +2 -2
- package/dist/cjs/tools/SelectionTool/SelectionTool.js +23 -4
- package/dist/cjs/tools/SelectionTool/ToPointerAutoscroller.js +1 -1
- package/dist/cjs/tools/ToolController.d.ts +17 -1
- package/dist/cjs/tools/ToolController.js +21 -8
- package/dist/cjs/tools/lib.d.ts +1 -4
- package/dist/cjs/tools/lib.js +2 -4
- package/dist/cjs/tools/localization.d.ts +2 -2
- package/dist/cjs/tools/localization.js +2 -2
- package/dist/cjs/util/ClipboardHandler.d.ts +27 -0
- package/dist/cjs/util/ClipboardHandler.js +205 -0
- package/dist/cjs/util/ClipboardHandler.test.d.ts +1 -0
- package/dist/cjs/version.d.ts +5 -0
- package/dist/cjs/version.js +6 -1
- package/dist/mjs/Editor.d.ts +75 -7
- package/dist/mjs/Editor.mjs +93 -90
- package/dist/mjs/Pointer.d.ts +2 -1
- package/dist/mjs/Pointer.mjs +9 -2
- package/dist/mjs/commands/localization.d.ts +1 -0
- package/dist/mjs/commands/localization.mjs +1 -0
- package/dist/mjs/commands/uniteCommands.d.ts +5 -1
- package/dist/mjs/commands/uniteCommands.mjs +33 -7
- package/dist/mjs/components/AbstractComponent.d.ts +17 -5
- package/dist/mjs/components/AbstractComponent.mjs +15 -15
- package/dist/mjs/components/Stroke.d.ts +4 -1
- package/dist/mjs/components/Stroke.mjs +159 -3
- package/dist/mjs/components/TextComponent.d.ts +36 -1
- package/dist/mjs/components/TextComponent.mjs +40 -2
- package/dist/mjs/components/builders/ArrowBuilder.mjs +1 -1
- package/dist/mjs/components/builders/PolylineBuilder.d.ts +35 -0
- package/dist/mjs/components/builders/PolylineBuilder.mjs +115 -0
- package/dist/mjs/components/builders/PressureSensitiveFreehandLineBuilder.d.ts +1 -1
- package/dist/mjs/components/builders/PressureSensitiveFreehandLineBuilder.mjs +45 -52
- package/dist/mjs/components/builders/autocorrect/makeShapeFitAutocorrect.mjs +1 -1
- package/dist/mjs/components/lib.d.ts +1 -0
- package/dist/mjs/components/lib.mjs +1 -0
- package/dist/mjs/components/util/StrokeSmoother.mjs +4 -4
- package/dist/mjs/image/EditorImage.d.ts +4 -1
- package/dist/mjs/image/EditorImage.mjs +5 -2
- package/dist/mjs/inputEvents.d.ts +11 -1
- package/dist/mjs/localizations/comments.d.ts +3 -0
- package/dist/mjs/localizations/comments.mjs +3 -0
- package/dist/mjs/localizations/de.mjs +1 -3
- package/dist/mjs/localizations/es.mjs +3 -3
- package/dist/mjs/rendering/renderers/CanvasRenderer.d.ts +7 -0
- package/dist/mjs/rendering/renderers/CanvasRenderer.mjs +16 -0
- package/dist/mjs/rendering/renderers/SVGRenderer.mjs +1 -1
- package/dist/mjs/testing/createEditor.d.ts +2 -2
- package/dist/mjs/testing/createEditor.mjs +2 -2
- package/dist/mjs/toolbar/IconProvider.d.ts +9 -4
- package/dist/mjs/toolbar/IconProvider.mjs +21 -7
- package/dist/mjs/toolbar/localization.d.ts +6 -1
- package/dist/mjs/toolbar/localization.mjs +7 -2
- package/dist/mjs/toolbar/widgets/DocumentPropertiesWidget.mjs +24 -1
- package/dist/mjs/toolbar/widgets/EraserToolWidget.d.ts +6 -1
- package/dist/mjs/toolbar/widgets/EraserToolWidget.mjs +47 -6
- package/dist/mjs/toolbar/widgets/PenToolWidget.d.ts +1 -1
- package/dist/mjs/toolbar/widgets/PenToolWidget.mjs +17 -4
- package/dist/mjs/toolbar/widgets/PenToolWidget.test.d.ts +1 -0
- package/dist/mjs/toolbar/widgets/keybindings.mjs +1 -1
- package/dist/mjs/tools/Eraser.d.ts +24 -4
- package/dist/mjs/tools/Eraser.mjs +108 -22
- package/dist/mjs/tools/InputFilter/InputStabilizer.mjs +3 -3
- package/dist/mjs/tools/PasteHandler.mjs +35 -10
- package/dist/mjs/tools/Pen.mjs +2 -2
- package/dist/mjs/tools/SelectionTool/SelectionTool.mjs +23 -4
- package/dist/mjs/tools/SelectionTool/ToPointerAutoscroller.mjs +1 -1
- package/dist/mjs/tools/ToolController.d.ts +17 -1
- package/dist/mjs/tools/ToolController.mjs +21 -8
- package/dist/mjs/tools/lib.d.ts +1 -4
- package/dist/mjs/tools/lib.mjs +1 -4
- package/dist/mjs/tools/localization.d.ts +2 -2
- package/dist/mjs/tools/localization.mjs +2 -2
- package/dist/mjs/util/ClipboardHandler.d.ts +27 -0
- package/dist/mjs/util/ClipboardHandler.mjs +200 -0
- package/dist/mjs/util/ClipboardHandler.test.d.ts +1 -0
- package/dist/mjs/version.d.ts +5 -0
- package/dist/mjs/version.mjs +6 -1
- 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.
|
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
|
160
|
-
if (!
|
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
|
158
|
+
return curve.p2.minus(curve.p1).normalized();
|
175
159
|
};
|
176
160
|
const getEnterDirection = (curve) => {
|
177
|
-
return
|
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.
|
189
|
-
|| getEnterDirection(lowerCurve).dot(getExitDirection(this.lastLowerBezier)) < 0.
|
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
|
-
|
229
|
+
const connector = {
|
246
230
|
kind: math_1.PathCommandType.LineTo,
|
247
231
|
point: startPoint,
|
248
232
|
};
|
249
|
-
this.
|
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
|
-
|
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
|
280
|
-
let startVec =
|
281
|
-
let endVec =
|
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
|
-
|
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 =
|
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
|
333
|
-
const lowerCurve = new
|
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.
|
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.
|
45
|
-
const toEndDist = endPt.
|
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.
|
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.
|
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
|
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
|
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 (
|
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
|
-
|
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
|
-
|
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
|
67
|
-
pasted: (count
|
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
|
7
|
-
*
|
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
|
66
|
-
*
|
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
|
-
|
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;
|