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.
@@ -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
- const currentPos = math_1.Vec2.of(event.pageX, event.pageY);
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 several libraries at runtime. Particularly noteworthy are:',
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 @@ export declare class StrokeSmoother {
15
15
  private onCurveAdded;
16
16
  private isFirstSegment;
17
17
  private buffer;
18
+ private centerOfMass;
18
19
  private lastPoint;
19
20
  private lastExitingVec;
20
21
  private currentCurve;
@@ -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
- this.momentum = velocity;
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 = 2.4;
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 or the intersection is one of the end points?
172
- if (!controlPoint || segmentStart.eq(controlPoint) || segmentEnd.eq(controlPoint)) {
173
- // Position the control point closer to the first -- the connecting
174
- // segment will be roughly a line.
175
- controlPoint = segmentStart.plus(enteringVec.times(startEndDist / 4));
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,2 @@
1
+ declare const mitLicenseAttribution: (copyright: string) => string;
2
+ export default mitLicenseAttribution;
@@ -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;
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = {
4
- number: '1.11.1',
4
+ number: '1.12.0',
5
5
  };
@@ -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
- const currentPos = Vec2.of(event.pageX, event.pageY);
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 several libraries at runtime. Particularly noteworthy are:',
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
  });
@@ -15,6 +15,7 @@ export declare class StrokeSmoother {
15
15
  private onCurveAdded;
16
16
  private isFirstSegment;
17
17
  private buffer;
18
+ private centerOfMass;
18
19
  private lastPoint;
19
20
  private lastExitingVec;
20
21
  private currentCurve;
@@ -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
- this.momentum = velocity;
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 = 2.4;
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 or the intersection is one of the end points?
169
- if (!controlPoint || segmentStart.eq(controlPoint) || segmentEnd.eq(controlPoint)) {
170
- // Position the control point closer to the first -- the connecting
171
- // segment will be roughly a line.
172
- controlPoint = segmentStart.plus(enteringVec.times(startEndDist / 4));
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,2 @@
1
+ declare const mitLicenseAttribution: (copyright: string) => string;
2
+ export default mitLicenseAttribution;
@@ -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;
@@ -1,3 +1,3 @@
1
1
  export default {
2
- number: '1.11.1',
2
+ number: '1.12.0',
3
3
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-draw",
3
- "version": "1.11.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": "695cfe01116839842668233a14fa858ad4ae0bac"
89
+ "gitHead": "c179c5b3ebca482dfb156527ec6631c8f5159033"
90
90
  }