js-draw 0.11.0 → 0.11.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.
@@ -19,8 +19,10 @@ import Duplicate from '../../commands/Duplicate';
19
19
  import Command from '../../commands/Command';
20
20
  import { DragTransformer, ResizeTransformer, RotateTransformer } from './TransformMode';
21
21
  import { ResizeMode } from './types';
22
+ import EditorImage from '../../EditorImage';
22
23
 
23
24
  const updateChunkSize = 100;
25
+ const maxPreviewElemCount = 500;
24
26
 
25
27
  // @internal
26
28
  export default class Selection {
@@ -28,9 +30,7 @@ export default class Selection {
28
30
  private originalRegion: Rect2;
29
31
 
30
32
  private transformers;
31
-
32
33
  private transform: Mat33 = Mat33.identity;
33
- private transformCommands: SerializableCommand[] = [];
34
34
 
35
35
  private selectedElems: AbstractComponent[] = [];
36
36
 
@@ -147,33 +147,22 @@ export default class Selection {
147
147
  return this.regionRotation + this.editor.viewport.getRotationAngle();
148
148
  }
149
149
 
150
- private computeTransformCommands(): SerializableCommand[] {
151
- return this.selectedElems.map(elem => {
152
- return elem.transformBy(this.transform);
153
- });
154
- }
155
-
156
150
  // Applies, previews, but doesn't finalize the given transformation.
157
151
  public setTransform(transform: Mat33, preview: boolean = true) {
158
152
  this.transform = transform;
159
153
 
160
154
  if (preview && this.hasParent) {
161
- this.previewTransformCmds();
162
155
  this.scrollTo();
156
+ this.previewTransformCmds();
163
157
  }
164
158
  }
165
159
 
166
160
  // Applies the current transformation to the selection
167
161
  public finalizeTransform() {
168
- this.transformCommands.forEach(cmd => {
169
- cmd.unapply(this.editor);
170
- });
171
-
172
162
  const fullTransform = this.transform;
173
163
  const selectedElems = this.selectedElems;
174
164
 
175
165
  // Reset for the next drag
176
- this.transformCommands = [];
177
166
  this.originalRegion = this.originalRegion.transformedBoundingBox(this.transform);
178
167
  this.transform = Mat33.identity;
179
168
 
@@ -277,14 +266,23 @@ export default class Selection {
277
266
  // Preview the effects of the current transformation on the selection
278
267
  private previewTransformCmds() {
279
268
  // Don't render what we're moving if it's likely to be slow.
280
- if (this.selectedElems.length > updateChunkSize) {
269
+ if (this.selectedElems.length > maxPreviewElemCount) {
281
270
  this.updateUI();
282
271
  return;
283
272
  }
284
273
 
285
- this.transformCommands.forEach(cmd => cmd.unapply(this.editor));
286
- this.transformCommands = this.computeTransformCommands();
287
- this.transformCommands.forEach(cmd => cmd.apply(this.editor));
274
+ const wetInkRenderer = this.editor.display.getWetInkRenderer();
275
+ wetInkRenderer.clear();
276
+ wetInkRenderer.pushTransform(this.transform);
277
+
278
+ const viewportVisibleRect = this.editor.viewport.visibleRect;
279
+ const visibleRect = viewportVisibleRect.transformedBoundingBox(this.transform.inverse());
280
+
281
+ for (const elem of this.selectedElems) {
282
+ elem.render(wetInkRenderer, visibleRect);
283
+ }
284
+
285
+ wetInkRenderer.popTransform();
288
286
 
289
287
  this.updateUI();
290
288
  }
@@ -394,9 +392,44 @@ export default class Selection {
394
392
  }
395
393
  }
396
394
 
395
+ // Add/remove the contents of this' seleciton from the editor.
396
+ // Used to prevent previewed content from looking like duplicate content
397
+ // while dragging.
398
+ //
399
+ // Does nothing if a large number of elements are selected (and so modifying
400
+ // the editor image is likely to be slow.)
401
+ //
402
+ // If removed from the image, selected elements are drawn as wet ink.
403
+ private async addRemoveSelectionFromImage(inImage: boolean) {
404
+ // Don't hide elements if doing so will be slow.
405
+ if (!inImage && this.selectedElems.length > maxPreviewElemCount) {
406
+ return;
407
+ }
408
+
409
+ for (const elem of this.selectedElems) {
410
+ const parent = this.editor.image.findParent(elem);
411
+
412
+ if (!inImage) {
413
+ parent?.remove();
414
+ }
415
+ // If we're making things visible and the selected object wasn't previously
416
+ // visible,
417
+ else if (!parent) {
418
+ EditorImage.addElement(elem).apply(this.editor);
419
+ }
420
+ }
421
+
422
+ await this.editor.queueRerender();
423
+ if (!inImage) {
424
+ this.previewTransformCmds();
425
+ }
426
+ }
427
+
397
428
  private targetHandle: SelectionHandle|null = null;
398
429
  private backgroundDragging: boolean = false;
399
430
  public onDragStart(pointer: Pointer, target: EventTarget): boolean {
431
+ void this.addRemoveSelectionFromImage(false);
432
+
400
433
  for (const handle of this.handles) {
401
434
  if (handle.isTarget(target)) {
402
435
  handle.handleDragStart(pointer);
@@ -422,8 +455,6 @@ export default class Selection {
422
455
  if (this.targetHandle) {
423
456
  this.targetHandle.handleDragUpdate(pointer);
424
457
  }
425
-
426
- this.updateUI();
427
458
  }
428
459
 
429
460
  public onDragEnd() {
@@ -434,6 +465,8 @@ export default class Selection {
434
465
  this.targetHandle.handleDragEnd();
435
466
  }
436
467
 
468
+ this.addRemoveSelectionFromImage(true);
469
+
437
470
  this.backgroundDragging = false;
438
471
  this.targetHandle = null;
439
472
  this.updateUI();
@@ -443,10 +476,12 @@ export default class Selection {
443
476
  this.backgroundDragging = false;
444
477
  this.targetHandle = null;
445
478
  this.setTransform(Mat33.identity);
479
+
480
+ this.addRemoveSelectionFromImage(true);
446
481
  }
447
482
 
448
483
  // Scroll the viewport to this. Does not zoom
449
- public scrollTo() {
484
+ public async scrollTo() {
450
485
  if (this.selectedElems.length === 0) {
451
486
  return;
452
487
  }
@@ -456,9 +491,14 @@ export default class Selection {
456
491
  const closestPoint = screenRect.getClosestPointOnBoundaryTo(this.screenRegion.center);
457
492
  const screenDelta = this.screenRegion.center.minus(closestPoint);
458
493
  const delta = this.editor.viewport.screenToCanvasTransform.transformVec3(screenDelta);
459
- this.editor.dispatchNoAnnounce(
494
+ await this.editor.dispatchNoAnnounce(
460
495
  Viewport.transformBy(Mat33.translation(delta.times(-1))), false
461
496
  );
497
+
498
+ // Re-renders clear wet ink, so we need to re-draw the preview
499
+ // after the full re-render.
500
+ await this.editor.queueRerender();
501
+ this.previewTransformCmds();
462
502
  }
463
503
  }
464
504
 
@@ -381,6 +381,18 @@ export default class SelectionTool extends BaseTool {
381
381
  // Only select selectable objects.
382
382
  objects = objects.filter(obj => obj.isSelectable());
383
383
 
384
+ // Sort by z-index
385
+ objects.sort((a, b) => a.getZIndex() - b.getZIndex());
386
+
387
+ // Remove duplicates
388
+ objects = objects.filter((current, idx) => {
389
+ if (idx > 0) {
390
+ return current !== objects[idx - 1];
391
+ }
392
+
393
+ return true;
394
+ });
395
+
384
396
  let bbox: Rect2|null = null;
385
397
  for (const object of objects) {
386
398
  if (bbox) {