@pooder/kit 6.1.2 → 6.2.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.
- package/.test-dist/src/extensions/background/BackgroundTool.js +177 -5
- package/.test-dist/src/extensions/constraintUtils.js +44 -0
- package/.test-dist/src/extensions/dieline/DielineTool.js +52 -409
- package/.test-dist/src/extensions/dieline/featureResolution.js +29 -0
- package/.test-dist/src/extensions/dieline/model.js +83 -0
- package/.test-dist/src/extensions/dieline/renderBuilder.js +227 -0
- package/.test-dist/src/extensions/feature/FeatureTool.js +156 -45
- package/.test-dist/src/extensions/featureCoordinates.js +21 -0
- package/.test-dist/src/extensions/featurePlacement.js +46 -0
- package/.test-dist/src/extensions/image/ImageTool.js +281 -25
- package/.test-dist/src/extensions/ruler/RulerTool.js +24 -1
- package/.test-dist/src/shared/constants/layers.js +3 -1
- package/.test-dist/tests/run.js +25 -0
- package/CHANGELOG.md +12 -0
- package/dist/index.d.mts +47 -13
- package/dist/index.d.ts +47 -13
- package/dist/index.js +1325 -977
- package/dist/index.mjs +1311 -966
- package/package.json +1 -1
- package/src/extensions/background/BackgroundTool.ts +264 -4
- package/src/extensions/dieline/DielineTool.ts +67 -548
- package/src/extensions/dieline/model.ts +165 -1
- package/src/extensions/dieline/renderBuilder.ts +301 -0
- package/src/extensions/feature/FeatureTool.ts +190 -47
- package/src/extensions/featureCoordinates.ts +35 -0
- package/src/extensions/featurePlacement.ts +118 -0
- package/src/extensions/image/ImageTool.ts +139 -157
- package/src/extensions/ruler/RulerTool.ts +24 -2
- package/src/shared/constants/layers.ts +2 -0
- package/tests/run.ts +37 -0
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
Canvas as FabricCanvas,
|
|
11
11
|
Control,
|
|
12
12
|
Image as FabricImage,
|
|
13
|
-
Path as FabricPath,
|
|
14
13
|
Pattern,
|
|
15
14
|
Point,
|
|
16
15
|
controlsUtils,
|
|
@@ -177,9 +176,6 @@ const IMAGE_DEFAULT_CONTROL_CAPABILITIES: ImageControlCapability[] = [
|
|
|
177
176
|
];
|
|
178
177
|
|
|
179
178
|
const IMAGE_MOVE_SNAP_THRESHOLD_PX = 6;
|
|
180
|
-
const IMAGE_MOVE_SNAP_RELEASE_THRESHOLD_PX = 10;
|
|
181
|
-
const IMAGE_SNAP_GUIDE_LAYER_ID = "image.snapGuide";
|
|
182
|
-
|
|
183
179
|
const IMAGE_CONTROL_DESCRIPTORS: ImageControlDescriptor[] = [
|
|
184
180
|
{
|
|
185
181
|
key: "tl",
|
|
@@ -236,9 +232,12 @@ export class ImageTool implements Extension {
|
|
|
236
232
|
private overlaySpecs: RenderObjectSpec[] = [];
|
|
237
233
|
private activeSnapX: SnapMatch | null = null;
|
|
238
234
|
private activeSnapY: SnapMatch | null = null;
|
|
239
|
-
private
|
|
240
|
-
private
|
|
235
|
+
private movingImageId: string | null = null;
|
|
236
|
+
private hasRenderedSnapGuides = false;
|
|
241
237
|
private canvasObjectMovingHandler?: (e: any) => void;
|
|
238
|
+
private canvasMouseUpHandler?: (e: any) => void;
|
|
239
|
+
private canvasBeforeRenderHandler?: () => void;
|
|
240
|
+
private canvasAfterRenderHandler?: () => void;
|
|
242
241
|
private renderProducerDisposable?: { dispose: () => void };
|
|
243
242
|
private readonly subscriptions = new SubscriptionBag();
|
|
244
243
|
private imageControlsByCapabilityKey: Map<string, Record<string, Control>> =
|
|
@@ -377,7 +376,7 @@ export class ImageTool implements Extension {
|
|
|
377
376
|
this.imageSpecs = [];
|
|
378
377
|
this.overlaySpecs = [];
|
|
379
378
|
this.imageControlsByCapabilityKey.clear();
|
|
380
|
-
this.
|
|
379
|
+
this.endMoveSnapInteraction();
|
|
381
380
|
this.unbindCanvasInteractionHandlers();
|
|
382
381
|
|
|
383
382
|
this.clearRenderedImages();
|
|
@@ -398,7 +397,7 @@ export class ImageTool implements Extension {
|
|
|
398
397
|
const before = this.isToolActive;
|
|
399
398
|
this.syncToolActiveFromWorkbench(event.id);
|
|
400
399
|
if (!this.isToolActive) {
|
|
401
|
-
this.
|
|
400
|
+
this.endMoveSnapInteraction();
|
|
402
401
|
this.setImageFocus(null, {
|
|
403
402
|
syncCanvasSelection: true,
|
|
404
403
|
skipRender: true,
|
|
@@ -448,7 +447,7 @@ export class ImageTool implements Extension {
|
|
|
448
447
|
};
|
|
449
448
|
|
|
450
449
|
private onSelectionCleared = () => {
|
|
451
|
-
this.
|
|
450
|
+
this.endMoveSnapInteraction();
|
|
452
451
|
this.setImageFocus(null, {
|
|
453
452
|
syncCanvasSelection: false,
|
|
454
453
|
skipRender: true,
|
|
@@ -458,7 +457,7 @@ export class ImageTool implements Extension {
|
|
|
458
457
|
};
|
|
459
458
|
|
|
460
459
|
private onSceneLayoutChanged = () => {
|
|
461
|
-
this.
|
|
460
|
+
this.canvasService?.requestRenderAll();
|
|
462
461
|
this.updateImages();
|
|
463
462
|
};
|
|
464
463
|
|
|
@@ -468,22 +467,65 @@ export class ImageTool implements Extension {
|
|
|
468
467
|
|
|
469
468
|
private bindCanvasInteractionHandlers() {
|
|
470
469
|
if (!this.canvasService || this.canvasObjectMovingHandler) return;
|
|
470
|
+
this.canvasMouseUpHandler = (e: any) => {
|
|
471
|
+
const target = this.getActiveImageTarget(e?.target);
|
|
472
|
+
if (
|
|
473
|
+
target &&
|
|
474
|
+
typeof target?.data?.id === "string" &&
|
|
475
|
+
target.data.id === this.movingImageId
|
|
476
|
+
) {
|
|
477
|
+
this.applyMoveSnapToTarget(target);
|
|
478
|
+
}
|
|
479
|
+
this.endMoveSnapInteraction();
|
|
480
|
+
};
|
|
471
481
|
this.canvasObjectMovingHandler = (e: any) => {
|
|
472
482
|
this.handleCanvasObjectMoving(e);
|
|
473
483
|
};
|
|
484
|
+
this.canvasBeforeRenderHandler = () => {
|
|
485
|
+
this.handleCanvasBeforeRender();
|
|
486
|
+
};
|
|
487
|
+
this.canvasAfterRenderHandler = () => {
|
|
488
|
+
this.handleCanvasAfterRender();
|
|
489
|
+
};
|
|
490
|
+
this.canvasService.canvas.on("mouse:up", this.canvasMouseUpHandler);
|
|
474
491
|
this.canvasService.canvas.on(
|
|
475
492
|
"object:moving",
|
|
476
493
|
this.canvasObjectMovingHandler,
|
|
477
494
|
);
|
|
495
|
+
this.canvasService.canvas.on(
|
|
496
|
+
"before:render",
|
|
497
|
+
this.canvasBeforeRenderHandler,
|
|
498
|
+
);
|
|
499
|
+
this.canvasService.canvas.on("after:render", this.canvasAfterRenderHandler);
|
|
478
500
|
}
|
|
479
501
|
|
|
480
502
|
private unbindCanvasInteractionHandlers() {
|
|
481
|
-
if (!this.canvasService
|
|
482
|
-
this.
|
|
483
|
-
"
|
|
484
|
-
|
|
485
|
-
)
|
|
503
|
+
if (!this.canvasService) return;
|
|
504
|
+
if (this.canvasMouseUpHandler) {
|
|
505
|
+
this.canvasService.canvas.off("mouse:up", this.canvasMouseUpHandler);
|
|
506
|
+
}
|
|
507
|
+
if (this.canvasObjectMovingHandler) {
|
|
508
|
+
this.canvasService.canvas.off(
|
|
509
|
+
"object:moving",
|
|
510
|
+
this.canvasObjectMovingHandler,
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
if (this.canvasBeforeRenderHandler) {
|
|
514
|
+
this.canvasService.canvas.off(
|
|
515
|
+
"before:render",
|
|
516
|
+
this.canvasBeforeRenderHandler,
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
if (this.canvasAfterRenderHandler) {
|
|
520
|
+
this.canvasService.canvas.off(
|
|
521
|
+
"after:render",
|
|
522
|
+
this.canvasAfterRenderHandler,
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
this.canvasMouseUpHandler = undefined;
|
|
486
526
|
this.canvasObjectMovingHandler = undefined;
|
|
527
|
+
this.canvasBeforeRenderHandler = undefined;
|
|
528
|
+
this.canvasAfterRenderHandler = undefined;
|
|
487
529
|
}
|
|
488
530
|
|
|
489
531
|
private getActiveImageTarget(target: any): any | null {
|
|
@@ -518,28 +560,12 @@ export class ImageTool implements Extension {
|
|
|
518
560
|
return this.canvasService.toSceneLength(px);
|
|
519
561
|
}
|
|
520
562
|
|
|
521
|
-
private pickSnapMatch(
|
|
522
|
-
candidates: SnapCandidate[],
|
|
523
|
-
previous: SnapMatch | null,
|
|
524
|
-
): SnapMatch | null {
|
|
563
|
+
private pickSnapMatch(candidates: SnapCandidate[]): SnapMatch | null {
|
|
525
564
|
if (!candidates.length) return null;
|
|
526
565
|
|
|
527
566
|
const snapThreshold = this.getSnapThresholdScene(
|
|
528
567
|
IMAGE_MOVE_SNAP_THRESHOLD_PX,
|
|
529
568
|
);
|
|
530
|
-
const releaseThreshold = this.getSnapThresholdScene(
|
|
531
|
-
IMAGE_MOVE_SNAP_RELEASE_THRESHOLD_PX,
|
|
532
|
-
);
|
|
533
|
-
|
|
534
|
-
if (previous) {
|
|
535
|
-
const sticky = candidates.find((candidate) => {
|
|
536
|
-
return (
|
|
537
|
-
candidate.lineId === previous.lineId &&
|
|
538
|
-
Math.abs(candidate.deltaScene) <= releaseThreshold
|
|
539
|
-
);
|
|
540
|
-
});
|
|
541
|
-
if (sticky) return sticky;
|
|
542
|
-
}
|
|
543
569
|
|
|
544
570
|
let best: SnapCandidate | null = null;
|
|
545
571
|
candidates.forEach((candidate) => {
|
|
@@ -552,10 +578,9 @@ export class ImageTool implements Extension {
|
|
|
552
578
|
}
|
|
553
579
|
|
|
554
580
|
private computeMoveSnapMatches(
|
|
555
|
-
|
|
581
|
+
bounds: FrameRect | null,
|
|
556
582
|
frame: FrameRect,
|
|
557
583
|
): { x: SnapMatch | null; y: SnapMatch | null } {
|
|
558
|
-
const bounds = this.getTargetBoundsScene(target);
|
|
559
584
|
if (!bounds || frame.width <= 0 || frame.height <= 0) {
|
|
560
585
|
return { x: null, y: null };
|
|
561
586
|
}
|
|
@@ -610,8 +635,8 @@ export class ImageTool implements Extension {
|
|
|
610
635
|
];
|
|
611
636
|
|
|
612
637
|
return {
|
|
613
|
-
x: this.pickSnapMatch(xCandidates
|
|
614
|
-
y: this.pickSnapMatch(yCandidates
|
|
638
|
+
x: this.pickSnapMatch(xCandidates),
|
|
639
|
+
y: this.pickSnapMatch(yCandidates),
|
|
615
640
|
};
|
|
616
641
|
}
|
|
617
642
|
|
|
@@ -634,93 +659,104 @@ export class ImageTool implements Extension {
|
|
|
634
659
|
this.activeSnapX = nextX;
|
|
635
660
|
this.activeSnapY = nextY;
|
|
636
661
|
if (changed) {
|
|
637
|
-
this.
|
|
662
|
+
this.canvasService?.requestRenderAll();
|
|
638
663
|
}
|
|
639
664
|
}
|
|
640
665
|
|
|
641
|
-
private
|
|
666
|
+
private clearSnapPreview() {
|
|
642
667
|
this.activeSnapX = null;
|
|
643
668
|
this.activeSnapY = null;
|
|
644
|
-
this.
|
|
645
|
-
this.removeSnapGuideObject("y");
|
|
669
|
+
this.hasRenderedSnapGuides = false;
|
|
646
670
|
this.canvasService?.requestRenderAll();
|
|
647
671
|
}
|
|
648
672
|
|
|
649
|
-
private
|
|
673
|
+
private endMoveSnapInteraction() {
|
|
674
|
+
this.movingImageId = null;
|
|
675
|
+
this.clearSnapPreview();
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
private applyMoveSnapToTarget(target: any): {
|
|
679
|
+
x: SnapMatch | null;
|
|
680
|
+
y: SnapMatch | null;
|
|
681
|
+
} {
|
|
682
|
+
if (!this.canvasService) {
|
|
683
|
+
return { x: null, y: null };
|
|
684
|
+
}
|
|
685
|
+
const frame = this.getFrameRect();
|
|
686
|
+
if (frame.width <= 0 || frame.height <= 0) {
|
|
687
|
+
return { x: null, y: null };
|
|
688
|
+
}
|
|
689
|
+
const bounds = this.getTargetBoundsScene(target);
|
|
690
|
+
const matches = this.computeMoveSnapMatches(bounds, frame);
|
|
691
|
+
const deltaScreenX = this.canvasService.toScreenLength(
|
|
692
|
+
matches.x?.deltaScene ?? 0,
|
|
693
|
+
);
|
|
694
|
+
const deltaScreenY = this.canvasService.toScreenLength(
|
|
695
|
+
matches.y?.deltaScene ?? 0,
|
|
696
|
+
);
|
|
697
|
+
if (deltaScreenX || deltaScreenY) {
|
|
698
|
+
target.set({
|
|
699
|
+
left: Number(target.left || 0) + deltaScreenX,
|
|
700
|
+
top: Number(target.top || 0) + deltaScreenY,
|
|
701
|
+
});
|
|
702
|
+
target.setCoords();
|
|
703
|
+
}
|
|
704
|
+
return matches;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
private handleCanvasBeforeRender() {
|
|
650
708
|
if (!this.canvasService) return;
|
|
651
|
-
|
|
652
|
-
const current =
|
|
653
|
-
axis === "x" ? this.snapGuideXObject : this.snapGuideYObject;
|
|
654
|
-
if (!current) return;
|
|
655
|
-
canvas.remove(current);
|
|
656
|
-
if (axis === "x") {
|
|
657
|
-
this.snapGuideXObject = undefined;
|
|
709
|
+
if (!this.hasRenderedSnapGuides && !this.activeSnapX && !this.activeSnapY) {
|
|
658
710
|
return;
|
|
659
711
|
}
|
|
660
|
-
this.
|
|
712
|
+
this.canvasService.canvas.clearContext(
|
|
713
|
+
this.canvasService.canvas.contextTop,
|
|
714
|
+
);
|
|
715
|
+
this.hasRenderedSnapGuides = false;
|
|
661
716
|
}
|
|
662
717
|
|
|
663
|
-
private
|
|
718
|
+
private drawSnapGuideLine(
|
|
719
|
+
from: { x: number; y: number },
|
|
720
|
+
to: { x: number; y: number },
|
|
721
|
+
) {
|
|
664
722
|
if (!this.canvasService) return;
|
|
665
|
-
const
|
|
723
|
+
const ctx = this.canvasService.canvas.contextTop;
|
|
724
|
+
if (!ctx) return;
|
|
666
725
|
const color =
|
|
667
726
|
this.getConfig<string>("image.control.borderColor", "#1677ff") ||
|
|
668
727
|
"#1677ff";
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
strokeWidth,
|
|
678
|
-
selectable: false,
|
|
679
|
-
evented: false,
|
|
680
|
-
excludeFromExport: true,
|
|
681
|
-
objectCaching: false,
|
|
682
|
-
data: {
|
|
683
|
-
id: `${IMAGE_SNAP_GUIDE_LAYER_ID}.${axis}`,
|
|
684
|
-
layerId: IMAGE_SNAP_GUIDE_LAYER_ID,
|
|
685
|
-
type: "image-snap-guide",
|
|
686
|
-
},
|
|
687
|
-
} as any);
|
|
688
|
-
created.setCoords();
|
|
689
|
-
canvas.add(created);
|
|
690
|
-
canvas.bringObjectToFront(created);
|
|
691
|
-
if (axis === "x") {
|
|
692
|
-
this.snapGuideXObject = created;
|
|
693
|
-
return;
|
|
694
|
-
}
|
|
695
|
-
this.snapGuideYObject = created;
|
|
728
|
+
ctx.save();
|
|
729
|
+
ctx.strokeStyle = color;
|
|
730
|
+
ctx.lineWidth = 1;
|
|
731
|
+
ctx.beginPath();
|
|
732
|
+
ctx.moveTo(from.x, from.y);
|
|
733
|
+
ctx.lineTo(to.x, to.y);
|
|
734
|
+
ctx.stroke();
|
|
735
|
+
ctx.restore();
|
|
696
736
|
}
|
|
697
737
|
|
|
698
|
-
private
|
|
738
|
+
private handleCanvasAfterRender() {
|
|
699
739
|
if (!this.canvasService || !this.isImageEditingVisible()) {
|
|
700
|
-
this.removeSnapGuideObject("x");
|
|
701
|
-
this.removeSnapGuideObject("y");
|
|
702
740
|
return;
|
|
703
741
|
}
|
|
704
742
|
|
|
705
743
|
const frame = this.getFrameRect();
|
|
706
744
|
if (frame.width <= 0 || frame.height <= 0) {
|
|
707
|
-
this.removeSnapGuideObject("x");
|
|
708
|
-
this.removeSnapGuideObject("y");
|
|
709
745
|
return;
|
|
710
746
|
}
|
|
711
747
|
const frameScreen = this.getFrameRectScreen(frame);
|
|
748
|
+
let drew = false;
|
|
712
749
|
|
|
713
750
|
if (this.activeSnapX) {
|
|
714
751
|
const x = this.canvasService.toScreenPoint({
|
|
715
752
|
x: this.activeSnapX.lineScene,
|
|
716
753
|
y: frame.top,
|
|
717
754
|
}).x;
|
|
718
|
-
this.
|
|
719
|
-
|
|
720
|
-
|
|
755
|
+
this.drawSnapGuideLine(
|
|
756
|
+
{ x, y: frameScreen.top },
|
|
757
|
+
{ x, y: frameScreen.top + frameScreen.height },
|
|
721
758
|
);
|
|
722
|
-
|
|
723
|
-
this.removeSnapGuideObject("x");
|
|
759
|
+
drew = true;
|
|
724
760
|
}
|
|
725
761
|
|
|
726
762
|
if (this.activeSnapY) {
|
|
@@ -728,60 +764,31 @@ export class ImageTool implements Extension {
|
|
|
728
764
|
x: frame.left,
|
|
729
765
|
y: this.activeSnapY.lineScene,
|
|
730
766
|
}).y;
|
|
731
|
-
this.
|
|
732
|
-
|
|
733
|
-
|
|
767
|
+
this.drawSnapGuideLine(
|
|
768
|
+
{ x: frameScreen.left, y },
|
|
769
|
+
{ x: frameScreen.left + frameScreen.width, y },
|
|
734
770
|
);
|
|
735
|
-
|
|
736
|
-
this.removeSnapGuideObject("y");
|
|
771
|
+
drew = true;
|
|
737
772
|
}
|
|
738
|
-
|
|
739
|
-
this.canvasService.requestRenderAll();
|
|
773
|
+
this.hasRenderedSnapGuides = drew;
|
|
740
774
|
}
|
|
741
775
|
|
|
742
776
|
private handleCanvasObjectMoving(e: any) {
|
|
743
777
|
const target = this.getActiveImageTarget(e?.target);
|
|
744
778
|
if (!target || !this.canvasService) return;
|
|
779
|
+
this.movingImageId =
|
|
780
|
+
typeof target?.data?.id === "string" ? target.data.id : null;
|
|
745
781
|
|
|
746
782
|
const frame = this.getFrameRect();
|
|
747
783
|
if (frame.width <= 0 || frame.height <= 0) {
|
|
748
|
-
this.
|
|
784
|
+
this.endMoveSnapInteraction();
|
|
749
785
|
return;
|
|
750
786
|
}
|
|
751
|
-
|
|
752
|
-
const matches = this.computeMoveSnapMatches(
|
|
753
|
-
const deltaX = matches.x?.deltaScene ?? 0;
|
|
754
|
-
const deltaY = matches.y?.deltaScene ?? 0;
|
|
755
|
-
|
|
756
|
-
if (deltaX || deltaY) {
|
|
757
|
-
target.set({
|
|
758
|
-
left:
|
|
759
|
-
Number(target.left || 0) + this.canvasService.toScreenLength(deltaX),
|
|
760
|
-
top:
|
|
761
|
-
Number(target.top || 0) + this.canvasService.toScreenLength(deltaY),
|
|
762
|
-
});
|
|
763
|
-
target.setCoords();
|
|
764
|
-
}
|
|
765
|
-
|
|
787
|
+
const rawBounds = this.getTargetBoundsScene(target);
|
|
788
|
+
const matches = this.computeMoveSnapMatches(rawBounds, frame);
|
|
766
789
|
this.updateSnapMatchState(matches.x, matches.y);
|
|
767
790
|
}
|
|
768
791
|
|
|
769
|
-
private applySnapMatchesToTarget(
|
|
770
|
-
target: any,
|
|
771
|
-
matches: { x: SnapMatch | null; y: SnapMatch | null },
|
|
772
|
-
) {
|
|
773
|
-
if (!this.canvasService || !target) return;
|
|
774
|
-
const deltaX = matches.x?.deltaScene ?? 0;
|
|
775
|
-
const deltaY = matches.y?.deltaScene ?? 0;
|
|
776
|
-
if (!deltaX && !deltaY) return;
|
|
777
|
-
|
|
778
|
-
target.set({
|
|
779
|
-
left: Number(target.left || 0) + this.canvasService.toScreenLength(deltaX),
|
|
780
|
-
top: Number(target.top || 0) + this.canvasService.toScreenLength(deltaY),
|
|
781
|
-
});
|
|
782
|
-
target.setCoords();
|
|
783
|
-
}
|
|
784
|
-
|
|
785
792
|
private syncToolActiveFromWorkbench(fallbackId?: string | null) {
|
|
786
793
|
const wb = this.context?.services.get<WorkbenchService>("WorkbenchService");
|
|
787
794
|
const activeId = wb?.activeToolId;
|
|
@@ -1546,33 +1553,9 @@ export class ImageTool implements Extension {
|
|
|
1546
1553
|
originY: "top",
|
|
1547
1554
|
fill: hatchFill,
|
|
1548
1555
|
opacity: patternFill ? 1 : 0.8,
|
|
1549
|
-
stroke: null,
|
|
1550
|
-
fillRule: "evenodd",
|
|
1551
|
-
selectable: false,
|
|
1552
|
-
evented: false,
|
|
1553
|
-
excludeFromExport: true,
|
|
1554
|
-
objectCaching: false,
|
|
1555
|
-
},
|
|
1556
|
-
},
|
|
1557
|
-
{
|
|
1558
|
-
id: "image.cropShapePath",
|
|
1559
|
-
type: "path",
|
|
1560
|
-
data: { id: "image.cropShapePath", zIndex: 6 },
|
|
1561
|
-
layout: {
|
|
1562
|
-
reference: "custom",
|
|
1563
|
-
referenceRect: frameRect,
|
|
1564
|
-
alignX: "start",
|
|
1565
|
-
alignY: "start",
|
|
1566
|
-
offsetX: shapeBounds.x,
|
|
1567
|
-
offsetY: shapeBounds.y,
|
|
1568
|
-
},
|
|
1569
|
-
props: {
|
|
1570
|
-
pathData: shapePathData,
|
|
1571
|
-
originX: "left",
|
|
1572
|
-
originY: "top",
|
|
1573
|
-
fill: "rgba(0,0,0,0)",
|
|
1574
1556
|
stroke: "rgba(255, 0, 0, 0.9)",
|
|
1575
1557
|
strokeWidth: this.canvasService?.toSceneLength(1) ?? 1,
|
|
1558
|
+
fillRule: "evenodd",
|
|
1576
1559
|
selectable: false,
|
|
1577
1560
|
evented: false,
|
|
1578
1561
|
excludeFromExport: true,
|
|
@@ -1959,7 +1942,6 @@ export class ImageTool implements Extension {
|
|
|
1959
1942
|
isImageSelectionActive: this.isImageSelectionActive,
|
|
1960
1943
|
focusedImageId: this.focusedImageId,
|
|
1961
1944
|
});
|
|
1962
|
-
this.updateSnapGuideVisuals();
|
|
1963
1945
|
this.canvasService.requestRenderAll();
|
|
1964
1946
|
}
|
|
1965
1947
|
|
|
@@ -1973,12 +1955,12 @@ export class ImageTool implements Extension {
|
|
|
1973
1955
|
const id = target?.data?.id;
|
|
1974
1956
|
const layerId = target?.data?.layerId;
|
|
1975
1957
|
if (typeof id !== "string" || layerId !== IMAGE_OBJECT_LAYER_ID) return;
|
|
1976
|
-
|
|
1958
|
+
if (this.movingImageId === id) {
|
|
1959
|
+
this.applyMoveSnapToTarget(target);
|
|
1960
|
+
}
|
|
1977
1961
|
const frame = this.getFrameRect();
|
|
1962
|
+
this.endMoveSnapInteraction();
|
|
1978
1963
|
if (!frame.width || !frame.height) return;
|
|
1979
|
-
const matches = this.computeMoveSnapMatches(target, frame);
|
|
1980
|
-
this.applySnapMatchesToTarget(target, matches);
|
|
1981
|
-
this.clearSnapGuides();
|
|
1982
1964
|
|
|
1983
1965
|
const center = target.getCenterPoint
|
|
1984
1966
|
? target.getCenterPoint()
|
|
@@ -20,11 +20,12 @@ const MIN_ARROW_SIZE = 4;
|
|
|
20
20
|
const THICKNESS_TO_STROKE_WIDTH_RATIO = 20;
|
|
21
21
|
|
|
22
22
|
const DEFAULT_THICKNESS = 20;
|
|
23
|
-
const DEFAULT_GAP =
|
|
23
|
+
const DEFAULT_GAP = 65;
|
|
24
24
|
const DEFAULT_FONT_SIZE = 10;
|
|
25
25
|
const DEFAULT_BACKGROUND_COLOR = "#f0f0f0";
|
|
26
26
|
const DEFAULT_TEXT_COLOR = "#333333";
|
|
27
27
|
const DEFAULT_LINE_COLOR = "#999999";
|
|
28
|
+
const RULER_DEBUG_KEY = "ruler.debug";
|
|
28
29
|
|
|
29
30
|
const RULER_THICKNESS_MIN = 10;
|
|
30
31
|
const RULER_THICKNESS_MAX = 100;
|
|
@@ -48,6 +49,7 @@ export class RulerTool implements Extension {
|
|
|
48
49
|
private textColor = DEFAULT_TEXT_COLOR;
|
|
49
50
|
private lineColor = DEFAULT_LINE_COLOR;
|
|
50
51
|
private fontSize = DEFAULT_FONT_SIZE;
|
|
52
|
+
private debugEnabled = false;
|
|
51
53
|
private renderSeq = 0;
|
|
52
54
|
private readonly numericProps = new Set(["thickness", "gap", "fontSize"]);
|
|
53
55
|
private specs: RenderObjectSpec[] = [];
|
|
@@ -112,7 +114,14 @@ export class RulerTool implements Extension {
|
|
|
112
114
|
this.syncConfig(configService);
|
|
113
115
|
configService.onAnyChange((e: { key: string; value: any }) => {
|
|
114
116
|
let shouldUpdate = false;
|
|
115
|
-
if (e.key
|
|
117
|
+
if (e.key === RULER_DEBUG_KEY) {
|
|
118
|
+
this.debugEnabled = e.value === true;
|
|
119
|
+
this.log("config:update", {
|
|
120
|
+
key: e.key,
|
|
121
|
+
raw: e.value,
|
|
122
|
+
normalized: this.debugEnabled,
|
|
123
|
+
});
|
|
124
|
+
} else if (e.key.startsWith("ruler.")) {
|
|
116
125
|
const prop = e.key.split(".")[1];
|
|
117
126
|
if (prop && prop in this) {
|
|
118
127
|
if (this.numericProps.has(prop)) {
|
|
@@ -203,6 +212,12 @@ export class RulerTool implements Extension {
|
|
|
203
212
|
max: RULER_FONT_SIZE_MAX,
|
|
204
213
|
default: DEFAULT_FONT_SIZE,
|
|
205
214
|
},
|
|
215
|
+
{
|
|
216
|
+
id: RULER_DEBUG_KEY,
|
|
217
|
+
type: "boolean",
|
|
218
|
+
label: "Ruler Debug Log",
|
|
219
|
+
default: false,
|
|
220
|
+
},
|
|
206
221
|
] as ConfigurationContribution[],
|
|
207
222
|
[ContributionPointIds.COMMANDS]: [
|
|
208
223
|
{
|
|
@@ -249,7 +264,12 @@ export class RulerTool implements Extension {
|
|
|
249
264
|
};
|
|
250
265
|
}
|
|
251
266
|
|
|
267
|
+
private isDebugEnabled(): boolean {
|
|
268
|
+
return this.debugEnabled;
|
|
269
|
+
}
|
|
270
|
+
|
|
252
271
|
private log(step: string, payload?: Record<string, unknown>) {
|
|
272
|
+
if (!this.isDebugEnabled()) return;
|
|
253
273
|
if (payload) {
|
|
254
274
|
console.debug(`[RulerTool] ${step}`, payload);
|
|
255
275
|
return;
|
|
@@ -279,6 +299,8 @@ export class RulerTool implements Extension {
|
|
|
279
299
|
configService.get("ruler.fontSize", this.fontSize),
|
|
280
300
|
DEFAULT_FONT_SIZE,
|
|
281
301
|
);
|
|
302
|
+
this.debugEnabled =
|
|
303
|
+
configService.get(RULER_DEBUG_KEY, this.debugEnabled) === true;
|
|
282
304
|
|
|
283
305
|
this.log("config:loaded", {
|
|
284
306
|
thickness: this.thickness,
|
|
@@ -5,6 +5,7 @@ export const WHITE_INK_OBJECT_LAYER_ID = "white-ink.user";
|
|
|
5
5
|
export const WHITE_INK_COVER_LAYER_ID = "white-ink.cover";
|
|
6
6
|
export const WHITE_INK_OVERLAY_LAYER_ID = "white-ink.overlay";
|
|
7
7
|
export const DIELINE_LAYER_ID = "dieline-overlay";
|
|
8
|
+
export const FEATURE_DIELINE_LAYER_ID = "feature-dieline-overlay";
|
|
8
9
|
export const FEATURE_OVERLAY_LAYER_ID = "feature-overlay";
|
|
9
10
|
export const RULER_LAYER_ID = "ruler-overlay";
|
|
10
11
|
export const FILM_LAYER_ID = "overlay";
|
|
@@ -17,6 +18,7 @@ export const LAYER_IDS = {
|
|
|
17
18
|
whiteInkCover: WHITE_INK_COVER_LAYER_ID,
|
|
18
19
|
whiteInkOverlay: WHITE_INK_OVERLAY_LAYER_ID,
|
|
19
20
|
dieline: DIELINE_LAYER_ID,
|
|
21
|
+
featureDieline: FEATURE_DIELINE_LAYER_ID,
|
|
20
22
|
featureOverlay: FEATURE_OVERLAY_LAYER_ID,
|
|
21
23
|
rulerOverlay: RULER_LAYER_ID,
|
|
22
24
|
filmOverlay: FILM_LAYER_ID,
|
package/tests/run.ts
CHANGED
|
@@ -21,6 +21,10 @@ import { createWhiteInkCommands } from "../src/extensions/white-ink/commands";
|
|
|
21
21
|
import { createWhiteInkConfigurations } from "../src/extensions/white-ink/config";
|
|
22
22
|
import { createDielineCommands } from "../src/extensions/dieline/commands";
|
|
23
23
|
import { createDielineConfigurations } from "../src/extensions/dieline/config";
|
|
24
|
+
import {
|
|
25
|
+
normalizePointInGeometry,
|
|
26
|
+
resolveFeaturePosition,
|
|
27
|
+
} from "../src/extensions/featureCoordinates";
|
|
24
28
|
|
|
25
29
|
function assert(condition: unknown, message: string) {
|
|
26
30
|
if (!condition) throw new Error(message);
|
|
@@ -120,6 +124,38 @@ function testEdgeScale() {
|
|
|
120
124
|
assert(height === 80, `expected height 80, got ${height}`);
|
|
121
125
|
}
|
|
122
126
|
|
|
127
|
+
function testFeaturePlacementProjection() {
|
|
128
|
+
const trimGeometry = {
|
|
129
|
+
x: 100,
|
|
130
|
+
y: 120,
|
|
131
|
+
width: 120,
|
|
132
|
+
height: 180,
|
|
133
|
+
};
|
|
134
|
+
const cutGeometry = {
|
|
135
|
+
x: 100,
|
|
136
|
+
y: 120,
|
|
137
|
+
width: 150,
|
|
138
|
+
height: 210,
|
|
139
|
+
};
|
|
140
|
+
const trimFeature = {
|
|
141
|
+
x: 0.82,
|
|
142
|
+
y: 0.68,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const trimCenter = resolveFeaturePosition(trimFeature, trimGeometry);
|
|
146
|
+
const cutFeature = normalizePointInGeometry(trimCenter, cutGeometry);
|
|
147
|
+
const cutCenter = resolveFeaturePosition(cutFeature, cutGeometry);
|
|
148
|
+
|
|
149
|
+
assert(
|
|
150
|
+
Math.abs(trimCenter.x - cutCenter.x) < 1e-6,
|
|
151
|
+
`expected projected feature x to stay fixed, got ${trimCenter.x} vs ${cutCenter.x}`,
|
|
152
|
+
);
|
|
153
|
+
assert(
|
|
154
|
+
Math.abs(trimCenter.y - cutCenter.y) < 1e-6,
|
|
155
|
+
`expected projected feature y to stay fixed, got ${trimCenter.y} vs ${cutCenter.y}`,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
123
159
|
function testVisibilityDsl() {
|
|
124
160
|
const layers = new Map([
|
|
125
161
|
["ruler-overlay", { exists: true, objectCount: 2 }],
|
|
@@ -396,6 +432,7 @@ function main() {
|
|
|
396
432
|
testBridgeSelection();
|
|
397
433
|
testMaskOps();
|
|
398
434
|
testEdgeScale();
|
|
435
|
+
testFeaturePlacementProjection();
|
|
399
436
|
testVisibilityDsl();
|
|
400
437
|
testContributionCompatibility();
|
|
401
438
|
console.log("ok");
|