js-draw 1.9.0 → 1.9.1

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.
@@ -189,19 +189,30 @@ class Editor {
189
189
  this.hideLoadingWarning();
190
190
  // Enforce zoom limits.
191
191
  this.notifier.on(types_1.EditorEventType.ViewportChanged, evt => {
192
- if (evt.kind === types_1.EditorEventType.ViewportChanged) {
193
- const zoom = evt.newTransform.transformVec3(math_1.Vec2.unitX).length();
194
- if (zoom > this.settings.maxZoom || zoom < this.settings.minZoom) {
195
- const oldZoom = evt.oldTransform.transformVec3(math_1.Vec2.unitX).length();
196
- let resetTransform = math_1.Mat33.identity;
197
- if (oldZoom <= this.settings.maxZoom && oldZoom >= this.settings.minZoom) {
198
- resetTransform = evt.oldTransform;
199
- }
200
- else {
201
- // If 1x zoom isn't acceptable, try a zoom between the minimum and maximum.
202
- resetTransform = math_1.Mat33.scaling2D((this.settings.minZoom + this.settings.maxZoom) / 2);
203
- }
204
- this.viewport.resetTransform(resetTransform);
192
+ if (evt.kind !== types_1.EditorEventType.ViewportChanged)
193
+ return;
194
+ const getZoom = (mat) => mat.transformVec3(math_1.Vec2.unitX).length();
195
+ const zoom = getZoom(evt.newTransform);
196
+ if (zoom > this.settings.maxZoom || zoom < this.settings.minZoom) {
197
+ const oldZoom = getZoom(evt.oldTransform);
198
+ let resetTransform = math_1.Mat33.identity;
199
+ if (oldZoom <= this.settings.maxZoom && oldZoom >= this.settings.minZoom) {
200
+ resetTransform = evt.oldTransform;
201
+ }
202
+ else {
203
+ // If 1x zoom isn't acceptable, try a zoom between the minimum and maximum.
204
+ resetTransform = math_1.Mat33.scaling2D((this.settings.minZoom + this.settings.maxZoom) / 2);
205
+ }
206
+ this.viewport.resetTransform(resetTransform);
207
+ }
208
+ else if (!isFinite(zoom)) {
209
+ // Recover from possible division-by-zero
210
+ console.warn(`Non-finite zoom (${zoom}) detected. Resetting the viewport. This was likely caused by division by zero.`);
211
+ if (isFinite(getZoom(evt.oldTransform))) {
212
+ this.viewport.resetTransform(evt.oldTransform);
213
+ }
214
+ else {
215
+ this.viewport.resetTransform();
205
216
  }
206
217
  }
207
218
  });
@@ -50,6 +50,11 @@ export declare class Viewport {
50
50
  private getScaleFactorToNearestPowerOf;
51
51
  /** Returns the size of a grid cell (in canvas units) as used by {@link snapToGrid}. */
52
52
  static getGridSize(scaleFactor: number): number;
53
+ /**
54
+ * Snaps `canvasPos` to the nearest grid cell corner.
55
+ *
56
+ * @see {@link getGridSize} and {@link getScaleFactorToNearestPowerOf}.
57
+ */
53
58
  snapToGrid(canvasPos: Point2): Vec3;
54
59
  /** Returns the size of one screen pixel in canvas units. */
55
60
  getSizeOfPixelOnCanvas(): number;
@@ -65,6 +70,7 @@ export declare class Viewport {
65
70
  * its original location. This is useful for preparing data for base-10 conversion.
66
71
  */
67
72
  static roundPoint<T extends Point2 | number>(point: T, tolerance: number): PointDataType<T>;
73
+ /** Round a point with a tolerance of ±1 screen unit. */
68
74
  roundPoint(point: Point2): Point2;
69
75
  static roundScaleRatio(scaleRatio: number, roundAmount?: number): number;
70
76
  computeZoomToTransform(toMakeVisible: Rect2, allowZoomIn?: boolean, allowZoomOut?: boolean): Mat33;
@@ -104,6 +104,11 @@ class Viewport {
104
104
  static getGridSize(scaleFactor) {
105
105
  return 50 / scaleFactor;
106
106
  }
107
+ /**
108
+ * Snaps `canvasPos` to the nearest grid cell corner.
109
+ *
110
+ * @see {@link getGridSize} and {@link getScaleFactorToNearestPowerOf}.
111
+ */
107
112
  snapToGrid(canvasPos) {
108
113
  const scaleFactor = this.getScaleFactorToNearestPowerOf(2);
109
114
  const snapCoordinate = (coordinate) => {
@@ -140,7 +145,7 @@ class Viewport {
140
145
  }
141
146
  return point.map(roundComponent);
142
147
  }
143
- // Round a point with a tolerance of ±1 screen unit.
148
+ /** Round a point with a tolerance of ±1 screen unit. */
144
149
  roundPoint(point) {
145
150
  return Viewport.roundPoint(point, 1 / this.getScaleFactor());
146
151
  }
@@ -138,6 +138,9 @@ class Display {
138
138
  // Ensure correct drawing operations on high-resolution screens.
139
139
  // See
140
140
  // https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas#scaling_for_high_resolution_displays
141
+ //
142
+ // This scaling causes the rendering contexts to automatically convert
143
+ // between screen coordinates and pixel coordinates.
141
144
  wetInkCtx.resetTransform();
142
145
  dryInkCtx.resetTransform();
143
146
  dryInkCtx.scale(this.devicePixelRatio, this.devicePixelRatio);
@@ -156,7 +159,11 @@ class Display {
156
159
  dryInkCtx.restore();
157
160
  };
158
161
  this.getColorAt = (screenPos) => {
159
- const pixel = dryInkCtx.getImageData(screenPos.x, screenPos.y, 1, 1);
162
+ // getImageData isn't affected by a transformation matrix -- we need to
163
+ // pre-transform screenPos to convert it from screen coordinates into pixel
164
+ // coordinates.
165
+ const adjustedScreenPos = screenPos.times(this.devicePixelRatio);
166
+ const pixel = dryInkCtx.getImageData(adjustedScreenPos.x, adjustedScreenPos.y, 1, 1);
160
167
  const data = pixel?.data;
161
168
  if (data) {
162
169
  const color = math_1.Color4.ofRGBA(data[0] / 255, data[1] / 255, data[2] / 255, data[3] / 255);
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = {
4
- number: '1.9.0',
4
+ number: '1.9.1',
5
5
  };
@@ -160,19 +160,30 @@ export class Editor {
160
160
  this.hideLoadingWarning();
161
161
  // Enforce zoom limits.
162
162
  this.notifier.on(EditorEventType.ViewportChanged, evt => {
163
- if (evt.kind === EditorEventType.ViewportChanged) {
164
- const zoom = evt.newTransform.transformVec3(Vec2.unitX).length();
165
- if (zoom > this.settings.maxZoom || zoom < this.settings.minZoom) {
166
- const oldZoom = evt.oldTransform.transformVec3(Vec2.unitX).length();
167
- let resetTransform = Mat33.identity;
168
- if (oldZoom <= this.settings.maxZoom && oldZoom >= this.settings.minZoom) {
169
- resetTransform = evt.oldTransform;
170
- }
171
- else {
172
- // If 1x zoom isn't acceptable, try a zoom between the minimum and maximum.
173
- resetTransform = Mat33.scaling2D((this.settings.minZoom + this.settings.maxZoom) / 2);
174
- }
175
- this.viewport.resetTransform(resetTransform);
163
+ if (evt.kind !== EditorEventType.ViewportChanged)
164
+ return;
165
+ const getZoom = (mat) => mat.transformVec3(Vec2.unitX).length();
166
+ const zoom = getZoom(evt.newTransform);
167
+ if (zoom > this.settings.maxZoom || zoom < this.settings.minZoom) {
168
+ const oldZoom = getZoom(evt.oldTransform);
169
+ let resetTransform = Mat33.identity;
170
+ if (oldZoom <= this.settings.maxZoom && oldZoom >= this.settings.minZoom) {
171
+ resetTransform = evt.oldTransform;
172
+ }
173
+ else {
174
+ // If 1x zoom isn't acceptable, try a zoom between the minimum and maximum.
175
+ resetTransform = Mat33.scaling2D((this.settings.minZoom + this.settings.maxZoom) / 2);
176
+ }
177
+ this.viewport.resetTransform(resetTransform);
178
+ }
179
+ else if (!isFinite(zoom)) {
180
+ // Recover from possible division-by-zero
181
+ console.warn(`Non-finite zoom (${zoom}) detected. Resetting the viewport. This was likely caused by division by zero.`);
182
+ if (isFinite(getZoom(evt.oldTransform))) {
183
+ this.viewport.resetTransform(evt.oldTransform);
184
+ }
185
+ else {
186
+ this.viewport.resetTransform();
176
187
  }
177
188
  }
178
189
  });
@@ -50,6 +50,11 @@ export declare class Viewport {
50
50
  private getScaleFactorToNearestPowerOf;
51
51
  /** Returns the size of a grid cell (in canvas units) as used by {@link snapToGrid}. */
52
52
  static getGridSize(scaleFactor: number): number;
53
+ /**
54
+ * Snaps `canvasPos` to the nearest grid cell corner.
55
+ *
56
+ * @see {@link getGridSize} and {@link getScaleFactorToNearestPowerOf}.
57
+ */
53
58
  snapToGrid(canvasPos: Point2): Vec3;
54
59
  /** Returns the size of one screen pixel in canvas units. */
55
60
  getSizeOfPixelOnCanvas(): number;
@@ -65,6 +70,7 @@ export declare class Viewport {
65
70
  * its original location. This is useful for preparing data for base-10 conversion.
66
71
  */
67
72
  static roundPoint<T extends Point2 | number>(point: T, tolerance: number): PointDataType<T>;
73
+ /** Round a point with a tolerance of ±1 screen unit. */
68
74
  roundPoint(point: Point2): Point2;
69
75
  static roundScaleRatio(scaleRatio: number, roundAmount?: number): number;
70
76
  computeZoomToTransform(toMakeVisible: Rect2, allowZoomIn?: boolean, allowZoomOut?: boolean): Mat33;
@@ -97,6 +97,11 @@ export class Viewport {
97
97
  static getGridSize(scaleFactor) {
98
98
  return 50 / scaleFactor;
99
99
  }
100
+ /**
101
+ * Snaps `canvasPos` to the nearest grid cell corner.
102
+ *
103
+ * @see {@link getGridSize} and {@link getScaleFactorToNearestPowerOf}.
104
+ */
100
105
  snapToGrid(canvasPos) {
101
106
  const scaleFactor = this.getScaleFactorToNearestPowerOf(2);
102
107
  const snapCoordinate = (coordinate) => {
@@ -133,7 +138,7 @@ export class Viewport {
133
138
  }
134
139
  return point.map(roundComponent);
135
140
  }
136
- // Round a point with a tolerance of ±1 screen unit.
141
+ /** Round a point with a tolerance of ±1 screen unit. */
137
142
  roundPoint(point) {
138
143
  return Viewport.roundPoint(point, 1 / this.getScaleFactor());
139
144
  }
@@ -132,6 +132,9 @@ export default class Display {
132
132
  // Ensure correct drawing operations on high-resolution screens.
133
133
  // See
134
134
  // https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas#scaling_for_high_resolution_displays
135
+ //
136
+ // This scaling causes the rendering contexts to automatically convert
137
+ // between screen coordinates and pixel coordinates.
135
138
  wetInkCtx.resetTransform();
136
139
  dryInkCtx.resetTransform();
137
140
  dryInkCtx.scale(this.devicePixelRatio, this.devicePixelRatio);
@@ -150,7 +153,11 @@ export default class Display {
150
153
  dryInkCtx.restore();
151
154
  };
152
155
  this.getColorAt = (screenPos) => {
153
- const pixel = dryInkCtx.getImageData(screenPos.x, screenPos.y, 1, 1);
156
+ // getImageData isn't affected by a transformation matrix -- we need to
157
+ // pre-transform screenPos to convert it from screen coordinates into pixel
158
+ // coordinates.
159
+ const adjustedScreenPos = screenPos.times(this.devicePixelRatio);
160
+ const pixel = dryInkCtx.getImageData(adjustedScreenPos.x, adjustedScreenPos.y, 1, 1);
154
161
  const data = pixel?.data;
155
162
  if (data) {
156
163
  const color = Color4.ofRGBA(data[0] / 255, data[1] / 255, data[2] / 255, data[3] / 255);
@@ -1,3 +1,3 @@
1
1
  export default {
2
- number: '1.9.0',
2
+ number: '1.9.1',
3
3
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-draw",
3
- "version": "1.9.0",
3
+ "version": "1.9.1",
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": "e824c37e9f216852cf096976e3a74fb4f177ead3"
89
+ "gitHead": "b4bae7b437b2ce4ba8378a062b8e3959dca0f26e"
90
90
  }