@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.js
CHANGED
|
@@ -434,6 +434,24 @@ var ImageTracer = class {
|
|
|
434
434
|
|
|
435
435
|
// src/coordinate.ts
|
|
436
436
|
var Coordinate = class {
|
|
437
|
+
/**
|
|
438
|
+
* Calculate layout to fit content within container while preserving aspect ratio.
|
|
439
|
+
*/
|
|
440
|
+
static calculateLayout(container, content, padding = 0) {
|
|
441
|
+
const availableWidth = Math.max(0, container.width - padding * 2);
|
|
442
|
+
const availableHeight = Math.max(0, container.height - padding * 2);
|
|
443
|
+
if (content.width === 0 || content.height === 0) {
|
|
444
|
+
return { scale: 1, offsetX: 0, offsetY: 0, width: 0, height: 0 };
|
|
445
|
+
}
|
|
446
|
+
const scaleX = availableWidth / content.width;
|
|
447
|
+
const scaleY = availableHeight / content.height;
|
|
448
|
+
const scale = Math.min(scaleX, scaleY);
|
|
449
|
+
const width = content.width * scale;
|
|
450
|
+
const height = content.height * scale;
|
|
451
|
+
const offsetX = (container.width - width) / 2;
|
|
452
|
+
const offsetY = (container.height - height) / 2;
|
|
453
|
+
return { scale, offsetX, offsetY, width, height };
|
|
454
|
+
}
|
|
437
455
|
/**
|
|
438
456
|
* Convert an absolute value to a normalized value (0-1).
|
|
439
457
|
* @param value Absolute value (e.g., pixels)
|
|
@@ -468,10 +486,85 @@ var Coordinate = class {
|
|
|
468
486
|
y: this.toAbsolute(point.y, size.height)
|
|
469
487
|
};
|
|
470
488
|
}
|
|
489
|
+
static convertUnit(value, from, to) {
|
|
490
|
+
if (from === to) return value;
|
|
491
|
+
const toMM = {
|
|
492
|
+
px: 0.264583,
|
|
493
|
+
// 1px = 0.264583mm (96 DPI)
|
|
494
|
+
mm: 1,
|
|
495
|
+
cm: 10,
|
|
496
|
+
in: 25.4
|
|
497
|
+
};
|
|
498
|
+
const mmValue = value * (from === "px" ? toMM.px : toMM[from] || 1);
|
|
499
|
+
if (to === "px") {
|
|
500
|
+
return mmValue / toMM.px;
|
|
501
|
+
}
|
|
502
|
+
return mmValue / (toMM[to] || 1);
|
|
503
|
+
}
|
|
471
504
|
};
|
|
472
505
|
|
|
473
506
|
// src/geometry.ts
|
|
474
507
|
var import_paper = __toESM(require("paper"));
|
|
508
|
+
function resolveHolePosition(hole, geometry, canvasSize) {
|
|
509
|
+
if (hole.anchor) {
|
|
510
|
+
const { x, y, width, height } = geometry;
|
|
511
|
+
let bx = x;
|
|
512
|
+
let by = y;
|
|
513
|
+
const left = x - width / 2;
|
|
514
|
+
const right = x + width / 2;
|
|
515
|
+
const top = y - height / 2;
|
|
516
|
+
const bottom = y + height / 2;
|
|
517
|
+
switch (hole.anchor) {
|
|
518
|
+
case "top-left":
|
|
519
|
+
bx = left;
|
|
520
|
+
by = top;
|
|
521
|
+
break;
|
|
522
|
+
case "top-center":
|
|
523
|
+
bx = x;
|
|
524
|
+
by = top;
|
|
525
|
+
break;
|
|
526
|
+
case "top-right":
|
|
527
|
+
bx = right;
|
|
528
|
+
by = top;
|
|
529
|
+
break;
|
|
530
|
+
case "center-left":
|
|
531
|
+
bx = left;
|
|
532
|
+
by = y;
|
|
533
|
+
break;
|
|
534
|
+
case "center":
|
|
535
|
+
bx = x;
|
|
536
|
+
by = y;
|
|
537
|
+
break;
|
|
538
|
+
case "center-right":
|
|
539
|
+
bx = right;
|
|
540
|
+
by = y;
|
|
541
|
+
break;
|
|
542
|
+
case "bottom-left":
|
|
543
|
+
bx = left;
|
|
544
|
+
by = bottom;
|
|
545
|
+
break;
|
|
546
|
+
case "bottom-center":
|
|
547
|
+
bx = x;
|
|
548
|
+
by = bottom;
|
|
549
|
+
break;
|
|
550
|
+
case "bottom-right":
|
|
551
|
+
bx = right;
|
|
552
|
+
by = bottom;
|
|
553
|
+
break;
|
|
554
|
+
}
|
|
555
|
+
return {
|
|
556
|
+
x: bx + (hole.offsetX || 0),
|
|
557
|
+
y: by + (hole.offsetY || 0)
|
|
558
|
+
};
|
|
559
|
+
} else if (hole.x !== void 0 && hole.y !== void 0) {
|
|
560
|
+
const { x, width, y, height } = geometry;
|
|
561
|
+
return {
|
|
562
|
+
x: hole.x * width + (x - width / 2) + (hole.offsetX || 0),
|
|
563
|
+
y: hole.y * height + (y - height / 2) + (hole.offsetY || 0)
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
return { x: 0, y: 0 };
|
|
567
|
+
}
|
|
475
568
|
function ensurePaper(width, height) {
|
|
476
569
|
if (!import_paper.default.project) {
|
|
477
570
|
import_paper.default.setup(new import_paper.default.Size(width, height));
|
|
@@ -573,7 +666,10 @@ function getDielineShape(options) {
|
|
|
573
666
|
cutsPath.remove();
|
|
574
667
|
mainShape = temp;
|
|
575
668
|
} catch (e) {
|
|
576
|
-
console.error(
|
|
669
|
+
console.error(
|
|
670
|
+
"Geometry: Failed to subtract cutsPath from mainShape",
|
|
671
|
+
e
|
|
672
|
+
);
|
|
577
673
|
}
|
|
578
674
|
}
|
|
579
675
|
}
|
|
@@ -666,7 +762,12 @@ function getPathBounds(pathData) {
|
|
|
666
762
|
path.pathData = pathData;
|
|
667
763
|
const bounds = path.bounds;
|
|
668
764
|
path.remove();
|
|
669
|
-
return {
|
|
765
|
+
return {
|
|
766
|
+
x: bounds.x,
|
|
767
|
+
y: bounds.y,
|
|
768
|
+
width: bounds.width,
|
|
769
|
+
height: bounds.height
|
|
770
|
+
};
|
|
670
771
|
}
|
|
671
772
|
|
|
672
773
|
// src/dieline.ts
|
|
@@ -676,6 +777,7 @@ var DielineTool = class {
|
|
|
676
777
|
this.metadata = {
|
|
677
778
|
name: "DielineTool"
|
|
678
779
|
};
|
|
780
|
+
this.unit = "mm";
|
|
679
781
|
this.shape = "rect";
|
|
680
782
|
this.width = 500;
|
|
681
783
|
this.height = 500;
|
|
@@ -686,6 +788,7 @@ var DielineTool = class {
|
|
|
686
788
|
this.outsideColor = "#ffffff";
|
|
687
789
|
this.showBleedLines = true;
|
|
688
790
|
this.holes = [];
|
|
791
|
+
this.padding = 140;
|
|
689
792
|
if (options) {
|
|
690
793
|
Object.assign(this, options);
|
|
691
794
|
}
|
|
@@ -699,14 +802,12 @@ var DielineTool = class {
|
|
|
699
802
|
}
|
|
700
803
|
const configService = context.services.get("ConfigurationService");
|
|
701
804
|
if (configService) {
|
|
805
|
+
this.unit = configService.get("dieline.unit", this.unit);
|
|
702
806
|
this.shape = configService.get("dieline.shape", this.shape);
|
|
703
807
|
this.width = configService.get("dieline.width", this.width);
|
|
704
808
|
this.height = configService.get("dieline.height", this.height);
|
|
705
809
|
this.radius = configService.get("dieline.radius", this.radius);
|
|
706
|
-
this.
|
|
707
|
-
"dieline.borderLength",
|
|
708
|
-
this.borderLength
|
|
709
|
-
);
|
|
810
|
+
this.padding = configService.get("dieline.padding", this.padding);
|
|
710
811
|
this.offset = configService.get("dieline.offset", this.offset);
|
|
711
812
|
this.style = configService.get("dieline.style", this.style);
|
|
712
813
|
this.insideColor = configService.get(
|
|
@@ -747,6 +848,13 @@ var DielineTool = class {
|
|
|
747
848
|
contribute() {
|
|
748
849
|
return {
|
|
749
850
|
[import_core2.ContributionPointIds.CONFIGURATIONS]: [
|
|
851
|
+
{
|
|
852
|
+
id: "dieline.unit",
|
|
853
|
+
type: "select",
|
|
854
|
+
label: "Unit",
|
|
855
|
+
options: ["px", "mm", "cm", "in"],
|
|
856
|
+
default: this.unit
|
|
857
|
+
},
|
|
750
858
|
{
|
|
751
859
|
id: "dieline.shape",
|
|
752
860
|
type: "select",
|
|
@@ -782,15 +890,14 @@ var DielineTool = class {
|
|
|
782
890
|
id: "dieline.position",
|
|
783
891
|
type: "json",
|
|
784
892
|
label: "Position (Normalized)",
|
|
785
|
-
default: this.
|
|
893
|
+
default: this.radius
|
|
786
894
|
},
|
|
787
895
|
{
|
|
788
|
-
id: "dieline.
|
|
789
|
-
type: "
|
|
790
|
-
label: "
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
default: this.borderLength
|
|
896
|
+
id: "dieline.padding",
|
|
897
|
+
type: "select",
|
|
898
|
+
label: "View Padding",
|
|
899
|
+
options: [0, 10, 20, 40, 60, 100, "2%", "5%", "10%", "15%", "20%"],
|
|
900
|
+
default: this.padding
|
|
794
901
|
},
|
|
795
902
|
{
|
|
796
903
|
id: "dieline.offset",
|
|
@@ -833,65 +940,6 @@ var DielineTool = class {
|
|
|
833
940
|
}
|
|
834
941
|
],
|
|
835
942
|
[import_core2.ContributionPointIds.COMMANDS]: [
|
|
836
|
-
{
|
|
837
|
-
command: "reset",
|
|
838
|
-
title: "Reset Dieline",
|
|
839
|
-
handler: () => {
|
|
840
|
-
this.shape = "rect";
|
|
841
|
-
this.width = 300;
|
|
842
|
-
this.height = 300;
|
|
843
|
-
this.radius = 0;
|
|
844
|
-
this.offset = 0;
|
|
845
|
-
this.style = "solid";
|
|
846
|
-
this.insideColor = "rgba(0,0,0,0)";
|
|
847
|
-
this.outsideColor = "#ffffff";
|
|
848
|
-
this.showBleedLines = true;
|
|
849
|
-
this.holes = [];
|
|
850
|
-
this.pathData = void 0;
|
|
851
|
-
this.updateDieline();
|
|
852
|
-
return true;
|
|
853
|
-
}
|
|
854
|
-
},
|
|
855
|
-
{
|
|
856
|
-
command: "setDimensions",
|
|
857
|
-
title: "Set Dimensions",
|
|
858
|
-
handler: (width, height) => {
|
|
859
|
-
if (this.width === width && this.height === height) return true;
|
|
860
|
-
this.width = width;
|
|
861
|
-
this.height = height;
|
|
862
|
-
this.updateDieline();
|
|
863
|
-
return true;
|
|
864
|
-
}
|
|
865
|
-
},
|
|
866
|
-
{
|
|
867
|
-
command: "setShape",
|
|
868
|
-
title: "Set Shape",
|
|
869
|
-
handler: (shape) => {
|
|
870
|
-
if (this.shape === shape) return true;
|
|
871
|
-
this.shape = shape;
|
|
872
|
-
this.updateDieline();
|
|
873
|
-
return true;
|
|
874
|
-
}
|
|
875
|
-
},
|
|
876
|
-
{
|
|
877
|
-
command: "setBleed",
|
|
878
|
-
title: "Set Bleed",
|
|
879
|
-
handler: (bleed) => {
|
|
880
|
-
if (this.offset === bleed) return true;
|
|
881
|
-
this.offset = bleed;
|
|
882
|
-
this.updateDieline();
|
|
883
|
-
return true;
|
|
884
|
-
}
|
|
885
|
-
},
|
|
886
|
-
{
|
|
887
|
-
command: "setHoles",
|
|
888
|
-
title: "Set Holes",
|
|
889
|
-
handler: (holes) => {
|
|
890
|
-
this.holes = holes;
|
|
891
|
-
this.updateDieline(false);
|
|
892
|
-
return true;
|
|
893
|
-
}
|
|
894
|
-
},
|
|
895
943
|
{
|
|
896
944
|
command: "getGeometry",
|
|
897
945
|
title: "Get Geometry",
|
|
@@ -910,16 +958,23 @@ var DielineTool = class {
|
|
|
910
958
|
command: "detectEdge",
|
|
911
959
|
title: "Detect Edge from Image",
|
|
912
960
|
handler: async (imageUrl, options) => {
|
|
961
|
+
var _a;
|
|
913
962
|
try {
|
|
914
963
|
const pathData = await ImageTracer.trace(imageUrl, options);
|
|
915
964
|
const bounds = getPathBounds(pathData);
|
|
916
965
|
const currentMax = Math.max(this.width, this.height);
|
|
917
966
|
const scale = currentMax / Math.max(bounds.width, bounds.height);
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
this.
|
|
921
|
-
|
|
922
|
-
|
|
967
|
+
const newWidth = bounds.width * scale;
|
|
968
|
+
const newHeight = bounds.height * scale;
|
|
969
|
+
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
970
|
+
"ConfigurationService"
|
|
971
|
+
);
|
|
972
|
+
if (configService) {
|
|
973
|
+
configService.update("dieline.width", newWidth);
|
|
974
|
+
configService.update("dieline.height", newHeight);
|
|
975
|
+
configService.update("dieline.shape", "custom");
|
|
976
|
+
configService.update("dieline.pathData", pathData);
|
|
977
|
+
}
|
|
923
978
|
return pathData;
|
|
924
979
|
} catch (e) {
|
|
925
980
|
console.error("Edge detection failed", e);
|
|
@@ -978,11 +1033,25 @@ var DielineTool = class {
|
|
|
978
1033
|
}
|
|
979
1034
|
return new import_fabric2.Pattern({ source: canvas, repetition: "repeat" });
|
|
980
1035
|
}
|
|
1036
|
+
resolvePadding(containerWidth, containerHeight) {
|
|
1037
|
+
if (typeof this.padding === "number") {
|
|
1038
|
+
return this.padding;
|
|
1039
|
+
}
|
|
1040
|
+
if (typeof this.padding === "string") {
|
|
1041
|
+
if (this.padding.endsWith("%")) {
|
|
1042
|
+
const percent = parseFloat(this.padding) / 100;
|
|
1043
|
+
return Math.min(containerWidth, containerHeight) * percent;
|
|
1044
|
+
}
|
|
1045
|
+
return parseFloat(this.padding) || 0;
|
|
1046
|
+
}
|
|
1047
|
+
return 0;
|
|
1048
|
+
}
|
|
981
1049
|
updateDieline(emitEvent = true) {
|
|
982
1050
|
if (!this.canvasService) return;
|
|
983
1051
|
const layer = this.getLayer();
|
|
984
1052
|
if (!layer) return;
|
|
985
1053
|
const {
|
|
1054
|
+
unit,
|
|
986
1055
|
shape,
|
|
987
1056
|
radius,
|
|
988
1057
|
offset,
|
|
@@ -990,35 +1059,60 @@ var DielineTool = class {
|
|
|
990
1059
|
insideColor,
|
|
991
1060
|
outsideColor,
|
|
992
1061
|
position,
|
|
993
|
-
borderLength,
|
|
994
1062
|
showBleedLines,
|
|
995
1063
|
holes
|
|
996
1064
|
} = this;
|
|
997
1065
|
let { width, height } = this;
|
|
998
1066
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
999
1067
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
const
|
|
1068
|
+
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
1069
|
+
const layout = Coordinate.calculateLayout(
|
|
1070
|
+
{ width: canvasW, height: canvasH },
|
|
1071
|
+
{ width, height },
|
|
1072
|
+
paddingPx
|
|
1073
|
+
);
|
|
1074
|
+
const scale = layout.scale;
|
|
1075
|
+
const cx = layout.offsetX + layout.width / 2;
|
|
1076
|
+
const cy = layout.offsetY + layout.height / 2;
|
|
1077
|
+
const visualWidth = layout.width;
|
|
1078
|
+
const visualHeight = layout.height;
|
|
1079
|
+
const visualRadius = radius * scale;
|
|
1080
|
+
const visualOffset = offset * scale;
|
|
1007
1081
|
layer.remove(...layer.getObjects());
|
|
1082
|
+
const geometryForHoles = {
|
|
1083
|
+
x: cx,
|
|
1084
|
+
y: cy,
|
|
1085
|
+
width: visualWidth,
|
|
1086
|
+
height: visualHeight
|
|
1087
|
+
// Pass scale/unit context if needed by resolveHolePosition (though currently unused there)
|
|
1088
|
+
};
|
|
1008
1089
|
const absoluteHoles = (holes || []).map((h) => {
|
|
1009
|
-
const
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1090
|
+
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1091
|
+
const offsetScale = unitScale * scale;
|
|
1092
|
+
const hWithPixelOffsets = {
|
|
1093
|
+
...h,
|
|
1094
|
+
offsetX: (h.offsetX || 0) * offsetScale,
|
|
1095
|
+
offsetY: (h.offsetY || 0) * offsetScale
|
|
1096
|
+
};
|
|
1097
|
+
const pos = resolveHolePosition(hWithPixelOffsets, geometryForHoles, {
|
|
1098
|
+
width: canvasW,
|
|
1099
|
+
height: canvasH
|
|
1100
|
+
});
|
|
1013
1101
|
return {
|
|
1014
1102
|
...h,
|
|
1015
|
-
x:
|
|
1016
|
-
y:
|
|
1103
|
+
x: pos.x,
|
|
1104
|
+
y: pos.y,
|
|
1105
|
+
// Scale hole radii: mm -> current unit -> pixels
|
|
1106
|
+
innerRadius: h.innerRadius * offsetScale,
|
|
1107
|
+
outerRadius: h.outerRadius * offsetScale,
|
|
1108
|
+
// Store scaled offsets in the result for consistency, though pos is already resolved
|
|
1109
|
+
offsetX: hWithPixelOffsets.offsetX,
|
|
1110
|
+
offsetY: hWithPixelOffsets.offsetY
|
|
1017
1111
|
};
|
|
1018
1112
|
});
|
|
1019
|
-
const cutW = Math.max(0,
|
|
1020
|
-
const cutH = Math.max(0,
|
|
1021
|
-
const cutR =
|
|
1113
|
+
const cutW = Math.max(0, visualWidth + visualOffset * 2);
|
|
1114
|
+
const cutH = Math.max(0, visualHeight + visualOffset * 2);
|
|
1115
|
+
const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
1022
1116
|
const maskPathData = generateMaskPath({
|
|
1023
1117
|
canvasWidth: canvasW,
|
|
1024
1118
|
canvasHeight: canvasH,
|
|
@@ -1068,15 +1162,15 @@ var DielineTool = class {
|
|
|
1068
1162
|
const bleedPathData = generateBleedZonePath(
|
|
1069
1163
|
{
|
|
1070
1164
|
shape,
|
|
1071
|
-
width,
|
|
1072
|
-
height,
|
|
1073
|
-
radius,
|
|
1165
|
+
width: visualWidth,
|
|
1166
|
+
height: visualHeight,
|
|
1167
|
+
radius: visualRadius,
|
|
1074
1168
|
x: cx,
|
|
1075
1169
|
y: cy,
|
|
1076
1170
|
holes: absoluteHoles,
|
|
1077
1171
|
pathData: this.pathData
|
|
1078
1172
|
},
|
|
1079
|
-
|
|
1173
|
+
visualOffset
|
|
1080
1174
|
);
|
|
1081
1175
|
if (showBleedLines !== false) {
|
|
1082
1176
|
const pattern = this.createHatchPattern("red");
|
|
@@ -1119,13 +1213,12 @@ var DielineTool = class {
|
|
|
1119
1213
|
}
|
|
1120
1214
|
const borderPathData = generateDielinePath({
|
|
1121
1215
|
shape,
|
|
1122
|
-
width,
|
|
1123
|
-
height,
|
|
1124
|
-
radius,
|
|
1216
|
+
width: visualWidth,
|
|
1217
|
+
height: visualHeight,
|
|
1218
|
+
radius: visualRadius,
|
|
1125
1219
|
x: cx,
|
|
1126
1220
|
y: cy,
|
|
1127
1221
|
holes: absoluteHoles,
|
|
1128
|
-
// FIX: Use absoluteHoles instead of holes
|
|
1129
1222
|
pathData: this.pathData
|
|
1130
1223
|
});
|
|
1131
1224
|
const borderObj = new import_fabric2.Path(borderPathData, {
|
|
@@ -1163,114 +1256,107 @@ var DielineTool = class {
|
|
|
1163
1256
|
}
|
|
1164
1257
|
}
|
|
1165
1258
|
getGeometry() {
|
|
1166
|
-
var _a, _b;
|
|
1167
1259
|
if (!this.canvasService) return null;
|
|
1168
|
-
const { shape, width, height, radius, position,
|
|
1260
|
+
const { unit, shape, width, height, radius, position, offset } = this;
|
|
1169
1261
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
1170
1262
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
const
|
|
1178
|
-
const
|
|
1263
|
+
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
1264
|
+
const layout = Coordinate.calculateLayout(
|
|
1265
|
+
{ width: canvasW, height: canvasH },
|
|
1266
|
+
{ width, height },
|
|
1267
|
+
paddingPx
|
|
1268
|
+
);
|
|
1269
|
+
const scale = layout.scale;
|
|
1270
|
+
const cx = layout.offsetX + layout.width / 2;
|
|
1271
|
+
const cy = layout.offsetY + layout.height / 2;
|
|
1272
|
+
const visualWidth = layout.width;
|
|
1273
|
+
const visualHeight = layout.height;
|
|
1179
1274
|
return {
|
|
1180
1275
|
shape,
|
|
1276
|
+
unit,
|
|
1181
1277
|
x: cx,
|
|
1182
1278
|
y: cy,
|
|
1183
1279
|
width: visualWidth,
|
|
1184
1280
|
height: visualHeight,
|
|
1185
|
-
radius,
|
|
1186
|
-
offset,
|
|
1187
|
-
|
|
1281
|
+
radius: radius * scale,
|
|
1282
|
+
offset: offset * scale,
|
|
1283
|
+
// Pass scale to help other tools (like HoleTool) convert units
|
|
1284
|
+
scale,
|
|
1188
1285
|
pathData: this.pathData
|
|
1189
1286
|
};
|
|
1190
1287
|
}
|
|
1191
|
-
exportCutImage() {
|
|
1192
|
-
var _a, _b, _c, _d;
|
|
1288
|
+
async exportCutImage() {
|
|
1193
1289
|
if (!this.canvasService) return null;
|
|
1194
|
-
const
|
|
1290
|
+
const userLayer = this.canvasService.getLayer("user");
|
|
1291
|
+
if (!userLayer) return null;
|
|
1195
1292
|
const { shape, width, height, radius, position, holes } = this;
|
|
1196
|
-
const canvasW = canvas.width || 800;
|
|
1197
|
-
const canvasH = canvas.height || 600;
|
|
1198
|
-
const
|
|
1199
|
-
const
|
|
1293
|
+
const canvasW = this.canvasService.canvas.width || 800;
|
|
1294
|
+
const canvasH = this.canvasService.canvas.height || 600;
|
|
1295
|
+
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
1296
|
+
const layout = Coordinate.calculateLayout(
|
|
1297
|
+
{ width: canvasW, height: canvasH },
|
|
1298
|
+
{ width, height },
|
|
1299
|
+
paddingPx
|
|
1300
|
+
);
|
|
1301
|
+
const scale = layout.scale;
|
|
1302
|
+
const cx = layout.offsetX + layout.width / 2;
|
|
1303
|
+
const cy = layout.offsetY + layout.height / 2;
|
|
1304
|
+
const visualWidth = layout.width;
|
|
1305
|
+
const visualHeight = layout.height;
|
|
1306
|
+
const visualRadius = radius * scale;
|
|
1200
1307
|
const absoluteHoles = (holes || []).map((h) => {
|
|
1201
|
-
const
|
|
1202
|
-
|
|
1308
|
+
const unit = this.unit || "mm";
|
|
1309
|
+
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1310
|
+
const pos = resolveHolePosition(
|
|
1311
|
+
{
|
|
1312
|
+
...h,
|
|
1313
|
+
offsetX: (h.offsetX || 0) * unitScale * scale,
|
|
1314
|
+
offsetY: (h.offsetY || 0) * unitScale * scale
|
|
1315
|
+
},
|
|
1316
|
+
{ x: cx, y: cy, width: visualWidth, height: visualHeight },
|
|
1203
1317
|
{ width: canvasW, height: canvasH }
|
|
1204
1318
|
);
|
|
1205
1319
|
return {
|
|
1206
1320
|
...h,
|
|
1207
|
-
x:
|
|
1208
|
-
y:
|
|
1321
|
+
x: pos.x,
|
|
1322
|
+
y: pos.y,
|
|
1323
|
+
innerRadius: h.innerRadius * unitScale * scale,
|
|
1324
|
+
outerRadius: h.outerRadius * unitScale * scale,
|
|
1325
|
+
offsetX: (h.offsetX || 0) * unitScale * scale,
|
|
1326
|
+
offsetY: (h.offsetY || 0) * unitScale * scale
|
|
1209
1327
|
};
|
|
1210
1328
|
});
|
|
1211
1329
|
const pathData = generateDielinePath({
|
|
1212
1330
|
shape,
|
|
1213
|
-
width,
|
|
1214
|
-
height,
|
|
1215
|
-
radius,
|
|
1331
|
+
width: visualWidth,
|
|
1332
|
+
height: visualHeight,
|
|
1333
|
+
radius: visualRadius,
|
|
1216
1334
|
x: cx,
|
|
1217
1335
|
y: cy,
|
|
1218
1336
|
holes: absoluteHoles,
|
|
1219
1337
|
pathData: this.pathData
|
|
1220
1338
|
});
|
|
1339
|
+
const clonedLayer = await userLayer.clone();
|
|
1221
1340
|
const clipPath = new import_fabric2.Path(pathData, {
|
|
1222
|
-
left: 0,
|
|
1223
|
-
top: 0,
|
|
1224
1341
|
originX: "left",
|
|
1225
1342
|
originY: "top",
|
|
1226
|
-
absolutePositioned: true
|
|
1227
|
-
});
|
|
1228
|
-
const layer = this.getLayer();
|
|
1229
|
-
const wasVisible = (_c = layer == null ? void 0 : layer.visible) != null ? _c : true;
|
|
1230
|
-
if (layer) layer.visible = false;
|
|
1231
|
-
const holeMarkers = canvas.getObjects().filter((o) => {
|
|
1232
|
-
var _a2;
|
|
1233
|
-
return ((_a2 = o.data) == null ? void 0 : _a2.type) === "hole-marker";
|
|
1234
|
-
});
|
|
1235
|
-
holeMarkers.forEach((o) => o.visible = false);
|
|
1236
|
-
const rulerLayer = canvas.getObjects().find((obj) => {
|
|
1237
|
-
var _a2;
|
|
1238
|
-
return ((_a2 = obj.data) == null ? void 0 : _a2.id) === "ruler-overlay";
|
|
1239
|
-
});
|
|
1240
|
-
const rulerWasVisible = (_d = rulerLayer == null ? void 0 : rulerLayer.visible) != null ? _d : true;
|
|
1241
|
-
if (rulerLayer) rulerLayer.visible = false;
|
|
1242
|
-
const originalClip = canvas.clipPath;
|
|
1243
|
-
canvas.clipPath = clipPath;
|
|
1244
|
-
const bbox = clipPath.getBoundingRect();
|
|
1245
|
-
const clipPathCorrected = new import_fabric2.Path(pathData, {
|
|
1246
|
-
absolutePositioned: true,
|
|
1247
1343
|
left: 0,
|
|
1248
|
-
top: 0
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
const tempBounds = tempPath.getBoundingRect();
|
|
1252
|
-
clipPathCorrected.set({
|
|
1253
|
-
left: tempBounds.left,
|
|
1254
|
-
top: tempBounds.top,
|
|
1255
|
-
originX: "left",
|
|
1256
|
-
originY: "top"
|
|
1344
|
+
top: 0,
|
|
1345
|
+
absolutePositioned: true
|
|
1346
|
+
// Important for groups
|
|
1257
1347
|
});
|
|
1258
|
-
|
|
1259
|
-
const
|
|
1260
|
-
const
|
|
1348
|
+
clonedLayer.clipPath = clipPath;
|
|
1349
|
+
const bounds = clipPath.getBoundingRect();
|
|
1350
|
+
const dataUrl = clonedLayer.toDataURL({
|
|
1261
1351
|
format: "png",
|
|
1262
1352
|
multiplier: 2,
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1353
|
+
// Better quality
|
|
1354
|
+
left: bounds.left,
|
|
1355
|
+
top: bounds.top,
|
|
1356
|
+
width: bounds.width,
|
|
1357
|
+
height: bounds.height
|
|
1267
1358
|
});
|
|
1268
|
-
|
|
1269
|
-
if (layer) layer.visible = wasVisible;
|
|
1270
|
-
if (rulerLayer) rulerLayer.visible = rulerWasVisible;
|
|
1271
|
-
holeMarkers.forEach((o) => o.visible = true);
|
|
1272
|
-
canvas.requestRenderAll();
|
|
1273
|
-
return dataURL;
|
|
1359
|
+
return dataUrl;
|
|
1274
1360
|
}
|
|
1275
1361
|
};
|
|
1276
1362
|
|
|
@@ -1442,9 +1528,6 @@ var HoleTool = class {
|
|
|
1442
1528
|
this.metadata = {
|
|
1443
1529
|
name: "HoleTool"
|
|
1444
1530
|
};
|
|
1445
|
-
this.innerRadius = 15;
|
|
1446
|
-
this.outerRadius = 25;
|
|
1447
|
-
this.style = "solid";
|
|
1448
1531
|
this.holes = [];
|
|
1449
1532
|
this.constraintTarget = "bleed";
|
|
1450
1533
|
this.isUpdatingConfig = false;
|
|
@@ -1468,53 +1551,20 @@ var HoleTool = class {
|
|
|
1468
1551
|
"ConfigurationService"
|
|
1469
1552
|
);
|
|
1470
1553
|
if (configService) {
|
|
1471
|
-
this.innerRadius = configService.get(
|
|
1472
|
-
"hole.innerRadius",
|
|
1473
|
-
this.innerRadius
|
|
1474
|
-
);
|
|
1475
|
-
this.outerRadius = configService.get(
|
|
1476
|
-
"hole.outerRadius",
|
|
1477
|
-
this.outerRadius
|
|
1478
|
-
);
|
|
1479
|
-
this.style = configService.get("hole.style", this.style);
|
|
1480
1554
|
this.constraintTarget = configService.get(
|
|
1481
1555
|
"hole.constraintTarget",
|
|
1482
1556
|
this.constraintTarget
|
|
1483
1557
|
);
|
|
1484
|
-
|
|
1485
|
-
if (this.canvasService) {
|
|
1486
|
-
const { width, height } = this.canvasService.canvas;
|
|
1487
|
-
this.holes = dielineHoles.map((h) => {
|
|
1488
|
-
const p = Coordinate.denormalizePoint(h, {
|
|
1489
|
-
width: width || 800,
|
|
1490
|
-
height: height || 600
|
|
1491
|
-
});
|
|
1492
|
-
return { x: p.x, y: p.y };
|
|
1493
|
-
});
|
|
1494
|
-
}
|
|
1558
|
+
this.holes = configService.get("dieline.holes", []);
|
|
1495
1559
|
configService.onAnyChange((e) => {
|
|
1496
1560
|
if (this.isUpdatingConfig) return;
|
|
1497
|
-
if (e.key
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
this[prop] = e.value;
|
|
1501
|
-
this.redraw();
|
|
1502
|
-
this.syncHolesToDieline();
|
|
1503
|
-
}
|
|
1561
|
+
if (e.key === "hole.constraintTarget") {
|
|
1562
|
+
this.constraintTarget = e.value;
|
|
1563
|
+
this.enforceConstraints();
|
|
1504
1564
|
}
|
|
1505
1565
|
if (e.key === "dieline.holes") {
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
const { width, height } = this.canvasService.canvas;
|
|
1509
|
-
this.holes = holes.map((h) => {
|
|
1510
|
-
const p = Coordinate.denormalizePoint(h, {
|
|
1511
|
-
width: width || 800,
|
|
1512
|
-
height: height || 600
|
|
1513
|
-
});
|
|
1514
|
-
return { x: p.x, y: p.y };
|
|
1515
|
-
});
|
|
1516
|
-
this.redraw();
|
|
1517
|
-
}
|
|
1566
|
+
this.holes = e.value || [];
|
|
1567
|
+
this.redraw();
|
|
1518
1568
|
}
|
|
1519
1569
|
});
|
|
1520
1570
|
}
|
|
@@ -1528,29 +1578,6 @@ var HoleTool = class {
|
|
|
1528
1578
|
contribute() {
|
|
1529
1579
|
return {
|
|
1530
1580
|
[import_core4.ContributionPointIds.CONFIGURATIONS]: [
|
|
1531
|
-
{
|
|
1532
|
-
id: "hole.innerRadius",
|
|
1533
|
-
type: "number",
|
|
1534
|
-
label: "Inner Radius",
|
|
1535
|
-
min: 1,
|
|
1536
|
-
max: 100,
|
|
1537
|
-
default: 15
|
|
1538
|
-
},
|
|
1539
|
-
{
|
|
1540
|
-
id: "hole.outerRadius",
|
|
1541
|
-
type: "number",
|
|
1542
|
-
label: "Outer Radius",
|
|
1543
|
-
min: 1,
|
|
1544
|
-
max: 100,
|
|
1545
|
-
default: 25
|
|
1546
|
-
},
|
|
1547
|
-
{
|
|
1548
|
-
id: "hole.style",
|
|
1549
|
-
type: "select",
|
|
1550
|
-
label: "Line Style",
|
|
1551
|
-
options: ["solid", "dashed"],
|
|
1552
|
-
default: "solid"
|
|
1553
|
-
},
|
|
1554
1581
|
{
|
|
1555
1582
|
id: "hole.constraintTarget",
|
|
1556
1583
|
type: "select",
|
|
@@ -1564,6 +1591,7 @@ var HoleTool = class {
|
|
|
1564
1591
|
command: "resetHoles",
|
|
1565
1592
|
title: "Reset Holes",
|
|
1566
1593
|
handler: () => {
|
|
1594
|
+
var _a;
|
|
1567
1595
|
if (!this.canvasService) return false;
|
|
1568
1596
|
let defaultPos = { x: this.canvasService.canvas.width / 2, y: 50 };
|
|
1569
1597
|
if (this.currentGeometry) {
|
|
@@ -1574,12 +1602,24 @@ var HoleTool = class {
|
|
|
1574
1602
|
holes: []
|
|
1575
1603
|
});
|
|
1576
1604
|
}
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
this.
|
|
1605
|
+
const { width, height } = this.canvasService.canvas;
|
|
1606
|
+
const normalizedHole = Coordinate.normalizePoint(defaultPos, {
|
|
1607
|
+
width: width || 800,
|
|
1608
|
+
height: height || 600
|
|
1609
|
+
});
|
|
1610
|
+
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1611
|
+
"ConfigurationService"
|
|
1612
|
+
);
|
|
1613
|
+
if (configService) {
|
|
1614
|
+
configService.update("dieline.holes", [
|
|
1615
|
+
{
|
|
1616
|
+
x: normalizedHole.x,
|
|
1617
|
+
y: normalizedHole.y,
|
|
1618
|
+
innerRadius: 15,
|
|
1619
|
+
outerRadius: 25
|
|
1620
|
+
}
|
|
1621
|
+
]);
|
|
1622
|
+
}
|
|
1583
1623
|
return true;
|
|
1584
1624
|
}
|
|
1585
1625
|
},
|
|
@@ -1587,10 +1627,37 @@ var HoleTool = class {
|
|
|
1587
1627
|
command: "addHole",
|
|
1588
1628
|
title: "Add Hole",
|
|
1589
1629
|
handler: (x, y) => {
|
|
1590
|
-
|
|
1591
|
-
this.
|
|
1592
|
-
|
|
1593
|
-
|
|
1630
|
+
var _a, _b, _c;
|
|
1631
|
+
if (!this.canvasService) return false;
|
|
1632
|
+
let normalizedX = 0.5;
|
|
1633
|
+
let normalizedY = 0.5;
|
|
1634
|
+
if (this.currentGeometry) {
|
|
1635
|
+
const { x: gx, y: gy, width: gw, height: gh } = this.currentGeometry;
|
|
1636
|
+
const left = gx - gw / 2;
|
|
1637
|
+
const top = gy - gh / 2;
|
|
1638
|
+
normalizedX = gw > 0 ? (x - left) / gw : 0.5;
|
|
1639
|
+
normalizedY = gh > 0 ? (y - top) / gh : 0.5;
|
|
1640
|
+
} else {
|
|
1641
|
+
const { width, height } = this.canvasService.canvas;
|
|
1642
|
+
normalizedX = Coordinate.toNormalized(x, width || 800);
|
|
1643
|
+
normalizedY = Coordinate.toNormalized(y, height || 600);
|
|
1644
|
+
}
|
|
1645
|
+
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1646
|
+
"ConfigurationService"
|
|
1647
|
+
);
|
|
1648
|
+
if (configService) {
|
|
1649
|
+
const currentHoles = configService.get("dieline.holes", []);
|
|
1650
|
+
const lastHole = currentHoles[currentHoles.length - 1];
|
|
1651
|
+
const innerRadius = (_b = lastHole == null ? void 0 : lastHole.innerRadius) != null ? _b : 15;
|
|
1652
|
+
const outerRadius = (_c = lastHole == null ? void 0 : lastHole.outerRadius) != null ? _c : 25;
|
|
1653
|
+
const newHole = {
|
|
1654
|
+
x: normalizedX,
|
|
1655
|
+
y: normalizedY,
|
|
1656
|
+
innerRadius,
|
|
1657
|
+
outerRadius
|
|
1658
|
+
};
|
|
1659
|
+
configService.update("dieline.holes", [...currentHoles, newHole]);
|
|
1660
|
+
}
|
|
1594
1661
|
return true;
|
|
1595
1662
|
}
|
|
1596
1663
|
},
|
|
@@ -1598,9 +1665,13 @@ var HoleTool = class {
|
|
|
1598
1665
|
command: "clearHoles",
|
|
1599
1666
|
title: "Clear Holes",
|
|
1600
1667
|
handler: () => {
|
|
1601
|
-
|
|
1602
|
-
this.
|
|
1603
|
-
|
|
1668
|
+
var _a;
|
|
1669
|
+
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1670
|
+
"ConfigurationService"
|
|
1671
|
+
);
|
|
1672
|
+
if (configService) {
|
|
1673
|
+
configService.update("dieline.holes", []);
|
|
1674
|
+
}
|
|
1604
1675
|
return true;
|
|
1605
1676
|
}
|
|
1606
1677
|
}
|
|
@@ -1641,10 +1712,12 @@ var HoleTool = class {
|
|
|
1641
1712
|
}
|
|
1642
1713
|
if (!this.handleMoving) {
|
|
1643
1714
|
this.handleMoving = (e) => {
|
|
1644
|
-
var _a;
|
|
1715
|
+
var _a, _b, _c, _d, _e;
|
|
1645
1716
|
const target = e.target;
|
|
1646
1717
|
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "hole-marker") return;
|
|
1647
1718
|
if (!this.currentGeometry) return;
|
|
1719
|
+
const index = (_c = (_b = target.data) == null ? void 0 : _b.index) != null ? _c : -1;
|
|
1720
|
+
const holeData = this.holes[index];
|
|
1648
1721
|
const effectiveOffset = this.constraintTarget === "original" ? 0 : this.currentGeometry.offset;
|
|
1649
1722
|
const constraintGeometry = {
|
|
1650
1723
|
...this.currentGeometry,
|
|
@@ -1656,7 +1729,12 @@ var HoleTool = class {
|
|
|
1656
1729
|
radius: Math.max(0, this.currentGeometry.radius + effectiveOffset)
|
|
1657
1730
|
};
|
|
1658
1731
|
const p = new import_fabric4.Point(target.left, target.top);
|
|
1659
|
-
const newPos = this.calculateConstrainedPosition(
|
|
1732
|
+
const newPos = this.calculateConstrainedPosition(
|
|
1733
|
+
p,
|
|
1734
|
+
constraintGeometry,
|
|
1735
|
+
(_d = holeData == null ? void 0 : holeData.innerRadius) != null ? _d : 15,
|
|
1736
|
+
(_e = holeData == null ? void 0 : holeData.outerRadius) != null ? _e : 25
|
|
1737
|
+
);
|
|
1660
1738
|
target.set({
|
|
1661
1739
|
left: newPos.x,
|
|
1662
1740
|
top: newPos.y
|
|
@@ -1669,7 +1747,10 @@ var HoleTool = class {
|
|
|
1669
1747
|
var _a;
|
|
1670
1748
|
const target = e.target;
|
|
1671
1749
|
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "hole-marker") return;
|
|
1672
|
-
this.
|
|
1750
|
+
const changed = this.enforceConstraints();
|
|
1751
|
+
if (!changed) {
|
|
1752
|
+
this.syncHolesFromCanvas();
|
|
1753
|
+
}
|
|
1673
1754
|
};
|
|
1674
1755
|
canvas.on("object:modified", this.handleModified);
|
|
1675
1756
|
}
|
|
@@ -1677,19 +1758,6 @@ var HoleTool = class {
|
|
|
1677
1758
|
}
|
|
1678
1759
|
initializeHoles() {
|
|
1679
1760
|
if (!this.canvasService) return;
|
|
1680
|
-
if (!this.holes || this.holes.length === 0) {
|
|
1681
|
-
let defaultPos = { x: this.canvasService.canvas.width / 2, y: 50 };
|
|
1682
|
-
if (this.currentGeometry) {
|
|
1683
|
-
const g = this.currentGeometry;
|
|
1684
|
-
const topCenter = { x: g.x, y: g.y - g.height / 2 };
|
|
1685
|
-
const snapped = getNearestPointOnDieline(topCenter, {
|
|
1686
|
-
...g,
|
|
1687
|
-
holes: []
|
|
1688
|
-
});
|
|
1689
|
-
defaultPos = snapped;
|
|
1690
|
-
}
|
|
1691
|
-
this.holes = [defaultPos];
|
|
1692
|
-
}
|
|
1693
1761
|
this.redraw();
|
|
1694
1762
|
this.syncHolesToDieline();
|
|
1695
1763
|
}
|
|
@@ -1733,32 +1801,110 @@ var HoleTool = class {
|
|
|
1733
1801
|
var _a;
|
|
1734
1802
|
return ((_a = obj.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
1735
1803
|
});
|
|
1736
|
-
|
|
1737
|
-
|
|
1804
|
+
objects.sort(
|
|
1805
|
+
(a, b) => {
|
|
1806
|
+
var _a, _b, _c, _d;
|
|
1807
|
+
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);
|
|
1808
|
+
}
|
|
1809
|
+
);
|
|
1810
|
+
const newHoles = objects.map((obj, i) => {
|
|
1811
|
+
var _a, _b, _c, _d;
|
|
1812
|
+
const original = this.holes[i];
|
|
1813
|
+
const newAbsX = obj.left;
|
|
1814
|
+
const newAbsY = obj.top;
|
|
1815
|
+
const scale = ((_a = this.currentGeometry) == null ? void 0 : _a.scale) || 1;
|
|
1816
|
+
const unit = ((_b = this.currentGeometry) == null ? void 0 : _b.unit) || "mm";
|
|
1817
|
+
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1818
|
+
if (original && original.anchor && this.currentGeometry) {
|
|
1819
|
+
const { x, y, width, height } = this.currentGeometry;
|
|
1820
|
+
let bx = x;
|
|
1821
|
+
let by = y;
|
|
1822
|
+
const left = x - width / 2;
|
|
1823
|
+
const right = x + width / 2;
|
|
1824
|
+
const top = y - height / 2;
|
|
1825
|
+
const bottom = y + height / 2;
|
|
1826
|
+
switch (original.anchor) {
|
|
1827
|
+
case "top-left":
|
|
1828
|
+
bx = left;
|
|
1829
|
+
by = top;
|
|
1830
|
+
break;
|
|
1831
|
+
case "top-center":
|
|
1832
|
+
bx = x;
|
|
1833
|
+
by = top;
|
|
1834
|
+
break;
|
|
1835
|
+
case "top-right":
|
|
1836
|
+
bx = right;
|
|
1837
|
+
by = top;
|
|
1838
|
+
break;
|
|
1839
|
+
case "center-left":
|
|
1840
|
+
bx = left;
|
|
1841
|
+
by = y;
|
|
1842
|
+
break;
|
|
1843
|
+
case "center":
|
|
1844
|
+
bx = x;
|
|
1845
|
+
by = y;
|
|
1846
|
+
break;
|
|
1847
|
+
case "center-right":
|
|
1848
|
+
bx = right;
|
|
1849
|
+
by = y;
|
|
1850
|
+
break;
|
|
1851
|
+
case "bottom-left":
|
|
1852
|
+
bx = left;
|
|
1853
|
+
by = bottom;
|
|
1854
|
+
break;
|
|
1855
|
+
case "bottom-center":
|
|
1856
|
+
bx = x;
|
|
1857
|
+
by = bottom;
|
|
1858
|
+
break;
|
|
1859
|
+
case "bottom-right":
|
|
1860
|
+
bx = right;
|
|
1861
|
+
by = bottom;
|
|
1862
|
+
break;
|
|
1863
|
+
}
|
|
1864
|
+
return {
|
|
1865
|
+
...original,
|
|
1866
|
+
// Denormalize offset back to physical units (mm)
|
|
1867
|
+
offsetX: (newAbsX - bx) / scale / unitScale,
|
|
1868
|
+
offsetY: (newAbsY - by) / scale / unitScale,
|
|
1869
|
+
// Clear direct coordinates if we use anchor
|
|
1870
|
+
x: void 0,
|
|
1871
|
+
y: void 0
|
|
1872
|
+
};
|
|
1873
|
+
}
|
|
1874
|
+
let normalizedX = 0.5;
|
|
1875
|
+
let normalizedY = 0.5;
|
|
1876
|
+
if (this.currentGeometry) {
|
|
1877
|
+
const { x, y, width, height } = this.currentGeometry;
|
|
1878
|
+
const left = x - width / 2;
|
|
1879
|
+
const top = y - height / 2;
|
|
1880
|
+
normalizedX = width > 0 ? (newAbsX - left) / width : 0.5;
|
|
1881
|
+
normalizedY = height > 0 ? (newAbsY - top) / height : 0.5;
|
|
1882
|
+
} else {
|
|
1883
|
+
const { width, height } = this.canvasService.canvas;
|
|
1884
|
+
normalizedX = Coordinate.toNormalized(newAbsX, width || 800);
|
|
1885
|
+
normalizedY = Coordinate.toNormalized(newAbsY, height || 600);
|
|
1886
|
+
}
|
|
1887
|
+
return {
|
|
1888
|
+
...original,
|
|
1889
|
+
x: normalizedX,
|
|
1890
|
+
y: normalizedY,
|
|
1891
|
+
// Ensure radii are preserved
|
|
1892
|
+
innerRadius: (_c = original == null ? void 0 : original.innerRadius) != null ? _c : 15,
|
|
1893
|
+
outerRadius: (_d = original == null ? void 0 : original.outerRadius) != null ? _d : 25
|
|
1894
|
+
};
|
|
1895
|
+
});
|
|
1896
|
+
this.holes = newHoles;
|
|
1738
1897
|
this.syncHolesToDieline();
|
|
1739
1898
|
}
|
|
1740
1899
|
syncHolesToDieline() {
|
|
1741
1900
|
if (!this.context || !this.canvasService) return;
|
|
1742
|
-
const { holes, innerRadius, outerRadius } = this;
|
|
1743
|
-
const currentHoles = holes || [];
|
|
1744
|
-
const width = this.canvasService.canvas.width || 800;
|
|
1745
|
-
const height = this.canvasService.canvas.height || 600;
|
|
1746
1901
|
const configService = this.context.services.get(
|
|
1747
1902
|
"ConfigurationService"
|
|
1748
1903
|
);
|
|
1749
1904
|
if (configService) {
|
|
1750
1905
|
this.isUpdatingConfig = true;
|
|
1751
1906
|
try {
|
|
1752
|
-
|
|
1753
|
-
const p = Coordinate.normalizePoint(h, { width, height });
|
|
1754
|
-
return {
|
|
1755
|
-
x: p.x,
|
|
1756
|
-
y: p.y,
|
|
1757
|
-
innerRadius,
|
|
1758
|
-
outerRadius
|
|
1759
|
-
};
|
|
1760
|
-
});
|
|
1761
|
-
configService.update("dieline.holes", normalizedHoles);
|
|
1907
|
+
configService.update("dieline.holes", this.holes);
|
|
1762
1908
|
} finally {
|
|
1763
1909
|
this.isUpdatingConfig = false;
|
|
1764
1910
|
}
|
|
@@ -1767,19 +1913,43 @@ var HoleTool = class {
|
|
|
1767
1913
|
redraw() {
|
|
1768
1914
|
if (!this.canvasService) return;
|
|
1769
1915
|
const canvas = this.canvasService.canvas;
|
|
1916
|
+
const { width, height } = canvas;
|
|
1770
1917
|
const existing = canvas.getObjects().filter((obj) => {
|
|
1771
1918
|
var _a;
|
|
1772
1919
|
return ((_a = obj.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
1773
1920
|
});
|
|
1774
1921
|
existing.forEach((obj) => canvas.remove(obj));
|
|
1775
|
-
const
|
|
1922
|
+
const holes = this.holes;
|
|
1776
1923
|
if (!holes || holes.length === 0) {
|
|
1777
1924
|
this.canvasService.requestRenderAll();
|
|
1778
1925
|
return;
|
|
1779
1926
|
}
|
|
1927
|
+
const geometry = this.currentGeometry || {
|
|
1928
|
+
x: (width || 800) / 2,
|
|
1929
|
+
y: (height || 600) / 2,
|
|
1930
|
+
width: width || 800,
|
|
1931
|
+
height: height || 600,
|
|
1932
|
+
scale: 1
|
|
1933
|
+
// Default scale if no geometry loaded
|
|
1934
|
+
};
|
|
1780
1935
|
holes.forEach((hole, index) => {
|
|
1936
|
+
const scale = geometry.scale || 1;
|
|
1937
|
+
const unit = geometry.unit || "mm";
|
|
1938
|
+
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1939
|
+
const visualInnerRadius = hole.innerRadius * unitScale * scale;
|
|
1940
|
+
const visualOuterRadius = hole.outerRadius * unitScale * scale;
|
|
1941
|
+
const pos = resolveHolePosition(
|
|
1942
|
+
{
|
|
1943
|
+
...hole,
|
|
1944
|
+
offsetX: (hole.offsetX || 0) * unitScale * scale,
|
|
1945
|
+
offsetY: (hole.offsetY || 0) * unitScale * scale
|
|
1946
|
+
},
|
|
1947
|
+
geometry,
|
|
1948
|
+
{ width: geometry.width, height: geometry.height }
|
|
1949
|
+
// Use geometry dims instead of canvas
|
|
1950
|
+
);
|
|
1781
1951
|
const innerCircle = new import_fabric4.Circle({
|
|
1782
|
-
radius:
|
|
1952
|
+
radius: visualInnerRadius,
|
|
1783
1953
|
fill: "transparent",
|
|
1784
1954
|
stroke: "red",
|
|
1785
1955
|
strokeWidth: 2,
|
|
@@ -1787,17 +1957,17 @@ var HoleTool = class {
|
|
|
1787
1957
|
originY: "center"
|
|
1788
1958
|
});
|
|
1789
1959
|
const outerCircle = new import_fabric4.Circle({
|
|
1790
|
-
radius:
|
|
1960
|
+
radius: visualOuterRadius,
|
|
1791
1961
|
fill: "transparent",
|
|
1792
1962
|
stroke: "#666",
|
|
1793
1963
|
strokeWidth: 1,
|
|
1794
|
-
strokeDashArray:
|
|
1964
|
+
strokeDashArray: [5, 5],
|
|
1795
1965
|
originX: "center",
|
|
1796
1966
|
originY: "center"
|
|
1797
1967
|
});
|
|
1798
1968
|
const holeGroup = new import_fabric4.Group([outerCircle, innerCircle], {
|
|
1799
|
-
left:
|
|
1800
|
-
top:
|
|
1969
|
+
left: pos.x,
|
|
1970
|
+
top: pos.y,
|
|
1801
1971
|
originX: "center",
|
|
1802
1972
|
originY: "center",
|
|
1803
1973
|
selectable: true,
|
|
@@ -1842,9 +2012,6 @@ var HoleTool = class {
|
|
|
1842
2012
|
enforceConstraints() {
|
|
1843
2013
|
const geometry = this.currentGeometry;
|
|
1844
2014
|
if (!geometry || !this.canvasService) {
|
|
1845
|
-
console.log(
|
|
1846
|
-
"[HoleTool] Skipping enforceConstraints: No geometry or canvas service"
|
|
1847
|
-
);
|
|
1848
2015
|
return false;
|
|
1849
2016
|
}
|
|
1850
2017
|
const effectiveOffset = this.constraintTarget === "original" ? 0 : geometry.offset;
|
|
@@ -1858,9 +2025,6 @@ var HoleTool = class {
|
|
|
1858
2025
|
var _a;
|
|
1859
2026
|
return ((_a = obj.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
1860
2027
|
});
|
|
1861
|
-
console.log(
|
|
1862
|
-
`[HoleTool] Enforcing constraints on ${objects.length} markers`
|
|
1863
|
-
);
|
|
1864
2028
|
let changed = false;
|
|
1865
2029
|
objects.sort(
|
|
1866
2030
|
(a, b) => {
|
|
@@ -1869,16 +2033,22 @@ var HoleTool = class {
|
|
|
1869
2033
|
}
|
|
1870
2034
|
);
|
|
1871
2035
|
const newHoles = [];
|
|
1872
|
-
objects.forEach((obj) => {
|
|
2036
|
+
objects.forEach((obj, i) => {
|
|
2037
|
+
var _a, _b;
|
|
1873
2038
|
const currentPos = new import_fabric4.Point(obj.left, obj.top);
|
|
2039
|
+
const holeData = this.holes[i];
|
|
2040
|
+
const scale = geometry.scale || 1;
|
|
2041
|
+
const unit = geometry.unit || "mm";
|
|
2042
|
+
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
2043
|
+
const innerR = ((_a = holeData == null ? void 0 : holeData.innerRadius) != null ? _a : 15) * unitScale * scale;
|
|
2044
|
+
const outerR = ((_b = holeData == null ? void 0 : holeData.outerRadius) != null ? _b : 25) * unitScale * scale;
|
|
1874
2045
|
const newPos = this.calculateConstrainedPosition(
|
|
1875
2046
|
currentPos,
|
|
1876
|
-
constraintGeometry
|
|
2047
|
+
constraintGeometry,
|
|
2048
|
+
innerR,
|
|
2049
|
+
outerR
|
|
1877
2050
|
);
|
|
1878
2051
|
if (currentPos.distanceFrom(newPos) > 0.1) {
|
|
1879
|
-
console.log(
|
|
1880
|
-
`[HoleTool] Moving hole from (${currentPos.x}, ${currentPos.y}) to (${newPos.x}, ${newPos.y})`
|
|
1881
|
-
);
|
|
1882
2052
|
obj.set({
|
|
1883
2053
|
left: newPos.x,
|
|
1884
2054
|
top: newPos.y
|
|
@@ -1886,16 +2056,14 @@ var HoleTool = class {
|
|
|
1886
2056
|
obj.setCoords();
|
|
1887
2057
|
changed = true;
|
|
1888
2058
|
}
|
|
1889
|
-
newHoles.push({ x: obj.left, y: obj.top });
|
|
1890
2059
|
});
|
|
1891
2060
|
if (changed) {
|
|
1892
|
-
this.
|
|
1893
|
-
this.canvasService.requestRenderAll();
|
|
2061
|
+
this.syncHolesFromCanvas();
|
|
1894
2062
|
return true;
|
|
1895
2063
|
}
|
|
1896
2064
|
return false;
|
|
1897
2065
|
}
|
|
1898
|
-
calculateConstrainedPosition(p, g) {
|
|
2066
|
+
calculateConstrainedPosition(p, g, innerRadius, outerRadius) {
|
|
1899
2067
|
const options = {
|
|
1900
2068
|
...g,
|
|
1901
2069
|
holes: []
|
|
@@ -1909,7 +2077,6 @@ var HoleTool = class {
|
|
|
1909
2077
|
const dist = p.distanceFrom(nearestP);
|
|
1910
2078
|
const v = p.subtract(nearestP);
|
|
1911
2079
|
const center = new import_fabric4.Point(g.x, g.y);
|
|
1912
|
-
const centerToNearest = nearestP.subtract(center);
|
|
1913
2080
|
const distToCenter = p.distanceFrom(center);
|
|
1914
2081
|
const nearestDistToCenter = nearestP.distanceFrom(center);
|
|
1915
2082
|
let signedDist = dist;
|
|
@@ -1918,9 +2085,9 @@ var HoleTool = class {
|
|
|
1918
2085
|
}
|
|
1919
2086
|
let clampedDist = signedDist;
|
|
1920
2087
|
if (signedDist > 0) {
|
|
1921
|
-
clampedDist = Math.min(signedDist,
|
|
2088
|
+
clampedDist = Math.min(signedDist, innerRadius);
|
|
1922
2089
|
} else {
|
|
1923
|
-
clampedDist = Math.max(signedDist, -
|
|
2090
|
+
clampedDist = Math.max(signedDist, -outerRadius);
|
|
1924
2091
|
}
|
|
1925
2092
|
if (dist < 1e-3) return nearestP;
|
|
1926
2093
|
const scale = Math.abs(clampedDist) / (dist || 1);
|
|
@@ -1933,17 +2100,14 @@ var HoleTool = class {
|
|
|
1933
2100
|
var import_core5 = require("@pooder/core");
|
|
1934
2101
|
var import_fabric5 = require("fabric");
|
|
1935
2102
|
var ImageTool = class {
|
|
1936
|
-
constructor(
|
|
2103
|
+
constructor() {
|
|
1937
2104
|
this.id = "pooder.kit.image";
|
|
1938
2105
|
this.metadata = {
|
|
1939
2106
|
name: "ImageTool"
|
|
1940
2107
|
};
|
|
1941
|
-
this.
|
|
1942
|
-
this.
|
|
1943
|
-
this.
|
|
1944
|
-
if (options) {
|
|
1945
|
-
Object.assign(this, options);
|
|
1946
|
-
}
|
|
2108
|
+
this.items = [];
|
|
2109
|
+
this.objectMap = /* @__PURE__ */ new Map();
|
|
2110
|
+
this.isUpdatingConfig = false;
|
|
1947
2111
|
}
|
|
1948
2112
|
activate(context) {
|
|
1949
2113
|
this.context = context;
|
|
@@ -1954,38 +2118,33 @@ var ImageTool = class {
|
|
|
1954
2118
|
}
|
|
1955
2119
|
const configService = context.services.get("ConfigurationService");
|
|
1956
2120
|
if (configService) {
|
|
1957
|
-
this.
|
|
1958
|
-
this.opacity = configService.get("image.opacity", this.opacity);
|
|
1959
|
-
this.width = configService.get("image.width", this.width);
|
|
1960
|
-
this.height = configService.get("image.height", this.height);
|
|
1961
|
-
this.angle = configService.get("image.angle", this.angle);
|
|
1962
|
-
this.left = configService.get("image.left", this.left);
|
|
1963
|
-
this.top = configService.get("image.top", this.top);
|
|
2121
|
+
this.items = configService.get("image.items", []) || [];
|
|
1964
2122
|
configService.onAnyChange((e) => {
|
|
1965
|
-
if (
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
2123
|
+
if (this.isUpdatingConfig) return;
|
|
2124
|
+
let shouldUpdate = false;
|
|
2125
|
+
if (e.key === "image.items") {
|
|
2126
|
+
this.items = e.value || [];
|
|
2127
|
+
shouldUpdate = true;
|
|
2128
|
+
} else if (e.key.startsWith("dieline.") && e.key !== "dieline.holes") {
|
|
2129
|
+
shouldUpdate = true;
|
|
2130
|
+
}
|
|
2131
|
+
if (shouldUpdate) {
|
|
2132
|
+
this.updateImages();
|
|
1974
2133
|
}
|
|
1975
2134
|
});
|
|
1976
2135
|
}
|
|
1977
2136
|
this.ensureLayer();
|
|
1978
|
-
this.
|
|
2137
|
+
this.updateImages();
|
|
1979
2138
|
}
|
|
1980
2139
|
deactivate(context) {
|
|
1981
2140
|
if (this.canvasService) {
|
|
1982
2141
|
const layer = this.canvasService.getLayer("user");
|
|
1983
2142
|
if (layer) {
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
2143
|
+
this.objectMap.forEach((obj) => {
|
|
2144
|
+
layer.remove(obj);
|
|
2145
|
+
});
|
|
2146
|
+
this.objectMap.clear();
|
|
2147
|
+
this.canvasService.requestRenderAll();
|
|
1989
2148
|
}
|
|
1990
2149
|
this.canvasService = void 0;
|
|
1991
2150
|
this.context = void 0;
|
|
@@ -1995,82 +2154,103 @@ var ImageTool = class {
|
|
|
1995
2154
|
return {
|
|
1996
2155
|
[import_core5.ContributionPointIds.CONFIGURATIONS]: [
|
|
1997
2156
|
{
|
|
1998
|
-
id: "image.
|
|
1999
|
-
type: "
|
|
2000
|
-
label: "
|
|
2001
|
-
default:
|
|
2002
|
-
}
|
|
2157
|
+
id: "image.items",
|
|
2158
|
+
type: "array",
|
|
2159
|
+
label: "Images",
|
|
2160
|
+
default: []
|
|
2161
|
+
}
|
|
2162
|
+
],
|
|
2163
|
+
[import_core5.ContributionPointIds.COMMANDS]: [
|
|
2003
2164
|
{
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2165
|
+
command: "addImage",
|
|
2166
|
+
title: "Add Image",
|
|
2167
|
+
handler: (url, options) => {
|
|
2168
|
+
const newItem = {
|
|
2169
|
+
id: this.generateId(),
|
|
2170
|
+
url,
|
|
2171
|
+
opacity: 1,
|
|
2172
|
+
...options
|
|
2173
|
+
};
|
|
2174
|
+
this.updateConfig([...this.items, newItem]);
|
|
2175
|
+
return newItem.id;
|
|
2176
|
+
}
|
|
2011
2177
|
},
|
|
2012
2178
|
{
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2179
|
+
command: "removeImage",
|
|
2180
|
+
title: "Remove Image",
|
|
2181
|
+
handler: (id) => {
|
|
2182
|
+
const newItems = this.items.filter((item) => item.id !== id);
|
|
2183
|
+
if (newItems.length !== this.items.length) {
|
|
2184
|
+
this.updateConfig(newItems);
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2019
2187
|
},
|
|
2020
2188
|
{
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2189
|
+
command: "updateImage",
|
|
2190
|
+
title: "Update Image",
|
|
2191
|
+
handler: (id, updates) => {
|
|
2192
|
+
const index = this.items.findIndex((item) => item.id === id);
|
|
2193
|
+
if (index !== -1) {
|
|
2194
|
+
const newItems = [...this.items];
|
|
2195
|
+
newItems[index] = { ...newItems[index], ...updates };
|
|
2196
|
+
this.updateConfig(newItems);
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2027
2199
|
},
|
|
2028
2200
|
{
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
default: this.angle
|
|
2201
|
+
command: "clearImages",
|
|
2202
|
+
title: "Clear Images",
|
|
2203
|
+
handler: () => {
|
|
2204
|
+
this.updateConfig([]);
|
|
2205
|
+
}
|
|
2035
2206
|
},
|
|
2036
2207
|
{
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2208
|
+
command: "bringToFront",
|
|
2209
|
+
title: "Bring Image to Front",
|
|
2210
|
+
handler: (id) => {
|
|
2211
|
+
const index = this.items.findIndex((item) => item.id === id);
|
|
2212
|
+
if (index !== -1 && index < this.items.length - 1) {
|
|
2213
|
+
const newItems = [...this.items];
|
|
2214
|
+
const [item] = newItems.splice(index, 1);
|
|
2215
|
+
newItems.push(item);
|
|
2216
|
+
this.updateConfig(newItems);
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2043
2219
|
},
|
|
2044
2220
|
{
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
command: "setUserImage",
|
|
2056
|
-
title: "Set User Image",
|
|
2057
|
-
handler: (url, opacity, width, height, angle, left, top) => {
|
|
2058
|
-
if (this.url === url && this.opacity === opacity && this.width === width && this.height === height && this.angle === angle && this.left === left && this.top === top)
|
|
2059
|
-
return true;
|
|
2060
|
-
this.url = url;
|
|
2061
|
-
this.opacity = opacity;
|
|
2062
|
-
this.width = width;
|
|
2063
|
-
this.height = height;
|
|
2064
|
-
this.angle = angle;
|
|
2065
|
-
this.left = left;
|
|
2066
|
-
this.top = top;
|
|
2067
|
-
this.updateImage();
|
|
2068
|
-
return true;
|
|
2221
|
+
command: "sendToBack",
|
|
2222
|
+
title: "Send Image to Back",
|
|
2223
|
+
handler: (id) => {
|
|
2224
|
+
const index = this.items.findIndex((item) => item.id === id);
|
|
2225
|
+
if (index > 0) {
|
|
2226
|
+
const newItems = [...this.items];
|
|
2227
|
+
const [item] = newItems.splice(index, 1);
|
|
2228
|
+
newItems.unshift(item);
|
|
2229
|
+
this.updateConfig(newItems);
|
|
2230
|
+
}
|
|
2069
2231
|
}
|
|
2070
2232
|
}
|
|
2071
2233
|
]
|
|
2072
2234
|
};
|
|
2073
2235
|
}
|
|
2236
|
+
generateId() {
|
|
2237
|
+
return Math.random().toString(36).substring(2, 9);
|
|
2238
|
+
}
|
|
2239
|
+
updateConfig(newItems, skipCanvasUpdate = false) {
|
|
2240
|
+
if (!this.context) return;
|
|
2241
|
+
this.isUpdatingConfig = true;
|
|
2242
|
+
this.items = newItems;
|
|
2243
|
+
const configService = this.context.services.get("ConfigurationService");
|
|
2244
|
+
if (configService) {
|
|
2245
|
+
configService.update("image.items", newItems);
|
|
2246
|
+
}
|
|
2247
|
+
if (!skipCanvasUpdate) {
|
|
2248
|
+
this.updateImages();
|
|
2249
|
+
}
|
|
2250
|
+
setTimeout(() => {
|
|
2251
|
+
this.isUpdatingConfig = false;
|
|
2252
|
+
}, 50);
|
|
2253
|
+
}
|
|
2074
2254
|
ensureLayer() {
|
|
2075
2255
|
if (!this.canvasService) return;
|
|
2076
2256
|
let userLayer = this.canvasService.getLayer("user");
|
|
@@ -2102,224 +2282,176 @@ var ImageTool = class {
|
|
|
2102
2282
|
this.canvasService.requestRenderAll();
|
|
2103
2283
|
}
|
|
2104
2284
|
}
|
|
2105
|
-
|
|
2285
|
+
getLayoutInfo() {
|
|
2106
2286
|
var _a, _b;
|
|
2287
|
+
const canvasW = ((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 800;
|
|
2288
|
+
const canvasH = ((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 600;
|
|
2289
|
+
let layoutScale = 1;
|
|
2290
|
+
let layoutOffsetX = 0;
|
|
2291
|
+
let layoutOffsetY = 0;
|
|
2292
|
+
let visualWidth = canvasW;
|
|
2293
|
+
let visualHeight = canvasH;
|
|
2294
|
+
let dielinePhysicalWidth = 500;
|
|
2295
|
+
let dielinePhysicalHeight = 500;
|
|
2296
|
+
if (this.context) {
|
|
2297
|
+
const configService = this.context.services.get("ConfigurationService");
|
|
2298
|
+
if (configService) {
|
|
2299
|
+
dielinePhysicalWidth = configService.get("dieline.width") || 500;
|
|
2300
|
+
dielinePhysicalHeight = configService.get("dieline.height") || 500;
|
|
2301
|
+
const padding = configService.get("dieline.padding") || 40;
|
|
2302
|
+
const layout = Coordinate.calculateLayout(
|
|
2303
|
+
{ width: canvasW, height: canvasH },
|
|
2304
|
+
{ width: dielinePhysicalWidth, height: dielinePhysicalHeight },
|
|
2305
|
+
padding
|
|
2306
|
+
);
|
|
2307
|
+
layoutScale = layout.scale;
|
|
2308
|
+
layoutOffsetX = layout.offsetX;
|
|
2309
|
+
layoutOffsetY = layout.offsetY;
|
|
2310
|
+
visualWidth = layout.width;
|
|
2311
|
+
visualHeight = layout.height;
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
return {
|
|
2315
|
+
layoutScale,
|
|
2316
|
+
layoutOffsetX,
|
|
2317
|
+
layoutOffsetY,
|
|
2318
|
+
visualWidth,
|
|
2319
|
+
visualHeight,
|
|
2320
|
+
dielinePhysicalWidth,
|
|
2321
|
+
dielinePhysicalHeight
|
|
2322
|
+
};
|
|
2323
|
+
}
|
|
2324
|
+
updateImages() {
|
|
2107
2325
|
if (!this.canvasService) return;
|
|
2108
|
-
let { url, opacity, width, height, angle, left, top } = this;
|
|
2109
2326
|
const layer = this.canvasService.getLayer("user");
|
|
2110
2327
|
if (!layer) {
|
|
2111
2328
|
console.warn("[ImageTool] User layer not found");
|
|
2112
2329
|
return;
|
|
2113
2330
|
}
|
|
2114
|
-
const
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2331
|
+
const currentIds = new Set(this.items.map((i) => i.id));
|
|
2332
|
+
for (const [id, obj] of this.objectMap) {
|
|
2333
|
+
if (!currentIds.has(id)) {
|
|
2334
|
+
layer.remove(obj);
|
|
2335
|
+
this.objectMap.delete(id);
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
const layout = this.getLayoutInfo();
|
|
2339
|
+
this.items.forEach((item, index) => {
|
|
2340
|
+
let obj = this.objectMap.get(item.id);
|
|
2341
|
+
if (!obj) {
|
|
2342
|
+
this.loadImage(item, layer, layout);
|
|
2120
2343
|
} else {
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
const centerX = canvasW / 2;
|
|
2125
|
-
const centerY = canvasH / 2;
|
|
2126
|
-
if (userImage.opacity !== opacity) updates.opacity = opacity;
|
|
2127
|
-
if (angle !== void 0 && userImage.angle !== angle)
|
|
2128
|
-
updates.angle = angle;
|
|
2129
|
-
if (userImage.originX !== "center") {
|
|
2130
|
-
userImage.set({
|
|
2131
|
-
originX: "center",
|
|
2132
|
-
originY: "center",
|
|
2133
|
-
left: userImage.left + userImage.width * userImage.scaleX / 2,
|
|
2134
|
-
top: userImage.top + userImage.height * userImage.scaleY / 2
|
|
2135
|
-
});
|
|
2136
|
-
}
|
|
2137
|
-
if (left !== void 0) {
|
|
2138
|
-
const globalLeft = Coordinate.toAbsolute(left, canvasW);
|
|
2139
|
-
const localLeft = globalLeft - centerX;
|
|
2140
|
-
if (Math.abs(userImage.left - localLeft) > 1)
|
|
2141
|
-
updates.left = localLeft;
|
|
2142
|
-
}
|
|
2143
|
-
if (top !== void 0) {
|
|
2144
|
-
const globalTop = Coordinate.toAbsolute(top, canvasH);
|
|
2145
|
-
const localTop = globalTop - centerY;
|
|
2146
|
-
if (Math.abs(userImage.top - localTop) > 1) updates.top = localTop;
|
|
2147
|
-
}
|
|
2148
|
-
if (width !== void 0 && userImage.width)
|
|
2149
|
-
updates.scaleX = width / userImage.width;
|
|
2150
|
-
if (height !== void 0 && userImage.height)
|
|
2151
|
-
updates.scaleY = height / userImage.height;
|
|
2152
|
-
if (Object.keys(updates).length > 0) {
|
|
2153
|
-
userImage.set(updates);
|
|
2154
|
-
layer.dirty = true;
|
|
2155
|
-
this.canvasService.requestRenderAll();
|
|
2156
|
-
}
|
|
2344
|
+
this.updateObjectProperties(obj, item, layout);
|
|
2345
|
+
layer.remove(obj);
|
|
2346
|
+
layer.add(obj);
|
|
2157
2347
|
}
|
|
2158
|
-
}
|
|
2159
|
-
|
|
2348
|
+
});
|
|
2349
|
+
layer.dirty = true;
|
|
2350
|
+
this.canvasService.requestRenderAll();
|
|
2351
|
+
}
|
|
2352
|
+
updateObjectProperties(obj, item, layout) {
|
|
2353
|
+
const { layoutScale, layoutOffsetX, layoutOffsetY, visualWidth, visualHeight } = layout;
|
|
2354
|
+
const updates = {};
|
|
2355
|
+
if (obj.opacity !== item.opacity) updates.opacity = item.opacity;
|
|
2356
|
+
if (item.angle !== void 0 && obj.angle !== item.angle) updates.angle = item.angle;
|
|
2357
|
+
if (item.left !== void 0) {
|
|
2358
|
+
const globalLeft = layoutOffsetX + item.left * visualWidth;
|
|
2359
|
+
if (Math.abs(obj.left - globalLeft) > 1) updates.left = globalLeft;
|
|
2360
|
+
}
|
|
2361
|
+
if (item.top !== void 0) {
|
|
2362
|
+
const globalTop = layoutOffsetY + item.top * visualHeight;
|
|
2363
|
+
if (Math.abs(obj.top - globalTop) > 1) updates.top = globalTop;
|
|
2364
|
+
}
|
|
2365
|
+
if (item.width !== void 0 && obj.width) {
|
|
2366
|
+
const targetScaleX = item.width * layoutScale / obj.width;
|
|
2367
|
+
if (Math.abs(obj.scaleX - targetScaleX) > 1e-3) updates.scaleX = targetScaleX;
|
|
2368
|
+
}
|
|
2369
|
+
if (item.height !== void 0 && obj.height) {
|
|
2370
|
+
const targetScaleY = item.height * layoutScale / obj.height;
|
|
2371
|
+
if (Math.abs(obj.scaleY - targetScaleY) > 1e-3) updates.scaleY = targetScaleY;
|
|
2372
|
+
}
|
|
2373
|
+
if (obj.originX !== "center") {
|
|
2374
|
+
updates.originX = "center";
|
|
2375
|
+
updates.originY = "center";
|
|
2376
|
+
}
|
|
2377
|
+
if (Object.keys(updates).length > 0) {
|
|
2378
|
+
obj.set(updates);
|
|
2160
2379
|
}
|
|
2161
2380
|
}
|
|
2162
|
-
loadImage(layer) {
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
let {
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
{
|
|
2181
|
-
widthVal: dielineWidth,
|
|
2182
|
-
heightVal: dielineHeight,
|
|
2183
|
-
// Debug: dump all keys to see what is available
|
|
2184
|
-
allKeys: Array.from(
|
|
2185
|
-
((_a = configService.configValues) == null ? void 0 : _a.keys()) || []
|
|
2186
|
-
)
|
|
2187
|
-
},
|
|
2188
|
-
configService
|
|
2189
|
-
);
|
|
2190
|
-
if (width === void 0 && height === void 0) {
|
|
2191
|
-
const scale = Math.min(
|
|
2192
|
-
dielineWidth / (image.width || 1),
|
|
2193
|
-
dielineHeight / (image.height || 1)
|
|
2194
|
-
);
|
|
2195
|
-
width = (image.width || 1) * scale;
|
|
2196
|
-
height = (image.height || 1) * scale;
|
|
2197
|
-
this.width = width;
|
|
2198
|
-
this.height = height;
|
|
2199
|
-
}
|
|
2200
|
-
if (left === void 0 && top === void 0) {
|
|
2201
|
-
const dielinePos = configService == null ? void 0 : configService.get("dieline.position");
|
|
2202
|
-
if (dielinePos) {
|
|
2203
|
-
this.left = dielinePos.x;
|
|
2204
|
-
this.top = dielinePos.y;
|
|
2205
|
-
} else {
|
|
2206
|
-
this.left = 0.5;
|
|
2207
|
-
this.top = 0.5;
|
|
2208
|
-
}
|
|
2209
|
-
left = this.left;
|
|
2210
|
-
top = this.top;
|
|
2211
|
-
}
|
|
2212
|
-
}
|
|
2213
|
-
const existingImage = this.canvasService.getObject(
|
|
2214
|
-
"user-image",
|
|
2215
|
-
"user"
|
|
2216
|
-
);
|
|
2217
|
-
if (existingImage) {
|
|
2218
|
-
const defaultLeft = existingImage.left;
|
|
2219
|
-
const defaultTop = existingImage.top;
|
|
2220
|
-
const defaultAngle = existingImage.angle;
|
|
2221
|
-
const defaultScaleX = existingImage.scaleX;
|
|
2222
|
-
const defaultScaleY = existingImage.scaleY;
|
|
2223
|
-
const canvasW = ((_b = this.canvasService) == null ? void 0 : _b.canvas.width) || 800;
|
|
2224
|
-
const canvasH = ((_c = this.canvasService) == null ? void 0 : _c.canvas.height) || 600;
|
|
2225
|
-
const centerX = canvasW / 2;
|
|
2226
|
-
const centerY = canvasH / 2;
|
|
2227
|
-
let targetLeft = left !== void 0 ? left : defaultLeft;
|
|
2228
|
-
let targetTop = top !== void 0 ? top : defaultTop;
|
|
2229
|
-
const configService = (_d = this.context) == null ? void 0 : _d.services.get(
|
|
2230
|
-
"ConfigurationService"
|
|
2231
|
-
);
|
|
2232
|
-
console.log("[ImageTool] Loading EXISTING image...", {
|
|
2233
|
-
canvasW,
|
|
2234
|
-
canvasH,
|
|
2235
|
-
centerX,
|
|
2236
|
-
centerY,
|
|
2237
|
-
incomingLeft: left,
|
|
2238
|
-
incomingTop: top,
|
|
2239
|
-
dielinePos: configService == null ? void 0 : configService.get("dieline.position"),
|
|
2240
|
-
existingImage: !!existingImage
|
|
2241
|
-
});
|
|
2242
|
-
if (left !== void 0) {
|
|
2243
|
-
const globalLeft = Coordinate.toAbsolute(left, canvasW);
|
|
2244
|
-
targetLeft = globalLeft;
|
|
2245
|
-
console.log("[ImageTool] Calculated targetLeft", {
|
|
2246
|
-
globalLeft,
|
|
2247
|
-
targetLeft
|
|
2248
|
-
});
|
|
2249
|
-
}
|
|
2250
|
-
if (top !== void 0) {
|
|
2251
|
-
const globalTop = Coordinate.toAbsolute(top, canvasH);
|
|
2252
|
-
targetTop = globalTop;
|
|
2253
|
-
console.log("[ImageTool] Calculated targetTop", {
|
|
2254
|
-
globalTop,
|
|
2255
|
-
targetTop
|
|
2256
|
-
});
|
|
2257
|
-
}
|
|
2258
|
-
image.set({
|
|
2259
|
-
originX: "center",
|
|
2260
|
-
// Use center origin for easier positioning
|
|
2261
|
-
originY: "center",
|
|
2262
|
-
left: targetLeft,
|
|
2263
|
-
top: targetTop,
|
|
2264
|
-
angle: angle !== void 0 ? angle : defaultAngle,
|
|
2265
|
-
scaleX: width !== void 0 && image.width ? width / image.width : defaultScaleX,
|
|
2266
|
-
scaleY: height !== void 0 && image.height ? height / image.height : defaultScaleY
|
|
2267
|
-
});
|
|
2268
|
-
layer.remove(existingImage);
|
|
2269
|
-
} else {
|
|
2270
|
-
image.set({
|
|
2271
|
-
originX: "center",
|
|
2272
|
-
originY: "center"
|
|
2273
|
-
});
|
|
2274
|
-
if (width !== void 0 && image.width)
|
|
2275
|
-
image.scaleX = width / image.width;
|
|
2276
|
-
if (height !== void 0 && image.height)
|
|
2277
|
-
image.scaleY = height / image.height;
|
|
2278
|
-
if (angle !== void 0) image.angle = angle;
|
|
2279
|
-
const canvasW = ((_e = this.canvasService) == null ? void 0 : _e.canvas.width) || 800;
|
|
2280
|
-
const canvasH = ((_f = this.canvasService) == null ? void 0 : _f.canvas.height) || 600;
|
|
2281
|
-
const centerX = canvasW / 2;
|
|
2282
|
-
const centerY = canvasH / 2;
|
|
2283
|
-
if (left !== void 0) {
|
|
2284
|
-
image.left = Coordinate.toAbsolute(left, canvasW);
|
|
2285
|
-
} else {
|
|
2286
|
-
image.left = centerX;
|
|
2287
|
-
}
|
|
2288
|
-
if (top !== void 0) {
|
|
2289
|
-
image.top = Coordinate.toAbsolute(top, canvasH);
|
|
2381
|
+
loadImage(item, layer, layout) {
|
|
2382
|
+
import_fabric5.Image.fromURL(item.url, { crossOrigin: "anonymous" }).then((image) => {
|
|
2383
|
+
var _a;
|
|
2384
|
+
if (!this.items.find((i) => i.id === item.id)) return;
|
|
2385
|
+
image.set({
|
|
2386
|
+
originX: "center",
|
|
2387
|
+
originY: "center",
|
|
2388
|
+
data: { id: item.id }
|
|
2389
|
+
});
|
|
2390
|
+
let { width, height, left, top } = item;
|
|
2391
|
+
const { layoutScale, layoutOffsetX, layoutOffsetY, visualWidth, visualHeight, dielinePhysicalWidth, dielinePhysicalHeight } = layout;
|
|
2392
|
+
if (width === void 0 && height === void 0) {
|
|
2393
|
+
const imgAspect = (image.width || 1) / (image.height || 1);
|
|
2394
|
+
const dielineAspect = dielinePhysicalWidth / dielinePhysicalHeight;
|
|
2395
|
+
if (imgAspect > dielineAspect) {
|
|
2396
|
+
const w = dielinePhysicalWidth;
|
|
2397
|
+
width = w;
|
|
2398
|
+
height = w / imgAspect;
|
|
2290
2399
|
} else {
|
|
2291
|
-
|
|
2400
|
+
const h = dielinePhysicalHeight;
|
|
2401
|
+
height = h;
|
|
2402
|
+
width = h * imgAspect;
|
|
2292
2403
|
}
|
|
2404
|
+
item.width = width;
|
|
2405
|
+
item.height = height;
|
|
2293
2406
|
}
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
}
|
|
2407
|
+
if (left === void 0 && top === void 0) {
|
|
2408
|
+
left = 0.5;
|
|
2409
|
+
top = 0.5;
|
|
2410
|
+
item.left = left;
|
|
2411
|
+
item.top = top;
|
|
2412
|
+
}
|
|
2413
|
+
this.updateObjectProperties(image, item, layout);
|
|
2300
2414
|
layer.add(image);
|
|
2415
|
+
this.objectMap.set(item.id, image);
|
|
2301
2416
|
image.on("modified", (e) => {
|
|
2302
|
-
|
|
2303
|
-
const matrix = image.calcTransformMatrix();
|
|
2304
|
-
const globalPoint = import_fabric5.util.transformPoint(new import_fabric5.Point(0, 0), matrix);
|
|
2305
|
-
const canvasW = ((_a2 = this.canvasService) == null ? void 0 : _a2.canvas.width) || 800;
|
|
2306
|
-
const canvasH = ((_b2 = this.canvasService) == null ? void 0 : _b2.canvas.height) || 600;
|
|
2307
|
-
this.left = Coordinate.toNormalized(globalPoint.x, canvasW);
|
|
2308
|
-
this.top = Coordinate.toNormalized(globalPoint.y, canvasH);
|
|
2309
|
-
this.angle = e.target.angle;
|
|
2310
|
-
if (image.width) this.width = e.target.width * e.target.scaleX;
|
|
2311
|
-
if (image.height) this.height = e.target.height * e.target.scaleY;
|
|
2312
|
-
if (this.context) {
|
|
2313
|
-
this.context.eventBus.emit("update");
|
|
2314
|
-
}
|
|
2417
|
+
this.handleObjectModified(item.id, image);
|
|
2315
2418
|
});
|
|
2316
2419
|
layer.dirty = true;
|
|
2317
|
-
this.canvasService.requestRenderAll();
|
|
2420
|
+
(_a = this.canvasService) == null ? void 0 : _a.requestRenderAll();
|
|
2421
|
+
if (item.width !== width || item.height !== height || item.left !== left || item.top !== top) {
|
|
2422
|
+
this.updateImageInConfig(item.id, { width, height, left, top });
|
|
2423
|
+
}
|
|
2318
2424
|
}).catch((err) => {
|
|
2319
|
-
|
|
2320
|
-
console.error("Failed to load image", url, err);
|
|
2425
|
+
console.error("Failed to load image", item.url, err);
|
|
2321
2426
|
});
|
|
2322
2427
|
}
|
|
2428
|
+
handleObjectModified(id, image) {
|
|
2429
|
+
const layout = this.getLayoutInfo();
|
|
2430
|
+
const { layoutScale, layoutOffsetX, layoutOffsetY, visualWidth, visualHeight } = layout;
|
|
2431
|
+
const matrix = image.calcTransformMatrix();
|
|
2432
|
+
const globalPoint = import_fabric5.util.transformPoint(new import_fabric5.Point(0, 0), matrix);
|
|
2433
|
+
const updates = {};
|
|
2434
|
+
updates.left = (globalPoint.x - layoutOffsetX) / visualWidth;
|
|
2435
|
+
updates.top = (globalPoint.y - layoutOffsetY) / visualHeight;
|
|
2436
|
+
updates.angle = image.angle;
|
|
2437
|
+
if (image.width) {
|
|
2438
|
+
const pixelWidth = image.width * image.scaleX;
|
|
2439
|
+
updates.width = pixelWidth / layoutScale;
|
|
2440
|
+
}
|
|
2441
|
+
if (image.height) {
|
|
2442
|
+
const pixelHeight = image.height * image.scaleY;
|
|
2443
|
+
updates.height = pixelHeight / layoutScale;
|
|
2444
|
+
}
|
|
2445
|
+
this.updateImageInConfig(id, updates);
|
|
2446
|
+
}
|
|
2447
|
+
updateImageInConfig(id, updates) {
|
|
2448
|
+
const index = this.items.findIndex((i) => i.id === id);
|
|
2449
|
+
if (index !== -1) {
|
|
2450
|
+
const newItems = [...this.items];
|
|
2451
|
+
newItems[index] = { ...newItems[index], ...updates };
|
|
2452
|
+
this.updateConfig(newItems, true);
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2323
2455
|
};
|
|
2324
2456
|
|
|
2325
2457
|
// src/white-ink.ts
|
|
@@ -2622,12 +2754,18 @@ var RulerTool = class {
|
|
|
2622
2754
|
this.metadata = {
|
|
2623
2755
|
name: "RulerTool"
|
|
2624
2756
|
};
|
|
2625
|
-
this.unit = "px";
|
|
2626
2757
|
this.thickness = 20;
|
|
2758
|
+
this.gap = 15;
|
|
2627
2759
|
this.backgroundColor = "#f0f0f0";
|
|
2628
2760
|
this.textColor = "#333333";
|
|
2629
2761
|
this.lineColor = "#999999";
|
|
2630
2762
|
this.fontSize = 10;
|
|
2763
|
+
// Dieline context for sync
|
|
2764
|
+
this.dielineWidth = 500;
|
|
2765
|
+
this.dielineHeight = 500;
|
|
2766
|
+
this.dielineUnit = "mm";
|
|
2767
|
+
this.dielinePadding = 40;
|
|
2768
|
+
this.dielineOffset = 0;
|
|
2631
2769
|
if (options) {
|
|
2632
2770
|
Object.assign(this, options);
|
|
2633
2771
|
}
|
|
@@ -2640,8 +2778,8 @@ var RulerTool = class {
|
|
|
2640
2778
|
}
|
|
2641
2779
|
const configService = context.services.get("ConfigurationService");
|
|
2642
2780
|
if (configService) {
|
|
2643
|
-
this.unit = configService.get("ruler.unit", this.unit);
|
|
2644
2781
|
this.thickness = configService.get("ruler.thickness", this.thickness);
|
|
2782
|
+
this.gap = configService.get("ruler.gap", this.gap);
|
|
2645
2783
|
this.backgroundColor = configService.get(
|
|
2646
2784
|
"ruler.backgroundColor",
|
|
2647
2785
|
this.backgroundColor
|
|
@@ -2649,13 +2787,38 @@ var RulerTool = class {
|
|
|
2649
2787
|
this.textColor = configService.get("ruler.textColor", this.textColor);
|
|
2650
2788
|
this.lineColor = configService.get("ruler.lineColor", this.lineColor);
|
|
2651
2789
|
this.fontSize = configService.get("ruler.fontSize", this.fontSize);
|
|
2790
|
+
this.dielineUnit = configService.get("dieline.unit", this.dielineUnit);
|
|
2791
|
+
this.dielineWidth = configService.get("dieline.width", this.dielineWidth);
|
|
2792
|
+
this.dielineHeight = configService.get(
|
|
2793
|
+
"dieline.height",
|
|
2794
|
+
this.dielineHeight
|
|
2795
|
+
);
|
|
2796
|
+
this.dielinePadding = configService.get(
|
|
2797
|
+
"dieline.padding",
|
|
2798
|
+
this.dielinePadding
|
|
2799
|
+
);
|
|
2800
|
+
this.dielineOffset = configService.get(
|
|
2801
|
+
"dieline.offset",
|
|
2802
|
+
this.dielineOffset
|
|
2803
|
+
);
|
|
2652
2804
|
configService.onAnyChange((e) => {
|
|
2805
|
+
let shouldUpdate = false;
|
|
2653
2806
|
if (e.key.startsWith("ruler.")) {
|
|
2654
2807
|
const prop = e.key.split(".")[1];
|
|
2655
2808
|
if (prop && prop in this) {
|
|
2656
2809
|
this[prop] = e.value;
|
|
2657
|
-
|
|
2810
|
+
shouldUpdate = true;
|
|
2658
2811
|
}
|
|
2812
|
+
} else if (e.key.startsWith("dieline.")) {
|
|
2813
|
+
if (e.key === "dieline.unit") this.dielineUnit = e.value;
|
|
2814
|
+
if (e.key === "dieline.width") this.dielineWidth = e.value;
|
|
2815
|
+
if (e.key === "dieline.height") this.dielineHeight = e.value;
|
|
2816
|
+
if (e.key === "dieline.padding") this.dielinePadding = e.value;
|
|
2817
|
+
if (e.key === "dieline.offset") this.dielineOffset = e.value;
|
|
2818
|
+
shouldUpdate = true;
|
|
2819
|
+
}
|
|
2820
|
+
if (shouldUpdate) {
|
|
2821
|
+
this.updateRuler();
|
|
2659
2822
|
}
|
|
2660
2823
|
});
|
|
2661
2824
|
}
|
|
@@ -2669,13 +2832,6 @@ var RulerTool = class {
|
|
|
2669
2832
|
contribute() {
|
|
2670
2833
|
return {
|
|
2671
2834
|
[import_core7.ContributionPointIds.CONFIGURATIONS]: [
|
|
2672
|
-
{
|
|
2673
|
-
id: "ruler.unit",
|
|
2674
|
-
type: "select",
|
|
2675
|
-
label: "Unit",
|
|
2676
|
-
options: ["px", "mm", "cm", "in"],
|
|
2677
|
-
default: "px"
|
|
2678
|
-
},
|
|
2679
2835
|
{
|
|
2680
2836
|
id: "ruler.thickness",
|
|
2681
2837
|
type: "number",
|
|
@@ -2684,6 +2840,14 @@ var RulerTool = class {
|
|
|
2684
2840
|
max: 100,
|
|
2685
2841
|
default: 20
|
|
2686
2842
|
},
|
|
2843
|
+
{
|
|
2844
|
+
id: "ruler.gap",
|
|
2845
|
+
type: "number",
|
|
2846
|
+
label: "Gap",
|
|
2847
|
+
min: 0,
|
|
2848
|
+
max: 100,
|
|
2849
|
+
default: 15
|
|
2850
|
+
},
|
|
2687
2851
|
{
|
|
2688
2852
|
id: "ruler.backgroundColor",
|
|
2689
2853
|
type: "color",
|
|
@@ -2712,16 +2876,6 @@ var RulerTool = class {
|
|
|
2712
2876
|
}
|
|
2713
2877
|
],
|
|
2714
2878
|
[import_core7.ContributionPointIds.COMMANDS]: [
|
|
2715
|
-
{
|
|
2716
|
-
command: "setUnit",
|
|
2717
|
-
title: "Set Ruler Unit",
|
|
2718
|
-
handler: (unit) => {
|
|
2719
|
-
if (this.unit === unit) return true;
|
|
2720
|
-
this.unit = unit;
|
|
2721
|
-
this.updateRuler();
|
|
2722
|
-
return true;
|
|
2723
|
-
}
|
|
2724
|
-
},
|
|
2725
2879
|
{
|
|
2726
2880
|
command: "setTheme",
|
|
2727
2881
|
title: "Set Ruler Theme",
|
|
@@ -2772,6 +2926,68 @@ var RulerTool = class {
|
|
|
2772
2926
|
this.canvasService.canvas.remove(layer);
|
|
2773
2927
|
}
|
|
2774
2928
|
}
|
|
2929
|
+
createArrowLine(x1, y1, x2, y2, color) {
|
|
2930
|
+
const line = new import_fabric7.Line([x1, y1, x2, y2], {
|
|
2931
|
+
stroke: color,
|
|
2932
|
+
strokeWidth: this.thickness / 20,
|
|
2933
|
+
// Scale stroke width relative to thickness (default 1)
|
|
2934
|
+
selectable: false,
|
|
2935
|
+
evented: false
|
|
2936
|
+
});
|
|
2937
|
+
const arrowSize = Math.max(4, this.thickness * 0.3);
|
|
2938
|
+
const angle = Math.atan2(y2 - y1, x2 - x1);
|
|
2939
|
+
const endArrow = new import_fabric7.Polygon(
|
|
2940
|
+
[
|
|
2941
|
+
{ x: 0, y: 0 },
|
|
2942
|
+
{ x: -arrowSize, y: -arrowSize / 2 },
|
|
2943
|
+
{ x: -arrowSize, y: arrowSize / 2 }
|
|
2944
|
+
],
|
|
2945
|
+
{
|
|
2946
|
+
fill: color,
|
|
2947
|
+
left: x2,
|
|
2948
|
+
top: y2,
|
|
2949
|
+
originX: "right",
|
|
2950
|
+
originY: "center",
|
|
2951
|
+
angle: angle * 180 / Math.PI,
|
|
2952
|
+
selectable: false,
|
|
2953
|
+
evented: false
|
|
2954
|
+
}
|
|
2955
|
+
);
|
|
2956
|
+
const startArrow = new import_fabric7.Polygon(
|
|
2957
|
+
[
|
|
2958
|
+
{ x: 0, y: 0 },
|
|
2959
|
+
{ x: arrowSize, y: -arrowSize / 2 },
|
|
2960
|
+
{ x: arrowSize, y: arrowSize / 2 }
|
|
2961
|
+
],
|
|
2962
|
+
{
|
|
2963
|
+
fill: color,
|
|
2964
|
+
left: x1,
|
|
2965
|
+
top: y1,
|
|
2966
|
+
originX: "left",
|
|
2967
|
+
originY: "center",
|
|
2968
|
+
angle: angle * 180 / Math.PI,
|
|
2969
|
+
selectable: false,
|
|
2970
|
+
evented: false
|
|
2971
|
+
}
|
|
2972
|
+
);
|
|
2973
|
+
return new import_fabric7.Group([line, startArrow, endArrow], {
|
|
2974
|
+
selectable: false,
|
|
2975
|
+
evented: false
|
|
2976
|
+
});
|
|
2977
|
+
}
|
|
2978
|
+
resolvePadding(containerWidth, containerHeight) {
|
|
2979
|
+
if (typeof this.dielinePadding === "number") {
|
|
2980
|
+
return this.dielinePadding;
|
|
2981
|
+
}
|
|
2982
|
+
if (typeof this.dielinePadding === "string") {
|
|
2983
|
+
if (this.dielinePadding.endsWith("%")) {
|
|
2984
|
+
const percent = parseFloat(this.dielinePadding) / 100;
|
|
2985
|
+
return Math.min(containerWidth, containerHeight) * percent;
|
|
2986
|
+
}
|
|
2987
|
+
return parseFloat(this.dielinePadding) || 0;
|
|
2988
|
+
}
|
|
2989
|
+
return 0;
|
|
2990
|
+
}
|
|
2775
2991
|
updateRuler() {
|
|
2776
2992
|
if (!this.canvasService) return;
|
|
2777
2993
|
const layer = this.getLayer();
|
|
@@ -2780,95 +2996,141 @@ var RulerTool = class {
|
|
|
2780
2996
|
const { thickness, backgroundColor, lineColor, textColor, fontSize } = this;
|
|
2781
2997
|
const width = this.canvasService.canvas.width || 800;
|
|
2782
2998
|
const height = this.canvasService.canvas.height || 600;
|
|
2783
|
-
const
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
width,
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
const
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
const
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2999
|
+
const paddingPx = this.resolvePadding(width, height);
|
|
3000
|
+
const layout = Coordinate.calculateLayout(
|
|
3001
|
+
{ width, height },
|
|
3002
|
+
{ width: this.dielineWidth, height: this.dielineHeight },
|
|
3003
|
+
paddingPx
|
|
3004
|
+
);
|
|
3005
|
+
const scale = layout.scale;
|
|
3006
|
+
const offsetX = layout.offsetX;
|
|
3007
|
+
const offsetY = layout.offsetY;
|
|
3008
|
+
const visualWidth = layout.width;
|
|
3009
|
+
const visualHeight = layout.height;
|
|
3010
|
+
const rawOffset = this.dielineOffset || 0;
|
|
3011
|
+
const effectiveOffset = rawOffset > 0 ? rawOffset : 0;
|
|
3012
|
+
const expandPixels = effectiveOffset * scale;
|
|
3013
|
+
const gap = this.gap || 15;
|
|
3014
|
+
const rulerLeft = offsetX - expandPixels;
|
|
3015
|
+
const rulerTop = offsetY - expandPixels;
|
|
3016
|
+
const rulerRight = offsetX + visualWidth + expandPixels;
|
|
3017
|
+
const rulerBottom = offsetY + visualHeight + expandPixels;
|
|
3018
|
+
const displayWidth = this.dielineWidth + effectiveOffset * 2;
|
|
3019
|
+
const displayHeight = this.dielineHeight + effectiveOffset * 2;
|
|
3020
|
+
const topRulerY = rulerTop - gap;
|
|
3021
|
+
const topRulerXStart = rulerLeft;
|
|
3022
|
+
const topRulerXEnd = rulerRight;
|
|
3023
|
+
const leftRulerX = rulerLeft - gap;
|
|
3024
|
+
const leftRulerYStart = rulerTop;
|
|
3025
|
+
const leftRulerYEnd = rulerBottom;
|
|
3026
|
+
const topDimLine = this.createArrowLine(
|
|
3027
|
+
topRulerXStart,
|
|
3028
|
+
topRulerY,
|
|
3029
|
+
topRulerXEnd,
|
|
3030
|
+
topRulerY,
|
|
3031
|
+
lineColor
|
|
3032
|
+
);
|
|
3033
|
+
layer.add(topDimLine);
|
|
3034
|
+
const extLen = 5;
|
|
3035
|
+
layer.add(
|
|
3036
|
+
new import_fabric7.Line(
|
|
3037
|
+
[
|
|
3038
|
+
topRulerXStart,
|
|
3039
|
+
topRulerY - extLen,
|
|
3040
|
+
topRulerXStart,
|
|
3041
|
+
topRulerY + extLen
|
|
3042
|
+
],
|
|
3043
|
+
{
|
|
3044
|
+
stroke: lineColor,
|
|
3045
|
+
strokeWidth: 1,
|
|
3046
|
+
selectable: false,
|
|
3047
|
+
evented: false
|
|
3048
|
+
}
|
|
3049
|
+
)
|
|
3050
|
+
);
|
|
3051
|
+
layer.add(
|
|
3052
|
+
new import_fabric7.Line(
|
|
3053
|
+
[topRulerXEnd, topRulerY - extLen, topRulerXEnd, topRulerY + extLen],
|
|
3054
|
+
{
|
|
3055
|
+
stroke: lineColor,
|
|
3056
|
+
strokeWidth: 1,
|
|
3057
|
+
selectable: false,
|
|
3058
|
+
evented: false
|
|
3059
|
+
}
|
|
3060
|
+
)
|
|
3061
|
+
);
|
|
3062
|
+
const widthStr = parseFloat(displayWidth.toFixed(2)).toString();
|
|
3063
|
+
const topTextContent = `${widthStr} ${this.dielineUnit}`;
|
|
3064
|
+
const topText = new import_fabric7.Text(topTextContent, {
|
|
3065
|
+
left: topRulerXStart + (rulerRight - rulerLeft) / 2,
|
|
3066
|
+
top: topRulerY,
|
|
3067
|
+
fontSize,
|
|
3068
|
+
fill: textColor,
|
|
3069
|
+
fontFamily: "Arial",
|
|
3070
|
+
originX: "center",
|
|
3071
|
+
originY: "center",
|
|
3072
|
+
backgroundColor,
|
|
3073
|
+
// Background mask for readability
|
|
2809
3074
|
selectable: false,
|
|
2810
3075
|
evented: false
|
|
2811
3076
|
});
|
|
2812
|
-
layer.add(
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
left: x + 2,
|
|
2833
|
-
top: 2,
|
|
2834
|
-
fontSize,
|
|
2835
|
-
fill: textColor,
|
|
2836
|
-
fontFamily: "Arial",
|
|
3077
|
+
layer.add(topText);
|
|
3078
|
+
const leftDimLine = this.createArrowLine(
|
|
3079
|
+
leftRulerX,
|
|
3080
|
+
leftRulerYStart,
|
|
3081
|
+
leftRulerX,
|
|
3082
|
+
leftRulerYEnd,
|
|
3083
|
+
lineColor
|
|
3084
|
+
);
|
|
3085
|
+
layer.add(leftDimLine);
|
|
3086
|
+
layer.add(
|
|
3087
|
+
new import_fabric7.Line(
|
|
3088
|
+
[
|
|
3089
|
+
leftRulerX - extLen,
|
|
3090
|
+
leftRulerYStart,
|
|
3091
|
+
leftRulerX + extLen,
|
|
3092
|
+
leftRulerYStart
|
|
3093
|
+
],
|
|
3094
|
+
{
|
|
3095
|
+
stroke: lineColor,
|
|
3096
|
+
strokeWidth: 1,
|
|
2837
3097
|
selectable: false,
|
|
2838
3098
|
evented: false
|
|
2839
|
-
}
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
});
|
|
2854
|
-
layer.add(line);
|
|
2855
|
-
if (y % step === 0) {
|
|
2856
|
-
const text = new import_fabric7.Text(y.toString(), {
|
|
2857
|
-
angle: -90,
|
|
2858
|
-
left: thickness / 2 - fontSize / 3,
|
|
2859
|
-
// approximate centering
|
|
2860
|
-
top: y + fontSize,
|
|
2861
|
-
fontSize,
|
|
2862
|
-
fill: textColor,
|
|
2863
|
-
fontFamily: "Arial",
|
|
2864
|
-
originX: "center",
|
|
2865
|
-
originY: "center",
|
|
3099
|
+
}
|
|
3100
|
+
)
|
|
3101
|
+
);
|
|
3102
|
+
layer.add(
|
|
3103
|
+
new import_fabric7.Line(
|
|
3104
|
+
[
|
|
3105
|
+
leftRulerX - extLen,
|
|
3106
|
+
leftRulerYEnd,
|
|
3107
|
+
leftRulerX + extLen,
|
|
3108
|
+
leftRulerYEnd
|
|
3109
|
+
],
|
|
3110
|
+
{
|
|
3111
|
+
stroke: lineColor,
|
|
3112
|
+
strokeWidth: 1,
|
|
2866
3113
|
selectable: false,
|
|
2867
3114
|
evented: false
|
|
2868
|
-
}
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
3115
|
+
}
|
|
3116
|
+
)
|
|
3117
|
+
);
|
|
3118
|
+
const heightStr = parseFloat(displayHeight.toFixed(2)).toString();
|
|
3119
|
+
const leftTextContent = `${heightStr} ${this.dielineUnit}`;
|
|
3120
|
+
const leftText = new import_fabric7.Text(leftTextContent, {
|
|
3121
|
+
left: leftRulerX,
|
|
3122
|
+
top: leftRulerYStart + (rulerBottom - rulerTop) / 2,
|
|
3123
|
+
angle: -90,
|
|
3124
|
+
fontSize,
|
|
3125
|
+
fill: textColor,
|
|
3126
|
+
fontFamily: "Arial",
|
|
3127
|
+
originX: "center",
|
|
3128
|
+
originY: "center",
|
|
3129
|
+
backgroundColor,
|
|
3130
|
+
selectable: false,
|
|
3131
|
+
evented: false
|
|
3132
|
+
});
|
|
3133
|
+
layer.add(leftText);
|
|
2872
3134
|
this.canvasService.canvas.bringObjectToFront(layer);
|
|
2873
3135
|
this.canvasService.canvas.requestRenderAll();
|
|
2874
3136
|
}
|