@pooder/kit 3.0.1 → 3.2.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/src/dieline.ts CHANGED
@@ -8,17 +8,19 @@ import {
8
8
  import { Path, Pattern } from "fabric";
9
9
  import CanvasService from "./CanvasService";
10
10
  import { ImageTracer } from "./tracer";
11
- import { Coordinate } from "./coordinate";
11
+ import { Coordinate, Unit } from "./coordinate";
12
12
  import {
13
13
  generateDielinePath,
14
14
  generateMaskPath,
15
15
  generateBleedZonePath,
16
16
  getPathBounds,
17
17
  HoleData,
18
+ resolveHolePosition,
18
19
  } from "./geometry";
19
20
 
20
21
  export interface DielineGeometry {
21
22
  shape: "rect" | "circle" | "ellipse" | "custom";
23
+ unit: Unit;
22
24
  x: number;
23
25
  y: number;
24
26
  width: number;
@@ -26,6 +28,7 @@ export interface DielineGeometry {
26
28
  radius: number;
27
29
  offset: number;
28
30
  borderLength?: number;
31
+ scale?: number;
29
32
  pathData?: string;
30
33
  }
31
34
 
@@ -35,6 +38,7 @@ export class DielineTool implements Extension {
35
38
  name: "DielineTool",
36
39
  };
37
40
 
41
+ private unit: Unit = "mm";
38
42
  private shape: "rect" | "circle" | "ellipse" | "custom" = "rect";
39
43
  private width: number = 500;
40
44
  private height: number = 500;
@@ -47,7 +51,7 @@ export class DielineTool implements Extension {
47
51
  private holes: HoleData[] = [];
48
52
  // Position is stored as normalized coordinates (0-1)
49
53
  private position?: { x: number; y: number };
50
- private borderLength?: number;
54
+ private padding: number | string = 140;
51
55
  private pathData?: string;
52
56
 
53
57
  private canvasService?: CanvasService;
@@ -55,13 +59,14 @@ export class DielineTool implements Extension {
55
59
 
56
60
  constructor(
57
61
  options?: Partial<{
62
+ unit: Unit;
58
63
  shape: "rect" | "circle" | "ellipse" | "custom";
59
64
  width: number;
60
65
  height: number;
61
66
  radius: number;
62
67
  // Position is normalized (0-1)
63
68
  position: { x: number; y: number };
64
- borderLength: number;
69
+ padding: number | string;
65
70
  offset: number;
66
71
  style: "solid" | "dashed";
67
72
  insideColor: string;
@@ -87,14 +92,12 @@ export class DielineTool implements Extension {
87
92
  const configService = context.services.get<any>("ConfigurationService");
88
93
  if (configService) {
89
94
  // Load initial config
95
+ this.unit = configService.get("dieline.unit", this.unit);
90
96
  this.shape = configService.get("dieline.shape", this.shape);
91
97
  this.width = configService.get("dieline.width", this.width);
92
98
  this.height = configService.get("dieline.height", this.height);
93
99
  this.radius = configService.get("dieline.radius", this.radius);
94
- this.borderLength = configService.get(
95
- "dieline.borderLength",
96
- this.borderLength,
97
- );
100
+ this.padding = configService.get("dieline.padding", this.padding);
98
101
  this.offset = configService.get("dieline.offset", this.offset);
99
102
  this.style = configService.get("dieline.style", this.style);
100
103
  this.insideColor = configService.get(
@@ -140,6 +143,13 @@ export class DielineTool implements Extension {
140
143
  contribute() {
141
144
  return {
142
145
  [ContributionPointIds.CONFIGURATIONS]: [
146
+ {
147
+ id: "dieline.unit",
148
+ type: "select",
149
+ label: "Unit",
150
+ options: ["px", "mm", "cm", "in"],
151
+ default: this.unit,
152
+ },
143
153
  {
144
154
  id: "dieline.shape",
145
155
  type: "select",
@@ -175,15 +185,14 @@ export class DielineTool implements Extension {
175
185
  id: "dieline.position",
176
186
  type: "json",
177
187
  label: "Position (Normalized)",
178
- default: this.position,
188
+ default: this.radius,
179
189
  },
180
190
  {
181
- id: "dieline.borderLength",
182
- type: "number",
183
- label: "Margin",
184
- min: 0,
185
- max: 500,
186
- default: this.borderLength,
191
+ id: "dieline.padding",
192
+ type: "select",
193
+ label: "View Padding",
194
+ options: [0, 10, 20, 40, 60, 100, "2%", "5%", "10%", "15%", "20%"],
195
+ default: this.padding,
187
196
  },
188
197
  {
189
198
  id: "dieline.offset",
@@ -226,65 +235,6 @@ export class DielineTool implements Extension {
226
235
  },
227
236
  ] as ConfigurationContribution[],
228
237
  [ContributionPointIds.COMMANDS]: [
229
- {
230
- command: "reset",
231
- title: "Reset Dieline",
232
- handler: () => {
233
- this.shape = "rect";
234
- this.width = 300;
235
- this.height = 300;
236
- this.radius = 0;
237
- this.offset = 0;
238
- this.style = "solid";
239
- this.insideColor = "rgba(0,0,0,0)";
240
- this.outsideColor = "#ffffff";
241
- this.showBleedLines = true;
242
- this.holes = [];
243
- this.pathData = undefined;
244
- this.updateDieline();
245
- return true;
246
- },
247
- },
248
- {
249
- command: "setDimensions",
250
- title: "Set Dimensions",
251
- handler: (width: number, height: number) => {
252
- if (this.width === width && this.height === height) return true;
253
- this.width = width;
254
- this.height = height;
255
- this.updateDieline();
256
- return true;
257
- },
258
- },
259
- {
260
- command: "setShape",
261
- title: "Set Shape",
262
- handler: (shape: "rect" | "circle" | "ellipse" | "custom") => {
263
- if (this.shape === shape) return true;
264
- this.shape = shape;
265
- this.updateDieline();
266
- return true;
267
- },
268
- },
269
- {
270
- command: "setBleed",
271
- title: "Set Bleed",
272
- handler: (bleed: number) => {
273
- if (this.offset === bleed) return true;
274
- this.offset = bleed;
275
- this.updateDieline();
276
- return true;
277
- },
278
- },
279
- {
280
- command: "setHoles",
281
- title: "Set Holes",
282
- handler: (holes: HoleData[]) => {
283
- this.holes = holes;
284
- this.updateDieline(false);
285
- return true;
286
- },
287
- },
288
238
  {
289
239
  command: "getGeometry",
290
240
  title: "Get Geometry",
@@ -304,39 +254,25 @@ export class DielineTool implements Extension {
304
254
  title: "Detect Edge from Image",
305
255
  handler: async (imageUrl: string, options?: any) => {
306
256
  try {
307
- // Pass current dimensions if we want to scale immediately?
308
- // But wait, the user said "It should be scaled according to width and height".
309
- // If the user already set width/height on the tool, we should respect it?
310
- // Or should we set width/height based on the image aspect ratio?
311
- // Usually for a new trace, we might want to respect the IMAGE aspect ratio but fit into current width/height?
312
- // Or just replace width/height with image dimensions?
313
- // Let's assume we want to keep the current "box" size but fit the shape inside?
314
- // Or if options has width/height use that.
315
-
316
- // Let's first trace to get the natural shape (and its aspect ratio)
317
- // Then we can decide how to update this.width/this.height.
318
-
319
257
  const pathData = await ImageTracer.trace(imageUrl, options);
320
-
321
- // We need to set width/height from the path bounds to avoid distortion
322
258
  const bounds = getPathBounds(pathData);
323
259
 
324
- // If we want to scale the path to specific dimensions, we can do it via ImageTracer options.scaleToWidth/Height
325
- // But here we got the raw path.
326
- // Let's update the TOOL's dimensions to match the detected shape's aspect ratio,
327
- // while keeping the size reasonable (e.g. max dimension 300 or current size).
328
-
329
- // If current tool size is default 300x300, we might want to resize tool to match image ratio.
330
260
  const currentMax = Math.max(this.width, this.height);
331
261
  const scale = currentMax / Math.max(bounds.width, bounds.height);
332
262
 
333
- this.width = bounds.width * scale;
334
- this.height = bounds.height * scale;
263
+ const newWidth = bounds.width * scale;
264
+ const newHeight = bounds.height * scale;
335
265
 
336
- this.shape = "custom";
337
- this.pathData = pathData;
266
+ const configService = this.context?.services.get<any>(
267
+ "ConfigurationService",
268
+ );
269
+ if (configService) {
270
+ configService.update("dieline.width", newWidth);
271
+ configService.update("dieline.height", newHeight);
272
+ configService.update("dieline.shape", "custom");
273
+ configService.update("dieline.pathData", pathData);
274
+ }
338
275
 
339
- this.updateDieline();
340
276
  return pathData;
341
277
  } catch (e) {
342
278
  console.error("Edge detection failed", e);
@@ -409,12 +345,30 @@ export class DielineTool implements Extension {
409
345
  return new Pattern({ source: canvas, repetition: "repeat" });
410
346
  }
411
347
 
348
+ private resolvePadding(
349
+ containerWidth: number,
350
+ containerHeight: number,
351
+ ): number {
352
+ if (typeof this.padding === "number") {
353
+ return this.padding;
354
+ }
355
+ if (typeof this.padding === "string") {
356
+ if (this.padding.endsWith("%")) {
357
+ const percent = parseFloat(this.padding) / 100;
358
+ return Math.min(containerWidth, containerHeight) * percent;
359
+ }
360
+ return parseFloat(this.padding) || 0;
361
+ }
362
+ return 0;
363
+ }
364
+
412
365
  public updateDieline(emitEvent: boolean = true) {
413
366
  if (!this.canvasService) return;
414
367
  const layer = this.getLayer();
415
368
  if (!layer) return;
416
369
 
417
370
  const {
371
+ unit,
418
372
  shape,
419
373
  radius,
420
374
  offset,
@@ -422,7 +376,6 @@ export class DielineTool implements Extension {
422
376
  insideColor,
423
377
  outsideColor,
424
378
  position,
425
- borderLength,
426
379
  showBleedLines,
427
380
  holes,
428
381
  } = this;
@@ -431,38 +384,72 @@ export class DielineTool implements Extension {
431
384
  const canvasW = this.canvasService.canvas.width || 800;
432
385
  const canvasH = this.canvasService.canvas.height || 600;
433
386
 
434
- // Handle borderLength (Margin)
435
- if (borderLength && borderLength > 0) {
436
- width = Math.max(0, canvasW - borderLength * 2);
437
- height = Math.max(0, canvasH - borderLength * 2);
438
- }
439
-
440
- // Handle Position
441
- // this.position is normalized (0-1). Default to center (0.5, 0.5).
442
- const normalizedPos = position ?? { x: 0.5, y: 0.5 };
443
- const cx = Coordinate.toAbsolute(normalizedPos.x, canvasW);
444
- const cy = Coordinate.toAbsolute(normalizedPos.y, canvasH);
387
+ // Calculate Layout based on Physical Dimensions and Canvas Size
388
+ // Add padding to avoid edge hugging
389
+ const paddingPx = this.resolvePadding(canvasW, canvasH);
390
+ const layout = Coordinate.calculateLayout(
391
+ { width: canvasW, height: canvasH },
392
+ { width, height },
393
+ paddingPx,
394
+ );
395
+
396
+ const scale = layout.scale;
397
+ const cx = layout.offsetX + layout.width / 2;
398
+ const cy = layout.offsetY + layout.height / 2;
399
+
400
+ // Scaled dimensions for rendering (Pixels)
401
+ const visualWidth = layout.width;
402
+ const visualHeight = layout.height;
403
+ const visualRadius = radius * scale;
404
+ const visualOffset = offset * scale;
445
405
 
446
406
  // Clear existing objects
447
407
  layer.remove(...layer.getObjects());
448
408
 
449
- // Denormalize Holes for Geometry Generation
409
+ // Resolve Holes for Geometry Generation (using visual coordinates)
410
+ const geometryForHoles = {
411
+ x: cx,
412
+ y: cy,
413
+ width: visualWidth,
414
+ height: visualHeight,
415
+ // Pass scale/unit context if needed by resolveHolePosition (though currently unused there)
416
+ };
417
+
450
418
  const absoluteHoles = (holes || []).map((h) => {
451
- const p = Coordinate.denormalizePoint(
452
- { x: h.x, y: h.y },
453
- { width: canvasW, height: canvasH },
454
- );
419
+ // Scale hole radii and offsets: mm -> current unit -> pixels
420
+ const unitScale = Coordinate.convertUnit(1, "mm", unit);
421
+ const offsetScale = unitScale * scale;
422
+
423
+ // Apply scaling to offsets BEFORE resolving position
424
+ const hWithPixelOffsets = {
425
+ ...h,
426
+ offsetX: (h.offsetX || 0) * offsetScale,
427
+ offsetY: (h.offsetY || 0) * offsetScale,
428
+ };
429
+
430
+ const pos = resolveHolePosition(hWithPixelOffsets, geometryForHoles, {
431
+ width: canvasW,
432
+ height: canvasH,
433
+ });
434
+
455
435
  return {
456
436
  ...h,
457
- x: p.x,
458
- y: p.y,
437
+ x: pos.x,
438
+ y: pos.y,
439
+ // Scale hole radii: mm -> current unit -> pixels
440
+ innerRadius: h.innerRadius * offsetScale,
441
+ outerRadius: h.outerRadius * offsetScale,
442
+ // Store scaled offsets in the result for consistency, though pos is already resolved
443
+ offsetX: hWithPixelOffsets.offsetX,
444
+ offsetY: hWithPixelOffsets.offsetY,
459
445
  };
460
446
  });
461
447
 
462
448
  // 1. Draw Mask (Outside)
463
- const cutW = Math.max(0, width + offset * 2);
464
- const cutH = Math.max(0, height + offset * 2);
465
- const cutR = radius === 0 ? 0 : Math.max(0, radius + offset);
449
+ const cutW = Math.max(0, visualWidth + visualOffset * 2);
450
+ const cutH = Math.max(0, visualHeight + visualOffset * 2);
451
+ const cutR =
452
+ visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
466
453
 
467
454
  // Use Paper.js to generate the complex mask path
468
455
  const maskPathData = generateMaskPath({
@@ -524,15 +511,15 @@ export class DielineTool implements Extension {
524
511
  const bleedPathData = generateBleedZonePath(
525
512
  {
526
513
  shape,
527
- width,
528
- height,
529
- radius,
514
+ width: visualWidth,
515
+ height: visualHeight,
516
+ radius: visualRadius,
530
517
  x: cx,
531
518
  y: cy,
532
519
  holes: absoluteHoles,
533
520
  pathData: this.pathData,
534
521
  },
535
- offset,
522
+ visualOffset,
536
523
  );
537
524
 
538
525
  // Use solid red for hatch lines to match dieline, background is transparent
@@ -583,12 +570,12 @@ export class DielineTool implements Extension {
583
570
  // generateDielinePath expects holes to be in absolute coordinates (matching width/height scale)
584
571
  const borderPathData = generateDielinePath({
585
572
  shape,
586
- width: width,
587
- height: height,
588
- radius: radius,
573
+ width: visualWidth,
574
+ height: visualHeight,
575
+ radius: visualRadius,
589
576
  x: cx,
590
577
  y: cy,
591
- holes: absoluteHoles, // FIX: Use absoluteHoles instead of holes
578
+ holes: absoluteHoles,
592
579
  pathData: this.pathData,
593
580
  });
594
581
 
@@ -632,6 +619,13 @@ export class DielineTool implements Extension {
632
619
  // Emit change event so other tools (like HoleTool) can react
633
620
  // Only emit if requested (to avoid loops when updating non-geometry props like holes)
634
621
  if (emitEvent && this.context) {
622
+ // FIX: Ensure we use the exact same geometry values as used in rendering above.
623
+ // Although getGeometry() recalculates layout, it should be identical if props haven't changed.
624
+ // But to be absolutely safe and avoid micro-differences or race conditions, we can reuse calculated values.
625
+ // However, getGeometry is public API, so it's better if it's correct.
626
+ // Let's verify getGeometry logic matches updateDieline logic perfectly.
627
+ // Yes, both use Coordinate.calculateLayout with the same inputs.
628
+
635
629
  const geometry = this.getGeometry();
636
630
  if (geometry) {
637
631
  this.context.eventBus.emit("dieline:geometry:change", geometry);
@@ -641,140 +635,138 @@ export class DielineTool implements Extension {
641
635
 
642
636
  public getGeometry(): DielineGeometry | null {
643
637
  if (!this.canvasService) return null;
644
- const { shape, width, height, radius, position, borderLength, offset } =
645
- this;
638
+ const { unit, shape, width, height, radius, position, offset } = this;
646
639
  const canvasW = this.canvasService.canvas.width || 800;
647
640
  const canvasH = this.canvasService.canvas.height || 600;
648
641
 
649
- let visualWidth = width;
650
- let visualHeight = height;
642
+ const paddingPx = this.resolvePadding(canvasW, canvasH);
643
+ const layout = Coordinate.calculateLayout(
644
+ { width: canvasW, height: canvasH },
645
+ { width, height },
646
+ paddingPx,
647
+ );
651
648
 
652
- if (borderLength && borderLength > 0) {
653
- visualWidth = Math.max(0, canvasW - borderLength * 2);
654
- visualHeight = Math.max(0, canvasH - borderLength * 2);
655
- }
649
+ const scale = layout.scale;
650
+ const cx = layout.offsetX + layout.width / 2;
651
+ const cy = layout.offsetY + layout.height / 2;
656
652
 
657
- const cx = Coordinate.toAbsolute(position?.x ?? 0.5, canvasW);
658
- const cy = Coordinate.toAbsolute(position?.y ?? 0.5, canvasH);
653
+ const visualWidth = layout.width;
654
+ const visualHeight = layout.height;
659
655
 
660
656
  return {
661
657
  shape,
658
+ unit,
662
659
  x: cx,
663
660
  y: cy,
664
661
  width: visualWidth,
665
662
  height: visualHeight,
666
- radius,
667
- offset,
668
- borderLength,
663
+ radius: radius * scale,
664
+ offset: offset * scale,
665
+ // Pass scale to help other tools (like HoleTool) convert units
666
+ scale,
669
667
  pathData: this.pathData,
670
- };
668
+ } as DielineGeometry;
671
669
  }
672
670
 
673
- public exportCutImage() {
671
+ public async exportCutImage() {
674
672
  if (!this.canvasService) return null;
675
- const canvas = this.canvasService.canvas;
673
+ const userLayer = this.canvasService.getLayer("user");
674
+
675
+ // Even if no user images, we might want to export the shape?
676
+ // But usually "Cut Image" implies the printed content.
677
+ // If empty, maybe just return null or empty string.
678
+ if (!userLayer) return null;
676
679
 
677
680
  // 1. Generate Path Data
678
681
  const { shape, width, height, radius, position, holes } = this;
679
- const canvasW = canvas.width || 800;
680
- const canvasH = canvas.height || 600;
681
- const cx = Coordinate.toAbsolute(position?.x ?? 0.5, canvasW);
682
- const cy = Coordinate.toAbsolute(position?.y ?? 0.5, canvasH);
682
+ const canvasW = this.canvasService.canvas.width || 800;
683
+ const canvasH = this.canvasService.canvas.height || 600;
684
+
685
+ const paddingPx = this.resolvePadding(canvasW, canvasH);
686
+ const layout = Coordinate.calculateLayout(
687
+ { width: canvasW, height: canvasH },
688
+ { width, height },
689
+ paddingPx,
690
+ );
691
+ const scale = layout.scale;
692
+ const cx = layout.offsetX + layout.width / 2;
693
+ const cy = layout.offsetY + layout.height / 2;
694
+ const visualWidth = layout.width;
695
+ const visualHeight = layout.height;
696
+ const visualRadius = radius * scale;
683
697
 
684
698
  // Denormalize Holes for Export
685
699
  const absoluteHoles = (holes || []).map((h) => {
686
- const p = Coordinate.denormalizePoint(
687
- { x: h.x, y: h.y },
700
+ const unit = this.unit || "mm";
701
+ const unitScale = Coordinate.convertUnit(1, "mm", unit);
702
+
703
+ const pos = resolveHolePosition(
704
+ {
705
+ ...h,
706
+ offsetX: (h.offsetX || 0) * unitScale * scale,
707
+ offsetY: (h.offsetY || 0) * unitScale * scale,
708
+ },
709
+ { x: cx, y: cy, width: visualWidth, height: visualHeight },
688
710
  { width: canvasW, height: canvasH },
689
711
  );
712
+
690
713
  return {
691
714
  ...h,
692
- x: p.x,
693
- y: p.y,
715
+ x: pos.x,
716
+ y: pos.y,
717
+ innerRadius: h.innerRadius * unitScale * scale,
718
+ outerRadius: h.outerRadius * unitScale * scale,
719
+ offsetX: (h.offsetX || 0) * unitScale * scale,
720
+ offsetY: (h.offsetY || 0) * unitScale * scale,
694
721
  };
695
722
  });
696
723
 
697
724
  const pathData = generateDielinePath({
698
725
  shape,
699
- width,
700
- height,
701
- radius,
726
+ width: visualWidth,
727
+ height: visualHeight,
728
+ radius: visualRadius,
702
729
  x: cx,
703
730
  y: cy,
704
731
  holes: absoluteHoles,
705
732
  pathData: this.pathData,
706
733
  });
707
734
 
708
- // 2. Create Clip Path
709
- // @ts-ignore
735
+ // 2. Prepare for Export
736
+ // Clone the layer to not affect the stage
737
+ const clonedLayer = await userLayer.clone();
738
+
739
+ // Create Clip Path
740
+ // Note: In Fabric, clipPath is relative to the object center usually,
741
+ // but for a Group that is full-canvas (left=0, top=0), absolute coordinates should work
742
+ // if we configure it correctly.
743
+ // However, Fabric's clipPath handling can be tricky with Groups.
744
+ // Safest bet: Position the clipPath absolutely and ensuring group is absolute.
745
+
710
746
  const clipPath = new Path(pathData, {
711
- left: 0,
712
- top: 0,
713
747
  originX: "left",
714
748
  originY: "top",
715
- absolutePositioned: true,
716
- });
717
-
718
- // 3. Hide UI Layers
719
- const layer = this.getLayer();
720
- const wasVisible = layer?.visible ?? true;
721
- if (layer) layer.visible = false;
722
-
723
- // Hide hole markers
724
- const holeMarkers = canvas
725
- .getObjects()
726
- .filter((o: any) => o.data?.type === "hole-marker");
727
- holeMarkers.forEach((o) => (o.visible = false));
728
-
729
- // Hide Ruler Overlay
730
- const rulerLayer = canvas
731
- .getObjects()
732
- .find((obj: any) => obj.data?.id === "ruler-overlay");
733
- const rulerWasVisible = rulerLayer?.visible ?? true;
734
- if (rulerLayer) rulerLayer.visible = false;
735
-
736
- // 4. Apply Clip & Export
737
- const originalClip = canvas.clipPath;
738
- canvas.clipPath = clipPath;
739
-
740
- const bbox = clipPath.getBoundingRect();
741
-
742
- const clipPathCorrected = new Path(pathData, {
743
- absolutePositioned: true,
744
749
  left: 0,
745
750
  top: 0,
751
+ absolutePositioned: true, // Important for groups
746
752
  });
747
753
 
748
- const tempPath = new Path(pathData);
749
- const tempBounds = tempPath.getBoundingRect();
754
+ clonedLayer.clipPath = clipPath;
750
755
 
751
- clipPathCorrected.set({
752
- left: tempBounds.left,
753
- top: tempBounds.top,
754
- originX: "left",
755
- originY: "top",
756
- });
756
+ // 3. Calculate Crop Area (The Dieline Bounds)
757
+ // We want to export only the area covered by the dieline
758
+ const bounds = clipPath.getBoundingRect();
757
759
 
758
- // 4. Apply Clip & Export
759
- canvas.clipPath = clipPathCorrected;
760
-
761
- const exportBbox = clipPathCorrected.getBoundingRect();
762
- const dataURL = canvas.toDataURL({
760
+ // 4. Export
761
+ const dataUrl = clonedLayer.toDataURL({
763
762
  format: "png",
764
- multiplier: 2,
765
- left: exportBbox.left,
766
- top: exportBbox.top,
767
- width: exportBbox.width,
768
- height: exportBbox.height,
763
+ multiplier: 2, // Better quality
764
+ left: bounds.left,
765
+ top: bounds.top,
766
+ width: bounds.width,
767
+ height: bounds.height,
769
768
  });
770
769
 
771
- // 5. Restore
772
- canvas.clipPath = originalClip;
773
- if (layer) layer.visible = wasVisible;
774
- if (rulerLayer) rulerLayer.visible = rulerWasVisible;
775
- holeMarkers.forEach((o) => (o.visible = true));
776
- canvas.requestRenderAll();
777
-
778
- return dataURL;
770
+ return dataUrl;
779
771
  }
780
772
  }