js-draw 0.0.9 → 0.0.10

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.
@@ -16,6 +16,7 @@ export default class Display {
16
16
  get height(): number;
17
17
  private initializeCanvasRendering;
18
18
  startRerender(): AbstractRenderer;
19
+ setDraftMode(draftMode: boolean): void;
19
20
  getDryInkRenderer(): AbstractRenderer;
20
21
  getWetInkRenderer(): AbstractRenderer;
21
22
  flatten(): void;
@@ -82,6 +82,9 @@ export default class Display {
82
82
  this.dryInkRenderer.clear();
83
83
  return this.dryInkRenderer;
84
84
  }
85
+ setDraftMode(draftMode) {
86
+ this.dryInkRenderer.setDraftMode(draftMode);
87
+ }
85
88
  getDryInkRenderer() {
86
89
  return this.dryInkRenderer;
87
90
  }
@@ -234,6 +234,7 @@ export class Editor {
234
234
  // has been applied.
235
235
  asyncApplyOrUnapplyCommands(commands, apply, updateChunkSize) {
236
236
  return __awaiter(this, void 0, void 0, function* () {
237
+ this.display.setDraftMode(true);
237
238
  for (let i = 0; i < commands.length; i += updateChunkSize) {
238
239
  this.showLoadingWarning(i / commands.length);
239
240
  for (let j = i; j < commands.length && j < i + updateChunkSize; j++) {
@@ -253,6 +254,7 @@ export class Editor {
253
254
  });
254
255
  }
255
256
  }
257
+ this.display.setDraftMode(false);
256
258
  this.hideLoadingWarning();
257
259
  });
258
260
  }
@@ -26,6 +26,7 @@ export default abstract class AbstractRenderer {
26
26
  protected abstract moveTo(point: Point2): void;
27
27
  protected abstract traceCubicBezierCurve(p1: Point2, p2: Point2, p3: Point2): void;
28
28
  protected abstract traceQuadraticBezierCurve(controlPoint: Point2, endPoint: Point2): void;
29
+ setDraftMode(_draftMode: boolean): void;
29
30
  private objectLevel;
30
31
  private currentPaths;
31
32
  private flushPath;
@@ -11,6 +11,7 @@ export default class AbstractRenderer {
11
11
  this.objectLevel = 0;
12
12
  this.currentPaths = null;
13
13
  }
14
+ setDraftMode(_draftMode) { }
14
15
  flushPath() {
15
16
  if (!this.currentPaths) {
16
17
  return;
@@ -7,7 +7,11 @@ export default class CanvasRenderer extends AbstractRenderer {
7
7
  private ctx;
8
8
  private ignoreObjectsAboveLevel;
9
9
  private ignoringObject;
10
+ private minSquareCurveApproxDist;
11
+ private minRenderSizeAnyDimen;
12
+ private minRenderSizeBothDimens;
10
13
  constructor(ctx: CanvasRenderingContext2D, viewport: Viewport);
14
+ setDraftMode(draftMode: boolean): void;
11
15
  displaySize(): Vec2;
12
16
  clear(): void;
13
17
  protected beginPath(startPoint: Point2): void;
@@ -1,13 +1,26 @@
1
1
  import Color4 from '../Color4';
2
2
  import { Vec2 } from '../geometry/Vec2';
3
3
  import AbstractRenderer from './AbstractRenderer';
4
- const minSquareCurveApproxDist = 25;
5
4
  export default class CanvasRenderer extends AbstractRenderer {
6
5
  constructor(ctx, viewport) {
7
6
  super(viewport);
8
7
  this.ctx = ctx;
9
8
  this.ignoreObjectsAboveLevel = null;
10
9
  this.ignoringObject = false;
10
+ this.setDraftMode(false);
11
+ }
12
+ // Set parameters for lower/higher quality rendering
13
+ setDraftMode(draftMode) {
14
+ if (draftMode) {
15
+ this.minSquareCurveApproxDist = 64;
16
+ this.minRenderSizeBothDimens = 8;
17
+ this.minRenderSizeAnyDimen = 2;
18
+ }
19
+ else {
20
+ this.minSquareCurveApproxDist = 1;
21
+ this.minRenderSizeBothDimens = 1;
22
+ this.minRenderSizeAnyDimen = 0;
23
+ }
11
24
  }
12
25
  displaySize() {
13
26
  return Vec2.of(this.ctx.canvas.clientWidth, this.ctx.canvas.clientHeight);
@@ -45,8 +58,8 @@ export default class CanvasRenderer extends AbstractRenderer {
45
58
  // Approximate the curve if small enough.
46
59
  const delta1 = p2.minus(p1);
47
60
  const delta2 = p3.minus(p2);
48
- if (delta1.magnitudeSquared() < minSquareCurveApproxDist
49
- && delta2.magnitudeSquared() < minSquareCurveApproxDist) {
61
+ if (delta1.magnitudeSquared() < this.minSquareCurveApproxDist
62
+ && delta2.magnitudeSquared() < this.minSquareCurveApproxDist) {
50
63
  this.ctx.lineTo(p3.x, p3.y);
51
64
  }
52
65
  else {
@@ -58,7 +71,7 @@ export default class CanvasRenderer extends AbstractRenderer {
58
71
  endPoint = this.viewport.canvasToScreen(endPoint);
59
72
  // Approximate the curve with a line if small enough
60
73
  const delta = controlPoint.minus(endPoint);
61
- if (delta.magnitudeSquared() < minSquareCurveApproxDist) {
74
+ if (delta.magnitudeSquared() < this.minSquareCurveApproxDist) {
62
75
  this.ctx.lineTo(endPoint.x, endPoint.y);
63
76
  }
64
77
  else {
@@ -74,8 +87,11 @@ export default class CanvasRenderer extends AbstractRenderer {
74
87
  startObject(boundingBox) {
75
88
  // Should we ignore all objects within this object's bbox?
76
89
  const diagonal = this.viewport.canvasToScreenTransform.transformVec3(boundingBox.size);
77
- const minRenderSize = 4;
78
- if (Math.abs(diagonal.x) < minRenderSize && Math.abs(diagonal.y) < minRenderSize) {
90
+ const bothDimenMinSize = this.minRenderSizeBothDimens;
91
+ const bothTooSmall = Math.abs(diagonal.x) < bothDimenMinSize && Math.abs(diagonal.y) < bothDimenMinSize;
92
+ const anyDimenMinSize = this.minRenderSizeAnyDimen;
93
+ const anyTooSmall = Math.abs(diagonal.x) < anyDimenMinSize || Math.abs(diagonal.y) < anyDimenMinSize;
94
+ if (bothTooSmall || anyTooSmall) {
79
95
  this.ignoreObjectsAboveLevel = this.getNestingLevel();
80
96
  this.ignoringObject = true;
81
97
  }
@@ -192,12 +192,20 @@ class SelectionWidget extends ToolbarWidget {
192
192
  fillDropdown(dropdown) {
193
193
  const container = document.createElement('div');
194
194
  const resizeButton = document.createElement('button');
195
+ const deleteButton = document.createElement('button');
195
196
  resizeButton.innerText = this.localizationTable.resizeImageToSelection;
196
197
  resizeButton.disabled = true;
198
+ deleteButton.innerText = this.localizationTable.deleteSelection;
199
+ deleteButton.disabled = true;
197
200
  resizeButton.onclick = () => {
198
201
  const selection = this.tool.getSelection();
199
202
  this.editor.dispatch(this.editor.setImportExportRect(selection.region));
200
203
  };
204
+ deleteButton.onclick = () => {
205
+ const selection = this.tool.getSelection();
206
+ this.editor.dispatch(selection.deleteSelectedObjects());
207
+ this.tool.clearSelection();
208
+ };
201
209
  // Enable/disable actions based on whether items are selected
202
210
  this.editor.notifier.on(EditorEventType.ToolUpdated, toolEvt => {
203
211
  if (toolEvt.kind !== EditorEventType.ToolUpdated) {
@@ -207,9 +215,10 @@ class SelectionWidget extends ToolbarWidget {
207
215
  const selection = this.tool.getSelection();
208
216
  const hasSelection = selection && selection.region.area > 0;
209
217
  resizeButton.disabled = !hasSelection;
218
+ deleteButton.disabled = resizeButton.disabled;
210
219
  }
211
220
  });
212
- container.replaceChildren(resizeButton);
221
+ container.replaceChildren(resizeButton, deleteButton);
213
222
  dropdown.appendChild(container);
214
223
  return true;
215
224
  }
@@ -12,6 +12,7 @@ export interface ToolbarLocalization {
12
12
  touchDrawing: string;
13
13
  thicknessLabel: string;
14
14
  resizeImageToSelection: string;
15
+ deleteSelection: string;
15
16
  undo: string;
16
17
  redo: string;
17
18
  dropdownShown: (toolName: string) => string;
@@ -6,6 +6,7 @@ export const defaultToolbarLocalization = {
6
6
  thicknessLabel: 'Thickness: ',
7
7
  colorLabel: 'Color: ',
8
8
  resizeImageToSelection: 'Resize image to selection',
9
+ deleteSelection: 'Delete selection',
9
10
  undo: 'Undo',
10
11
  redo: 'Redo',
11
12
  selectObjectType: 'Object type: ',
@@ -56,6 +56,7 @@ export default class PanZoom extends BaseTool {
56
56
  }
57
57
  if (handlingGesture) {
58
58
  (_a = this.transform) !== null && _a !== void 0 ? _a : (this.transform = new Viewport.ViewportTransform(Mat33.identity));
59
+ this.editor.display.setDraftMode(true);
59
60
  }
60
61
  return handlingGesture;
61
62
  }
@@ -100,11 +101,13 @@ export default class PanZoom extends BaseTool {
100
101
  this.transform.unapply(this.editor);
101
102
  this.editor.dispatch(this.transform, false);
102
103
  }
104
+ this.editor.display.setDraftMode(false);
103
105
  this.transform = null;
104
106
  }
105
107
  onGestureCancel() {
106
108
  var _a;
107
109
  (_a = this.transform) === null || _a === void 0 ? void 0 : _a.unapply(this.editor);
110
+ this.editor.display.setDraftMode(false);
108
111
  this.transform = null;
109
112
  }
110
113
  // Applies [transformUpdate] to the editor. This stacks on top of the
@@ -1,3 +1,4 @@
1
+ import Command from '../commands/Command';
1
2
  import Editor from '../Editor';
2
3
  import Rect2 from '../geometry/Rect2';
3
4
  import { Point2, Vec2 } from '../geometry/Vec2';
@@ -30,6 +31,7 @@ declare class Selection {
30
31
  private recomputeBoxRotation;
31
32
  getSelectedItemCount(): number;
32
33
  updateUI(): void;
34
+ deleteSelectedObjects(): Command;
33
35
  }
34
36
  export default class SelectionTool extends BaseTool {
35
37
  private editor;
@@ -45,5 +47,6 @@ export default class SelectionTool extends BaseTool {
45
47
  onGestureCancel(): void;
46
48
  setEnabled(enabled: boolean): void;
47
49
  getSelection(): Selection | null;
50
+ clearSelection(): void;
48
51
  }
49
52
  export {};
@@ -7,6 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ import Erase from '../commands/Erase';
10
11
  import Mat33 from '../geometry/Mat33';
11
12
  // import Mat33 from "../geometry/Mat33";
12
13
  import Rect2 from '../geometry/Rect2';
@@ -344,6 +345,9 @@ class Selection {
344
345
  this.backgroundBox.style.transform = `rotate(${rotationDeg}deg)`;
345
346
  this.rotateCircle.style.transform = `rotate(${-rotationDeg}deg)`;
346
347
  }
348
+ deleteSelectedObjects() {
349
+ return new Erase(this.selectedElems);
350
+ }
347
351
  }
348
352
  export default class SelectionTool extends BaseTool {
349
353
  constructor(editor, description) {
@@ -434,4 +438,13 @@ export default class SelectionTool extends BaseTool {
434
438
  getSelection() {
435
439
  return this.selectionBox;
436
440
  }
441
+ clearSelection() {
442
+ this.handleOverlay.replaceChildren();
443
+ this.prevSelectionBox = this.selectionBox;
444
+ this.selectionBox = null;
445
+ this.editor.notifier.dispatch(EditorEventType.ToolUpdated, {
446
+ kind: EditorEventType.ToolUpdated,
447
+ tool: this,
448
+ });
449
+ }
437
450
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-draw",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "description": "Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript. ",
5
5
  "main": "dist/src/Editor.js",
6
6
  "types": "dist/src/Editor.d.ts",
package/src/Display.ts CHANGED
@@ -105,6 +105,10 @@ export default class Display {
105
105
  return this.dryInkRenderer;
106
106
  }
107
107
 
108
+ public setDraftMode(draftMode: boolean) {
109
+ this.dryInkRenderer.setDraftMode(draftMode);
110
+ }
111
+
108
112
  public getDryInkRenderer(): AbstractRenderer {
109
113
  return this.dryInkRenderer;
110
114
  }
package/src/Editor.ts CHANGED
@@ -311,6 +311,7 @@ export class Editor {
311
311
  private async asyncApplyOrUnapplyCommands(
312
312
  commands: Command[], apply: boolean, updateChunkSize: number
313
313
  ) {
314
+ this.display.setDraftMode(true);
314
315
  for (let i = 0; i < commands.length; i += updateChunkSize) {
315
316
  this.showLoadingWarning(i / commands.length);
316
317
 
@@ -332,6 +333,7 @@ export class Editor {
332
333
  });
333
334
  }
334
335
  }
336
+ this.display.setDraftMode(false);
335
337
  this.hideLoadingWarning();
336
338
  }
337
339
 
@@ -43,6 +43,8 @@ export default abstract class AbstractRenderer {
43
43
  controlPoint: Point2, endPoint: Point2,
44
44
  ): void;
45
45
 
46
+ public setDraftMode(_draftMode: boolean) { }
47
+
46
48
  private objectLevel: number = 0;
47
49
  private currentPaths: RenderablePathSpec[]|null = null;
48
50
  private flushPath() {
@@ -5,13 +5,37 @@ import Vec3 from '../geometry/Vec3';
5
5
  import Viewport from '../Viewport';
6
6
  import AbstractRenderer, { RenderablePathSpec, RenderingStyle } from './AbstractRenderer';
7
7
 
8
- const minSquareCurveApproxDist = 25;
9
8
  export default class CanvasRenderer extends AbstractRenderer {
10
9
  private ignoreObjectsAboveLevel: number|null = null;
11
10
  private ignoringObject: boolean = false;
12
11
 
12
+ // Minimum square distance of a control point from the line between the end points
13
+ // for the curve not to be drawn as a line.
14
+ // For example, if [minSquareCurveApproxDist] = 25 = 5², then a control point on a quadratic
15
+ // bezier curve needs to be at least 5 units away from the line between the curve's end points
16
+ // for the curve to be drawn as a Bezier curve (and not a line).
17
+ private minSquareCurveApproxDist: number;
18
+
19
+ // Minimum size of an object (in pixels) for it to be rendered.
20
+ private minRenderSizeAnyDimen: number;
21
+ private minRenderSizeBothDimens: number;
22
+
13
23
  public constructor(private ctx: CanvasRenderingContext2D, viewport: Viewport) {
14
24
  super(viewport);
25
+ this.setDraftMode(false);
26
+ }
27
+
28
+ // Set parameters for lower/higher quality rendering
29
+ public setDraftMode(draftMode: boolean) {
30
+ if (draftMode) {
31
+ this.minSquareCurveApproxDist = 64;
32
+ this.minRenderSizeBothDimens = 8;
33
+ this.minRenderSizeAnyDimen = 2;
34
+ } else {
35
+ this.minSquareCurveApproxDist = 1;
36
+ this.minRenderSizeBothDimens = 1;
37
+ this.minRenderSizeAnyDimen = 0;
38
+ }
15
39
  }
16
40
 
17
41
  public displaySize(): Vec2 {
@@ -63,8 +87,8 @@ export default class CanvasRenderer extends AbstractRenderer {
63
87
  // Approximate the curve if small enough.
64
88
  const delta1 = p2.minus(p1);
65
89
  const delta2 = p3.minus(p2);
66
- if (delta1.magnitudeSquared() < minSquareCurveApproxDist
67
- && delta2.magnitudeSquared() < minSquareCurveApproxDist) {
90
+ if (delta1.magnitudeSquared() < this.minSquareCurveApproxDist
91
+ && delta2.magnitudeSquared() < this.minSquareCurveApproxDist) {
68
92
  this.ctx.lineTo(p3.x, p3.y);
69
93
  } else {
70
94
  this.ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
@@ -77,7 +101,7 @@ export default class CanvasRenderer extends AbstractRenderer {
77
101
 
78
102
  // Approximate the curve with a line if small enough
79
103
  const delta = controlPoint.minus(endPoint);
80
- if (delta.magnitudeSquared() < minSquareCurveApproxDist) {
104
+ if (delta.magnitudeSquared() < this.minSquareCurveApproxDist) {
81
105
  this.ctx.lineTo(endPoint.x, endPoint.y);
82
106
  } else {
83
107
  this.ctx.quadraticCurveTo(
@@ -97,8 +121,13 @@ export default class CanvasRenderer extends AbstractRenderer {
97
121
  public startObject(boundingBox: Rect2) {
98
122
  // Should we ignore all objects within this object's bbox?
99
123
  const diagonal = this.viewport.canvasToScreenTransform.transformVec3(boundingBox.size);
100
- const minRenderSize = 4;
101
- if (Math.abs(diagonal.x) < minRenderSize && Math.abs(diagonal.y) < minRenderSize) {
124
+
125
+ const bothDimenMinSize = this.minRenderSizeBothDimens;
126
+ const bothTooSmall = Math.abs(diagonal.x) < bothDimenMinSize && Math.abs(diagonal.y) < bothDimenMinSize;
127
+ const anyDimenMinSize = this.minRenderSizeAnyDimen;
128
+ const anyTooSmall = Math.abs(diagonal.x) < anyDimenMinSize || Math.abs(diagonal.y) < anyDimenMinSize;
129
+
130
+ if (bothTooSmall || anyTooSmall) {
102
131
  this.ignoreObjectsAboveLevel = this.getNestingLevel();
103
132
  this.ignoringObject = true;
104
133
  }
@@ -245,15 +245,24 @@ class SelectionWidget extends ToolbarWidget {
245
245
  protected fillDropdown(dropdown: HTMLElement): boolean {
246
246
  const container = document.createElement('div');
247
247
  const resizeButton = document.createElement('button');
248
+ const deleteButton = document.createElement('button');
248
249
 
249
250
  resizeButton.innerText = this.localizationTable.resizeImageToSelection;
250
251
  resizeButton.disabled = true;
252
+ deleteButton.innerText = this.localizationTable.deleteSelection;
253
+ deleteButton.disabled = true;
251
254
 
252
255
  resizeButton.onclick = () => {
253
256
  const selection = this.tool.getSelection();
254
257
  this.editor.dispatch(this.editor.setImportExportRect(selection!.region));
255
258
  };
256
259
 
260
+ deleteButton.onclick = () => {
261
+ const selection = this.tool.getSelection();
262
+ this.editor.dispatch(selection!.deleteSelectedObjects());
263
+ this.tool.clearSelection();
264
+ };
265
+
257
266
  // Enable/disable actions based on whether items are selected
258
267
  this.editor.notifier.on(EditorEventType.ToolUpdated, toolEvt => {
259
268
  if (toolEvt.kind !== EditorEventType.ToolUpdated) {
@@ -263,11 +272,13 @@ class SelectionWidget extends ToolbarWidget {
263
272
  if (toolEvt.tool === this.tool) {
264
273
  const selection = this.tool.getSelection();
265
274
  const hasSelection = selection && selection.region.area > 0;
275
+
266
276
  resizeButton.disabled = !hasSelection;
277
+ deleteButton.disabled = resizeButton.disabled;
267
278
  }
268
279
  });
269
280
 
270
- container.replaceChildren(resizeButton);
281
+ container.replaceChildren(resizeButton, deleteButton);
271
282
  dropdown.appendChild(container);
272
283
  return true;
273
284
  }
@@ -14,6 +14,7 @@ export interface ToolbarLocalization {
14
14
  touchDrawing: string;
15
15
  thicknessLabel: string;
16
16
  resizeImageToSelection: string;
17
+ deleteSelection: string;
17
18
  undo: string;
18
19
  redo: string;
19
20
 
@@ -29,6 +30,7 @@ export const defaultToolbarLocalization: ToolbarLocalization = {
29
30
  thicknessLabel: 'Thickness: ',
30
31
  colorLabel: 'Color: ',
31
32
  resizeImageToSelection: 'Resize image to selection',
33
+ deleteSelection: 'Delete selection',
32
34
  undo: 'Undo',
33
35
  redo: 'Redo',
34
36
  selectObjectType: 'Object type: ',
@@ -78,6 +78,7 @@ export default class PanZoom extends BaseTool {
78
78
 
79
79
  if (handlingGesture) {
80
80
  this.transform ??= new Viewport.ViewportTransform(Mat33.identity);
81
+ this.editor.display.setDraftMode(true);
81
82
  }
82
83
 
83
84
  return handlingGesture;
@@ -136,11 +137,13 @@ export default class PanZoom extends BaseTool {
136
137
  this.editor.dispatch(this.transform, false);
137
138
  }
138
139
 
140
+ this.editor.display.setDraftMode(false);
139
141
  this.transform = null;
140
142
  }
141
143
 
142
144
  public onGestureCancel(): void {
143
145
  this.transform?.unapply(this.editor);
146
+ this.editor.display.setDraftMode(false);
144
147
  this.transform = null;
145
148
  }
146
149
 
@@ -1,4 +1,5 @@
1
1
  import Command from '../commands/Command';
2
+ import Erase from '../commands/Erase';
2
3
  import AbstractComponent from '../components/AbstractComponent';
3
4
  import Editor from '../Editor';
4
5
  import Mat33 from '../geometry/Mat33';
@@ -421,6 +422,10 @@ class Selection {
421
422
  this.backgroundBox.style.transform = `rotate(${rotationDeg}deg)`;
422
423
  this.rotateCircle.style.transform = `rotate(${-rotationDeg}deg)`;
423
424
  }
425
+
426
+ public deleteSelectedObjects(): Command {
427
+ return new Erase(this.selectedElems);
428
+ }
424
429
  }
425
430
 
426
431
  export default class SelectionTool extends BaseTool {
@@ -542,4 +547,15 @@ export default class SelectionTool extends BaseTool {
542
547
  public getSelection(): Selection|null {
543
548
  return this.selectionBox;
544
549
  }
550
+
551
+ public clearSelection() {
552
+ this.handleOverlay.replaceChildren();
553
+ this.prevSelectionBox = this.selectionBox;
554
+ this.selectionBox = null;
555
+
556
+ this.editor.notifier.dispatch(EditorEventType.ToolUpdated, {
557
+ kind: EditorEventType.ToolUpdated,
558
+ tool: this,
559
+ });
560
+ }
545
561
  }