@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/CHANGELOG.md +17 -0
- package/dist/index.d.mts +46 -36
- package/dist/index.d.ts +46 -36
- package/dist/index.js +958 -696
- package/dist/index.mjs +962 -700
- package/package.json +2 -2
- package/src/coordinate.ts +57 -0
- package/src/dieline.ts +206 -214
- package/src/geometry.ts +101 -4
- package/src/hole.ts +277 -170
- package/src/image.ts +334 -365
- package/src/ruler.ts +295 -120
package/dist/index.mjs
CHANGED
|
@@ -394,6 +394,24 @@ var ImageTracer = class {
|
|
|
394
394
|
|
|
395
395
|
// src/coordinate.ts
|
|
396
396
|
var Coordinate = class {
|
|
397
|
+
/**
|
|
398
|
+
* Calculate layout to fit content within container while preserving aspect ratio.
|
|
399
|
+
*/
|
|
400
|
+
static calculateLayout(container, content, padding = 0) {
|
|
401
|
+
const availableWidth = Math.max(0, container.width - padding * 2);
|
|
402
|
+
const availableHeight = Math.max(0, container.height - padding * 2);
|
|
403
|
+
if (content.width === 0 || content.height === 0) {
|
|
404
|
+
return { scale: 1, offsetX: 0, offsetY: 0, width: 0, height: 0 };
|
|
405
|
+
}
|
|
406
|
+
const scaleX = availableWidth / content.width;
|
|
407
|
+
const scaleY = availableHeight / content.height;
|
|
408
|
+
const scale = Math.min(scaleX, scaleY);
|
|
409
|
+
const width = content.width * scale;
|
|
410
|
+
const height = content.height * scale;
|
|
411
|
+
const offsetX = (container.width - width) / 2;
|
|
412
|
+
const offsetY = (container.height - height) / 2;
|
|
413
|
+
return { scale, offsetX, offsetY, width, height };
|
|
414
|
+
}
|
|
397
415
|
/**
|
|
398
416
|
* Convert an absolute value to a normalized value (0-1).
|
|
399
417
|
* @param value Absolute value (e.g., pixels)
|
|
@@ -428,10 +446,85 @@ var Coordinate = class {
|
|
|
428
446
|
y: this.toAbsolute(point.y, size.height)
|
|
429
447
|
};
|
|
430
448
|
}
|
|
449
|
+
static convertUnit(value, from, to) {
|
|
450
|
+
if (from === to) return value;
|
|
451
|
+
const toMM = {
|
|
452
|
+
px: 0.264583,
|
|
453
|
+
// 1px = 0.264583mm (96 DPI)
|
|
454
|
+
mm: 1,
|
|
455
|
+
cm: 10,
|
|
456
|
+
in: 25.4
|
|
457
|
+
};
|
|
458
|
+
const mmValue = value * (from === "px" ? toMM.px : toMM[from] || 1);
|
|
459
|
+
if (to === "px") {
|
|
460
|
+
return mmValue / toMM.px;
|
|
461
|
+
}
|
|
462
|
+
return mmValue / (toMM[to] || 1);
|
|
463
|
+
}
|
|
431
464
|
};
|
|
432
465
|
|
|
433
466
|
// src/geometry.ts
|
|
434
467
|
import paper from "paper";
|
|
468
|
+
function resolveHolePosition(hole, geometry, canvasSize) {
|
|
469
|
+
if (hole.anchor) {
|
|
470
|
+
const { x, y, width, height } = geometry;
|
|
471
|
+
let bx = x;
|
|
472
|
+
let by = y;
|
|
473
|
+
const left = x - width / 2;
|
|
474
|
+
const right = x + width / 2;
|
|
475
|
+
const top = y - height / 2;
|
|
476
|
+
const bottom = y + height / 2;
|
|
477
|
+
switch (hole.anchor) {
|
|
478
|
+
case "top-left":
|
|
479
|
+
bx = left;
|
|
480
|
+
by = top;
|
|
481
|
+
break;
|
|
482
|
+
case "top-center":
|
|
483
|
+
bx = x;
|
|
484
|
+
by = top;
|
|
485
|
+
break;
|
|
486
|
+
case "top-right":
|
|
487
|
+
bx = right;
|
|
488
|
+
by = top;
|
|
489
|
+
break;
|
|
490
|
+
case "center-left":
|
|
491
|
+
bx = left;
|
|
492
|
+
by = y;
|
|
493
|
+
break;
|
|
494
|
+
case "center":
|
|
495
|
+
bx = x;
|
|
496
|
+
by = y;
|
|
497
|
+
break;
|
|
498
|
+
case "center-right":
|
|
499
|
+
bx = right;
|
|
500
|
+
by = y;
|
|
501
|
+
break;
|
|
502
|
+
case "bottom-left":
|
|
503
|
+
bx = left;
|
|
504
|
+
by = bottom;
|
|
505
|
+
break;
|
|
506
|
+
case "bottom-center":
|
|
507
|
+
bx = x;
|
|
508
|
+
by = bottom;
|
|
509
|
+
break;
|
|
510
|
+
case "bottom-right":
|
|
511
|
+
bx = right;
|
|
512
|
+
by = bottom;
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
return {
|
|
516
|
+
x: bx + (hole.offsetX || 0),
|
|
517
|
+
y: by + (hole.offsetY || 0)
|
|
518
|
+
};
|
|
519
|
+
} else if (hole.x !== void 0 && hole.y !== void 0) {
|
|
520
|
+
const { x, width, y, height } = geometry;
|
|
521
|
+
return {
|
|
522
|
+
x: hole.x * width + (x - width / 2) + (hole.offsetX || 0),
|
|
523
|
+
y: hole.y * height + (y - height / 2) + (hole.offsetY || 0)
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
return { x: 0, y: 0 };
|
|
527
|
+
}
|
|
435
528
|
function ensurePaper(width, height) {
|
|
436
529
|
if (!paper.project) {
|
|
437
530
|
paper.setup(new paper.Size(width, height));
|
|
@@ -533,7 +626,10 @@ function getDielineShape(options) {
|
|
|
533
626
|
cutsPath.remove();
|
|
534
627
|
mainShape = temp;
|
|
535
628
|
} catch (e) {
|
|
536
|
-
console.error(
|
|
629
|
+
console.error(
|
|
630
|
+
"Geometry: Failed to subtract cutsPath from mainShape",
|
|
631
|
+
e
|
|
632
|
+
);
|
|
537
633
|
}
|
|
538
634
|
}
|
|
539
635
|
}
|
|
@@ -626,7 +722,12 @@ function getPathBounds(pathData) {
|
|
|
626
722
|
path.pathData = pathData;
|
|
627
723
|
const bounds = path.bounds;
|
|
628
724
|
path.remove();
|
|
629
|
-
return {
|
|
725
|
+
return {
|
|
726
|
+
x: bounds.x,
|
|
727
|
+
y: bounds.y,
|
|
728
|
+
width: bounds.width,
|
|
729
|
+
height: bounds.height
|
|
730
|
+
};
|
|
630
731
|
}
|
|
631
732
|
|
|
632
733
|
// src/dieline.ts
|
|
@@ -636,6 +737,7 @@ var DielineTool = class {
|
|
|
636
737
|
this.metadata = {
|
|
637
738
|
name: "DielineTool"
|
|
638
739
|
};
|
|
740
|
+
this.unit = "mm";
|
|
639
741
|
this.shape = "rect";
|
|
640
742
|
this.width = 500;
|
|
641
743
|
this.height = 500;
|
|
@@ -646,6 +748,7 @@ var DielineTool = class {
|
|
|
646
748
|
this.outsideColor = "#ffffff";
|
|
647
749
|
this.showBleedLines = true;
|
|
648
750
|
this.holes = [];
|
|
751
|
+
this.padding = 140;
|
|
649
752
|
if (options) {
|
|
650
753
|
Object.assign(this, options);
|
|
651
754
|
}
|
|
@@ -659,14 +762,12 @@ var DielineTool = class {
|
|
|
659
762
|
}
|
|
660
763
|
const configService = context.services.get("ConfigurationService");
|
|
661
764
|
if (configService) {
|
|
765
|
+
this.unit = configService.get("dieline.unit", this.unit);
|
|
662
766
|
this.shape = configService.get("dieline.shape", this.shape);
|
|
663
767
|
this.width = configService.get("dieline.width", this.width);
|
|
664
768
|
this.height = configService.get("dieline.height", this.height);
|
|
665
769
|
this.radius = configService.get("dieline.radius", this.radius);
|
|
666
|
-
this.
|
|
667
|
-
"dieline.borderLength",
|
|
668
|
-
this.borderLength
|
|
669
|
-
);
|
|
770
|
+
this.padding = configService.get("dieline.padding", this.padding);
|
|
670
771
|
this.offset = configService.get("dieline.offset", this.offset);
|
|
671
772
|
this.style = configService.get("dieline.style", this.style);
|
|
672
773
|
this.insideColor = configService.get(
|
|
@@ -707,6 +808,13 @@ var DielineTool = class {
|
|
|
707
808
|
contribute() {
|
|
708
809
|
return {
|
|
709
810
|
[ContributionPointIds2.CONFIGURATIONS]: [
|
|
811
|
+
{
|
|
812
|
+
id: "dieline.unit",
|
|
813
|
+
type: "select",
|
|
814
|
+
label: "Unit",
|
|
815
|
+
options: ["px", "mm", "cm", "in"],
|
|
816
|
+
default: this.unit
|
|
817
|
+
},
|
|
710
818
|
{
|
|
711
819
|
id: "dieline.shape",
|
|
712
820
|
type: "select",
|
|
@@ -742,15 +850,14 @@ var DielineTool = class {
|
|
|
742
850
|
id: "dieline.position",
|
|
743
851
|
type: "json",
|
|
744
852
|
label: "Position (Normalized)",
|
|
745
|
-
default: this.
|
|
853
|
+
default: this.radius
|
|
746
854
|
},
|
|
747
855
|
{
|
|
748
|
-
id: "dieline.
|
|
749
|
-
type: "
|
|
750
|
-
label: "
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
default: this.borderLength
|
|
856
|
+
id: "dieline.padding",
|
|
857
|
+
type: "select",
|
|
858
|
+
label: "View Padding",
|
|
859
|
+
options: [0, 10, 20, 40, 60, 100, "2%", "5%", "10%", "15%", "20%"],
|
|
860
|
+
default: this.padding
|
|
754
861
|
},
|
|
755
862
|
{
|
|
756
863
|
id: "dieline.offset",
|
|
@@ -793,65 +900,6 @@ var DielineTool = class {
|
|
|
793
900
|
}
|
|
794
901
|
],
|
|
795
902
|
[ContributionPointIds2.COMMANDS]: [
|
|
796
|
-
{
|
|
797
|
-
command: "reset",
|
|
798
|
-
title: "Reset Dieline",
|
|
799
|
-
handler: () => {
|
|
800
|
-
this.shape = "rect";
|
|
801
|
-
this.width = 300;
|
|
802
|
-
this.height = 300;
|
|
803
|
-
this.radius = 0;
|
|
804
|
-
this.offset = 0;
|
|
805
|
-
this.style = "solid";
|
|
806
|
-
this.insideColor = "rgba(0,0,0,0)";
|
|
807
|
-
this.outsideColor = "#ffffff";
|
|
808
|
-
this.showBleedLines = true;
|
|
809
|
-
this.holes = [];
|
|
810
|
-
this.pathData = void 0;
|
|
811
|
-
this.updateDieline();
|
|
812
|
-
return true;
|
|
813
|
-
}
|
|
814
|
-
},
|
|
815
|
-
{
|
|
816
|
-
command: "setDimensions",
|
|
817
|
-
title: "Set Dimensions",
|
|
818
|
-
handler: (width, height) => {
|
|
819
|
-
if (this.width === width && this.height === height) return true;
|
|
820
|
-
this.width = width;
|
|
821
|
-
this.height = height;
|
|
822
|
-
this.updateDieline();
|
|
823
|
-
return true;
|
|
824
|
-
}
|
|
825
|
-
},
|
|
826
|
-
{
|
|
827
|
-
command: "setShape",
|
|
828
|
-
title: "Set Shape",
|
|
829
|
-
handler: (shape) => {
|
|
830
|
-
if (this.shape === shape) return true;
|
|
831
|
-
this.shape = shape;
|
|
832
|
-
this.updateDieline();
|
|
833
|
-
return true;
|
|
834
|
-
}
|
|
835
|
-
},
|
|
836
|
-
{
|
|
837
|
-
command: "setBleed",
|
|
838
|
-
title: "Set Bleed",
|
|
839
|
-
handler: (bleed) => {
|
|
840
|
-
if (this.offset === bleed) return true;
|
|
841
|
-
this.offset = bleed;
|
|
842
|
-
this.updateDieline();
|
|
843
|
-
return true;
|
|
844
|
-
}
|
|
845
|
-
},
|
|
846
|
-
{
|
|
847
|
-
command: "setHoles",
|
|
848
|
-
title: "Set Holes",
|
|
849
|
-
handler: (holes) => {
|
|
850
|
-
this.holes = holes;
|
|
851
|
-
this.updateDieline(false);
|
|
852
|
-
return true;
|
|
853
|
-
}
|
|
854
|
-
},
|
|
855
903
|
{
|
|
856
904
|
command: "getGeometry",
|
|
857
905
|
title: "Get Geometry",
|
|
@@ -870,16 +918,23 @@ var DielineTool = class {
|
|
|
870
918
|
command: "detectEdge",
|
|
871
919
|
title: "Detect Edge from Image",
|
|
872
920
|
handler: async (imageUrl, options) => {
|
|
921
|
+
var _a;
|
|
873
922
|
try {
|
|
874
923
|
const pathData = await ImageTracer.trace(imageUrl, options);
|
|
875
924
|
const bounds = getPathBounds(pathData);
|
|
876
925
|
const currentMax = Math.max(this.width, this.height);
|
|
877
926
|
const scale = currentMax / Math.max(bounds.width, bounds.height);
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
this.
|
|
881
|
-
|
|
882
|
-
|
|
927
|
+
const newWidth = bounds.width * scale;
|
|
928
|
+
const newHeight = bounds.height * scale;
|
|
929
|
+
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
930
|
+
"ConfigurationService"
|
|
931
|
+
);
|
|
932
|
+
if (configService) {
|
|
933
|
+
configService.update("dieline.width", newWidth);
|
|
934
|
+
configService.update("dieline.height", newHeight);
|
|
935
|
+
configService.update("dieline.shape", "custom");
|
|
936
|
+
configService.update("dieline.pathData", pathData);
|
|
937
|
+
}
|
|
883
938
|
return pathData;
|
|
884
939
|
} catch (e) {
|
|
885
940
|
console.error("Edge detection failed", e);
|
|
@@ -938,11 +993,25 @@ var DielineTool = class {
|
|
|
938
993
|
}
|
|
939
994
|
return new Pattern({ source: canvas, repetition: "repeat" });
|
|
940
995
|
}
|
|
996
|
+
resolvePadding(containerWidth, containerHeight) {
|
|
997
|
+
if (typeof this.padding === "number") {
|
|
998
|
+
return this.padding;
|
|
999
|
+
}
|
|
1000
|
+
if (typeof this.padding === "string") {
|
|
1001
|
+
if (this.padding.endsWith("%")) {
|
|
1002
|
+
const percent = parseFloat(this.padding) / 100;
|
|
1003
|
+
return Math.min(containerWidth, containerHeight) * percent;
|
|
1004
|
+
}
|
|
1005
|
+
return parseFloat(this.padding) || 0;
|
|
1006
|
+
}
|
|
1007
|
+
return 0;
|
|
1008
|
+
}
|
|
941
1009
|
updateDieline(emitEvent = true) {
|
|
942
1010
|
if (!this.canvasService) return;
|
|
943
1011
|
const layer = this.getLayer();
|
|
944
1012
|
if (!layer) return;
|
|
945
1013
|
const {
|
|
1014
|
+
unit,
|
|
946
1015
|
shape,
|
|
947
1016
|
radius,
|
|
948
1017
|
offset,
|
|
@@ -950,35 +1019,60 @@ var DielineTool = class {
|
|
|
950
1019
|
insideColor,
|
|
951
1020
|
outsideColor,
|
|
952
1021
|
position,
|
|
953
|
-
borderLength,
|
|
954
1022
|
showBleedLines,
|
|
955
1023
|
holes
|
|
956
1024
|
} = this;
|
|
957
1025
|
let { width, height } = this;
|
|
958
1026
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
959
1027
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
const
|
|
1028
|
+
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
1029
|
+
const layout = Coordinate.calculateLayout(
|
|
1030
|
+
{ width: canvasW, height: canvasH },
|
|
1031
|
+
{ width, height },
|
|
1032
|
+
paddingPx
|
|
1033
|
+
);
|
|
1034
|
+
const scale = layout.scale;
|
|
1035
|
+
const cx = layout.offsetX + layout.width / 2;
|
|
1036
|
+
const cy = layout.offsetY + layout.height / 2;
|
|
1037
|
+
const visualWidth = layout.width;
|
|
1038
|
+
const visualHeight = layout.height;
|
|
1039
|
+
const visualRadius = radius * scale;
|
|
1040
|
+
const visualOffset = offset * scale;
|
|
967
1041
|
layer.remove(...layer.getObjects());
|
|
1042
|
+
const geometryForHoles = {
|
|
1043
|
+
x: cx,
|
|
1044
|
+
y: cy,
|
|
1045
|
+
width: visualWidth,
|
|
1046
|
+
height: visualHeight
|
|
1047
|
+
// Pass scale/unit context if needed by resolveHolePosition (though currently unused there)
|
|
1048
|
+
};
|
|
968
1049
|
const absoluteHoles = (holes || []).map((h) => {
|
|
969
|
-
const
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1050
|
+
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1051
|
+
const offsetScale = unitScale * scale;
|
|
1052
|
+
const hWithPixelOffsets = {
|
|
1053
|
+
...h,
|
|
1054
|
+
offsetX: (h.offsetX || 0) * offsetScale,
|
|
1055
|
+
offsetY: (h.offsetY || 0) * offsetScale
|
|
1056
|
+
};
|
|
1057
|
+
const pos = resolveHolePosition(hWithPixelOffsets, geometryForHoles, {
|
|
1058
|
+
width: canvasW,
|
|
1059
|
+
height: canvasH
|
|
1060
|
+
});
|
|
973
1061
|
return {
|
|
974
1062
|
...h,
|
|
975
|
-
x:
|
|
976
|
-
y:
|
|
1063
|
+
x: pos.x,
|
|
1064
|
+
y: pos.y,
|
|
1065
|
+
// Scale hole radii: mm -> current unit -> pixels
|
|
1066
|
+
innerRadius: h.innerRadius * offsetScale,
|
|
1067
|
+
outerRadius: h.outerRadius * offsetScale,
|
|
1068
|
+
// Store scaled offsets in the result for consistency, though pos is already resolved
|
|
1069
|
+
offsetX: hWithPixelOffsets.offsetX,
|
|
1070
|
+
offsetY: hWithPixelOffsets.offsetY
|
|
977
1071
|
};
|
|
978
1072
|
});
|
|
979
|
-
const cutW = Math.max(0,
|
|
980
|
-
const cutH = Math.max(0,
|
|
981
|
-
const cutR =
|
|
1073
|
+
const cutW = Math.max(0, visualWidth + visualOffset * 2);
|
|
1074
|
+
const cutH = Math.max(0, visualHeight + visualOffset * 2);
|
|
1075
|
+
const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
982
1076
|
const maskPathData = generateMaskPath({
|
|
983
1077
|
canvasWidth: canvasW,
|
|
984
1078
|
canvasHeight: canvasH,
|
|
@@ -1028,15 +1122,15 @@ var DielineTool = class {
|
|
|
1028
1122
|
const bleedPathData = generateBleedZonePath(
|
|
1029
1123
|
{
|
|
1030
1124
|
shape,
|
|
1031
|
-
width,
|
|
1032
|
-
height,
|
|
1033
|
-
radius,
|
|
1125
|
+
width: visualWidth,
|
|
1126
|
+
height: visualHeight,
|
|
1127
|
+
radius: visualRadius,
|
|
1034
1128
|
x: cx,
|
|
1035
1129
|
y: cy,
|
|
1036
1130
|
holes: absoluteHoles,
|
|
1037
1131
|
pathData: this.pathData
|
|
1038
1132
|
},
|
|
1039
|
-
|
|
1133
|
+
visualOffset
|
|
1040
1134
|
);
|
|
1041
1135
|
if (showBleedLines !== false) {
|
|
1042
1136
|
const pattern = this.createHatchPattern("red");
|
|
@@ -1079,13 +1173,12 @@ var DielineTool = class {
|
|
|
1079
1173
|
}
|
|
1080
1174
|
const borderPathData = generateDielinePath({
|
|
1081
1175
|
shape,
|
|
1082
|
-
width,
|
|
1083
|
-
height,
|
|
1084
|
-
radius,
|
|
1176
|
+
width: visualWidth,
|
|
1177
|
+
height: visualHeight,
|
|
1178
|
+
radius: visualRadius,
|
|
1085
1179
|
x: cx,
|
|
1086
1180
|
y: cy,
|
|
1087
1181
|
holes: absoluteHoles,
|
|
1088
|
-
// FIX: Use absoluteHoles instead of holes
|
|
1089
1182
|
pathData: this.pathData
|
|
1090
1183
|
});
|
|
1091
1184
|
const borderObj = new Path(borderPathData, {
|
|
@@ -1123,114 +1216,107 @@ var DielineTool = class {
|
|
|
1123
1216
|
}
|
|
1124
1217
|
}
|
|
1125
1218
|
getGeometry() {
|
|
1126
|
-
var _a, _b;
|
|
1127
1219
|
if (!this.canvasService) return null;
|
|
1128
|
-
const { shape, width, height, radius, position,
|
|
1220
|
+
const { unit, shape, width, height, radius, position, offset } = this;
|
|
1129
1221
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
1130
1222
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
const
|
|
1138
|
-
const
|
|
1223
|
+
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
1224
|
+
const layout = Coordinate.calculateLayout(
|
|
1225
|
+
{ width: canvasW, height: canvasH },
|
|
1226
|
+
{ width, height },
|
|
1227
|
+
paddingPx
|
|
1228
|
+
);
|
|
1229
|
+
const scale = layout.scale;
|
|
1230
|
+
const cx = layout.offsetX + layout.width / 2;
|
|
1231
|
+
const cy = layout.offsetY + layout.height / 2;
|
|
1232
|
+
const visualWidth = layout.width;
|
|
1233
|
+
const visualHeight = layout.height;
|
|
1139
1234
|
return {
|
|
1140
1235
|
shape,
|
|
1236
|
+
unit,
|
|
1141
1237
|
x: cx,
|
|
1142
1238
|
y: cy,
|
|
1143
1239
|
width: visualWidth,
|
|
1144
1240
|
height: visualHeight,
|
|
1145
|
-
radius,
|
|
1146
|
-
offset,
|
|
1147
|
-
|
|
1241
|
+
radius: radius * scale,
|
|
1242
|
+
offset: offset * scale,
|
|
1243
|
+
// Pass scale to help other tools (like HoleTool) convert units
|
|
1244
|
+
scale,
|
|
1148
1245
|
pathData: this.pathData
|
|
1149
1246
|
};
|
|
1150
1247
|
}
|
|
1151
|
-
exportCutImage() {
|
|
1152
|
-
var _a, _b, _c, _d;
|
|
1248
|
+
async exportCutImage() {
|
|
1153
1249
|
if (!this.canvasService) return null;
|
|
1154
|
-
const
|
|
1250
|
+
const userLayer = this.canvasService.getLayer("user");
|
|
1251
|
+
if (!userLayer) return null;
|
|
1155
1252
|
const { shape, width, height, radius, position, holes } = this;
|
|
1156
|
-
const canvasW = canvas.width || 800;
|
|
1157
|
-
const canvasH = canvas.height || 600;
|
|
1158
|
-
const
|
|
1159
|
-
const
|
|
1253
|
+
const canvasW = this.canvasService.canvas.width || 800;
|
|
1254
|
+
const canvasH = this.canvasService.canvas.height || 600;
|
|
1255
|
+
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
1256
|
+
const layout = Coordinate.calculateLayout(
|
|
1257
|
+
{ width: canvasW, height: canvasH },
|
|
1258
|
+
{ width, height },
|
|
1259
|
+
paddingPx
|
|
1260
|
+
);
|
|
1261
|
+
const scale = layout.scale;
|
|
1262
|
+
const cx = layout.offsetX + layout.width / 2;
|
|
1263
|
+
const cy = layout.offsetY + layout.height / 2;
|
|
1264
|
+
const visualWidth = layout.width;
|
|
1265
|
+
const visualHeight = layout.height;
|
|
1266
|
+
const visualRadius = radius * scale;
|
|
1160
1267
|
const absoluteHoles = (holes || []).map((h) => {
|
|
1161
|
-
const
|
|
1162
|
-
|
|
1268
|
+
const unit = this.unit || "mm";
|
|
1269
|
+
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1270
|
+
const pos = resolveHolePosition(
|
|
1271
|
+
{
|
|
1272
|
+
...h,
|
|
1273
|
+
offsetX: (h.offsetX || 0) * unitScale * scale,
|
|
1274
|
+
offsetY: (h.offsetY || 0) * unitScale * scale
|
|
1275
|
+
},
|
|
1276
|
+
{ x: cx, y: cy, width: visualWidth, height: visualHeight },
|
|
1163
1277
|
{ width: canvasW, height: canvasH }
|
|
1164
1278
|
);
|
|
1165
1279
|
return {
|
|
1166
1280
|
...h,
|
|
1167
|
-
x:
|
|
1168
|
-
y:
|
|
1281
|
+
x: pos.x,
|
|
1282
|
+
y: pos.y,
|
|
1283
|
+
innerRadius: h.innerRadius * unitScale * scale,
|
|
1284
|
+
outerRadius: h.outerRadius * unitScale * scale,
|
|
1285
|
+
offsetX: (h.offsetX || 0) * unitScale * scale,
|
|
1286
|
+
offsetY: (h.offsetY || 0) * unitScale * scale
|
|
1169
1287
|
};
|
|
1170
1288
|
});
|
|
1171
1289
|
const pathData = generateDielinePath({
|
|
1172
1290
|
shape,
|
|
1173
|
-
width,
|
|
1174
|
-
height,
|
|
1175
|
-
radius,
|
|
1291
|
+
width: visualWidth,
|
|
1292
|
+
height: visualHeight,
|
|
1293
|
+
radius: visualRadius,
|
|
1176
1294
|
x: cx,
|
|
1177
1295
|
y: cy,
|
|
1178
1296
|
holes: absoluteHoles,
|
|
1179
1297
|
pathData: this.pathData
|
|
1180
1298
|
});
|
|
1299
|
+
const clonedLayer = await userLayer.clone();
|
|
1181
1300
|
const clipPath = new Path(pathData, {
|
|
1182
|
-
left: 0,
|
|
1183
|
-
top: 0,
|
|
1184
1301
|
originX: "left",
|
|
1185
1302
|
originY: "top",
|
|
1186
|
-
absolutePositioned: true
|
|
1187
|
-
});
|
|
1188
|
-
const layer = this.getLayer();
|
|
1189
|
-
const wasVisible = (_c = layer == null ? void 0 : layer.visible) != null ? _c : true;
|
|
1190
|
-
if (layer) layer.visible = false;
|
|
1191
|
-
const holeMarkers = canvas.getObjects().filter((o) => {
|
|
1192
|
-
var _a2;
|
|
1193
|
-
return ((_a2 = o.data) == null ? void 0 : _a2.type) === "hole-marker";
|
|
1194
|
-
});
|
|
1195
|
-
holeMarkers.forEach((o) => o.visible = false);
|
|
1196
|
-
const rulerLayer = canvas.getObjects().find((obj) => {
|
|
1197
|
-
var _a2;
|
|
1198
|
-
return ((_a2 = obj.data) == null ? void 0 : _a2.id) === "ruler-overlay";
|
|
1199
|
-
});
|
|
1200
|
-
const rulerWasVisible = (_d = rulerLayer == null ? void 0 : rulerLayer.visible) != null ? _d : true;
|
|
1201
|
-
if (rulerLayer) rulerLayer.visible = false;
|
|
1202
|
-
const originalClip = canvas.clipPath;
|
|
1203
|
-
canvas.clipPath = clipPath;
|
|
1204
|
-
const bbox = clipPath.getBoundingRect();
|
|
1205
|
-
const clipPathCorrected = new Path(pathData, {
|
|
1206
|
-
absolutePositioned: true,
|
|
1207
1303
|
left: 0,
|
|
1208
|
-
top: 0
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
const tempBounds = tempPath.getBoundingRect();
|
|
1212
|
-
clipPathCorrected.set({
|
|
1213
|
-
left: tempBounds.left,
|
|
1214
|
-
top: tempBounds.top,
|
|
1215
|
-
originX: "left",
|
|
1216
|
-
originY: "top"
|
|
1304
|
+
top: 0,
|
|
1305
|
+
absolutePositioned: true
|
|
1306
|
+
// Important for groups
|
|
1217
1307
|
});
|
|
1218
|
-
|
|
1219
|
-
const
|
|
1220
|
-
const
|
|
1308
|
+
clonedLayer.clipPath = clipPath;
|
|
1309
|
+
const bounds = clipPath.getBoundingRect();
|
|
1310
|
+
const dataUrl = clonedLayer.toDataURL({
|
|
1221
1311
|
format: "png",
|
|
1222
1312
|
multiplier: 2,
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1313
|
+
// Better quality
|
|
1314
|
+
left: bounds.left,
|
|
1315
|
+
top: bounds.top,
|
|
1316
|
+
width: bounds.width,
|
|
1317
|
+
height: bounds.height
|
|
1227
1318
|
});
|
|
1228
|
-
|
|
1229
|
-
if (layer) layer.visible = wasVisible;
|
|
1230
|
-
if (rulerLayer) rulerLayer.visible = rulerWasVisible;
|
|
1231
|
-
holeMarkers.forEach((o) => o.visible = true);
|
|
1232
|
-
canvas.requestRenderAll();
|
|
1233
|
-
return dataURL;
|
|
1319
|
+
return dataUrl;
|
|
1234
1320
|
}
|
|
1235
1321
|
};
|
|
1236
1322
|
|
|
@@ -1406,9 +1492,6 @@ var HoleTool = class {
|
|
|
1406
1492
|
this.metadata = {
|
|
1407
1493
|
name: "HoleTool"
|
|
1408
1494
|
};
|
|
1409
|
-
this.innerRadius = 15;
|
|
1410
|
-
this.outerRadius = 25;
|
|
1411
|
-
this.style = "solid";
|
|
1412
1495
|
this.holes = [];
|
|
1413
1496
|
this.constraintTarget = "bleed";
|
|
1414
1497
|
this.isUpdatingConfig = false;
|
|
@@ -1432,53 +1515,20 @@ var HoleTool = class {
|
|
|
1432
1515
|
"ConfigurationService"
|
|
1433
1516
|
);
|
|
1434
1517
|
if (configService) {
|
|
1435
|
-
this.innerRadius = configService.get(
|
|
1436
|
-
"hole.innerRadius",
|
|
1437
|
-
this.innerRadius
|
|
1438
|
-
);
|
|
1439
|
-
this.outerRadius = configService.get(
|
|
1440
|
-
"hole.outerRadius",
|
|
1441
|
-
this.outerRadius
|
|
1442
|
-
);
|
|
1443
|
-
this.style = configService.get("hole.style", this.style);
|
|
1444
1518
|
this.constraintTarget = configService.get(
|
|
1445
1519
|
"hole.constraintTarget",
|
|
1446
1520
|
this.constraintTarget
|
|
1447
1521
|
);
|
|
1448
|
-
|
|
1449
|
-
if (this.canvasService) {
|
|
1450
|
-
const { width, height } = this.canvasService.canvas;
|
|
1451
|
-
this.holes = dielineHoles.map((h) => {
|
|
1452
|
-
const p = Coordinate.denormalizePoint(h, {
|
|
1453
|
-
width: width || 800,
|
|
1454
|
-
height: height || 600
|
|
1455
|
-
});
|
|
1456
|
-
return { x: p.x, y: p.y };
|
|
1457
|
-
});
|
|
1458
|
-
}
|
|
1522
|
+
this.holes = configService.get("dieline.holes", []);
|
|
1459
1523
|
configService.onAnyChange((e) => {
|
|
1460
1524
|
if (this.isUpdatingConfig) return;
|
|
1461
|
-
if (e.key
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
this[prop] = e.value;
|
|
1465
|
-
this.redraw();
|
|
1466
|
-
this.syncHolesToDieline();
|
|
1467
|
-
}
|
|
1525
|
+
if (e.key === "hole.constraintTarget") {
|
|
1526
|
+
this.constraintTarget = e.value;
|
|
1527
|
+
this.enforceConstraints();
|
|
1468
1528
|
}
|
|
1469
1529
|
if (e.key === "dieline.holes") {
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
const { width, height } = this.canvasService.canvas;
|
|
1473
|
-
this.holes = holes.map((h) => {
|
|
1474
|
-
const p = Coordinate.denormalizePoint(h, {
|
|
1475
|
-
width: width || 800,
|
|
1476
|
-
height: height || 600
|
|
1477
|
-
});
|
|
1478
|
-
return { x: p.x, y: p.y };
|
|
1479
|
-
});
|
|
1480
|
-
this.redraw();
|
|
1481
|
-
}
|
|
1530
|
+
this.holes = e.value || [];
|
|
1531
|
+
this.redraw();
|
|
1482
1532
|
}
|
|
1483
1533
|
});
|
|
1484
1534
|
}
|
|
@@ -1492,29 +1542,6 @@ var HoleTool = class {
|
|
|
1492
1542
|
contribute() {
|
|
1493
1543
|
return {
|
|
1494
1544
|
[ContributionPointIds4.CONFIGURATIONS]: [
|
|
1495
|
-
{
|
|
1496
|
-
id: "hole.innerRadius",
|
|
1497
|
-
type: "number",
|
|
1498
|
-
label: "Inner Radius",
|
|
1499
|
-
min: 1,
|
|
1500
|
-
max: 100,
|
|
1501
|
-
default: 15
|
|
1502
|
-
},
|
|
1503
|
-
{
|
|
1504
|
-
id: "hole.outerRadius",
|
|
1505
|
-
type: "number",
|
|
1506
|
-
label: "Outer Radius",
|
|
1507
|
-
min: 1,
|
|
1508
|
-
max: 100,
|
|
1509
|
-
default: 25
|
|
1510
|
-
},
|
|
1511
|
-
{
|
|
1512
|
-
id: "hole.style",
|
|
1513
|
-
type: "select",
|
|
1514
|
-
label: "Line Style",
|
|
1515
|
-
options: ["solid", "dashed"],
|
|
1516
|
-
default: "solid"
|
|
1517
|
-
},
|
|
1518
1545
|
{
|
|
1519
1546
|
id: "hole.constraintTarget",
|
|
1520
1547
|
type: "select",
|
|
@@ -1528,6 +1555,7 @@ var HoleTool = class {
|
|
|
1528
1555
|
command: "resetHoles",
|
|
1529
1556
|
title: "Reset Holes",
|
|
1530
1557
|
handler: () => {
|
|
1558
|
+
var _a;
|
|
1531
1559
|
if (!this.canvasService) return false;
|
|
1532
1560
|
let defaultPos = { x: this.canvasService.canvas.width / 2, y: 50 };
|
|
1533
1561
|
if (this.currentGeometry) {
|
|
@@ -1538,12 +1566,24 @@ var HoleTool = class {
|
|
|
1538
1566
|
holes: []
|
|
1539
1567
|
});
|
|
1540
1568
|
}
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
this.
|
|
1569
|
+
const { width, height } = this.canvasService.canvas;
|
|
1570
|
+
const normalizedHole = Coordinate.normalizePoint(defaultPos, {
|
|
1571
|
+
width: width || 800,
|
|
1572
|
+
height: height || 600
|
|
1573
|
+
});
|
|
1574
|
+
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1575
|
+
"ConfigurationService"
|
|
1576
|
+
);
|
|
1577
|
+
if (configService) {
|
|
1578
|
+
configService.update("dieline.holes", [
|
|
1579
|
+
{
|
|
1580
|
+
x: normalizedHole.x,
|
|
1581
|
+
y: normalizedHole.y,
|
|
1582
|
+
innerRadius: 15,
|
|
1583
|
+
outerRadius: 25
|
|
1584
|
+
}
|
|
1585
|
+
]);
|
|
1586
|
+
}
|
|
1547
1587
|
return true;
|
|
1548
1588
|
}
|
|
1549
1589
|
},
|
|
@@ -1551,10 +1591,37 @@ var HoleTool = class {
|
|
|
1551
1591
|
command: "addHole",
|
|
1552
1592
|
title: "Add Hole",
|
|
1553
1593
|
handler: (x, y) => {
|
|
1554
|
-
|
|
1555
|
-
this.
|
|
1556
|
-
|
|
1557
|
-
|
|
1594
|
+
var _a, _b, _c;
|
|
1595
|
+
if (!this.canvasService) return false;
|
|
1596
|
+
let normalizedX = 0.5;
|
|
1597
|
+
let normalizedY = 0.5;
|
|
1598
|
+
if (this.currentGeometry) {
|
|
1599
|
+
const { x: gx, y: gy, width: gw, height: gh } = this.currentGeometry;
|
|
1600
|
+
const left = gx - gw / 2;
|
|
1601
|
+
const top = gy - gh / 2;
|
|
1602
|
+
normalizedX = gw > 0 ? (x - left) / gw : 0.5;
|
|
1603
|
+
normalizedY = gh > 0 ? (y - top) / gh : 0.5;
|
|
1604
|
+
} else {
|
|
1605
|
+
const { width, height } = this.canvasService.canvas;
|
|
1606
|
+
normalizedX = Coordinate.toNormalized(x, width || 800);
|
|
1607
|
+
normalizedY = Coordinate.toNormalized(y, height || 600);
|
|
1608
|
+
}
|
|
1609
|
+
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1610
|
+
"ConfigurationService"
|
|
1611
|
+
);
|
|
1612
|
+
if (configService) {
|
|
1613
|
+
const currentHoles = configService.get("dieline.holes", []);
|
|
1614
|
+
const lastHole = currentHoles[currentHoles.length - 1];
|
|
1615
|
+
const innerRadius = (_b = lastHole == null ? void 0 : lastHole.innerRadius) != null ? _b : 15;
|
|
1616
|
+
const outerRadius = (_c = lastHole == null ? void 0 : lastHole.outerRadius) != null ? _c : 25;
|
|
1617
|
+
const newHole = {
|
|
1618
|
+
x: normalizedX,
|
|
1619
|
+
y: normalizedY,
|
|
1620
|
+
innerRadius,
|
|
1621
|
+
outerRadius
|
|
1622
|
+
};
|
|
1623
|
+
configService.update("dieline.holes", [...currentHoles, newHole]);
|
|
1624
|
+
}
|
|
1558
1625
|
return true;
|
|
1559
1626
|
}
|
|
1560
1627
|
},
|
|
@@ -1562,9 +1629,13 @@ var HoleTool = class {
|
|
|
1562
1629
|
command: "clearHoles",
|
|
1563
1630
|
title: "Clear Holes",
|
|
1564
1631
|
handler: () => {
|
|
1565
|
-
|
|
1566
|
-
this.
|
|
1567
|
-
|
|
1632
|
+
var _a;
|
|
1633
|
+
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1634
|
+
"ConfigurationService"
|
|
1635
|
+
);
|
|
1636
|
+
if (configService) {
|
|
1637
|
+
configService.update("dieline.holes", []);
|
|
1638
|
+
}
|
|
1568
1639
|
return true;
|
|
1569
1640
|
}
|
|
1570
1641
|
}
|
|
@@ -1605,10 +1676,12 @@ var HoleTool = class {
|
|
|
1605
1676
|
}
|
|
1606
1677
|
if (!this.handleMoving) {
|
|
1607
1678
|
this.handleMoving = (e) => {
|
|
1608
|
-
var _a;
|
|
1679
|
+
var _a, _b, _c, _d, _e;
|
|
1609
1680
|
const target = e.target;
|
|
1610
1681
|
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "hole-marker") return;
|
|
1611
1682
|
if (!this.currentGeometry) return;
|
|
1683
|
+
const index = (_c = (_b = target.data) == null ? void 0 : _b.index) != null ? _c : -1;
|
|
1684
|
+
const holeData = this.holes[index];
|
|
1612
1685
|
const effectiveOffset = this.constraintTarget === "original" ? 0 : this.currentGeometry.offset;
|
|
1613
1686
|
const constraintGeometry = {
|
|
1614
1687
|
...this.currentGeometry,
|
|
@@ -1620,7 +1693,12 @@ var HoleTool = class {
|
|
|
1620
1693
|
radius: Math.max(0, this.currentGeometry.radius + effectiveOffset)
|
|
1621
1694
|
};
|
|
1622
1695
|
const p = new Point(target.left, target.top);
|
|
1623
|
-
const newPos = this.calculateConstrainedPosition(
|
|
1696
|
+
const newPos = this.calculateConstrainedPosition(
|
|
1697
|
+
p,
|
|
1698
|
+
constraintGeometry,
|
|
1699
|
+
(_d = holeData == null ? void 0 : holeData.innerRadius) != null ? _d : 15,
|
|
1700
|
+
(_e = holeData == null ? void 0 : holeData.outerRadius) != null ? _e : 25
|
|
1701
|
+
);
|
|
1624
1702
|
target.set({
|
|
1625
1703
|
left: newPos.x,
|
|
1626
1704
|
top: newPos.y
|
|
@@ -1633,7 +1711,10 @@ var HoleTool = class {
|
|
|
1633
1711
|
var _a;
|
|
1634
1712
|
const target = e.target;
|
|
1635
1713
|
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "hole-marker") return;
|
|
1636
|
-
this.
|
|
1714
|
+
const changed = this.enforceConstraints();
|
|
1715
|
+
if (!changed) {
|
|
1716
|
+
this.syncHolesFromCanvas();
|
|
1717
|
+
}
|
|
1637
1718
|
};
|
|
1638
1719
|
canvas.on("object:modified", this.handleModified);
|
|
1639
1720
|
}
|
|
@@ -1641,19 +1722,6 @@ var HoleTool = class {
|
|
|
1641
1722
|
}
|
|
1642
1723
|
initializeHoles() {
|
|
1643
1724
|
if (!this.canvasService) return;
|
|
1644
|
-
if (!this.holes || this.holes.length === 0) {
|
|
1645
|
-
let defaultPos = { x: this.canvasService.canvas.width / 2, y: 50 };
|
|
1646
|
-
if (this.currentGeometry) {
|
|
1647
|
-
const g = this.currentGeometry;
|
|
1648
|
-
const topCenter = { x: g.x, y: g.y - g.height / 2 };
|
|
1649
|
-
const snapped = getNearestPointOnDieline(topCenter, {
|
|
1650
|
-
...g,
|
|
1651
|
-
holes: []
|
|
1652
|
-
});
|
|
1653
|
-
defaultPos = snapped;
|
|
1654
|
-
}
|
|
1655
|
-
this.holes = [defaultPos];
|
|
1656
|
-
}
|
|
1657
1725
|
this.redraw();
|
|
1658
1726
|
this.syncHolesToDieline();
|
|
1659
1727
|
}
|
|
@@ -1697,32 +1765,110 @@ var HoleTool = class {
|
|
|
1697
1765
|
var _a;
|
|
1698
1766
|
return ((_a = obj.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
1699
1767
|
});
|
|
1700
|
-
|
|
1701
|
-
|
|
1768
|
+
objects.sort(
|
|
1769
|
+
(a, b) => {
|
|
1770
|
+
var _a, _b, _c, _d;
|
|
1771
|
+
return ((_b = (_a = a.data) == null ? void 0 : _a.index) != null ? _b : 0) - ((_d = (_c = b.data) == null ? void 0 : _c.index) != null ? _d : 0);
|
|
1772
|
+
}
|
|
1773
|
+
);
|
|
1774
|
+
const newHoles = objects.map((obj, i) => {
|
|
1775
|
+
var _a, _b, _c, _d;
|
|
1776
|
+
const original = this.holes[i];
|
|
1777
|
+
const newAbsX = obj.left;
|
|
1778
|
+
const newAbsY = obj.top;
|
|
1779
|
+
const scale = ((_a = this.currentGeometry) == null ? void 0 : _a.scale) || 1;
|
|
1780
|
+
const unit = ((_b = this.currentGeometry) == null ? void 0 : _b.unit) || "mm";
|
|
1781
|
+
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1782
|
+
if (original && original.anchor && this.currentGeometry) {
|
|
1783
|
+
const { x, y, width, height } = this.currentGeometry;
|
|
1784
|
+
let bx = x;
|
|
1785
|
+
let by = y;
|
|
1786
|
+
const left = x - width / 2;
|
|
1787
|
+
const right = x + width / 2;
|
|
1788
|
+
const top = y - height / 2;
|
|
1789
|
+
const bottom = y + height / 2;
|
|
1790
|
+
switch (original.anchor) {
|
|
1791
|
+
case "top-left":
|
|
1792
|
+
bx = left;
|
|
1793
|
+
by = top;
|
|
1794
|
+
break;
|
|
1795
|
+
case "top-center":
|
|
1796
|
+
bx = x;
|
|
1797
|
+
by = top;
|
|
1798
|
+
break;
|
|
1799
|
+
case "top-right":
|
|
1800
|
+
bx = right;
|
|
1801
|
+
by = top;
|
|
1802
|
+
break;
|
|
1803
|
+
case "center-left":
|
|
1804
|
+
bx = left;
|
|
1805
|
+
by = y;
|
|
1806
|
+
break;
|
|
1807
|
+
case "center":
|
|
1808
|
+
bx = x;
|
|
1809
|
+
by = y;
|
|
1810
|
+
break;
|
|
1811
|
+
case "center-right":
|
|
1812
|
+
bx = right;
|
|
1813
|
+
by = y;
|
|
1814
|
+
break;
|
|
1815
|
+
case "bottom-left":
|
|
1816
|
+
bx = left;
|
|
1817
|
+
by = bottom;
|
|
1818
|
+
break;
|
|
1819
|
+
case "bottom-center":
|
|
1820
|
+
bx = x;
|
|
1821
|
+
by = bottom;
|
|
1822
|
+
break;
|
|
1823
|
+
case "bottom-right":
|
|
1824
|
+
bx = right;
|
|
1825
|
+
by = bottom;
|
|
1826
|
+
break;
|
|
1827
|
+
}
|
|
1828
|
+
return {
|
|
1829
|
+
...original,
|
|
1830
|
+
// Denormalize offset back to physical units (mm)
|
|
1831
|
+
offsetX: (newAbsX - bx) / scale / unitScale,
|
|
1832
|
+
offsetY: (newAbsY - by) / scale / unitScale,
|
|
1833
|
+
// Clear direct coordinates if we use anchor
|
|
1834
|
+
x: void 0,
|
|
1835
|
+
y: void 0
|
|
1836
|
+
};
|
|
1837
|
+
}
|
|
1838
|
+
let normalizedX = 0.5;
|
|
1839
|
+
let normalizedY = 0.5;
|
|
1840
|
+
if (this.currentGeometry) {
|
|
1841
|
+
const { x, y, width, height } = this.currentGeometry;
|
|
1842
|
+
const left = x - width / 2;
|
|
1843
|
+
const top = y - height / 2;
|
|
1844
|
+
normalizedX = width > 0 ? (newAbsX - left) / width : 0.5;
|
|
1845
|
+
normalizedY = height > 0 ? (newAbsY - top) / height : 0.5;
|
|
1846
|
+
} else {
|
|
1847
|
+
const { width, height } = this.canvasService.canvas;
|
|
1848
|
+
normalizedX = Coordinate.toNormalized(newAbsX, width || 800);
|
|
1849
|
+
normalizedY = Coordinate.toNormalized(newAbsY, height || 600);
|
|
1850
|
+
}
|
|
1851
|
+
return {
|
|
1852
|
+
...original,
|
|
1853
|
+
x: normalizedX,
|
|
1854
|
+
y: normalizedY,
|
|
1855
|
+
// Ensure radii are preserved
|
|
1856
|
+
innerRadius: (_c = original == null ? void 0 : original.innerRadius) != null ? _c : 15,
|
|
1857
|
+
outerRadius: (_d = original == null ? void 0 : original.outerRadius) != null ? _d : 25
|
|
1858
|
+
};
|
|
1859
|
+
});
|
|
1860
|
+
this.holes = newHoles;
|
|
1702
1861
|
this.syncHolesToDieline();
|
|
1703
1862
|
}
|
|
1704
1863
|
syncHolesToDieline() {
|
|
1705
1864
|
if (!this.context || !this.canvasService) return;
|
|
1706
|
-
const { holes, innerRadius, outerRadius } = this;
|
|
1707
|
-
const currentHoles = holes || [];
|
|
1708
|
-
const width = this.canvasService.canvas.width || 800;
|
|
1709
|
-
const height = this.canvasService.canvas.height || 600;
|
|
1710
1865
|
const configService = this.context.services.get(
|
|
1711
1866
|
"ConfigurationService"
|
|
1712
1867
|
);
|
|
1713
1868
|
if (configService) {
|
|
1714
1869
|
this.isUpdatingConfig = true;
|
|
1715
1870
|
try {
|
|
1716
|
-
|
|
1717
|
-
const p = Coordinate.normalizePoint(h, { width, height });
|
|
1718
|
-
return {
|
|
1719
|
-
x: p.x,
|
|
1720
|
-
y: p.y,
|
|
1721
|
-
innerRadius,
|
|
1722
|
-
outerRadius
|
|
1723
|
-
};
|
|
1724
|
-
});
|
|
1725
|
-
configService.update("dieline.holes", normalizedHoles);
|
|
1871
|
+
configService.update("dieline.holes", this.holes);
|
|
1726
1872
|
} finally {
|
|
1727
1873
|
this.isUpdatingConfig = false;
|
|
1728
1874
|
}
|
|
@@ -1731,19 +1877,43 @@ var HoleTool = class {
|
|
|
1731
1877
|
redraw() {
|
|
1732
1878
|
if (!this.canvasService) return;
|
|
1733
1879
|
const canvas = this.canvasService.canvas;
|
|
1880
|
+
const { width, height } = canvas;
|
|
1734
1881
|
const existing = canvas.getObjects().filter((obj) => {
|
|
1735
1882
|
var _a;
|
|
1736
1883
|
return ((_a = obj.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
1737
1884
|
});
|
|
1738
1885
|
existing.forEach((obj) => canvas.remove(obj));
|
|
1739
|
-
const
|
|
1886
|
+
const holes = this.holes;
|
|
1740
1887
|
if (!holes || holes.length === 0) {
|
|
1741
1888
|
this.canvasService.requestRenderAll();
|
|
1742
1889
|
return;
|
|
1743
1890
|
}
|
|
1891
|
+
const geometry = this.currentGeometry || {
|
|
1892
|
+
x: (width || 800) / 2,
|
|
1893
|
+
y: (height || 600) / 2,
|
|
1894
|
+
width: width || 800,
|
|
1895
|
+
height: height || 600,
|
|
1896
|
+
scale: 1
|
|
1897
|
+
// Default scale if no geometry loaded
|
|
1898
|
+
};
|
|
1744
1899
|
holes.forEach((hole, index) => {
|
|
1900
|
+
const scale = geometry.scale || 1;
|
|
1901
|
+
const unit = geometry.unit || "mm";
|
|
1902
|
+
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1903
|
+
const visualInnerRadius = hole.innerRadius * unitScale * scale;
|
|
1904
|
+
const visualOuterRadius = hole.outerRadius * unitScale * scale;
|
|
1905
|
+
const pos = resolveHolePosition(
|
|
1906
|
+
{
|
|
1907
|
+
...hole,
|
|
1908
|
+
offsetX: (hole.offsetX || 0) * unitScale * scale,
|
|
1909
|
+
offsetY: (hole.offsetY || 0) * unitScale * scale
|
|
1910
|
+
},
|
|
1911
|
+
geometry,
|
|
1912
|
+
{ width: geometry.width, height: geometry.height }
|
|
1913
|
+
// Use geometry dims instead of canvas
|
|
1914
|
+
);
|
|
1745
1915
|
const innerCircle = new Circle({
|
|
1746
|
-
radius:
|
|
1916
|
+
radius: visualInnerRadius,
|
|
1747
1917
|
fill: "transparent",
|
|
1748
1918
|
stroke: "red",
|
|
1749
1919
|
strokeWidth: 2,
|
|
@@ -1751,17 +1921,17 @@ var HoleTool = class {
|
|
|
1751
1921
|
originY: "center"
|
|
1752
1922
|
});
|
|
1753
1923
|
const outerCircle = new Circle({
|
|
1754
|
-
radius:
|
|
1924
|
+
radius: visualOuterRadius,
|
|
1755
1925
|
fill: "transparent",
|
|
1756
1926
|
stroke: "#666",
|
|
1757
1927
|
strokeWidth: 1,
|
|
1758
|
-
strokeDashArray:
|
|
1928
|
+
strokeDashArray: [5, 5],
|
|
1759
1929
|
originX: "center",
|
|
1760
1930
|
originY: "center"
|
|
1761
1931
|
});
|
|
1762
1932
|
const holeGroup = new Group([outerCircle, innerCircle], {
|
|
1763
|
-
left:
|
|
1764
|
-
top:
|
|
1933
|
+
left: pos.x,
|
|
1934
|
+
top: pos.y,
|
|
1765
1935
|
originX: "center",
|
|
1766
1936
|
originY: "center",
|
|
1767
1937
|
selectable: true,
|
|
@@ -1806,9 +1976,6 @@ var HoleTool = class {
|
|
|
1806
1976
|
enforceConstraints() {
|
|
1807
1977
|
const geometry = this.currentGeometry;
|
|
1808
1978
|
if (!geometry || !this.canvasService) {
|
|
1809
|
-
console.log(
|
|
1810
|
-
"[HoleTool] Skipping enforceConstraints: No geometry or canvas service"
|
|
1811
|
-
);
|
|
1812
1979
|
return false;
|
|
1813
1980
|
}
|
|
1814
1981
|
const effectiveOffset = this.constraintTarget === "original" ? 0 : geometry.offset;
|
|
@@ -1822,9 +1989,6 @@ var HoleTool = class {
|
|
|
1822
1989
|
var _a;
|
|
1823
1990
|
return ((_a = obj.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
1824
1991
|
});
|
|
1825
|
-
console.log(
|
|
1826
|
-
`[HoleTool] Enforcing constraints on ${objects.length} markers`
|
|
1827
|
-
);
|
|
1828
1992
|
let changed = false;
|
|
1829
1993
|
objects.sort(
|
|
1830
1994
|
(a, b) => {
|
|
@@ -1833,16 +1997,22 @@ var HoleTool = class {
|
|
|
1833
1997
|
}
|
|
1834
1998
|
);
|
|
1835
1999
|
const newHoles = [];
|
|
1836
|
-
objects.forEach((obj) => {
|
|
2000
|
+
objects.forEach((obj, i) => {
|
|
2001
|
+
var _a, _b;
|
|
1837
2002
|
const currentPos = new Point(obj.left, obj.top);
|
|
2003
|
+
const holeData = this.holes[i];
|
|
2004
|
+
const scale = geometry.scale || 1;
|
|
2005
|
+
const unit = geometry.unit || "mm";
|
|
2006
|
+
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
2007
|
+
const innerR = ((_a = holeData == null ? void 0 : holeData.innerRadius) != null ? _a : 15) * unitScale * scale;
|
|
2008
|
+
const outerR = ((_b = holeData == null ? void 0 : holeData.outerRadius) != null ? _b : 25) * unitScale * scale;
|
|
1838
2009
|
const newPos = this.calculateConstrainedPosition(
|
|
1839
2010
|
currentPos,
|
|
1840
|
-
constraintGeometry
|
|
2011
|
+
constraintGeometry,
|
|
2012
|
+
innerR,
|
|
2013
|
+
outerR
|
|
1841
2014
|
);
|
|
1842
2015
|
if (currentPos.distanceFrom(newPos) > 0.1) {
|
|
1843
|
-
console.log(
|
|
1844
|
-
`[HoleTool] Moving hole from (${currentPos.x}, ${currentPos.y}) to (${newPos.x}, ${newPos.y})`
|
|
1845
|
-
);
|
|
1846
2016
|
obj.set({
|
|
1847
2017
|
left: newPos.x,
|
|
1848
2018
|
top: newPos.y
|
|
@@ -1850,16 +2020,14 @@ var HoleTool = class {
|
|
|
1850
2020
|
obj.setCoords();
|
|
1851
2021
|
changed = true;
|
|
1852
2022
|
}
|
|
1853
|
-
newHoles.push({ x: obj.left, y: obj.top });
|
|
1854
2023
|
});
|
|
1855
2024
|
if (changed) {
|
|
1856
|
-
this.
|
|
1857
|
-
this.canvasService.requestRenderAll();
|
|
2025
|
+
this.syncHolesFromCanvas();
|
|
1858
2026
|
return true;
|
|
1859
2027
|
}
|
|
1860
2028
|
return false;
|
|
1861
2029
|
}
|
|
1862
|
-
calculateConstrainedPosition(p, g) {
|
|
2030
|
+
calculateConstrainedPosition(p, g, innerRadius, outerRadius) {
|
|
1863
2031
|
const options = {
|
|
1864
2032
|
...g,
|
|
1865
2033
|
holes: []
|
|
@@ -1873,7 +2041,6 @@ var HoleTool = class {
|
|
|
1873
2041
|
const dist = p.distanceFrom(nearestP);
|
|
1874
2042
|
const v = p.subtract(nearestP);
|
|
1875
2043
|
const center = new Point(g.x, g.y);
|
|
1876
|
-
const centerToNearest = nearestP.subtract(center);
|
|
1877
2044
|
const distToCenter = p.distanceFrom(center);
|
|
1878
2045
|
const nearestDistToCenter = nearestP.distanceFrom(center);
|
|
1879
2046
|
let signedDist = dist;
|
|
@@ -1882,9 +2049,9 @@ var HoleTool = class {
|
|
|
1882
2049
|
}
|
|
1883
2050
|
let clampedDist = signedDist;
|
|
1884
2051
|
if (signedDist > 0) {
|
|
1885
|
-
clampedDist = Math.min(signedDist,
|
|
2052
|
+
clampedDist = Math.min(signedDist, innerRadius);
|
|
1886
2053
|
} else {
|
|
1887
|
-
clampedDist = Math.max(signedDist, -
|
|
2054
|
+
clampedDist = Math.max(signedDist, -outerRadius);
|
|
1888
2055
|
}
|
|
1889
2056
|
if (dist < 1e-3) return nearestP;
|
|
1890
2057
|
const scale = Math.abs(clampedDist) / (dist || 1);
|
|
@@ -1897,19 +2064,16 @@ var HoleTool = class {
|
|
|
1897
2064
|
import {
|
|
1898
2065
|
ContributionPointIds as ContributionPointIds5
|
|
1899
2066
|
} from "@pooder/core";
|
|
1900
|
-
import {
|
|
2067
|
+
import { Image as Image4, Point as Point2, util } from "fabric";
|
|
1901
2068
|
var ImageTool = class {
|
|
1902
|
-
constructor(
|
|
2069
|
+
constructor() {
|
|
1903
2070
|
this.id = "pooder.kit.image";
|
|
1904
2071
|
this.metadata = {
|
|
1905
2072
|
name: "ImageTool"
|
|
1906
2073
|
};
|
|
1907
|
-
this.
|
|
1908
|
-
this.
|
|
1909
|
-
this.
|
|
1910
|
-
if (options) {
|
|
1911
|
-
Object.assign(this, options);
|
|
1912
|
-
}
|
|
2074
|
+
this.items = [];
|
|
2075
|
+
this.objectMap = /* @__PURE__ */ new Map();
|
|
2076
|
+
this.isUpdatingConfig = false;
|
|
1913
2077
|
}
|
|
1914
2078
|
activate(context) {
|
|
1915
2079
|
this.context = context;
|
|
@@ -1920,38 +2084,33 @@ var ImageTool = class {
|
|
|
1920
2084
|
}
|
|
1921
2085
|
const configService = context.services.get("ConfigurationService");
|
|
1922
2086
|
if (configService) {
|
|
1923
|
-
this.
|
|
1924
|
-
this.opacity = configService.get("image.opacity", this.opacity);
|
|
1925
|
-
this.width = configService.get("image.width", this.width);
|
|
1926
|
-
this.height = configService.get("image.height", this.height);
|
|
1927
|
-
this.angle = configService.get("image.angle", this.angle);
|
|
1928
|
-
this.left = configService.get("image.left", this.left);
|
|
1929
|
-
this.top = configService.get("image.top", this.top);
|
|
2087
|
+
this.items = configService.get("image.items", []) || [];
|
|
1930
2088
|
configService.onAnyChange((e) => {
|
|
1931
|
-
if (
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
2089
|
+
if (this.isUpdatingConfig) return;
|
|
2090
|
+
let shouldUpdate = false;
|
|
2091
|
+
if (e.key === "image.items") {
|
|
2092
|
+
this.items = e.value || [];
|
|
2093
|
+
shouldUpdate = true;
|
|
2094
|
+
} else if (e.key.startsWith("dieline.") && e.key !== "dieline.holes") {
|
|
2095
|
+
shouldUpdate = true;
|
|
2096
|
+
}
|
|
2097
|
+
if (shouldUpdate) {
|
|
2098
|
+
this.updateImages();
|
|
1940
2099
|
}
|
|
1941
2100
|
});
|
|
1942
2101
|
}
|
|
1943
2102
|
this.ensureLayer();
|
|
1944
|
-
this.
|
|
2103
|
+
this.updateImages();
|
|
1945
2104
|
}
|
|
1946
2105
|
deactivate(context) {
|
|
1947
2106
|
if (this.canvasService) {
|
|
1948
2107
|
const layer = this.canvasService.getLayer("user");
|
|
1949
2108
|
if (layer) {
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
2109
|
+
this.objectMap.forEach((obj) => {
|
|
2110
|
+
layer.remove(obj);
|
|
2111
|
+
});
|
|
2112
|
+
this.objectMap.clear();
|
|
2113
|
+
this.canvasService.requestRenderAll();
|
|
1955
2114
|
}
|
|
1956
2115
|
this.canvasService = void 0;
|
|
1957
2116
|
this.context = void 0;
|
|
@@ -1961,82 +2120,103 @@ var ImageTool = class {
|
|
|
1961
2120
|
return {
|
|
1962
2121
|
[ContributionPointIds5.CONFIGURATIONS]: [
|
|
1963
2122
|
{
|
|
1964
|
-
id: "image.
|
|
1965
|
-
type: "
|
|
1966
|
-
label: "
|
|
1967
|
-
default:
|
|
1968
|
-
}
|
|
2123
|
+
id: "image.items",
|
|
2124
|
+
type: "array",
|
|
2125
|
+
label: "Images",
|
|
2126
|
+
default: []
|
|
2127
|
+
}
|
|
2128
|
+
],
|
|
2129
|
+
[ContributionPointIds5.COMMANDS]: [
|
|
1969
2130
|
{
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
2131
|
+
command: "addImage",
|
|
2132
|
+
title: "Add Image",
|
|
2133
|
+
handler: (url, options) => {
|
|
2134
|
+
const newItem = {
|
|
2135
|
+
id: this.generateId(),
|
|
2136
|
+
url,
|
|
2137
|
+
opacity: 1,
|
|
2138
|
+
...options
|
|
2139
|
+
};
|
|
2140
|
+
this.updateConfig([...this.items, newItem]);
|
|
2141
|
+
return newItem.id;
|
|
2142
|
+
}
|
|
1977
2143
|
},
|
|
1978
2144
|
{
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
2145
|
+
command: "removeImage",
|
|
2146
|
+
title: "Remove Image",
|
|
2147
|
+
handler: (id) => {
|
|
2148
|
+
const newItems = this.items.filter((item) => item.id !== id);
|
|
2149
|
+
if (newItems.length !== this.items.length) {
|
|
2150
|
+
this.updateConfig(newItems);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
1985
2153
|
},
|
|
1986
2154
|
{
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
2155
|
+
command: "updateImage",
|
|
2156
|
+
title: "Update Image",
|
|
2157
|
+
handler: (id, updates) => {
|
|
2158
|
+
const index = this.items.findIndex((item) => item.id === id);
|
|
2159
|
+
if (index !== -1) {
|
|
2160
|
+
const newItems = [...this.items];
|
|
2161
|
+
newItems[index] = { ...newItems[index], ...updates };
|
|
2162
|
+
this.updateConfig(newItems);
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
1993
2165
|
},
|
|
1994
2166
|
{
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
default: this.angle
|
|
2167
|
+
command: "clearImages",
|
|
2168
|
+
title: "Clear Images",
|
|
2169
|
+
handler: () => {
|
|
2170
|
+
this.updateConfig([]);
|
|
2171
|
+
}
|
|
2001
2172
|
},
|
|
2002
2173
|
{
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2174
|
+
command: "bringToFront",
|
|
2175
|
+
title: "Bring Image to Front",
|
|
2176
|
+
handler: (id) => {
|
|
2177
|
+
const index = this.items.findIndex((item) => item.id === id);
|
|
2178
|
+
if (index !== -1 && index < this.items.length - 1) {
|
|
2179
|
+
const newItems = [...this.items];
|
|
2180
|
+
const [item] = newItems.splice(index, 1);
|
|
2181
|
+
newItems.push(item);
|
|
2182
|
+
this.updateConfig(newItems);
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2009
2185
|
},
|
|
2010
2186
|
{
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
command: "setUserImage",
|
|
2022
|
-
title: "Set User Image",
|
|
2023
|
-
handler: (url, opacity, width, height, angle, left, top) => {
|
|
2024
|
-
if (this.url === url && this.opacity === opacity && this.width === width && this.height === height && this.angle === angle && this.left === left && this.top === top)
|
|
2025
|
-
return true;
|
|
2026
|
-
this.url = url;
|
|
2027
|
-
this.opacity = opacity;
|
|
2028
|
-
this.width = width;
|
|
2029
|
-
this.height = height;
|
|
2030
|
-
this.angle = angle;
|
|
2031
|
-
this.left = left;
|
|
2032
|
-
this.top = top;
|
|
2033
|
-
this.updateImage();
|
|
2034
|
-
return true;
|
|
2187
|
+
command: "sendToBack",
|
|
2188
|
+
title: "Send Image to Back",
|
|
2189
|
+
handler: (id) => {
|
|
2190
|
+
const index = this.items.findIndex((item) => item.id === id);
|
|
2191
|
+
if (index > 0) {
|
|
2192
|
+
const newItems = [...this.items];
|
|
2193
|
+
const [item] = newItems.splice(index, 1);
|
|
2194
|
+
newItems.unshift(item);
|
|
2195
|
+
this.updateConfig(newItems);
|
|
2196
|
+
}
|
|
2035
2197
|
}
|
|
2036
2198
|
}
|
|
2037
2199
|
]
|
|
2038
2200
|
};
|
|
2039
2201
|
}
|
|
2202
|
+
generateId() {
|
|
2203
|
+
return Math.random().toString(36).substring(2, 9);
|
|
2204
|
+
}
|
|
2205
|
+
updateConfig(newItems, skipCanvasUpdate = false) {
|
|
2206
|
+
if (!this.context) return;
|
|
2207
|
+
this.isUpdatingConfig = true;
|
|
2208
|
+
this.items = newItems;
|
|
2209
|
+
const configService = this.context.services.get("ConfigurationService");
|
|
2210
|
+
if (configService) {
|
|
2211
|
+
configService.update("image.items", newItems);
|
|
2212
|
+
}
|
|
2213
|
+
if (!skipCanvasUpdate) {
|
|
2214
|
+
this.updateImages();
|
|
2215
|
+
}
|
|
2216
|
+
setTimeout(() => {
|
|
2217
|
+
this.isUpdatingConfig = false;
|
|
2218
|
+
}, 50);
|
|
2219
|
+
}
|
|
2040
2220
|
ensureLayer() {
|
|
2041
2221
|
if (!this.canvasService) return;
|
|
2042
2222
|
let userLayer = this.canvasService.getLayer("user");
|
|
@@ -2068,224 +2248,176 @@ var ImageTool = class {
|
|
|
2068
2248
|
this.canvasService.requestRenderAll();
|
|
2069
2249
|
}
|
|
2070
2250
|
}
|
|
2071
|
-
|
|
2251
|
+
getLayoutInfo() {
|
|
2072
2252
|
var _a, _b;
|
|
2253
|
+
const canvasW = ((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 800;
|
|
2254
|
+
const canvasH = ((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 600;
|
|
2255
|
+
let layoutScale = 1;
|
|
2256
|
+
let layoutOffsetX = 0;
|
|
2257
|
+
let layoutOffsetY = 0;
|
|
2258
|
+
let visualWidth = canvasW;
|
|
2259
|
+
let visualHeight = canvasH;
|
|
2260
|
+
let dielinePhysicalWidth = 500;
|
|
2261
|
+
let dielinePhysicalHeight = 500;
|
|
2262
|
+
if (this.context) {
|
|
2263
|
+
const configService = this.context.services.get("ConfigurationService");
|
|
2264
|
+
if (configService) {
|
|
2265
|
+
dielinePhysicalWidth = configService.get("dieline.width") || 500;
|
|
2266
|
+
dielinePhysicalHeight = configService.get("dieline.height") || 500;
|
|
2267
|
+
const padding = configService.get("dieline.padding") || 40;
|
|
2268
|
+
const layout = Coordinate.calculateLayout(
|
|
2269
|
+
{ width: canvasW, height: canvasH },
|
|
2270
|
+
{ width: dielinePhysicalWidth, height: dielinePhysicalHeight },
|
|
2271
|
+
padding
|
|
2272
|
+
);
|
|
2273
|
+
layoutScale = layout.scale;
|
|
2274
|
+
layoutOffsetX = layout.offsetX;
|
|
2275
|
+
layoutOffsetY = layout.offsetY;
|
|
2276
|
+
visualWidth = layout.width;
|
|
2277
|
+
visualHeight = layout.height;
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
return {
|
|
2281
|
+
layoutScale,
|
|
2282
|
+
layoutOffsetX,
|
|
2283
|
+
layoutOffsetY,
|
|
2284
|
+
visualWidth,
|
|
2285
|
+
visualHeight,
|
|
2286
|
+
dielinePhysicalWidth,
|
|
2287
|
+
dielinePhysicalHeight
|
|
2288
|
+
};
|
|
2289
|
+
}
|
|
2290
|
+
updateImages() {
|
|
2073
2291
|
if (!this.canvasService) return;
|
|
2074
|
-
let { url, opacity, width, height, angle, left, top } = this;
|
|
2075
2292
|
const layer = this.canvasService.getLayer("user");
|
|
2076
2293
|
if (!layer) {
|
|
2077
2294
|
console.warn("[ImageTool] User layer not found");
|
|
2078
2295
|
return;
|
|
2079
2296
|
}
|
|
2080
|
-
const
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2297
|
+
const currentIds = new Set(this.items.map((i) => i.id));
|
|
2298
|
+
for (const [id, obj] of this.objectMap) {
|
|
2299
|
+
if (!currentIds.has(id)) {
|
|
2300
|
+
layer.remove(obj);
|
|
2301
|
+
this.objectMap.delete(id);
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
const layout = this.getLayoutInfo();
|
|
2305
|
+
this.items.forEach((item, index) => {
|
|
2306
|
+
let obj = this.objectMap.get(item.id);
|
|
2307
|
+
if (!obj) {
|
|
2308
|
+
this.loadImage(item, layer, layout);
|
|
2086
2309
|
} else {
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
const centerX = canvasW / 2;
|
|
2091
|
-
const centerY = canvasH / 2;
|
|
2092
|
-
if (userImage.opacity !== opacity) updates.opacity = opacity;
|
|
2093
|
-
if (angle !== void 0 && userImage.angle !== angle)
|
|
2094
|
-
updates.angle = angle;
|
|
2095
|
-
if (userImage.originX !== "center") {
|
|
2096
|
-
userImage.set({
|
|
2097
|
-
originX: "center",
|
|
2098
|
-
originY: "center",
|
|
2099
|
-
left: userImage.left + userImage.width * userImage.scaleX / 2,
|
|
2100
|
-
top: userImage.top + userImage.height * userImage.scaleY / 2
|
|
2101
|
-
});
|
|
2102
|
-
}
|
|
2103
|
-
if (left !== void 0) {
|
|
2104
|
-
const globalLeft = Coordinate.toAbsolute(left, canvasW);
|
|
2105
|
-
const localLeft = globalLeft - centerX;
|
|
2106
|
-
if (Math.abs(userImage.left - localLeft) > 1)
|
|
2107
|
-
updates.left = localLeft;
|
|
2108
|
-
}
|
|
2109
|
-
if (top !== void 0) {
|
|
2110
|
-
const globalTop = Coordinate.toAbsolute(top, canvasH);
|
|
2111
|
-
const localTop = globalTop - centerY;
|
|
2112
|
-
if (Math.abs(userImage.top - localTop) > 1) updates.top = localTop;
|
|
2113
|
-
}
|
|
2114
|
-
if (width !== void 0 && userImage.width)
|
|
2115
|
-
updates.scaleX = width / userImage.width;
|
|
2116
|
-
if (height !== void 0 && userImage.height)
|
|
2117
|
-
updates.scaleY = height / userImage.height;
|
|
2118
|
-
if (Object.keys(updates).length > 0) {
|
|
2119
|
-
userImage.set(updates);
|
|
2120
|
-
layer.dirty = true;
|
|
2121
|
-
this.canvasService.requestRenderAll();
|
|
2122
|
-
}
|
|
2310
|
+
this.updateObjectProperties(obj, item, layout);
|
|
2311
|
+
layer.remove(obj);
|
|
2312
|
+
layer.add(obj);
|
|
2123
2313
|
}
|
|
2124
|
-
}
|
|
2125
|
-
|
|
2314
|
+
});
|
|
2315
|
+
layer.dirty = true;
|
|
2316
|
+
this.canvasService.requestRenderAll();
|
|
2317
|
+
}
|
|
2318
|
+
updateObjectProperties(obj, item, layout) {
|
|
2319
|
+
const { layoutScale, layoutOffsetX, layoutOffsetY, visualWidth, visualHeight } = layout;
|
|
2320
|
+
const updates = {};
|
|
2321
|
+
if (obj.opacity !== item.opacity) updates.opacity = item.opacity;
|
|
2322
|
+
if (item.angle !== void 0 && obj.angle !== item.angle) updates.angle = item.angle;
|
|
2323
|
+
if (item.left !== void 0) {
|
|
2324
|
+
const globalLeft = layoutOffsetX + item.left * visualWidth;
|
|
2325
|
+
if (Math.abs(obj.left - globalLeft) > 1) updates.left = globalLeft;
|
|
2326
|
+
}
|
|
2327
|
+
if (item.top !== void 0) {
|
|
2328
|
+
const globalTop = layoutOffsetY + item.top * visualHeight;
|
|
2329
|
+
if (Math.abs(obj.top - globalTop) > 1) updates.top = globalTop;
|
|
2330
|
+
}
|
|
2331
|
+
if (item.width !== void 0 && obj.width) {
|
|
2332
|
+
const targetScaleX = item.width * layoutScale / obj.width;
|
|
2333
|
+
if (Math.abs(obj.scaleX - targetScaleX) > 1e-3) updates.scaleX = targetScaleX;
|
|
2334
|
+
}
|
|
2335
|
+
if (item.height !== void 0 && obj.height) {
|
|
2336
|
+
const targetScaleY = item.height * layoutScale / obj.height;
|
|
2337
|
+
if (Math.abs(obj.scaleY - targetScaleY) > 1e-3) updates.scaleY = targetScaleY;
|
|
2338
|
+
}
|
|
2339
|
+
if (obj.originX !== "center") {
|
|
2340
|
+
updates.originX = "center";
|
|
2341
|
+
updates.originY = "center";
|
|
2342
|
+
}
|
|
2343
|
+
if (Object.keys(updates).length > 0) {
|
|
2344
|
+
obj.set(updates);
|
|
2126
2345
|
}
|
|
2127
2346
|
}
|
|
2128
|
-
loadImage(layer) {
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
let {
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
{
|
|
2147
|
-
widthVal: dielineWidth,
|
|
2148
|
-
heightVal: dielineHeight,
|
|
2149
|
-
// Debug: dump all keys to see what is available
|
|
2150
|
-
allKeys: Array.from(
|
|
2151
|
-
((_a = configService.configValues) == null ? void 0 : _a.keys()) || []
|
|
2152
|
-
)
|
|
2153
|
-
},
|
|
2154
|
-
configService
|
|
2155
|
-
);
|
|
2156
|
-
if (width === void 0 && height === void 0) {
|
|
2157
|
-
const scale = Math.min(
|
|
2158
|
-
dielineWidth / (image.width || 1),
|
|
2159
|
-
dielineHeight / (image.height || 1)
|
|
2160
|
-
);
|
|
2161
|
-
width = (image.width || 1) * scale;
|
|
2162
|
-
height = (image.height || 1) * scale;
|
|
2163
|
-
this.width = width;
|
|
2164
|
-
this.height = height;
|
|
2165
|
-
}
|
|
2166
|
-
if (left === void 0 && top === void 0) {
|
|
2167
|
-
const dielinePos = configService == null ? void 0 : configService.get("dieline.position");
|
|
2168
|
-
if (dielinePos) {
|
|
2169
|
-
this.left = dielinePos.x;
|
|
2170
|
-
this.top = dielinePos.y;
|
|
2171
|
-
} else {
|
|
2172
|
-
this.left = 0.5;
|
|
2173
|
-
this.top = 0.5;
|
|
2174
|
-
}
|
|
2175
|
-
left = this.left;
|
|
2176
|
-
top = this.top;
|
|
2177
|
-
}
|
|
2178
|
-
}
|
|
2179
|
-
const existingImage = this.canvasService.getObject(
|
|
2180
|
-
"user-image",
|
|
2181
|
-
"user"
|
|
2182
|
-
);
|
|
2183
|
-
if (existingImage) {
|
|
2184
|
-
const defaultLeft = existingImage.left;
|
|
2185
|
-
const defaultTop = existingImage.top;
|
|
2186
|
-
const defaultAngle = existingImage.angle;
|
|
2187
|
-
const defaultScaleX = existingImage.scaleX;
|
|
2188
|
-
const defaultScaleY = existingImage.scaleY;
|
|
2189
|
-
const canvasW = ((_b = this.canvasService) == null ? void 0 : _b.canvas.width) || 800;
|
|
2190
|
-
const canvasH = ((_c = this.canvasService) == null ? void 0 : _c.canvas.height) || 600;
|
|
2191
|
-
const centerX = canvasW / 2;
|
|
2192
|
-
const centerY = canvasH / 2;
|
|
2193
|
-
let targetLeft = left !== void 0 ? left : defaultLeft;
|
|
2194
|
-
let targetTop = top !== void 0 ? top : defaultTop;
|
|
2195
|
-
const configService = (_d = this.context) == null ? void 0 : _d.services.get(
|
|
2196
|
-
"ConfigurationService"
|
|
2197
|
-
);
|
|
2198
|
-
console.log("[ImageTool] Loading EXISTING image...", {
|
|
2199
|
-
canvasW,
|
|
2200
|
-
canvasH,
|
|
2201
|
-
centerX,
|
|
2202
|
-
centerY,
|
|
2203
|
-
incomingLeft: left,
|
|
2204
|
-
incomingTop: top,
|
|
2205
|
-
dielinePos: configService == null ? void 0 : configService.get("dieline.position"),
|
|
2206
|
-
existingImage: !!existingImage
|
|
2207
|
-
});
|
|
2208
|
-
if (left !== void 0) {
|
|
2209
|
-
const globalLeft = Coordinate.toAbsolute(left, canvasW);
|
|
2210
|
-
targetLeft = globalLeft;
|
|
2211
|
-
console.log("[ImageTool] Calculated targetLeft", {
|
|
2212
|
-
globalLeft,
|
|
2213
|
-
targetLeft
|
|
2214
|
-
});
|
|
2215
|
-
}
|
|
2216
|
-
if (top !== void 0) {
|
|
2217
|
-
const globalTop = Coordinate.toAbsolute(top, canvasH);
|
|
2218
|
-
targetTop = globalTop;
|
|
2219
|
-
console.log("[ImageTool] Calculated targetTop", {
|
|
2220
|
-
globalTop,
|
|
2221
|
-
targetTop
|
|
2222
|
-
});
|
|
2223
|
-
}
|
|
2224
|
-
image.set({
|
|
2225
|
-
originX: "center",
|
|
2226
|
-
// Use center origin for easier positioning
|
|
2227
|
-
originY: "center",
|
|
2228
|
-
left: targetLeft,
|
|
2229
|
-
top: targetTop,
|
|
2230
|
-
angle: angle !== void 0 ? angle : defaultAngle,
|
|
2231
|
-
scaleX: width !== void 0 && image.width ? width / image.width : defaultScaleX,
|
|
2232
|
-
scaleY: height !== void 0 && image.height ? height / image.height : defaultScaleY
|
|
2233
|
-
});
|
|
2234
|
-
layer.remove(existingImage);
|
|
2235
|
-
} else {
|
|
2236
|
-
image.set({
|
|
2237
|
-
originX: "center",
|
|
2238
|
-
originY: "center"
|
|
2239
|
-
});
|
|
2240
|
-
if (width !== void 0 && image.width)
|
|
2241
|
-
image.scaleX = width / image.width;
|
|
2242
|
-
if (height !== void 0 && image.height)
|
|
2243
|
-
image.scaleY = height / image.height;
|
|
2244
|
-
if (angle !== void 0) image.angle = angle;
|
|
2245
|
-
const canvasW = ((_e = this.canvasService) == null ? void 0 : _e.canvas.width) || 800;
|
|
2246
|
-
const canvasH = ((_f = this.canvasService) == null ? void 0 : _f.canvas.height) || 600;
|
|
2247
|
-
const centerX = canvasW / 2;
|
|
2248
|
-
const centerY = canvasH / 2;
|
|
2249
|
-
if (left !== void 0) {
|
|
2250
|
-
image.left = Coordinate.toAbsolute(left, canvasW);
|
|
2251
|
-
} else {
|
|
2252
|
-
image.left = centerX;
|
|
2253
|
-
}
|
|
2254
|
-
if (top !== void 0) {
|
|
2255
|
-
image.top = Coordinate.toAbsolute(top, canvasH);
|
|
2347
|
+
loadImage(item, layer, layout) {
|
|
2348
|
+
Image4.fromURL(item.url, { crossOrigin: "anonymous" }).then((image) => {
|
|
2349
|
+
var _a;
|
|
2350
|
+
if (!this.items.find((i) => i.id === item.id)) return;
|
|
2351
|
+
image.set({
|
|
2352
|
+
originX: "center",
|
|
2353
|
+
originY: "center",
|
|
2354
|
+
data: { id: item.id }
|
|
2355
|
+
});
|
|
2356
|
+
let { width, height, left, top } = item;
|
|
2357
|
+
const { layoutScale, layoutOffsetX, layoutOffsetY, visualWidth, visualHeight, dielinePhysicalWidth, dielinePhysicalHeight } = layout;
|
|
2358
|
+
if (width === void 0 && height === void 0) {
|
|
2359
|
+
const imgAspect = (image.width || 1) / (image.height || 1);
|
|
2360
|
+
const dielineAspect = dielinePhysicalWidth / dielinePhysicalHeight;
|
|
2361
|
+
if (imgAspect > dielineAspect) {
|
|
2362
|
+
const w = dielinePhysicalWidth;
|
|
2363
|
+
width = w;
|
|
2364
|
+
height = w / imgAspect;
|
|
2256
2365
|
} else {
|
|
2257
|
-
|
|
2366
|
+
const h = dielinePhysicalHeight;
|
|
2367
|
+
height = h;
|
|
2368
|
+
width = h * imgAspect;
|
|
2258
2369
|
}
|
|
2370
|
+
item.width = width;
|
|
2371
|
+
item.height = height;
|
|
2259
2372
|
}
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
}
|
|
2373
|
+
if (left === void 0 && top === void 0) {
|
|
2374
|
+
left = 0.5;
|
|
2375
|
+
top = 0.5;
|
|
2376
|
+
item.left = left;
|
|
2377
|
+
item.top = top;
|
|
2378
|
+
}
|
|
2379
|
+
this.updateObjectProperties(image, item, layout);
|
|
2266
2380
|
layer.add(image);
|
|
2381
|
+
this.objectMap.set(item.id, image);
|
|
2267
2382
|
image.on("modified", (e) => {
|
|
2268
|
-
|
|
2269
|
-
const matrix = image.calcTransformMatrix();
|
|
2270
|
-
const globalPoint = util.transformPoint(new Point2(0, 0), matrix);
|
|
2271
|
-
const canvasW = ((_a2 = this.canvasService) == null ? void 0 : _a2.canvas.width) || 800;
|
|
2272
|
-
const canvasH = ((_b2 = this.canvasService) == null ? void 0 : _b2.canvas.height) || 600;
|
|
2273
|
-
this.left = Coordinate.toNormalized(globalPoint.x, canvasW);
|
|
2274
|
-
this.top = Coordinate.toNormalized(globalPoint.y, canvasH);
|
|
2275
|
-
this.angle = e.target.angle;
|
|
2276
|
-
if (image.width) this.width = e.target.width * e.target.scaleX;
|
|
2277
|
-
if (image.height) this.height = e.target.height * e.target.scaleY;
|
|
2278
|
-
if (this.context) {
|
|
2279
|
-
this.context.eventBus.emit("update");
|
|
2280
|
-
}
|
|
2383
|
+
this.handleObjectModified(item.id, image);
|
|
2281
2384
|
});
|
|
2282
2385
|
layer.dirty = true;
|
|
2283
|
-
this.canvasService.requestRenderAll();
|
|
2386
|
+
(_a = this.canvasService) == null ? void 0 : _a.requestRenderAll();
|
|
2387
|
+
if (item.width !== width || item.height !== height || item.left !== left || item.top !== top) {
|
|
2388
|
+
this.updateImageInConfig(item.id, { width, height, left, top });
|
|
2389
|
+
}
|
|
2284
2390
|
}).catch((err) => {
|
|
2285
|
-
|
|
2286
|
-
console.error("Failed to load image", url, err);
|
|
2391
|
+
console.error("Failed to load image", item.url, err);
|
|
2287
2392
|
});
|
|
2288
2393
|
}
|
|
2394
|
+
handleObjectModified(id, image) {
|
|
2395
|
+
const layout = this.getLayoutInfo();
|
|
2396
|
+
const { layoutScale, layoutOffsetX, layoutOffsetY, visualWidth, visualHeight } = layout;
|
|
2397
|
+
const matrix = image.calcTransformMatrix();
|
|
2398
|
+
const globalPoint = util.transformPoint(new Point2(0, 0), matrix);
|
|
2399
|
+
const updates = {};
|
|
2400
|
+
updates.left = (globalPoint.x - layoutOffsetX) / visualWidth;
|
|
2401
|
+
updates.top = (globalPoint.y - layoutOffsetY) / visualHeight;
|
|
2402
|
+
updates.angle = image.angle;
|
|
2403
|
+
if (image.width) {
|
|
2404
|
+
const pixelWidth = image.width * image.scaleX;
|
|
2405
|
+
updates.width = pixelWidth / layoutScale;
|
|
2406
|
+
}
|
|
2407
|
+
if (image.height) {
|
|
2408
|
+
const pixelHeight = image.height * image.scaleY;
|
|
2409
|
+
updates.height = pixelHeight / layoutScale;
|
|
2410
|
+
}
|
|
2411
|
+
this.updateImageInConfig(id, updates);
|
|
2412
|
+
}
|
|
2413
|
+
updateImageInConfig(id, updates) {
|
|
2414
|
+
const index = this.items.findIndex((i) => i.id === id);
|
|
2415
|
+
if (index !== -1) {
|
|
2416
|
+
const newItems = [...this.items];
|
|
2417
|
+
newItems[index] = { ...newItems[index], ...updates };
|
|
2418
|
+
this.updateConfig(newItems, true);
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2289
2421
|
};
|
|
2290
2422
|
|
|
2291
2423
|
// src/white-ink.ts
|
|
@@ -2585,19 +2717,25 @@ var WhiteInkTool = class {
|
|
|
2585
2717
|
import {
|
|
2586
2718
|
ContributionPointIds as ContributionPointIds7
|
|
2587
2719
|
} from "@pooder/core";
|
|
2588
|
-
import {
|
|
2720
|
+
import { Line, Text, Group as Group2, Polygon } from "fabric";
|
|
2589
2721
|
var RulerTool = class {
|
|
2590
2722
|
constructor(options) {
|
|
2591
2723
|
this.id = "pooder.kit.ruler";
|
|
2592
2724
|
this.metadata = {
|
|
2593
2725
|
name: "RulerTool"
|
|
2594
2726
|
};
|
|
2595
|
-
this.unit = "px";
|
|
2596
2727
|
this.thickness = 20;
|
|
2728
|
+
this.gap = 15;
|
|
2597
2729
|
this.backgroundColor = "#f0f0f0";
|
|
2598
2730
|
this.textColor = "#333333";
|
|
2599
2731
|
this.lineColor = "#999999";
|
|
2600
2732
|
this.fontSize = 10;
|
|
2733
|
+
// Dieline context for sync
|
|
2734
|
+
this.dielineWidth = 500;
|
|
2735
|
+
this.dielineHeight = 500;
|
|
2736
|
+
this.dielineUnit = "mm";
|
|
2737
|
+
this.dielinePadding = 40;
|
|
2738
|
+
this.dielineOffset = 0;
|
|
2601
2739
|
if (options) {
|
|
2602
2740
|
Object.assign(this, options);
|
|
2603
2741
|
}
|
|
@@ -2610,8 +2748,8 @@ var RulerTool = class {
|
|
|
2610
2748
|
}
|
|
2611
2749
|
const configService = context.services.get("ConfigurationService");
|
|
2612
2750
|
if (configService) {
|
|
2613
|
-
this.unit = configService.get("ruler.unit", this.unit);
|
|
2614
2751
|
this.thickness = configService.get("ruler.thickness", this.thickness);
|
|
2752
|
+
this.gap = configService.get("ruler.gap", this.gap);
|
|
2615
2753
|
this.backgroundColor = configService.get(
|
|
2616
2754
|
"ruler.backgroundColor",
|
|
2617
2755
|
this.backgroundColor
|
|
@@ -2619,13 +2757,38 @@ var RulerTool = class {
|
|
|
2619
2757
|
this.textColor = configService.get("ruler.textColor", this.textColor);
|
|
2620
2758
|
this.lineColor = configService.get("ruler.lineColor", this.lineColor);
|
|
2621
2759
|
this.fontSize = configService.get("ruler.fontSize", this.fontSize);
|
|
2760
|
+
this.dielineUnit = configService.get("dieline.unit", this.dielineUnit);
|
|
2761
|
+
this.dielineWidth = configService.get("dieline.width", this.dielineWidth);
|
|
2762
|
+
this.dielineHeight = configService.get(
|
|
2763
|
+
"dieline.height",
|
|
2764
|
+
this.dielineHeight
|
|
2765
|
+
);
|
|
2766
|
+
this.dielinePadding = configService.get(
|
|
2767
|
+
"dieline.padding",
|
|
2768
|
+
this.dielinePadding
|
|
2769
|
+
);
|
|
2770
|
+
this.dielineOffset = configService.get(
|
|
2771
|
+
"dieline.offset",
|
|
2772
|
+
this.dielineOffset
|
|
2773
|
+
);
|
|
2622
2774
|
configService.onAnyChange((e) => {
|
|
2775
|
+
let shouldUpdate = false;
|
|
2623
2776
|
if (e.key.startsWith("ruler.")) {
|
|
2624
2777
|
const prop = e.key.split(".")[1];
|
|
2625
2778
|
if (prop && prop in this) {
|
|
2626
2779
|
this[prop] = e.value;
|
|
2627
|
-
|
|
2780
|
+
shouldUpdate = true;
|
|
2628
2781
|
}
|
|
2782
|
+
} else if (e.key.startsWith("dieline.")) {
|
|
2783
|
+
if (e.key === "dieline.unit") this.dielineUnit = e.value;
|
|
2784
|
+
if (e.key === "dieline.width") this.dielineWidth = e.value;
|
|
2785
|
+
if (e.key === "dieline.height") this.dielineHeight = e.value;
|
|
2786
|
+
if (e.key === "dieline.padding") this.dielinePadding = e.value;
|
|
2787
|
+
if (e.key === "dieline.offset") this.dielineOffset = e.value;
|
|
2788
|
+
shouldUpdate = true;
|
|
2789
|
+
}
|
|
2790
|
+
if (shouldUpdate) {
|
|
2791
|
+
this.updateRuler();
|
|
2629
2792
|
}
|
|
2630
2793
|
});
|
|
2631
2794
|
}
|
|
@@ -2639,13 +2802,6 @@ var RulerTool = class {
|
|
|
2639
2802
|
contribute() {
|
|
2640
2803
|
return {
|
|
2641
2804
|
[ContributionPointIds7.CONFIGURATIONS]: [
|
|
2642
|
-
{
|
|
2643
|
-
id: "ruler.unit",
|
|
2644
|
-
type: "select",
|
|
2645
|
-
label: "Unit",
|
|
2646
|
-
options: ["px", "mm", "cm", "in"],
|
|
2647
|
-
default: "px"
|
|
2648
|
-
},
|
|
2649
2805
|
{
|
|
2650
2806
|
id: "ruler.thickness",
|
|
2651
2807
|
type: "number",
|
|
@@ -2654,6 +2810,14 @@ var RulerTool = class {
|
|
|
2654
2810
|
max: 100,
|
|
2655
2811
|
default: 20
|
|
2656
2812
|
},
|
|
2813
|
+
{
|
|
2814
|
+
id: "ruler.gap",
|
|
2815
|
+
type: "number",
|
|
2816
|
+
label: "Gap",
|
|
2817
|
+
min: 0,
|
|
2818
|
+
max: 100,
|
|
2819
|
+
default: 15
|
|
2820
|
+
},
|
|
2657
2821
|
{
|
|
2658
2822
|
id: "ruler.backgroundColor",
|
|
2659
2823
|
type: "color",
|
|
@@ -2682,16 +2846,6 @@ var RulerTool = class {
|
|
|
2682
2846
|
}
|
|
2683
2847
|
],
|
|
2684
2848
|
[ContributionPointIds7.COMMANDS]: [
|
|
2685
|
-
{
|
|
2686
|
-
command: "setUnit",
|
|
2687
|
-
title: "Set Ruler Unit",
|
|
2688
|
-
handler: (unit) => {
|
|
2689
|
-
if (this.unit === unit) return true;
|
|
2690
|
-
this.unit = unit;
|
|
2691
|
-
this.updateRuler();
|
|
2692
|
-
return true;
|
|
2693
|
-
}
|
|
2694
|
-
},
|
|
2695
2849
|
{
|
|
2696
2850
|
command: "setTheme",
|
|
2697
2851
|
title: "Set Ruler Theme",
|
|
@@ -2742,6 +2896,68 @@ var RulerTool = class {
|
|
|
2742
2896
|
this.canvasService.canvas.remove(layer);
|
|
2743
2897
|
}
|
|
2744
2898
|
}
|
|
2899
|
+
createArrowLine(x1, y1, x2, y2, color) {
|
|
2900
|
+
const line = new Line([x1, y1, x2, y2], {
|
|
2901
|
+
stroke: color,
|
|
2902
|
+
strokeWidth: this.thickness / 20,
|
|
2903
|
+
// Scale stroke width relative to thickness (default 1)
|
|
2904
|
+
selectable: false,
|
|
2905
|
+
evented: false
|
|
2906
|
+
});
|
|
2907
|
+
const arrowSize = Math.max(4, this.thickness * 0.3);
|
|
2908
|
+
const angle = Math.atan2(y2 - y1, x2 - x1);
|
|
2909
|
+
const endArrow = new Polygon(
|
|
2910
|
+
[
|
|
2911
|
+
{ x: 0, y: 0 },
|
|
2912
|
+
{ x: -arrowSize, y: -arrowSize / 2 },
|
|
2913
|
+
{ x: -arrowSize, y: arrowSize / 2 }
|
|
2914
|
+
],
|
|
2915
|
+
{
|
|
2916
|
+
fill: color,
|
|
2917
|
+
left: x2,
|
|
2918
|
+
top: y2,
|
|
2919
|
+
originX: "right",
|
|
2920
|
+
originY: "center",
|
|
2921
|
+
angle: angle * 180 / Math.PI,
|
|
2922
|
+
selectable: false,
|
|
2923
|
+
evented: false
|
|
2924
|
+
}
|
|
2925
|
+
);
|
|
2926
|
+
const startArrow = new Polygon(
|
|
2927
|
+
[
|
|
2928
|
+
{ x: 0, y: 0 },
|
|
2929
|
+
{ x: arrowSize, y: -arrowSize / 2 },
|
|
2930
|
+
{ x: arrowSize, y: arrowSize / 2 }
|
|
2931
|
+
],
|
|
2932
|
+
{
|
|
2933
|
+
fill: color,
|
|
2934
|
+
left: x1,
|
|
2935
|
+
top: y1,
|
|
2936
|
+
originX: "left",
|
|
2937
|
+
originY: "center",
|
|
2938
|
+
angle: angle * 180 / Math.PI,
|
|
2939
|
+
selectable: false,
|
|
2940
|
+
evented: false
|
|
2941
|
+
}
|
|
2942
|
+
);
|
|
2943
|
+
return new Group2([line, startArrow, endArrow], {
|
|
2944
|
+
selectable: false,
|
|
2945
|
+
evented: false
|
|
2946
|
+
});
|
|
2947
|
+
}
|
|
2948
|
+
resolvePadding(containerWidth, containerHeight) {
|
|
2949
|
+
if (typeof this.dielinePadding === "number") {
|
|
2950
|
+
return this.dielinePadding;
|
|
2951
|
+
}
|
|
2952
|
+
if (typeof this.dielinePadding === "string") {
|
|
2953
|
+
if (this.dielinePadding.endsWith("%")) {
|
|
2954
|
+
const percent = parseFloat(this.dielinePadding) / 100;
|
|
2955
|
+
return Math.min(containerWidth, containerHeight) * percent;
|
|
2956
|
+
}
|
|
2957
|
+
return parseFloat(this.dielinePadding) || 0;
|
|
2958
|
+
}
|
|
2959
|
+
return 0;
|
|
2960
|
+
}
|
|
2745
2961
|
updateRuler() {
|
|
2746
2962
|
if (!this.canvasService) return;
|
|
2747
2963
|
const layer = this.getLayer();
|
|
@@ -2750,95 +2966,141 @@ var RulerTool = class {
|
|
|
2750
2966
|
const { thickness, backgroundColor, lineColor, textColor, fontSize } = this;
|
|
2751
2967
|
const width = this.canvasService.canvas.width || 800;
|
|
2752
2968
|
const height = this.canvasService.canvas.height || 600;
|
|
2753
|
-
const
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
width,
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
const
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
const
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2969
|
+
const paddingPx = this.resolvePadding(width, height);
|
|
2970
|
+
const layout = Coordinate.calculateLayout(
|
|
2971
|
+
{ width, height },
|
|
2972
|
+
{ width: this.dielineWidth, height: this.dielineHeight },
|
|
2973
|
+
paddingPx
|
|
2974
|
+
);
|
|
2975
|
+
const scale = layout.scale;
|
|
2976
|
+
const offsetX = layout.offsetX;
|
|
2977
|
+
const offsetY = layout.offsetY;
|
|
2978
|
+
const visualWidth = layout.width;
|
|
2979
|
+
const visualHeight = layout.height;
|
|
2980
|
+
const rawOffset = this.dielineOffset || 0;
|
|
2981
|
+
const effectiveOffset = rawOffset > 0 ? rawOffset : 0;
|
|
2982
|
+
const expandPixels = effectiveOffset * scale;
|
|
2983
|
+
const gap = this.gap || 15;
|
|
2984
|
+
const rulerLeft = offsetX - expandPixels;
|
|
2985
|
+
const rulerTop = offsetY - expandPixels;
|
|
2986
|
+
const rulerRight = offsetX + visualWidth + expandPixels;
|
|
2987
|
+
const rulerBottom = offsetY + visualHeight + expandPixels;
|
|
2988
|
+
const displayWidth = this.dielineWidth + effectiveOffset * 2;
|
|
2989
|
+
const displayHeight = this.dielineHeight + effectiveOffset * 2;
|
|
2990
|
+
const topRulerY = rulerTop - gap;
|
|
2991
|
+
const topRulerXStart = rulerLeft;
|
|
2992
|
+
const topRulerXEnd = rulerRight;
|
|
2993
|
+
const leftRulerX = rulerLeft - gap;
|
|
2994
|
+
const leftRulerYStart = rulerTop;
|
|
2995
|
+
const leftRulerYEnd = rulerBottom;
|
|
2996
|
+
const topDimLine = this.createArrowLine(
|
|
2997
|
+
topRulerXStart,
|
|
2998
|
+
topRulerY,
|
|
2999
|
+
topRulerXEnd,
|
|
3000
|
+
topRulerY,
|
|
3001
|
+
lineColor
|
|
3002
|
+
);
|
|
3003
|
+
layer.add(topDimLine);
|
|
3004
|
+
const extLen = 5;
|
|
3005
|
+
layer.add(
|
|
3006
|
+
new Line(
|
|
3007
|
+
[
|
|
3008
|
+
topRulerXStart,
|
|
3009
|
+
topRulerY - extLen,
|
|
3010
|
+
topRulerXStart,
|
|
3011
|
+
topRulerY + extLen
|
|
3012
|
+
],
|
|
3013
|
+
{
|
|
3014
|
+
stroke: lineColor,
|
|
3015
|
+
strokeWidth: 1,
|
|
3016
|
+
selectable: false,
|
|
3017
|
+
evented: false
|
|
3018
|
+
}
|
|
3019
|
+
)
|
|
3020
|
+
);
|
|
3021
|
+
layer.add(
|
|
3022
|
+
new Line(
|
|
3023
|
+
[topRulerXEnd, topRulerY - extLen, topRulerXEnd, topRulerY + extLen],
|
|
3024
|
+
{
|
|
3025
|
+
stroke: lineColor,
|
|
3026
|
+
strokeWidth: 1,
|
|
3027
|
+
selectable: false,
|
|
3028
|
+
evented: false
|
|
3029
|
+
}
|
|
3030
|
+
)
|
|
3031
|
+
);
|
|
3032
|
+
const widthStr = parseFloat(displayWidth.toFixed(2)).toString();
|
|
3033
|
+
const topTextContent = `${widthStr} ${this.dielineUnit}`;
|
|
3034
|
+
const topText = new Text(topTextContent, {
|
|
3035
|
+
left: topRulerXStart + (rulerRight - rulerLeft) / 2,
|
|
3036
|
+
top: topRulerY,
|
|
3037
|
+
fontSize,
|
|
3038
|
+
fill: textColor,
|
|
3039
|
+
fontFamily: "Arial",
|
|
3040
|
+
originX: "center",
|
|
3041
|
+
originY: "center",
|
|
3042
|
+
backgroundColor,
|
|
3043
|
+
// Background mask for readability
|
|
2779
3044
|
selectable: false,
|
|
2780
3045
|
evented: false
|
|
2781
3046
|
});
|
|
2782
|
-
layer.add(
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
left: x + 2,
|
|
2803
|
-
top: 2,
|
|
2804
|
-
fontSize,
|
|
2805
|
-
fill: textColor,
|
|
2806
|
-
fontFamily: "Arial",
|
|
3047
|
+
layer.add(topText);
|
|
3048
|
+
const leftDimLine = this.createArrowLine(
|
|
3049
|
+
leftRulerX,
|
|
3050
|
+
leftRulerYStart,
|
|
3051
|
+
leftRulerX,
|
|
3052
|
+
leftRulerYEnd,
|
|
3053
|
+
lineColor
|
|
3054
|
+
);
|
|
3055
|
+
layer.add(leftDimLine);
|
|
3056
|
+
layer.add(
|
|
3057
|
+
new Line(
|
|
3058
|
+
[
|
|
3059
|
+
leftRulerX - extLen,
|
|
3060
|
+
leftRulerYStart,
|
|
3061
|
+
leftRulerX + extLen,
|
|
3062
|
+
leftRulerYStart
|
|
3063
|
+
],
|
|
3064
|
+
{
|
|
3065
|
+
stroke: lineColor,
|
|
3066
|
+
strokeWidth: 1,
|
|
2807
3067
|
selectable: false,
|
|
2808
3068
|
evented: false
|
|
2809
|
-
}
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
});
|
|
2824
|
-
layer.add(line);
|
|
2825
|
-
if (y % step === 0) {
|
|
2826
|
-
const text = new Text(y.toString(), {
|
|
2827
|
-
angle: -90,
|
|
2828
|
-
left: thickness / 2 - fontSize / 3,
|
|
2829
|
-
// approximate centering
|
|
2830
|
-
top: y + fontSize,
|
|
2831
|
-
fontSize,
|
|
2832
|
-
fill: textColor,
|
|
2833
|
-
fontFamily: "Arial",
|
|
2834
|
-
originX: "center",
|
|
2835
|
-
originY: "center",
|
|
3069
|
+
}
|
|
3070
|
+
)
|
|
3071
|
+
);
|
|
3072
|
+
layer.add(
|
|
3073
|
+
new Line(
|
|
3074
|
+
[
|
|
3075
|
+
leftRulerX - extLen,
|
|
3076
|
+
leftRulerYEnd,
|
|
3077
|
+
leftRulerX + extLen,
|
|
3078
|
+
leftRulerYEnd
|
|
3079
|
+
],
|
|
3080
|
+
{
|
|
3081
|
+
stroke: lineColor,
|
|
3082
|
+
strokeWidth: 1,
|
|
2836
3083
|
selectable: false,
|
|
2837
3084
|
evented: false
|
|
2838
|
-
}
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
3085
|
+
}
|
|
3086
|
+
)
|
|
3087
|
+
);
|
|
3088
|
+
const heightStr = parseFloat(displayHeight.toFixed(2)).toString();
|
|
3089
|
+
const leftTextContent = `${heightStr} ${this.dielineUnit}`;
|
|
3090
|
+
const leftText = new Text(leftTextContent, {
|
|
3091
|
+
left: leftRulerX,
|
|
3092
|
+
top: leftRulerYStart + (rulerBottom - rulerTop) / 2,
|
|
3093
|
+
angle: -90,
|
|
3094
|
+
fontSize,
|
|
3095
|
+
fill: textColor,
|
|
3096
|
+
fontFamily: "Arial",
|
|
3097
|
+
originX: "center",
|
|
3098
|
+
originY: "center",
|
|
3099
|
+
backgroundColor,
|
|
3100
|
+
selectable: false,
|
|
3101
|
+
evented: false
|
|
3102
|
+
});
|
|
3103
|
+
layer.add(leftText);
|
|
2842
3104
|
this.canvasService.canvas.bringObjectToFront(layer);
|
|
2843
3105
|
this.canvasService.canvas.requestRenderAll();
|
|
2844
3106
|
}
|
|
@@ -2937,7 +3199,7 @@ var MirrorTool = class {
|
|
|
2937
3199
|
};
|
|
2938
3200
|
|
|
2939
3201
|
// src/CanvasService.ts
|
|
2940
|
-
import { Canvas, Group as
|
|
3202
|
+
import { Canvas, Group as Group3 } from "fabric";
|
|
2941
3203
|
var CanvasService = class {
|
|
2942
3204
|
constructor(el, options) {
|
|
2943
3205
|
if (el instanceof Canvas) {
|
|
@@ -2974,7 +3236,7 @@ var CanvasService = class {
|
|
|
2974
3236
|
...options,
|
|
2975
3237
|
data: { ...options.data, id }
|
|
2976
3238
|
};
|
|
2977
|
-
layer = new
|
|
3239
|
+
layer = new Group3([], defaultOptions);
|
|
2978
3240
|
this.canvas.add(layer);
|
|
2979
3241
|
}
|
|
2980
3242
|
return layer;
|