@pooder/kit 3.1.0 → 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 +11 -0
- package/dist/index.d.mts +39 -24
- package/dist/index.d.ts +39 -24
- package/dist/index.js +740 -534
- package/dist/index.mjs +744 -538
- package/package.json +2 -2
- package/src/coordinate.ts +57 -0
- package/src/dieline.ts +186 -129
- package/src/geometry.ts +19 -10
- package/src/hole.ts +88 -31
- 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,6 +446,21 @@ 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
|
|
@@ -484,9 +517,10 @@ function resolveHolePosition(hole, geometry, canvasSize) {
|
|
|
484
517
|
y: by + (hole.offsetY || 0)
|
|
485
518
|
};
|
|
486
519
|
} else if (hole.x !== void 0 && hole.y !== void 0) {
|
|
520
|
+
const { x, width, y, height } = geometry;
|
|
487
521
|
return {
|
|
488
|
-
x: hole.x *
|
|
489
|
-
y: hole.y *
|
|
522
|
+
x: hole.x * width + (x - width / 2) + (hole.offsetX || 0),
|
|
523
|
+
y: hole.y * height + (y - height / 2) + (hole.offsetY || 0)
|
|
490
524
|
};
|
|
491
525
|
}
|
|
492
526
|
return { x: 0, y: 0 };
|
|
@@ -592,7 +626,10 @@ function getDielineShape(options) {
|
|
|
592
626
|
cutsPath.remove();
|
|
593
627
|
mainShape = temp;
|
|
594
628
|
} catch (e) {
|
|
595
|
-
console.error(
|
|
629
|
+
console.error(
|
|
630
|
+
"Geometry: Failed to subtract cutsPath from mainShape",
|
|
631
|
+
e
|
|
632
|
+
);
|
|
596
633
|
}
|
|
597
634
|
}
|
|
598
635
|
}
|
|
@@ -685,7 +722,12 @@ function getPathBounds(pathData) {
|
|
|
685
722
|
path.pathData = pathData;
|
|
686
723
|
const bounds = path.bounds;
|
|
687
724
|
path.remove();
|
|
688
|
-
return {
|
|
725
|
+
return {
|
|
726
|
+
x: bounds.x,
|
|
727
|
+
y: bounds.y,
|
|
728
|
+
width: bounds.width,
|
|
729
|
+
height: bounds.height
|
|
730
|
+
};
|
|
689
731
|
}
|
|
690
732
|
|
|
691
733
|
// src/dieline.ts
|
|
@@ -695,6 +737,7 @@ var DielineTool = class {
|
|
|
695
737
|
this.metadata = {
|
|
696
738
|
name: "DielineTool"
|
|
697
739
|
};
|
|
740
|
+
this.unit = "mm";
|
|
698
741
|
this.shape = "rect";
|
|
699
742
|
this.width = 500;
|
|
700
743
|
this.height = 500;
|
|
@@ -705,6 +748,7 @@ var DielineTool = class {
|
|
|
705
748
|
this.outsideColor = "#ffffff";
|
|
706
749
|
this.showBleedLines = true;
|
|
707
750
|
this.holes = [];
|
|
751
|
+
this.padding = 140;
|
|
708
752
|
if (options) {
|
|
709
753
|
Object.assign(this, options);
|
|
710
754
|
}
|
|
@@ -718,14 +762,12 @@ var DielineTool = class {
|
|
|
718
762
|
}
|
|
719
763
|
const configService = context.services.get("ConfigurationService");
|
|
720
764
|
if (configService) {
|
|
765
|
+
this.unit = configService.get("dieline.unit", this.unit);
|
|
721
766
|
this.shape = configService.get("dieline.shape", this.shape);
|
|
722
767
|
this.width = configService.get("dieline.width", this.width);
|
|
723
768
|
this.height = configService.get("dieline.height", this.height);
|
|
724
769
|
this.radius = configService.get("dieline.radius", this.radius);
|
|
725
|
-
this.
|
|
726
|
-
"dieline.borderLength",
|
|
727
|
-
this.borderLength
|
|
728
|
-
);
|
|
770
|
+
this.padding = configService.get("dieline.padding", this.padding);
|
|
729
771
|
this.offset = configService.get("dieline.offset", this.offset);
|
|
730
772
|
this.style = configService.get("dieline.style", this.style);
|
|
731
773
|
this.insideColor = configService.get(
|
|
@@ -766,6 +808,13 @@ var DielineTool = class {
|
|
|
766
808
|
contribute() {
|
|
767
809
|
return {
|
|
768
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
|
+
},
|
|
769
818
|
{
|
|
770
819
|
id: "dieline.shape",
|
|
771
820
|
type: "select",
|
|
@@ -801,15 +850,14 @@ var DielineTool = class {
|
|
|
801
850
|
id: "dieline.position",
|
|
802
851
|
type: "json",
|
|
803
852
|
label: "Position (Normalized)",
|
|
804
|
-
default: this.
|
|
853
|
+
default: this.radius
|
|
805
854
|
},
|
|
806
855
|
{
|
|
807
|
-
id: "dieline.
|
|
808
|
-
type: "
|
|
809
|
-
label: "
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
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
|
|
813
861
|
},
|
|
814
862
|
{
|
|
815
863
|
id: "dieline.offset",
|
|
@@ -878,7 +926,9 @@ var DielineTool = class {
|
|
|
878
926
|
const scale = currentMax / Math.max(bounds.width, bounds.height);
|
|
879
927
|
const newWidth = bounds.width * scale;
|
|
880
928
|
const newHeight = bounds.height * scale;
|
|
881
|
-
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
929
|
+
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
930
|
+
"ConfigurationService"
|
|
931
|
+
);
|
|
882
932
|
if (configService) {
|
|
883
933
|
configService.update("dieline.width", newWidth);
|
|
884
934
|
configService.update("dieline.height", newHeight);
|
|
@@ -943,12 +993,25 @@ var DielineTool = class {
|
|
|
943
993
|
}
|
|
944
994
|
return new Pattern({ source: canvas, repetition: "repeat" });
|
|
945
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
|
+
}
|
|
946
1009
|
updateDieline(emitEvent = true) {
|
|
947
|
-
var _a, _b;
|
|
948
1010
|
if (!this.canvasService) return;
|
|
949
1011
|
const layer = this.getLayer();
|
|
950
1012
|
if (!layer) return;
|
|
951
1013
|
const {
|
|
1014
|
+
unit,
|
|
952
1015
|
shape,
|
|
953
1016
|
radius,
|
|
954
1017
|
offset,
|
|
@@ -956,43 +1019,60 @@ var DielineTool = class {
|
|
|
956
1019
|
insideColor,
|
|
957
1020
|
outsideColor,
|
|
958
1021
|
position,
|
|
959
|
-
borderLength,
|
|
960
1022
|
showBleedLines,
|
|
961
1023
|
holes
|
|
962
1024
|
} = this;
|
|
963
1025
|
let { width, height } = this;
|
|
964
1026
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
965
1027
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
const
|
|
973
|
-
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;
|
|
974
1041
|
layer.remove(...layer.getObjects());
|
|
975
1042
|
const geometryForHoles = {
|
|
976
1043
|
x: cx,
|
|
977
1044
|
y: cy,
|
|
978
1045
|
width: visualWidth,
|
|
979
1046
|
height: visualHeight
|
|
1047
|
+
// Pass scale/unit context if needed by resolveHolePosition (though currently unused there)
|
|
980
1048
|
};
|
|
981
1049
|
const absoluteHoles = (holes || []).map((h) => {
|
|
982
|
-
const
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
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
|
+
});
|
|
987
1061
|
return {
|
|
988
1062
|
...h,
|
|
989
1063
|
x: pos.x,
|
|
990
|
-
y: pos.y
|
|
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
|
|
991
1071
|
};
|
|
992
1072
|
});
|
|
993
|
-
const cutW = Math.max(0,
|
|
994
|
-
const cutH = Math.max(0,
|
|
995
|
-
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);
|
|
996
1076
|
const maskPathData = generateMaskPath({
|
|
997
1077
|
canvasWidth: canvasW,
|
|
998
1078
|
canvasHeight: canvasH,
|
|
@@ -1042,15 +1122,15 @@ var DielineTool = class {
|
|
|
1042
1122
|
const bleedPathData = generateBleedZonePath(
|
|
1043
1123
|
{
|
|
1044
1124
|
shape,
|
|
1045
|
-
width,
|
|
1046
|
-
height,
|
|
1047
|
-
radius,
|
|
1125
|
+
width: visualWidth,
|
|
1126
|
+
height: visualHeight,
|
|
1127
|
+
radius: visualRadius,
|
|
1048
1128
|
x: cx,
|
|
1049
1129
|
y: cy,
|
|
1050
1130
|
holes: absoluteHoles,
|
|
1051
1131
|
pathData: this.pathData
|
|
1052
1132
|
},
|
|
1053
|
-
|
|
1133
|
+
visualOffset
|
|
1054
1134
|
);
|
|
1055
1135
|
if (showBleedLines !== false) {
|
|
1056
1136
|
const pattern = this.createHatchPattern("red");
|
|
@@ -1093,13 +1173,12 @@ var DielineTool = class {
|
|
|
1093
1173
|
}
|
|
1094
1174
|
const borderPathData = generateDielinePath({
|
|
1095
1175
|
shape,
|
|
1096
|
-
width,
|
|
1097
|
-
height,
|
|
1098
|
-
radius,
|
|
1176
|
+
width: visualWidth,
|
|
1177
|
+
height: visualHeight,
|
|
1178
|
+
radius: visualRadius,
|
|
1099
1179
|
x: cx,
|
|
1100
1180
|
y: cy,
|
|
1101
1181
|
holes: absoluteHoles,
|
|
1102
|
-
// FIX: Use absoluteHoles instead of holes
|
|
1103
1182
|
pathData: this.pathData
|
|
1104
1183
|
});
|
|
1105
1184
|
const borderObj = new Path(borderPathData, {
|
|
@@ -1137,115 +1216,107 @@ var DielineTool = class {
|
|
|
1137
1216
|
}
|
|
1138
1217
|
}
|
|
1139
1218
|
getGeometry() {
|
|
1140
|
-
var _a, _b;
|
|
1141
1219
|
if (!this.canvasService) return null;
|
|
1142
|
-
const { shape, width, height, radius, position,
|
|
1220
|
+
const { unit, shape, width, height, radius, position, offset } = this;
|
|
1143
1221
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
1144
1222
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
const
|
|
1152
|
-
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;
|
|
1153
1234
|
return {
|
|
1154
1235
|
shape,
|
|
1236
|
+
unit,
|
|
1155
1237
|
x: cx,
|
|
1156
1238
|
y: cy,
|
|
1157
1239
|
width: visualWidth,
|
|
1158
1240
|
height: visualHeight,
|
|
1159
|
-
radius,
|
|
1160
|
-
offset,
|
|
1161
|
-
|
|
1241
|
+
radius: radius * scale,
|
|
1242
|
+
offset: offset * scale,
|
|
1243
|
+
// Pass scale to help other tools (like HoleTool) convert units
|
|
1244
|
+
scale,
|
|
1162
1245
|
pathData: this.pathData
|
|
1163
1246
|
};
|
|
1164
1247
|
}
|
|
1165
|
-
exportCutImage() {
|
|
1166
|
-
var _a, _b, _c, _d;
|
|
1248
|
+
async exportCutImage() {
|
|
1167
1249
|
if (!this.canvasService) return null;
|
|
1168
|
-
const
|
|
1250
|
+
const userLayer = this.canvasService.getLayer("user");
|
|
1251
|
+
if (!userLayer) return null;
|
|
1169
1252
|
const { shape, width, height, radius, position, holes } = this;
|
|
1170
|
-
const canvasW = canvas.width || 800;
|
|
1171
|
-
const canvasH = canvas.height || 600;
|
|
1172
|
-
const
|
|
1173
|
-
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;
|
|
1174
1267
|
const absoluteHoles = (holes || []).map((h) => {
|
|
1268
|
+
const unit = this.unit || "mm";
|
|
1269
|
+
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1175
1270
|
const pos = resolveHolePosition(
|
|
1176
|
-
|
|
1177
|
-
|
|
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 },
|
|
1178
1277
|
{ width: canvasW, height: canvasH }
|
|
1179
1278
|
);
|
|
1180
1279
|
return {
|
|
1181
1280
|
...h,
|
|
1182
1281
|
x: pos.x,
|
|
1183
|
-
y: pos.y
|
|
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
|
|
1184
1287
|
};
|
|
1185
1288
|
});
|
|
1186
1289
|
const pathData = generateDielinePath({
|
|
1187
1290
|
shape,
|
|
1188
|
-
width,
|
|
1189
|
-
height,
|
|
1190
|
-
radius,
|
|
1291
|
+
width: visualWidth,
|
|
1292
|
+
height: visualHeight,
|
|
1293
|
+
radius: visualRadius,
|
|
1191
1294
|
x: cx,
|
|
1192
1295
|
y: cy,
|
|
1193
1296
|
holes: absoluteHoles,
|
|
1194
1297
|
pathData: this.pathData
|
|
1195
1298
|
});
|
|
1299
|
+
const clonedLayer = await userLayer.clone();
|
|
1196
1300
|
const clipPath = new Path(pathData, {
|
|
1197
|
-
left: 0,
|
|
1198
|
-
top: 0,
|
|
1199
1301
|
originX: "left",
|
|
1200
1302
|
originY: "top",
|
|
1201
|
-
absolutePositioned: true
|
|
1202
|
-
});
|
|
1203
|
-
const layer = this.getLayer();
|
|
1204
|
-
const wasVisible = (_c = layer == null ? void 0 : layer.visible) != null ? _c : true;
|
|
1205
|
-
if (layer) layer.visible = false;
|
|
1206
|
-
const holeMarkers = canvas.getObjects().filter((o) => {
|
|
1207
|
-
var _a2;
|
|
1208
|
-
return ((_a2 = o.data) == null ? void 0 : _a2.type) === "hole-marker";
|
|
1209
|
-
});
|
|
1210
|
-
holeMarkers.forEach((o) => o.visible = false);
|
|
1211
|
-
const rulerLayer = canvas.getObjects().find((obj) => {
|
|
1212
|
-
var _a2;
|
|
1213
|
-
return ((_a2 = obj.data) == null ? void 0 : _a2.id) === "ruler-overlay";
|
|
1214
|
-
});
|
|
1215
|
-
const rulerWasVisible = (_d = rulerLayer == null ? void 0 : rulerLayer.visible) != null ? _d : true;
|
|
1216
|
-
if (rulerLayer) rulerLayer.visible = false;
|
|
1217
|
-
const originalClip = canvas.clipPath;
|
|
1218
|
-
canvas.clipPath = clipPath;
|
|
1219
|
-
const bbox = clipPath.getBoundingRect();
|
|
1220
|
-
const clipPathCorrected = new Path(pathData, {
|
|
1221
|
-
absolutePositioned: true,
|
|
1222
1303
|
left: 0,
|
|
1223
|
-
top: 0
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
const tempBounds = tempPath.getBoundingRect();
|
|
1227
|
-
clipPathCorrected.set({
|
|
1228
|
-
left: tempBounds.left,
|
|
1229
|
-
top: tempBounds.top,
|
|
1230
|
-
originX: "left",
|
|
1231
|
-
originY: "top"
|
|
1304
|
+
top: 0,
|
|
1305
|
+
absolutePositioned: true
|
|
1306
|
+
// Important for groups
|
|
1232
1307
|
});
|
|
1233
|
-
|
|
1234
|
-
const
|
|
1235
|
-
const
|
|
1308
|
+
clonedLayer.clipPath = clipPath;
|
|
1309
|
+
const bounds = clipPath.getBoundingRect();
|
|
1310
|
+
const dataUrl = clonedLayer.toDataURL({
|
|
1236
1311
|
format: "png",
|
|
1237
1312
|
multiplier: 2,
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1313
|
+
// Better quality
|
|
1314
|
+
left: bounds.left,
|
|
1315
|
+
top: bounds.top,
|
|
1316
|
+
width: bounds.width,
|
|
1317
|
+
height: bounds.height
|
|
1242
1318
|
});
|
|
1243
|
-
|
|
1244
|
-
if (layer) layer.visible = wasVisible;
|
|
1245
|
-
if (rulerLayer) rulerLayer.visible = rulerWasVisible;
|
|
1246
|
-
holeMarkers.forEach((o) => o.visible = true);
|
|
1247
|
-
canvas.requestRenderAll();
|
|
1248
|
-
return dataURL;
|
|
1319
|
+
return dataUrl;
|
|
1249
1320
|
}
|
|
1250
1321
|
};
|
|
1251
1322
|
|
|
@@ -1522,11 +1593,19 @@ var HoleTool = class {
|
|
|
1522
1593
|
handler: (x, y) => {
|
|
1523
1594
|
var _a, _b, _c;
|
|
1524
1595
|
if (!this.canvasService) return false;
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
{
|
|
1529
|
-
|
|
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
|
+
}
|
|
1530
1609
|
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1531
1610
|
"ConfigurationService"
|
|
1532
1611
|
);
|
|
@@ -1536,8 +1615,8 @@ var HoleTool = class {
|
|
|
1536
1615
|
const innerRadius = (_b = lastHole == null ? void 0 : lastHole.innerRadius) != null ? _b : 15;
|
|
1537
1616
|
const outerRadius = (_c = lastHole == null ? void 0 : lastHole.outerRadius) != null ? _c : 25;
|
|
1538
1617
|
const newHole = {
|
|
1539
|
-
x:
|
|
1540
|
-
y:
|
|
1618
|
+
x: normalizedX,
|
|
1619
|
+
y: normalizedY,
|
|
1541
1620
|
innerRadius,
|
|
1542
1621
|
outerRadius
|
|
1543
1622
|
};
|
|
@@ -1632,7 +1711,10 @@ var HoleTool = class {
|
|
|
1632
1711
|
var _a;
|
|
1633
1712
|
const target = e.target;
|
|
1634
1713
|
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "hole-marker") return;
|
|
1635
|
-
this.
|
|
1714
|
+
const changed = this.enforceConstraints();
|
|
1715
|
+
if (!changed) {
|
|
1716
|
+
this.syncHolesFromCanvas();
|
|
1717
|
+
}
|
|
1636
1718
|
};
|
|
1637
1719
|
canvas.on("object:modified", this.handleModified);
|
|
1638
1720
|
}
|
|
@@ -1690,18 +1772,21 @@ var HoleTool = class {
|
|
|
1690
1772
|
}
|
|
1691
1773
|
);
|
|
1692
1774
|
const newHoles = objects.map((obj, i) => {
|
|
1693
|
-
var _a, _b;
|
|
1775
|
+
var _a, _b, _c, _d;
|
|
1694
1776
|
const original = this.holes[i];
|
|
1695
1777
|
const newAbsX = obj.left;
|
|
1696
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);
|
|
1697
1782
|
if (original && original.anchor && this.currentGeometry) {
|
|
1698
|
-
const { x, y, width
|
|
1783
|
+
const { x, y, width, height } = this.currentGeometry;
|
|
1699
1784
|
let bx = x;
|
|
1700
1785
|
let by = y;
|
|
1701
|
-
const left = x -
|
|
1702
|
-
const right = x +
|
|
1703
|
-
const top = y -
|
|
1704
|
-
const bottom = 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;
|
|
1705
1790
|
switch (original.anchor) {
|
|
1706
1791
|
case "top-left":
|
|
1707
1792
|
bx = left;
|
|
@@ -1742,25 +1827,34 @@ var HoleTool = class {
|
|
|
1742
1827
|
}
|
|
1743
1828
|
return {
|
|
1744
1829
|
...original,
|
|
1745
|
-
|
|
1746
|
-
|
|
1830
|
+
// Denormalize offset back to physical units (mm)
|
|
1831
|
+
offsetX: (newAbsX - bx) / scale / unitScale,
|
|
1832
|
+
offsetY: (newAbsY - by) / scale / unitScale,
|
|
1747
1833
|
// Clear direct coordinates if we use anchor
|
|
1748
1834
|
x: void 0,
|
|
1749
1835
|
y: void 0
|
|
1750
1836
|
};
|
|
1751
1837
|
}
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
{
|
|
1756
|
-
|
|
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
|
+
}
|
|
1757
1851
|
return {
|
|
1758
1852
|
...original,
|
|
1759
|
-
x:
|
|
1760
|
-
y:
|
|
1853
|
+
x: normalizedX,
|
|
1854
|
+
y: normalizedY,
|
|
1761
1855
|
// Ensure radii are preserved
|
|
1762
|
-
innerRadius: (
|
|
1763
|
-
outerRadius: (
|
|
1856
|
+
innerRadius: (_c = original == null ? void 0 : original.innerRadius) != null ? _c : 15,
|
|
1857
|
+
outerRadius: (_d = original == null ? void 0 : original.outerRadius) != null ? _d : 25
|
|
1764
1858
|
};
|
|
1765
1859
|
});
|
|
1766
1860
|
this.holes = newHoles;
|
|
@@ -1798,16 +1892,28 @@ var HoleTool = class {
|
|
|
1798
1892
|
x: (width || 800) / 2,
|
|
1799
1893
|
y: (height || 600) / 2,
|
|
1800
1894
|
width: width || 800,
|
|
1801
|
-
height: height || 600
|
|
1895
|
+
height: height || 600,
|
|
1896
|
+
scale: 1
|
|
1897
|
+
// Default scale if no geometry loaded
|
|
1802
1898
|
};
|
|
1803
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;
|
|
1804
1905
|
const pos = resolveHolePosition(
|
|
1805
|
-
|
|
1906
|
+
{
|
|
1907
|
+
...hole,
|
|
1908
|
+
offsetX: (hole.offsetX || 0) * unitScale * scale,
|
|
1909
|
+
offsetY: (hole.offsetY || 0) * unitScale * scale
|
|
1910
|
+
},
|
|
1806
1911
|
geometry,
|
|
1807
|
-
{ width: width
|
|
1912
|
+
{ width: geometry.width, height: geometry.height }
|
|
1913
|
+
// Use geometry dims instead of canvas
|
|
1808
1914
|
);
|
|
1809
1915
|
const innerCircle = new Circle({
|
|
1810
|
-
radius:
|
|
1916
|
+
radius: visualInnerRadius,
|
|
1811
1917
|
fill: "transparent",
|
|
1812
1918
|
stroke: "red",
|
|
1813
1919
|
strokeWidth: 2,
|
|
@@ -1815,7 +1921,7 @@ var HoleTool = class {
|
|
|
1815
1921
|
originY: "center"
|
|
1816
1922
|
});
|
|
1817
1923
|
const outerCircle = new Circle({
|
|
1818
|
-
radius:
|
|
1924
|
+
radius: visualOuterRadius,
|
|
1819
1925
|
fill: "transparent",
|
|
1820
1926
|
stroke: "#666",
|
|
1821
1927
|
strokeWidth: 1,
|
|
@@ -1895,11 +2001,16 @@ var HoleTool = class {
|
|
|
1895
2001
|
var _a, _b;
|
|
1896
2002
|
const currentPos = new Point(obj.left, obj.top);
|
|
1897
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;
|
|
1898
2009
|
const newPos = this.calculateConstrainedPosition(
|
|
1899
2010
|
currentPos,
|
|
1900
2011
|
constraintGeometry,
|
|
1901
|
-
|
|
1902
|
-
|
|
2012
|
+
innerR,
|
|
2013
|
+
outerR
|
|
1903
2014
|
);
|
|
1904
2015
|
if (currentPos.distanceFrom(newPos) > 0.1) {
|
|
1905
2016
|
obj.set({
|
|
@@ -1953,19 +2064,16 @@ var HoleTool = class {
|
|
|
1953
2064
|
import {
|
|
1954
2065
|
ContributionPointIds as ContributionPointIds5
|
|
1955
2066
|
} from "@pooder/core";
|
|
1956
|
-
import {
|
|
2067
|
+
import { Image as Image4, Point as Point2, util } from "fabric";
|
|
1957
2068
|
var ImageTool = class {
|
|
1958
|
-
constructor(
|
|
2069
|
+
constructor() {
|
|
1959
2070
|
this.id = "pooder.kit.image";
|
|
1960
2071
|
this.metadata = {
|
|
1961
2072
|
name: "ImageTool"
|
|
1962
2073
|
};
|
|
1963
|
-
this.
|
|
1964
|
-
this.
|
|
1965
|
-
this.
|
|
1966
|
-
if (options) {
|
|
1967
|
-
Object.assign(this, options);
|
|
1968
|
-
}
|
|
2074
|
+
this.items = [];
|
|
2075
|
+
this.objectMap = /* @__PURE__ */ new Map();
|
|
2076
|
+
this.isUpdatingConfig = false;
|
|
1969
2077
|
}
|
|
1970
2078
|
activate(context) {
|
|
1971
2079
|
this.context = context;
|
|
@@ -1976,38 +2084,33 @@ var ImageTool = class {
|
|
|
1976
2084
|
}
|
|
1977
2085
|
const configService = context.services.get("ConfigurationService");
|
|
1978
2086
|
if (configService) {
|
|
1979
|
-
this.
|
|
1980
|
-
this.opacity = configService.get("image.opacity", this.opacity);
|
|
1981
|
-
this.width = configService.get("image.width", this.width);
|
|
1982
|
-
this.height = configService.get("image.height", this.height);
|
|
1983
|
-
this.angle = configService.get("image.angle", this.angle);
|
|
1984
|
-
this.left = configService.get("image.left", this.left);
|
|
1985
|
-
this.top = configService.get("image.top", this.top);
|
|
2087
|
+
this.items = configService.get("image.items", []) || [];
|
|
1986
2088
|
configService.onAnyChange((e) => {
|
|
1987
|
-
if (
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
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();
|
|
1996
2099
|
}
|
|
1997
2100
|
});
|
|
1998
2101
|
}
|
|
1999
2102
|
this.ensureLayer();
|
|
2000
|
-
this.
|
|
2103
|
+
this.updateImages();
|
|
2001
2104
|
}
|
|
2002
2105
|
deactivate(context) {
|
|
2003
2106
|
if (this.canvasService) {
|
|
2004
2107
|
const layer = this.canvasService.getLayer("user");
|
|
2005
2108
|
if (layer) {
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2109
|
+
this.objectMap.forEach((obj) => {
|
|
2110
|
+
layer.remove(obj);
|
|
2111
|
+
});
|
|
2112
|
+
this.objectMap.clear();
|
|
2113
|
+
this.canvasService.requestRenderAll();
|
|
2011
2114
|
}
|
|
2012
2115
|
this.canvasService = void 0;
|
|
2013
2116
|
this.context = void 0;
|
|
@@ -2017,82 +2120,103 @@ var ImageTool = class {
|
|
|
2017
2120
|
return {
|
|
2018
2121
|
[ContributionPointIds5.CONFIGURATIONS]: [
|
|
2019
2122
|
{
|
|
2020
|
-
id: "image.
|
|
2021
|
-
type: "
|
|
2022
|
-
label: "
|
|
2023
|
-
default:
|
|
2024
|
-
}
|
|
2123
|
+
id: "image.items",
|
|
2124
|
+
type: "array",
|
|
2125
|
+
label: "Images",
|
|
2126
|
+
default: []
|
|
2127
|
+
}
|
|
2128
|
+
],
|
|
2129
|
+
[ContributionPointIds5.COMMANDS]: [
|
|
2025
2130
|
{
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
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
|
+
}
|
|
2033
2143
|
},
|
|
2034
2144
|
{
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
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
|
+
}
|
|
2041
2153
|
},
|
|
2042
2154
|
{
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
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
|
+
}
|
|
2049
2165
|
},
|
|
2050
2166
|
{
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
default: this.angle
|
|
2167
|
+
command: "clearImages",
|
|
2168
|
+
title: "Clear Images",
|
|
2169
|
+
handler: () => {
|
|
2170
|
+
this.updateConfig([]);
|
|
2171
|
+
}
|
|
2057
2172
|
},
|
|
2058
2173
|
{
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
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
|
+
}
|
|
2065
2185
|
},
|
|
2066
2186
|
{
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
command: "setUserImage",
|
|
2078
|
-
title: "Set User Image",
|
|
2079
|
-
handler: (url, opacity, width, height, angle, left, top) => {
|
|
2080
|
-
if (this.url === url && this.opacity === opacity && this.width === width && this.height === height && this.angle === angle && this.left === left && this.top === top)
|
|
2081
|
-
return true;
|
|
2082
|
-
this.url = url;
|
|
2083
|
-
this.opacity = opacity;
|
|
2084
|
-
this.width = width;
|
|
2085
|
-
this.height = height;
|
|
2086
|
-
this.angle = angle;
|
|
2087
|
-
this.left = left;
|
|
2088
|
-
this.top = top;
|
|
2089
|
-
this.updateImage();
|
|
2090
|
-
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
|
+
}
|
|
2091
2197
|
}
|
|
2092
2198
|
}
|
|
2093
2199
|
]
|
|
2094
2200
|
};
|
|
2095
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
|
+
}
|
|
2096
2220
|
ensureLayer() {
|
|
2097
2221
|
if (!this.canvasService) return;
|
|
2098
2222
|
let userLayer = this.canvasService.getLayer("user");
|
|
@@ -2124,224 +2248,176 @@ var ImageTool = class {
|
|
|
2124
2248
|
this.canvasService.requestRenderAll();
|
|
2125
2249
|
}
|
|
2126
2250
|
}
|
|
2127
|
-
|
|
2251
|
+
getLayoutInfo() {
|
|
2128
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() {
|
|
2129
2291
|
if (!this.canvasService) return;
|
|
2130
|
-
let { url, opacity, width, height, angle, left, top } = this;
|
|
2131
2292
|
const layer = this.canvasService.getLayer("user");
|
|
2132
2293
|
if (!layer) {
|
|
2133
2294
|
console.warn("[ImageTool] User layer not found");
|
|
2134
2295
|
return;
|
|
2135
2296
|
}
|
|
2136
|
-
const
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
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);
|
|
2142
2309
|
} else {
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
const centerX = canvasW / 2;
|
|
2147
|
-
const centerY = canvasH / 2;
|
|
2148
|
-
if (userImage.opacity !== opacity) updates.opacity = opacity;
|
|
2149
|
-
if (angle !== void 0 && userImage.angle !== angle)
|
|
2150
|
-
updates.angle = angle;
|
|
2151
|
-
if (userImage.originX !== "center") {
|
|
2152
|
-
userImage.set({
|
|
2153
|
-
originX: "center",
|
|
2154
|
-
originY: "center",
|
|
2155
|
-
left: userImage.left + userImage.width * userImage.scaleX / 2,
|
|
2156
|
-
top: userImage.top + userImage.height * userImage.scaleY / 2
|
|
2157
|
-
});
|
|
2158
|
-
}
|
|
2159
|
-
if (left !== void 0) {
|
|
2160
|
-
const globalLeft = Coordinate.toAbsolute(left, canvasW);
|
|
2161
|
-
const localLeft = globalLeft - centerX;
|
|
2162
|
-
if (Math.abs(userImage.left - localLeft) > 1)
|
|
2163
|
-
updates.left = localLeft;
|
|
2164
|
-
}
|
|
2165
|
-
if (top !== void 0) {
|
|
2166
|
-
const globalTop = Coordinate.toAbsolute(top, canvasH);
|
|
2167
|
-
const localTop = globalTop - centerY;
|
|
2168
|
-
if (Math.abs(userImage.top - localTop) > 1) updates.top = localTop;
|
|
2169
|
-
}
|
|
2170
|
-
if (width !== void 0 && userImage.width)
|
|
2171
|
-
updates.scaleX = width / userImage.width;
|
|
2172
|
-
if (height !== void 0 && userImage.height)
|
|
2173
|
-
updates.scaleY = height / userImage.height;
|
|
2174
|
-
if (Object.keys(updates).length > 0) {
|
|
2175
|
-
userImage.set(updates);
|
|
2176
|
-
layer.dirty = true;
|
|
2177
|
-
this.canvasService.requestRenderAll();
|
|
2178
|
-
}
|
|
2310
|
+
this.updateObjectProperties(obj, item, layout);
|
|
2311
|
+
layer.remove(obj);
|
|
2312
|
+
layer.add(obj);
|
|
2179
2313
|
}
|
|
2180
|
-
}
|
|
2181
|
-
|
|
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);
|
|
2182
2345
|
}
|
|
2183
2346
|
}
|
|
2184
|
-
loadImage(layer) {
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
let {
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
{
|
|
2203
|
-
widthVal: dielineWidth,
|
|
2204
|
-
heightVal: dielineHeight,
|
|
2205
|
-
// Debug: dump all keys to see what is available
|
|
2206
|
-
allKeys: Array.from(
|
|
2207
|
-
((_a = configService.configValues) == null ? void 0 : _a.keys()) || []
|
|
2208
|
-
)
|
|
2209
|
-
},
|
|
2210
|
-
configService
|
|
2211
|
-
);
|
|
2212
|
-
if (width === void 0 && height === void 0) {
|
|
2213
|
-
const scale = Math.min(
|
|
2214
|
-
dielineWidth / (image.width || 1),
|
|
2215
|
-
dielineHeight / (image.height || 1)
|
|
2216
|
-
);
|
|
2217
|
-
width = (image.width || 1) * scale;
|
|
2218
|
-
height = (image.height || 1) * scale;
|
|
2219
|
-
this.width = width;
|
|
2220
|
-
this.height = height;
|
|
2221
|
-
}
|
|
2222
|
-
if (left === void 0 && top === void 0) {
|
|
2223
|
-
const dielinePos = configService == null ? void 0 : configService.get("dieline.position");
|
|
2224
|
-
if (dielinePos) {
|
|
2225
|
-
this.left = dielinePos.x;
|
|
2226
|
-
this.top = dielinePos.y;
|
|
2227
|
-
} else {
|
|
2228
|
-
this.left = 0.5;
|
|
2229
|
-
this.top = 0.5;
|
|
2230
|
-
}
|
|
2231
|
-
left = this.left;
|
|
2232
|
-
top = this.top;
|
|
2233
|
-
}
|
|
2234
|
-
}
|
|
2235
|
-
const existingImage = this.canvasService.getObject(
|
|
2236
|
-
"user-image",
|
|
2237
|
-
"user"
|
|
2238
|
-
);
|
|
2239
|
-
if (existingImage) {
|
|
2240
|
-
const defaultLeft = existingImage.left;
|
|
2241
|
-
const defaultTop = existingImage.top;
|
|
2242
|
-
const defaultAngle = existingImage.angle;
|
|
2243
|
-
const defaultScaleX = existingImage.scaleX;
|
|
2244
|
-
const defaultScaleY = existingImage.scaleY;
|
|
2245
|
-
const canvasW = ((_b = this.canvasService) == null ? void 0 : _b.canvas.width) || 800;
|
|
2246
|
-
const canvasH = ((_c = this.canvasService) == null ? void 0 : _c.canvas.height) || 600;
|
|
2247
|
-
const centerX = canvasW / 2;
|
|
2248
|
-
const centerY = canvasH / 2;
|
|
2249
|
-
let targetLeft = left !== void 0 ? left : defaultLeft;
|
|
2250
|
-
let targetTop = top !== void 0 ? top : defaultTop;
|
|
2251
|
-
const configService = (_d = this.context) == null ? void 0 : _d.services.get(
|
|
2252
|
-
"ConfigurationService"
|
|
2253
|
-
);
|
|
2254
|
-
console.log("[ImageTool] Loading EXISTING image...", {
|
|
2255
|
-
canvasW,
|
|
2256
|
-
canvasH,
|
|
2257
|
-
centerX,
|
|
2258
|
-
centerY,
|
|
2259
|
-
incomingLeft: left,
|
|
2260
|
-
incomingTop: top,
|
|
2261
|
-
dielinePos: configService == null ? void 0 : configService.get("dieline.position"),
|
|
2262
|
-
existingImage: !!existingImage
|
|
2263
|
-
});
|
|
2264
|
-
if (left !== void 0) {
|
|
2265
|
-
const globalLeft = Coordinate.toAbsolute(left, canvasW);
|
|
2266
|
-
targetLeft = globalLeft;
|
|
2267
|
-
console.log("[ImageTool] Calculated targetLeft", {
|
|
2268
|
-
globalLeft,
|
|
2269
|
-
targetLeft
|
|
2270
|
-
});
|
|
2271
|
-
}
|
|
2272
|
-
if (top !== void 0) {
|
|
2273
|
-
const globalTop = Coordinate.toAbsolute(top, canvasH);
|
|
2274
|
-
targetTop = globalTop;
|
|
2275
|
-
console.log("[ImageTool] Calculated targetTop", {
|
|
2276
|
-
globalTop,
|
|
2277
|
-
targetTop
|
|
2278
|
-
});
|
|
2279
|
-
}
|
|
2280
|
-
image.set({
|
|
2281
|
-
originX: "center",
|
|
2282
|
-
// Use center origin for easier positioning
|
|
2283
|
-
originY: "center",
|
|
2284
|
-
left: targetLeft,
|
|
2285
|
-
top: targetTop,
|
|
2286
|
-
angle: angle !== void 0 ? angle : defaultAngle,
|
|
2287
|
-
scaleX: width !== void 0 && image.width ? width / image.width : defaultScaleX,
|
|
2288
|
-
scaleY: height !== void 0 && image.height ? height / image.height : defaultScaleY
|
|
2289
|
-
});
|
|
2290
|
-
layer.remove(existingImage);
|
|
2291
|
-
} else {
|
|
2292
|
-
image.set({
|
|
2293
|
-
originX: "center",
|
|
2294
|
-
originY: "center"
|
|
2295
|
-
});
|
|
2296
|
-
if (width !== void 0 && image.width)
|
|
2297
|
-
image.scaleX = width / image.width;
|
|
2298
|
-
if (height !== void 0 && image.height)
|
|
2299
|
-
image.scaleY = height / image.height;
|
|
2300
|
-
if (angle !== void 0) image.angle = angle;
|
|
2301
|
-
const canvasW = ((_e = this.canvasService) == null ? void 0 : _e.canvas.width) || 800;
|
|
2302
|
-
const canvasH = ((_f = this.canvasService) == null ? void 0 : _f.canvas.height) || 600;
|
|
2303
|
-
const centerX = canvasW / 2;
|
|
2304
|
-
const centerY = canvasH / 2;
|
|
2305
|
-
if (left !== void 0) {
|
|
2306
|
-
image.left = Coordinate.toAbsolute(left, canvasW);
|
|
2307
|
-
} else {
|
|
2308
|
-
image.left = centerX;
|
|
2309
|
-
}
|
|
2310
|
-
if (top !== void 0) {
|
|
2311
|
-
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;
|
|
2312
2365
|
} else {
|
|
2313
|
-
|
|
2366
|
+
const h = dielinePhysicalHeight;
|
|
2367
|
+
height = h;
|
|
2368
|
+
width = h * imgAspect;
|
|
2314
2369
|
}
|
|
2370
|
+
item.width = width;
|
|
2371
|
+
item.height = height;
|
|
2315
2372
|
}
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
}
|
|
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);
|
|
2322
2380
|
layer.add(image);
|
|
2381
|
+
this.objectMap.set(item.id, image);
|
|
2323
2382
|
image.on("modified", (e) => {
|
|
2324
|
-
|
|
2325
|
-
const matrix = image.calcTransformMatrix();
|
|
2326
|
-
const globalPoint = util.transformPoint(new Point2(0, 0), matrix);
|
|
2327
|
-
const canvasW = ((_a2 = this.canvasService) == null ? void 0 : _a2.canvas.width) || 800;
|
|
2328
|
-
const canvasH = ((_b2 = this.canvasService) == null ? void 0 : _b2.canvas.height) || 600;
|
|
2329
|
-
this.left = Coordinate.toNormalized(globalPoint.x, canvasW);
|
|
2330
|
-
this.top = Coordinate.toNormalized(globalPoint.y, canvasH);
|
|
2331
|
-
this.angle = e.target.angle;
|
|
2332
|
-
if (image.width) this.width = e.target.width * e.target.scaleX;
|
|
2333
|
-
if (image.height) this.height = e.target.height * e.target.scaleY;
|
|
2334
|
-
if (this.context) {
|
|
2335
|
-
this.context.eventBus.emit("update");
|
|
2336
|
-
}
|
|
2383
|
+
this.handleObjectModified(item.id, image);
|
|
2337
2384
|
});
|
|
2338
2385
|
layer.dirty = true;
|
|
2339
|
-
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
|
+
}
|
|
2340
2390
|
}).catch((err) => {
|
|
2341
|
-
|
|
2342
|
-
console.error("Failed to load image", url, err);
|
|
2391
|
+
console.error("Failed to load image", item.url, err);
|
|
2343
2392
|
});
|
|
2344
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
|
+
}
|
|
2345
2421
|
};
|
|
2346
2422
|
|
|
2347
2423
|
// src/white-ink.ts
|
|
@@ -2641,19 +2717,25 @@ var WhiteInkTool = class {
|
|
|
2641
2717
|
import {
|
|
2642
2718
|
ContributionPointIds as ContributionPointIds7
|
|
2643
2719
|
} from "@pooder/core";
|
|
2644
|
-
import {
|
|
2720
|
+
import { Line, Text, Group as Group2, Polygon } from "fabric";
|
|
2645
2721
|
var RulerTool = class {
|
|
2646
2722
|
constructor(options) {
|
|
2647
2723
|
this.id = "pooder.kit.ruler";
|
|
2648
2724
|
this.metadata = {
|
|
2649
2725
|
name: "RulerTool"
|
|
2650
2726
|
};
|
|
2651
|
-
this.unit = "px";
|
|
2652
2727
|
this.thickness = 20;
|
|
2728
|
+
this.gap = 15;
|
|
2653
2729
|
this.backgroundColor = "#f0f0f0";
|
|
2654
2730
|
this.textColor = "#333333";
|
|
2655
2731
|
this.lineColor = "#999999";
|
|
2656
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;
|
|
2657
2739
|
if (options) {
|
|
2658
2740
|
Object.assign(this, options);
|
|
2659
2741
|
}
|
|
@@ -2666,8 +2748,8 @@ var RulerTool = class {
|
|
|
2666
2748
|
}
|
|
2667
2749
|
const configService = context.services.get("ConfigurationService");
|
|
2668
2750
|
if (configService) {
|
|
2669
|
-
this.unit = configService.get("ruler.unit", this.unit);
|
|
2670
2751
|
this.thickness = configService.get("ruler.thickness", this.thickness);
|
|
2752
|
+
this.gap = configService.get("ruler.gap", this.gap);
|
|
2671
2753
|
this.backgroundColor = configService.get(
|
|
2672
2754
|
"ruler.backgroundColor",
|
|
2673
2755
|
this.backgroundColor
|
|
@@ -2675,13 +2757,38 @@ var RulerTool = class {
|
|
|
2675
2757
|
this.textColor = configService.get("ruler.textColor", this.textColor);
|
|
2676
2758
|
this.lineColor = configService.get("ruler.lineColor", this.lineColor);
|
|
2677
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
|
+
);
|
|
2678
2774
|
configService.onAnyChange((e) => {
|
|
2775
|
+
let shouldUpdate = false;
|
|
2679
2776
|
if (e.key.startsWith("ruler.")) {
|
|
2680
2777
|
const prop = e.key.split(".")[1];
|
|
2681
2778
|
if (prop && prop in this) {
|
|
2682
2779
|
this[prop] = e.value;
|
|
2683
|
-
|
|
2780
|
+
shouldUpdate = true;
|
|
2684
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();
|
|
2685
2792
|
}
|
|
2686
2793
|
});
|
|
2687
2794
|
}
|
|
@@ -2695,13 +2802,6 @@ var RulerTool = class {
|
|
|
2695
2802
|
contribute() {
|
|
2696
2803
|
return {
|
|
2697
2804
|
[ContributionPointIds7.CONFIGURATIONS]: [
|
|
2698
|
-
{
|
|
2699
|
-
id: "ruler.unit",
|
|
2700
|
-
type: "select",
|
|
2701
|
-
label: "Unit",
|
|
2702
|
-
options: ["px", "mm", "cm", "in"],
|
|
2703
|
-
default: "px"
|
|
2704
|
-
},
|
|
2705
2805
|
{
|
|
2706
2806
|
id: "ruler.thickness",
|
|
2707
2807
|
type: "number",
|
|
@@ -2710,6 +2810,14 @@ var RulerTool = class {
|
|
|
2710
2810
|
max: 100,
|
|
2711
2811
|
default: 20
|
|
2712
2812
|
},
|
|
2813
|
+
{
|
|
2814
|
+
id: "ruler.gap",
|
|
2815
|
+
type: "number",
|
|
2816
|
+
label: "Gap",
|
|
2817
|
+
min: 0,
|
|
2818
|
+
max: 100,
|
|
2819
|
+
default: 15
|
|
2820
|
+
},
|
|
2713
2821
|
{
|
|
2714
2822
|
id: "ruler.backgroundColor",
|
|
2715
2823
|
type: "color",
|
|
@@ -2738,16 +2846,6 @@ var RulerTool = class {
|
|
|
2738
2846
|
}
|
|
2739
2847
|
],
|
|
2740
2848
|
[ContributionPointIds7.COMMANDS]: [
|
|
2741
|
-
{
|
|
2742
|
-
command: "setUnit",
|
|
2743
|
-
title: "Set Ruler Unit",
|
|
2744
|
-
handler: (unit) => {
|
|
2745
|
-
if (this.unit === unit) return true;
|
|
2746
|
-
this.unit = unit;
|
|
2747
|
-
this.updateRuler();
|
|
2748
|
-
return true;
|
|
2749
|
-
}
|
|
2750
|
-
},
|
|
2751
2849
|
{
|
|
2752
2850
|
command: "setTheme",
|
|
2753
2851
|
title: "Set Ruler Theme",
|
|
@@ -2798,6 +2896,68 @@ var RulerTool = class {
|
|
|
2798
2896
|
this.canvasService.canvas.remove(layer);
|
|
2799
2897
|
}
|
|
2800
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
|
+
}
|
|
2801
2961
|
updateRuler() {
|
|
2802
2962
|
if (!this.canvasService) return;
|
|
2803
2963
|
const layer = this.getLayer();
|
|
@@ -2806,95 +2966,141 @@ var RulerTool = class {
|
|
|
2806
2966
|
const { thickness, backgroundColor, lineColor, textColor, fontSize } = this;
|
|
2807
2967
|
const width = this.canvasService.canvas.width || 800;
|
|
2808
2968
|
const height = this.canvasService.canvas.height || 600;
|
|
2809
|
-
const
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
width,
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
const
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
const
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
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
|
|
2835
3044
|
selectable: false,
|
|
2836
3045
|
evented: false
|
|
2837
3046
|
});
|
|
2838
|
-
layer.add(
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
left: x + 2,
|
|
2859
|
-
top: 2,
|
|
2860
|
-
fontSize,
|
|
2861
|
-
fill: textColor,
|
|
2862
|
-
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,
|
|
2863
3067
|
selectable: false,
|
|
2864
3068
|
evented: false
|
|
2865
|
-
}
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
});
|
|
2880
|
-
layer.add(line);
|
|
2881
|
-
if (y % step === 0) {
|
|
2882
|
-
const text = new Text(y.toString(), {
|
|
2883
|
-
angle: -90,
|
|
2884
|
-
left: thickness / 2 - fontSize / 3,
|
|
2885
|
-
// approximate centering
|
|
2886
|
-
top: y + fontSize,
|
|
2887
|
-
fontSize,
|
|
2888
|
-
fill: textColor,
|
|
2889
|
-
fontFamily: "Arial",
|
|
2890
|
-
originX: "center",
|
|
2891
|
-
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,
|
|
2892
3083
|
selectable: false,
|
|
2893
3084
|
evented: false
|
|
2894
|
-
}
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
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);
|
|
2898
3104
|
this.canvasService.canvas.bringObjectToFront(layer);
|
|
2899
3105
|
this.canvasService.canvas.requestRenderAll();
|
|
2900
3106
|
}
|
|
@@ -2993,7 +3199,7 @@ var MirrorTool = class {
|
|
|
2993
3199
|
};
|
|
2994
3200
|
|
|
2995
3201
|
// src/CanvasService.ts
|
|
2996
|
-
import { Canvas, Group as
|
|
3202
|
+
import { Canvas, Group as Group3 } from "fabric";
|
|
2997
3203
|
var CanvasService = class {
|
|
2998
3204
|
constructor(el, options) {
|
|
2999
3205
|
if (el instanceof Canvas) {
|
|
@@ -3030,7 +3236,7 @@ var CanvasService = class {
|
|
|
3030
3236
|
...options,
|
|
3031
3237
|
data: { ...options.data, id }
|
|
3032
3238
|
};
|
|
3033
|
-
layer = new
|
|
3239
|
+
layer = new Group3([], defaultOptions);
|
|
3034
3240
|
this.canvas.add(layer);
|
|
3035
3241
|
}
|
|
3036
3242
|
return layer;
|