js-draw 1.11.1 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.js +2 -2
- package/dist/cjs/Editor.js +18 -4
- package/dist/cjs/components/util/StrokeSmoother.d.ts +1 -0
- package/dist/cjs/components/util/StrokeSmoother.js +25 -7
- package/dist/cjs/util/mitLicenseAttribution.d.ts +2 -0
- package/dist/cjs/util/mitLicenseAttribution.js +28 -0
- package/dist/cjs/version.js +1 -1
- package/dist/mjs/Editor.mjs +18 -4
- package/dist/mjs/components/util/StrokeSmoother.d.ts +1 -0
- package/dist/mjs/components/util/StrokeSmoother.mjs +25 -7
- package/dist/mjs/util/mitLicenseAttribution.d.ts +2 -0
- package/dist/mjs/util/mitLicenseAttribution.mjs +26 -0
- package/dist/mjs/version.mjs +1 -1
- package/package.json +2 -2
package/dist/cjs/Editor.js
CHANGED
@@ -57,6 +57,7 @@ const version_1 = __importDefault(require("./version"));
|
|
57
57
|
const editorImageToSVG_1 = require("./image/export/editorImageToSVG");
|
58
58
|
const ReactiveValue_1 = require("./util/ReactiveValue");
|
59
59
|
const listenForKeyboardEventsFrom_1 = __importDefault(require("./util/listenForKeyboardEventsFrom"));
|
60
|
+
const mitLicenseAttribution_1 = __importDefault(require("./util/mitLicenseAttribution"));
|
60
61
|
/**
|
61
62
|
* The main entrypoint for the full editor.
|
62
63
|
*
|
@@ -591,7 +592,8 @@ class Editor {
|
|
591
592
|
return false;
|
592
593
|
}
|
593
594
|
// Position of the current event.
|
594
|
-
|
595
|
+
// jsdom doesn't seem to support pageX/pageY -- use clientX/clientY if unavailable
|
596
|
+
const currentPos = math_1.Vec2.of(event.pageX ?? event.clientX, event.pageY ?? event.clientY);
|
595
597
|
const pointerId = event.pointerId ?? 0;
|
596
598
|
// Whether to send the current event to the editor
|
597
599
|
let sendToEditor = true;
|
@@ -601,6 +603,7 @@ class Editor {
|
|
601
603
|
gestureData[pointerId] = {
|
602
604
|
eventBuffer: [[eventName, event]],
|
603
605
|
startPoint: currentPos,
|
606
|
+
hasMovedSignificantly: false,
|
604
607
|
};
|
605
608
|
// Capture the pointer so we receive future events even if the overlay is hidden.
|
606
609
|
this.setPointerCapture(elem, event.pointerId);
|
@@ -613,7 +616,7 @@ class Editor {
|
|
613
616
|
// Skip if the pointer hasn't moved enough to not be a "click".
|
614
617
|
const strokeStartThreshold = 10;
|
615
618
|
const isWithinClickThreshold = gestureStartPos && currentPos.minus(gestureStartPos).magnitude() < strokeStartThreshold;
|
616
|
-
if (isWithinClickThreshold) {
|
619
|
+
if (isWithinClickThreshold && !gestureData[pointerId].hasMovedSignificantly) {
|
617
620
|
eventBuffer.push([eventName, event]);
|
618
621
|
sendToEditor = false;
|
619
622
|
}
|
@@ -623,6 +626,7 @@ class Editor {
|
|
623
626
|
this.handleHTMLPointerEvent(eventName, event);
|
624
627
|
}
|
625
628
|
gestureData[pointerId].eventBuffer = [];
|
629
|
+
gestureData[pointerId].hasMovedSignificantly = true;
|
626
630
|
sendToEditor = true;
|
627
631
|
}
|
628
632
|
}
|
@@ -1199,9 +1203,19 @@ class Editor {
|
|
1199
1203
|
text: [
|
1200
1204
|
`This image editor is powered by js-draw v${version_1.default.number}.`,
|
1201
1205
|
'',
|
1202
|
-
'js-draw uses
|
1206
|
+
'At runtime, js-draw uses',
|
1203
1207
|
' - The Coloris color picker: https://github.com/mdbassit/Coloris',
|
1204
|
-
' - The bezier.js Bézier curve library: https://github.com/Pomax/bezierjs'
|
1208
|
+
' - The bezier.js Bézier curve library: https://github.com/Pomax/bezierjs',
|
1209
|
+
'',
|
1210
|
+
'Both are licensed under the MIT license:',
|
1211
|
+
'',
|
1212
|
+
'',
|
1213
|
+
'== Coloris ==',
|
1214
|
+
(0, mitLicenseAttribution_1.default)('2021 Mohammed Bassit'),
|
1215
|
+
'',
|
1216
|
+
'',
|
1217
|
+
'== Bezier.js ==',
|
1218
|
+
(0, mitLicenseAttribution_1.default)('2023 Mike "Pomax" Kamermans'),
|
1205
1219
|
].join('\n'),
|
1206
1220
|
minimized: true,
|
1207
1221
|
});
|
@@ -15,6 +15,7 @@ class StrokeSmoother {
|
|
15
15
|
this.maxFitAllowed = maxFitAllowed;
|
16
16
|
this.onCurveAdded = onCurveAdded;
|
17
17
|
this.isFirstSegment = true;
|
18
|
+
this.centerOfMass = null;
|
18
19
|
this.lastExitingVec = null;
|
19
20
|
this.currentCurve = null;
|
20
21
|
this.lastPoint = this.startPoint;
|
@@ -60,6 +61,7 @@ class StrokeSmoother {
|
|
60
61
|
this.buffer[this.buffer.length - 2], lastPoint,
|
61
62
|
];
|
62
63
|
this.currentCurve = null;
|
64
|
+
this.centerOfMass = null;
|
63
65
|
this.isFirstSegment = false;
|
64
66
|
}
|
65
67
|
// Returns [upper curve, connector, lower curve]
|
@@ -107,10 +109,20 @@ class StrokeSmoother {
|
|
107
109
|
if (shouldSnapToInitial) {
|
108
110
|
return;
|
109
111
|
}
|
112
|
+
if (!this.centerOfMass) {
|
113
|
+
this.centerOfMass = newPoint.pos;
|
114
|
+
}
|
115
|
+
else {
|
116
|
+
this.centerOfMass = this.centerOfMass
|
117
|
+
.times(this.buffer.length)
|
118
|
+
.plus(newPoint.pos).times(1 / (this.buffer.length + 1));
|
119
|
+
}
|
120
|
+
const toCenterOfMass = this.centerOfMass.minus(newPoint.pos);
|
110
121
|
const deltaTimeSeconds = deltaTime / 1000;
|
111
122
|
const velocity = newPoint.pos.minus(this.lastPoint.pos).times(1 / deltaTimeSeconds);
|
112
123
|
// TODO: Do we need momentum smoothing? (this.momentum.lerp(velocity, 0.9);)
|
113
|
-
|
124
|
+
const k = 1;
|
125
|
+
this.momentum = velocity.plus(toCenterOfMass.times(k));
|
114
126
|
}
|
115
127
|
const lastPoint = this.lastPoint ?? newPoint;
|
116
128
|
this.lastPoint = newPoint;
|
@@ -147,7 +159,7 @@ class StrokeSmoother {
|
|
147
159
|
}
|
148
160
|
let exitingVec = this.computeExitingVec();
|
149
161
|
// Find the intersection between the entering vector and the exiting vector
|
150
|
-
const maxRelativeLength =
|
162
|
+
const maxRelativeLength = 1.6;
|
151
163
|
const segmentStart = this.buffer[0];
|
152
164
|
const segmentEnd = newPoint.pos;
|
153
165
|
const startEndDist = segmentEnd.minus(segmentStart).magnitude();
|
@@ -168,11 +180,17 @@ class StrokeSmoother {
|
|
168
180
|
if (intersection) {
|
169
181
|
controlPoint = intersection.point;
|
170
182
|
}
|
171
|
-
// No intersection
|
172
|
-
if (!controlPoint
|
173
|
-
//
|
174
|
-
|
175
|
-
|
183
|
+
// No intersection?
|
184
|
+
if (!controlPoint) {
|
185
|
+
// Estimate the control point position based on the entering tangent line
|
186
|
+
controlPoint = segmentStart
|
187
|
+
.lerp(segmentEnd, 0.5)
|
188
|
+
.lerp(segmentStart.plus(enteringVec.times(startEndDist)), 0.25);
|
189
|
+
}
|
190
|
+
// Equal to an endpoint?
|
191
|
+
if (segmentStart.eq(controlPoint) || segmentEnd.eq(controlPoint)) {
|
192
|
+
// Position the control point between the two end points
|
193
|
+
controlPoint = segmentStart.lerp(segmentEnd, 0.5);
|
176
194
|
}
|
177
195
|
console.assert(!segmentStart.eq(controlPoint, 1e-11), 'Start and control points are equal!');
|
178
196
|
console.assert(!controlPoint.eq(segmentEnd, 1e-11), 'Control and end points are equal!');
|
@@ -0,0 +1,28 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
const mitLicenseAttribution = (copyright) => {
|
4
|
+
const removeSingleLineBreaks = (text) => text.replace(/([^\n])[\n]([^\n])/g, '$1 $2');
|
5
|
+
return removeSingleLineBreaks(`
|
6
|
+
MIT License
|
7
|
+
|
8
|
+
Copyright (c) ${copyright}
|
9
|
+
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
12
|
+
in the Software without restriction, including without limitation the rights
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
15
|
+
furnished to do so, subject to the following conditions:
|
16
|
+
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
18
|
+
copies or substantial portions of the Software.
|
19
|
+
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
26
|
+
SOFTWARE.`);
|
27
|
+
};
|
28
|
+
exports.default = mitLicenseAttribution;
|
package/dist/cjs/version.js
CHANGED
package/dist/mjs/Editor.mjs
CHANGED
@@ -28,6 +28,7 @@ import version from './version.mjs';
|
|
28
28
|
import { editorImageToSVGSync, editorImageToSVGAsync } from './image/export/editorImageToSVG.mjs';
|
29
29
|
import { MutableReactiveValue } from './util/ReactiveValue.mjs';
|
30
30
|
import listenForKeyboardEventsFrom from './util/listenForKeyboardEventsFrom.mjs';
|
31
|
+
import mitLicenseAttribution from './util/mitLicenseAttribution.mjs';
|
31
32
|
/**
|
32
33
|
* The main entrypoint for the full editor.
|
33
34
|
*
|
@@ -562,7 +563,8 @@ export class Editor {
|
|
562
563
|
return false;
|
563
564
|
}
|
564
565
|
// Position of the current event.
|
565
|
-
|
566
|
+
// jsdom doesn't seem to support pageX/pageY -- use clientX/clientY if unavailable
|
567
|
+
const currentPos = Vec2.of(event.pageX ?? event.clientX, event.pageY ?? event.clientY);
|
566
568
|
const pointerId = event.pointerId ?? 0;
|
567
569
|
// Whether to send the current event to the editor
|
568
570
|
let sendToEditor = true;
|
@@ -572,6 +574,7 @@ export class Editor {
|
|
572
574
|
gestureData[pointerId] = {
|
573
575
|
eventBuffer: [[eventName, event]],
|
574
576
|
startPoint: currentPos,
|
577
|
+
hasMovedSignificantly: false,
|
575
578
|
};
|
576
579
|
// Capture the pointer so we receive future events even if the overlay is hidden.
|
577
580
|
this.setPointerCapture(elem, event.pointerId);
|
@@ -584,7 +587,7 @@ export class Editor {
|
|
584
587
|
// Skip if the pointer hasn't moved enough to not be a "click".
|
585
588
|
const strokeStartThreshold = 10;
|
586
589
|
const isWithinClickThreshold = gestureStartPos && currentPos.minus(gestureStartPos).magnitude() < strokeStartThreshold;
|
587
|
-
if (isWithinClickThreshold) {
|
590
|
+
if (isWithinClickThreshold && !gestureData[pointerId].hasMovedSignificantly) {
|
588
591
|
eventBuffer.push([eventName, event]);
|
589
592
|
sendToEditor = false;
|
590
593
|
}
|
@@ -594,6 +597,7 @@ export class Editor {
|
|
594
597
|
this.handleHTMLPointerEvent(eventName, event);
|
595
598
|
}
|
596
599
|
gestureData[pointerId].eventBuffer = [];
|
600
|
+
gestureData[pointerId].hasMovedSignificantly = true;
|
597
601
|
sendToEditor = true;
|
598
602
|
}
|
599
603
|
}
|
@@ -1170,9 +1174,19 @@ export class Editor {
|
|
1170
1174
|
text: [
|
1171
1175
|
`This image editor is powered by js-draw v${version.number}.`,
|
1172
1176
|
'',
|
1173
|
-
'js-draw uses
|
1177
|
+
'At runtime, js-draw uses',
|
1174
1178
|
' - The Coloris color picker: https://github.com/mdbassit/Coloris',
|
1175
|
-
' - The bezier.js Bézier curve library: https://github.com/Pomax/bezierjs'
|
1179
|
+
' - The bezier.js Bézier curve library: https://github.com/Pomax/bezierjs',
|
1180
|
+
'',
|
1181
|
+
'Both are licensed under the MIT license:',
|
1182
|
+
'',
|
1183
|
+
'',
|
1184
|
+
'== Coloris ==',
|
1185
|
+
mitLicenseAttribution('2021 Mohammed Bassit'),
|
1186
|
+
'',
|
1187
|
+
'',
|
1188
|
+
'== Bezier.js ==',
|
1189
|
+
mitLicenseAttribution('2023 Mike "Pomax" Kamermans'),
|
1176
1190
|
].join('\n'),
|
1177
1191
|
minimized: true,
|
1178
1192
|
});
|
@@ -12,6 +12,7 @@ export class StrokeSmoother {
|
|
12
12
|
this.maxFitAllowed = maxFitAllowed;
|
13
13
|
this.onCurveAdded = onCurveAdded;
|
14
14
|
this.isFirstSegment = true;
|
15
|
+
this.centerOfMass = null;
|
15
16
|
this.lastExitingVec = null;
|
16
17
|
this.currentCurve = null;
|
17
18
|
this.lastPoint = this.startPoint;
|
@@ -57,6 +58,7 @@ export class StrokeSmoother {
|
|
57
58
|
this.buffer[this.buffer.length - 2], lastPoint,
|
58
59
|
];
|
59
60
|
this.currentCurve = null;
|
61
|
+
this.centerOfMass = null;
|
60
62
|
this.isFirstSegment = false;
|
61
63
|
}
|
62
64
|
// Returns [upper curve, connector, lower curve]
|
@@ -104,10 +106,20 @@ export class StrokeSmoother {
|
|
104
106
|
if (shouldSnapToInitial) {
|
105
107
|
return;
|
106
108
|
}
|
109
|
+
if (!this.centerOfMass) {
|
110
|
+
this.centerOfMass = newPoint.pos;
|
111
|
+
}
|
112
|
+
else {
|
113
|
+
this.centerOfMass = this.centerOfMass
|
114
|
+
.times(this.buffer.length)
|
115
|
+
.plus(newPoint.pos).times(1 / (this.buffer.length + 1));
|
116
|
+
}
|
117
|
+
const toCenterOfMass = this.centerOfMass.minus(newPoint.pos);
|
107
118
|
const deltaTimeSeconds = deltaTime / 1000;
|
108
119
|
const velocity = newPoint.pos.minus(this.lastPoint.pos).times(1 / deltaTimeSeconds);
|
109
120
|
// TODO: Do we need momentum smoothing? (this.momentum.lerp(velocity, 0.9);)
|
110
|
-
|
121
|
+
const k = 1;
|
122
|
+
this.momentum = velocity.plus(toCenterOfMass.times(k));
|
111
123
|
}
|
112
124
|
const lastPoint = this.lastPoint ?? newPoint;
|
113
125
|
this.lastPoint = newPoint;
|
@@ -144,7 +156,7 @@ export class StrokeSmoother {
|
|
144
156
|
}
|
145
157
|
let exitingVec = this.computeExitingVec();
|
146
158
|
// Find the intersection between the entering vector and the exiting vector
|
147
|
-
const maxRelativeLength =
|
159
|
+
const maxRelativeLength = 1.6;
|
148
160
|
const segmentStart = this.buffer[0];
|
149
161
|
const segmentEnd = newPoint.pos;
|
150
162
|
const startEndDist = segmentEnd.minus(segmentStart).magnitude();
|
@@ -165,11 +177,17 @@ export class StrokeSmoother {
|
|
165
177
|
if (intersection) {
|
166
178
|
controlPoint = intersection.point;
|
167
179
|
}
|
168
|
-
// No intersection
|
169
|
-
if (!controlPoint
|
170
|
-
//
|
171
|
-
|
172
|
-
|
180
|
+
// No intersection?
|
181
|
+
if (!controlPoint) {
|
182
|
+
// Estimate the control point position based on the entering tangent line
|
183
|
+
controlPoint = segmentStart
|
184
|
+
.lerp(segmentEnd, 0.5)
|
185
|
+
.lerp(segmentStart.plus(enteringVec.times(startEndDist)), 0.25);
|
186
|
+
}
|
187
|
+
// Equal to an endpoint?
|
188
|
+
if (segmentStart.eq(controlPoint) || segmentEnd.eq(controlPoint)) {
|
189
|
+
// Position the control point between the two end points
|
190
|
+
controlPoint = segmentStart.lerp(segmentEnd, 0.5);
|
173
191
|
}
|
174
192
|
console.assert(!segmentStart.eq(controlPoint, 1e-11), 'Start and control points are equal!');
|
175
193
|
console.assert(!controlPoint.eq(segmentEnd, 1e-11), 'Control and end points are equal!');
|
@@ -0,0 +1,26 @@
|
|
1
|
+
const mitLicenseAttribution = (copyright) => {
|
2
|
+
const removeSingleLineBreaks = (text) => text.replace(/([^\n])[\n]([^\n])/g, '$1 $2');
|
3
|
+
return removeSingleLineBreaks(`
|
4
|
+
MIT License
|
5
|
+
|
6
|
+
Copyright (c) ${copyright}
|
7
|
+
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
of this software and associated documentation files (the "Software"), to deal
|
10
|
+
in the Software without restriction, including without limitation the rights
|
11
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
copies of the Software, and to permit persons to whom the Software is
|
13
|
+
furnished to do so, subject to the following conditions:
|
14
|
+
|
15
|
+
The above copyright notice and this permission notice shall be included in all
|
16
|
+
copies or substantial portions of the Software.
|
17
|
+
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
24
|
+
SOFTWARE.`);
|
25
|
+
};
|
26
|
+
export default mitLicenseAttribution;
|
package/dist/mjs/version.mjs
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "js-draw",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.12.0",
|
4
4
|
"description": "Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript. ",
|
5
5
|
"types": "./dist/mjs/lib.d.ts",
|
6
6
|
"main": "./dist/cjs/lib.js",
|
@@ -86,5 +86,5 @@
|
|
86
86
|
"freehand",
|
87
87
|
"svg"
|
88
88
|
],
|
89
|
-
"gitHead": "
|
89
|
+
"gitHead": "c179c5b3ebca482dfb156527ec6631c8f5159033"
|
90
90
|
}
|