@pooder/kit 2.0.0 → 3.0.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 +246 -134
- package/dist/index.d.ts +246 -134
- package/dist/index.js +2051 -1045
- package/dist/index.mjs +2042 -1050
- package/package.json +3 -2
- package/src/CanvasService.ts +65 -0
- package/src/background.ts +156 -109
- package/src/coordinate.ts +49 -0
- package/src/dieline.ts +536 -336
- package/src/film.ts +120 -89
- package/src/geometry.ts +251 -38
- package/src/hole.ts +422 -286
- package/src/image.ts +374 -174
- package/src/index.ts +1 -0
- package/src/mirror.ts +86 -49
- package/src/ruler.ts +188 -118
- package/src/tracer.ts +372 -0
- package/src/white-ink.ts +186 -142
package/dist/index.mjs
CHANGED
|
@@ -1,115 +1,147 @@
|
|
|
1
1
|
// src/background.ts
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
PooderLayer,
|
|
5
|
-
Rect
|
|
3
|
+
ContributionPointIds
|
|
6
4
|
} from "@pooder/core";
|
|
5
|
+
import { Rect, FabricImage as Image2 } from "fabric";
|
|
7
6
|
var BackgroundTool = class {
|
|
8
|
-
constructor() {
|
|
9
|
-
this.
|
|
10
|
-
this.
|
|
11
|
-
|
|
12
|
-
url: ""
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.id = "pooder.kit.background";
|
|
9
|
+
this.metadata = {
|
|
10
|
+
name: "BackgroundTool"
|
|
13
11
|
};
|
|
14
|
-
this.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
12
|
+
this.color = "";
|
|
13
|
+
this.url = "";
|
|
14
|
+
if (options) {
|
|
15
|
+
Object.assign(this, options);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
activate(context) {
|
|
19
|
+
this.canvasService = context.services.get("CanvasService");
|
|
20
|
+
if (!this.canvasService) {
|
|
21
|
+
console.warn("CanvasService not found for BackgroundTool");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const configService = context.services.get("ConfigurationService");
|
|
25
|
+
if (configService) {
|
|
26
|
+
this.color = configService.get("background.color", this.color);
|
|
27
|
+
this.url = configService.get("background.url", this.url);
|
|
28
|
+
configService.onAnyChange((e) => {
|
|
29
|
+
if (e.key.startsWith("background.")) {
|
|
30
|
+
const prop = e.key.split(".")[1];
|
|
31
|
+
console.log(
|
|
32
|
+
`[BackgroundTool] Config change detected: ${e.key} -> ${e.value}, prop: ${prop}`
|
|
33
|
+
);
|
|
34
|
+
if (prop && prop in this) {
|
|
35
|
+
console.log(
|
|
36
|
+
`[BackgroundTool] Updating option ${prop} to ${e.value}`
|
|
37
|
+
);
|
|
38
|
+
this[prop] = e.value;
|
|
39
|
+
this.updateBackground();
|
|
40
|
+
} else {
|
|
41
|
+
console.warn(
|
|
42
|
+
`[BackgroundTool] Property ${prop} not found in options`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
29
45
|
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
this.initLayer();
|
|
49
|
+
this.updateBackground();
|
|
50
|
+
}
|
|
51
|
+
deactivate(context) {
|
|
52
|
+
if (this.canvasService) {
|
|
53
|
+
const layer = this.canvasService.getLayer("background");
|
|
54
|
+
if (layer) {
|
|
55
|
+
this.canvasService.canvas.remove(layer);
|
|
56
|
+
}
|
|
57
|
+
this.canvasService = void 0;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
contribute() {
|
|
61
|
+
return {
|
|
62
|
+
[ContributionPointIds.CONFIGURATIONS]: [
|
|
63
|
+
{
|
|
64
|
+
id: "background.color",
|
|
65
|
+
type: "color",
|
|
66
|
+
label: "Background Color",
|
|
67
|
+
default: ""
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "background.url",
|
|
71
|
+
type: "string",
|
|
72
|
+
label: "Image URL",
|
|
73
|
+
default: ""
|
|
39
74
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
75
|
+
],
|
|
76
|
+
[ContributionPointIds.COMMANDS]: [
|
|
77
|
+
{
|
|
78
|
+
command: "reset",
|
|
79
|
+
title: "Reset Background",
|
|
80
|
+
handler: () => {
|
|
81
|
+
this.updateBackground();
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
command: "clear",
|
|
87
|
+
title: "Clear Background",
|
|
88
|
+
handler: () => {
|
|
89
|
+
this.color = "transparent";
|
|
90
|
+
this.url = "";
|
|
91
|
+
this.updateBackground();
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
47
94
|
},
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
95
|
+
{
|
|
96
|
+
command: "setBackgroundColor",
|
|
97
|
+
title: "Set Background Color",
|
|
98
|
+
handler: (color) => {
|
|
99
|
+
if (this.color === color) return true;
|
|
100
|
+
this.color = color;
|
|
101
|
+
this.updateBackground();
|
|
102
|
+
return true;
|
|
54
103
|
}
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
setBackgroundImage: {
|
|
58
|
-
execute: (editor, url) => {
|
|
59
|
-
if (this.options.url === url) return true;
|
|
60
|
-
this.options.url = url;
|
|
61
|
-
this.updateBackground(editor, this.options);
|
|
62
|
-
return true;
|
|
63
104
|
},
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
105
|
+
{
|
|
106
|
+
command: "setBackgroundImage",
|
|
107
|
+
title: "Set Background Image",
|
|
108
|
+
handler: (url) => {
|
|
109
|
+
if (this.url === url) return true;
|
|
110
|
+
this.url = url;
|
|
111
|
+
this.updateBackground();
|
|
112
|
+
return true;
|
|
69
113
|
}
|
|
70
114
|
}
|
|
71
|
-
|
|
115
|
+
]
|
|
72
116
|
};
|
|
73
117
|
}
|
|
74
|
-
initLayer(
|
|
75
|
-
|
|
118
|
+
initLayer() {
|
|
119
|
+
if (!this.canvasService) return;
|
|
120
|
+
let backgroundLayer = this.canvasService.getLayer("background");
|
|
76
121
|
if (!backgroundLayer) {
|
|
77
|
-
backgroundLayer =
|
|
78
|
-
width:
|
|
79
|
-
height:
|
|
122
|
+
backgroundLayer = this.canvasService.createLayer("background", {
|
|
123
|
+
width: this.canvasService.canvas.width,
|
|
124
|
+
height: this.canvasService.canvas.height,
|
|
80
125
|
selectable: false,
|
|
81
|
-
evented: false
|
|
82
|
-
data: {
|
|
83
|
-
id: "background"
|
|
84
|
-
}
|
|
126
|
+
evented: false
|
|
85
127
|
});
|
|
86
|
-
|
|
87
|
-
editor.canvas.sendObjectToBack(backgroundLayer);
|
|
128
|
+
this.canvasService.canvas.sendObjectToBack(backgroundLayer);
|
|
88
129
|
}
|
|
89
130
|
}
|
|
90
|
-
|
|
91
|
-
this.
|
|
92
|
-
this.
|
|
93
|
-
}
|
|
94
|
-
onUnmount(editor) {
|
|
95
|
-
const layer = editor.getLayer("background");
|
|
96
|
-
if (layer) {
|
|
97
|
-
editor.canvas.remove(layer);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
onUpdate(editor, state) {
|
|
101
|
-
this.updateBackground(editor, this.options);
|
|
102
|
-
}
|
|
103
|
-
async updateBackground(editor, options) {
|
|
104
|
-
const layer = editor.getLayer("background");
|
|
131
|
+
async updateBackground() {
|
|
132
|
+
if (!this.canvasService) return;
|
|
133
|
+
const layer = this.canvasService.getLayer("background");
|
|
105
134
|
if (!layer) {
|
|
106
135
|
console.warn("[BackgroundTool] Background layer not found");
|
|
107
136
|
return;
|
|
108
137
|
}
|
|
109
|
-
const { color, url } =
|
|
110
|
-
const width =
|
|
111
|
-
const height =
|
|
112
|
-
let rect =
|
|
138
|
+
const { color, url } = this;
|
|
139
|
+
const width = this.canvasService.canvas.width || 800;
|
|
140
|
+
const height = this.canvasService.canvas.height || 600;
|
|
141
|
+
let rect = this.canvasService.getObject(
|
|
142
|
+
"background-color-rect",
|
|
143
|
+
"background"
|
|
144
|
+
);
|
|
113
145
|
if (rect) {
|
|
114
146
|
rect.set({
|
|
115
147
|
fill: color
|
|
@@ -128,7 +160,10 @@ var BackgroundTool = class {
|
|
|
128
160
|
layer.add(rect);
|
|
129
161
|
layer.sendObjectToBack(rect);
|
|
130
162
|
}
|
|
131
|
-
let img =
|
|
163
|
+
let img = this.canvasService.getObject(
|
|
164
|
+
"background-image",
|
|
165
|
+
"background"
|
|
166
|
+
);
|
|
132
167
|
try {
|
|
133
168
|
if (img) {
|
|
134
169
|
if (img.getSrc() !== url) {
|
|
@@ -140,7 +175,7 @@ var BackgroundTool = class {
|
|
|
140
175
|
}
|
|
141
176
|
} else {
|
|
142
177
|
if (url) {
|
|
143
|
-
img = await
|
|
178
|
+
img = await Image2.fromURL(url, { crossOrigin: "anonymous" });
|
|
144
179
|
img.set({
|
|
145
180
|
originX: "left",
|
|
146
181
|
originY: "top",
|
|
@@ -157,29 +192,255 @@ var BackgroundTool = class {
|
|
|
157
192
|
layer.add(img);
|
|
158
193
|
}
|
|
159
194
|
}
|
|
160
|
-
|
|
195
|
+
this.canvasService.requestRenderAll();
|
|
161
196
|
} catch (e) {
|
|
162
197
|
console.error("[BackgroundTool] Failed to load image", e);
|
|
163
198
|
}
|
|
199
|
+
layer.dirty = true;
|
|
200
|
+
this.canvasService.requestRenderAll();
|
|
164
201
|
}
|
|
165
202
|
};
|
|
166
203
|
|
|
167
204
|
// src/dieline.ts
|
|
168
205
|
import {
|
|
169
|
-
|
|
170
|
-
PooderLayer as PooderLayer2,
|
|
171
|
-
Pattern
|
|
206
|
+
ContributionPointIds as ContributionPointIds2
|
|
172
207
|
} from "@pooder/core";
|
|
208
|
+
import { Path, Pattern } from "fabric";
|
|
209
|
+
|
|
210
|
+
// src/tracer.ts
|
|
211
|
+
var ImageTracer = class {
|
|
212
|
+
/**
|
|
213
|
+
* Main entry point: Traces an image URL to an SVG path string.
|
|
214
|
+
* @param imageUrl The URL or Base64 string of the image.
|
|
215
|
+
* @param options Configuration options.
|
|
216
|
+
*/
|
|
217
|
+
static async trace(imageUrl, options = {}) {
|
|
218
|
+
var _a, _b;
|
|
219
|
+
const img = await this.loadImage(imageUrl);
|
|
220
|
+
const width = img.width;
|
|
221
|
+
const height = img.height;
|
|
222
|
+
const canvas = document.createElement("canvas");
|
|
223
|
+
canvas.width = width;
|
|
224
|
+
canvas.height = height;
|
|
225
|
+
const ctx = canvas.getContext("2d");
|
|
226
|
+
if (!ctx) throw new Error("Could not get 2D context");
|
|
227
|
+
ctx.drawImage(img, 0, 0);
|
|
228
|
+
const imageData = ctx.getImageData(0, 0, width, height);
|
|
229
|
+
const points = this.marchingSquares(imageData, (_a = options.threshold) != null ? _a : 10);
|
|
230
|
+
let finalPoints = points;
|
|
231
|
+
if (options.scaleToWidth && options.scaleToHeight && points.length > 0) {
|
|
232
|
+
finalPoints = this.scalePoints(
|
|
233
|
+
points,
|
|
234
|
+
options.scaleToWidth,
|
|
235
|
+
options.scaleToHeight
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
const simplifiedPoints = this.douglasPeucker(
|
|
239
|
+
finalPoints,
|
|
240
|
+
(_b = options.simplifyTolerance) != null ? _b : 0.5
|
|
241
|
+
);
|
|
242
|
+
return this.pointsToSVG(simplifiedPoints);
|
|
243
|
+
}
|
|
244
|
+
static loadImage(url) {
|
|
245
|
+
return new Promise((resolve, reject) => {
|
|
246
|
+
const img = new Image();
|
|
247
|
+
img.crossOrigin = "Anonymous";
|
|
248
|
+
img.onload = () => resolve(img);
|
|
249
|
+
img.onerror = (e) => reject(e);
|
|
250
|
+
img.src = url;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Moore-Neighbor Tracing Algorithm
|
|
255
|
+
* More robust for irregular shapes than simple Marching Squares walker.
|
|
256
|
+
*/
|
|
257
|
+
static marchingSquares(imageData, alphaThreshold) {
|
|
258
|
+
const width = imageData.width;
|
|
259
|
+
const height = imageData.height;
|
|
260
|
+
const data = imageData.data;
|
|
261
|
+
const isSolid = (x, y) => {
|
|
262
|
+
if (x < 0 || x >= width || y < 0 || y >= height) return false;
|
|
263
|
+
const index = (y * width + x) * 4;
|
|
264
|
+
const r = data[index];
|
|
265
|
+
const g = data[index + 1];
|
|
266
|
+
const b = data[index + 2];
|
|
267
|
+
const a = data[index + 3];
|
|
268
|
+
if (a <= alphaThreshold) return false;
|
|
269
|
+
if (r > 240 && g > 240 && b > 240) return false;
|
|
270
|
+
return true;
|
|
271
|
+
};
|
|
272
|
+
let startX = -1;
|
|
273
|
+
let startY = -1;
|
|
274
|
+
searchLoop: for (let y = 0; y < height; y++) {
|
|
275
|
+
for (let x = 0; x < width; x++) {
|
|
276
|
+
if (isSolid(x, y)) {
|
|
277
|
+
startX = x;
|
|
278
|
+
startY = y;
|
|
279
|
+
break searchLoop;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (startX === -1) return [];
|
|
284
|
+
const points = [];
|
|
285
|
+
let cx = startX;
|
|
286
|
+
let cy = startY;
|
|
287
|
+
const neighbors = [
|
|
288
|
+
{ x: 0, y: -1 },
|
|
289
|
+
{ x: 1, y: -1 },
|
|
290
|
+
{ x: 1, y: 0 },
|
|
291
|
+
{ x: 1, y: 1 },
|
|
292
|
+
{ x: 0, y: 1 },
|
|
293
|
+
{ x: -1, y: 1 },
|
|
294
|
+
{ x: -1, y: 0 },
|
|
295
|
+
{ x: -1, y: -1 }
|
|
296
|
+
];
|
|
297
|
+
let backtrack = 6;
|
|
298
|
+
const maxSteps = width * height * 3;
|
|
299
|
+
let steps = 0;
|
|
300
|
+
do {
|
|
301
|
+
points.push({ x: cx, y: cy });
|
|
302
|
+
let found = false;
|
|
303
|
+
for (let i = 0; i < 8; i++) {
|
|
304
|
+
const idx = (backtrack + 1 + i) % 8;
|
|
305
|
+
const nx = cx + neighbors[idx].x;
|
|
306
|
+
const ny = cy + neighbors[idx].y;
|
|
307
|
+
if (isSolid(nx, ny)) {
|
|
308
|
+
cx = nx;
|
|
309
|
+
cy = ny;
|
|
310
|
+
backtrack = (idx + 4) % 8;
|
|
311
|
+
backtrack = (idx + 4 + 1) % 8;
|
|
312
|
+
backtrack = (idx + 4 + 1) % 8;
|
|
313
|
+
found = true;
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (!found) {
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
steps++;
|
|
321
|
+
} while ((cx !== startX || cy !== startY) && steps < maxSteps);
|
|
322
|
+
return points;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Douglas-Peucker Line Simplification
|
|
326
|
+
*/
|
|
327
|
+
static douglasPeucker(points, tolerance) {
|
|
328
|
+
if (points.length <= 2) return points;
|
|
329
|
+
const sqTolerance = tolerance * tolerance;
|
|
330
|
+
let maxSqDist = 0;
|
|
331
|
+
let index = 0;
|
|
332
|
+
const first = points[0];
|
|
333
|
+
const last = points[points.length - 1];
|
|
334
|
+
for (let i = 1; i < points.length - 1; i++) {
|
|
335
|
+
const sqDist = this.getSqSegDist(points[i], first, last);
|
|
336
|
+
if (sqDist > maxSqDist) {
|
|
337
|
+
index = i;
|
|
338
|
+
maxSqDist = sqDist;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (maxSqDist > sqTolerance) {
|
|
342
|
+
const left = this.douglasPeucker(points.slice(0, index + 1), tolerance);
|
|
343
|
+
const right = this.douglasPeucker(points.slice(index), tolerance);
|
|
344
|
+
return left.slice(0, left.length - 1).concat(right);
|
|
345
|
+
} else {
|
|
346
|
+
return [first, last];
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
static getSqSegDist(p, p1, p2) {
|
|
350
|
+
let x = p1.x;
|
|
351
|
+
let y = p1.y;
|
|
352
|
+
let dx = p2.x - x;
|
|
353
|
+
let dy = p2.y - y;
|
|
354
|
+
if (dx !== 0 || dy !== 0) {
|
|
355
|
+
const t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy);
|
|
356
|
+
if (t > 1) {
|
|
357
|
+
x = p2.x;
|
|
358
|
+
y = p2.y;
|
|
359
|
+
} else if (t > 0) {
|
|
360
|
+
x += dx * t;
|
|
361
|
+
y += dy * t;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
dx = p.x - x;
|
|
365
|
+
dy = p.y - y;
|
|
366
|
+
return dx * dx + dy * dy;
|
|
367
|
+
}
|
|
368
|
+
static scalePoints(points, targetWidth, targetHeight) {
|
|
369
|
+
if (points.length === 0) return points;
|
|
370
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
371
|
+
for (const p of points) {
|
|
372
|
+
if (p.x < minX) minX = p.x;
|
|
373
|
+
if (p.y < minY) minY = p.y;
|
|
374
|
+
if (p.x > maxX) maxX = p.x;
|
|
375
|
+
if (p.y > maxY) maxY = p.y;
|
|
376
|
+
}
|
|
377
|
+
const srcW = maxX - minX;
|
|
378
|
+
const srcH = maxY - minY;
|
|
379
|
+
if (srcW === 0 || srcH === 0) return points;
|
|
380
|
+
const scaleX = targetWidth / srcW;
|
|
381
|
+
const scaleY = targetHeight / srcH;
|
|
382
|
+
return points.map((p) => ({
|
|
383
|
+
x: (p.x - minX) * scaleX,
|
|
384
|
+
y: (p.y - minY) * scaleY
|
|
385
|
+
}));
|
|
386
|
+
}
|
|
387
|
+
static pointsToSVG(points) {
|
|
388
|
+
if (points.length === 0) return "";
|
|
389
|
+
const head = points[0];
|
|
390
|
+
const tail = points.slice(1);
|
|
391
|
+
return `M ${head.x} ${head.y} ` + tail.map((p) => `L ${p.x} ${p.y}`).join(" ") + " Z";
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
// src/coordinate.ts
|
|
396
|
+
var Coordinate = class {
|
|
397
|
+
/**
|
|
398
|
+
* Convert an absolute value to a normalized value (0-1).
|
|
399
|
+
* @param value Absolute value (e.g., pixels)
|
|
400
|
+
* @param total Total dimension size (e.g., canvas width)
|
|
401
|
+
*/
|
|
402
|
+
static toNormalized(value, total) {
|
|
403
|
+
return total === 0 ? 0 : value / total;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Convert a normalized value (0-1) to an absolute value.
|
|
407
|
+
* @param normalized Normalized value (0-1)
|
|
408
|
+
* @param total Total dimension size (e.g., canvas width)
|
|
409
|
+
*/
|
|
410
|
+
static toAbsolute(normalized, total) {
|
|
411
|
+
return normalized * total;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Normalize a point's coordinates.
|
|
415
|
+
*/
|
|
416
|
+
static normalizePoint(point, size) {
|
|
417
|
+
return {
|
|
418
|
+
x: this.toNormalized(point.x, size.width),
|
|
419
|
+
y: this.toNormalized(point.y, size.height)
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Denormalize a point's coordinates to absolute pixels.
|
|
424
|
+
*/
|
|
425
|
+
static denormalizePoint(point, size) {
|
|
426
|
+
return {
|
|
427
|
+
x: this.toAbsolute(point.x, size.width),
|
|
428
|
+
y: this.toAbsolute(point.y, size.height)
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
};
|
|
173
432
|
|
|
174
433
|
// src/geometry.ts
|
|
175
434
|
import paper from "paper";
|
|
176
435
|
function ensurePaper(width, height) {
|
|
177
436
|
if (!paper.project) {
|
|
178
437
|
paper.setup(new paper.Size(width, height));
|
|
438
|
+
} else {
|
|
439
|
+
paper.view.viewSize = new paper.Size(width, height);
|
|
179
440
|
}
|
|
180
441
|
}
|
|
181
442
|
function createBaseShape(options) {
|
|
182
|
-
const { shape, width, height, radius, x, y } = options;
|
|
443
|
+
const { shape, width, height, radius, x, y, pathData } = options;
|
|
183
444
|
const center = new paper.Point(x, y);
|
|
184
445
|
if (shape === "rect") {
|
|
185
446
|
return new paper.Path.Rectangle({
|
|
@@ -193,11 +454,24 @@ function createBaseShape(options) {
|
|
|
193
454
|
center,
|
|
194
455
|
radius: Math.max(0, r)
|
|
195
456
|
});
|
|
196
|
-
} else {
|
|
457
|
+
} else if (shape === "ellipse") {
|
|
197
458
|
return new paper.Path.Ellipse({
|
|
198
459
|
center,
|
|
199
460
|
radius: [Math.max(0, width / 2), Math.max(0, height / 2)]
|
|
200
461
|
});
|
|
462
|
+
} else if (shape === "custom" && pathData) {
|
|
463
|
+
const path = new paper.Path();
|
|
464
|
+
path.pathData = pathData;
|
|
465
|
+
path.position = center;
|
|
466
|
+
if (width > 0 && height > 0 && path.bounds.width > 0 && path.bounds.height > 0) {
|
|
467
|
+
path.scale(width / path.bounds.width, height / path.bounds.height);
|
|
468
|
+
}
|
|
469
|
+
return path;
|
|
470
|
+
} else {
|
|
471
|
+
return new paper.Path.Rectangle({
|
|
472
|
+
point: [x - width / 2, y - height / 2],
|
|
473
|
+
size: [Math.max(0, width), Math.max(0, height)]
|
|
474
|
+
});
|
|
201
475
|
}
|
|
202
476
|
}
|
|
203
477
|
function getDielineShape(options) {
|
|
@@ -211,10 +485,6 @@ function getDielineShape(options) {
|
|
|
211
485
|
center: [hole.x, hole.y],
|
|
212
486
|
radius: hole.outerRadius
|
|
213
487
|
});
|
|
214
|
-
if (!mainShape.intersects(lug) && !mainShape.contains(lug.position)) {
|
|
215
|
-
lug.remove();
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
488
|
const cut = new paper.Path.Circle({
|
|
219
489
|
center: [hole.x, hole.y],
|
|
220
490
|
radius: hole.innerRadius
|
|
@@ -222,31 +492,49 @@ function getDielineShape(options) {
|
|
|
222
492
|
if (!lugsPath) {
|
|
223
493
|
lugsPath = lug;
|
|
224
494
|
} else {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
495
|
+
try {
|
|
496
|
+
const temp = lugsPath.unite(lug);
|
|
497
|
+
lugsPath.remove();
|
|
498
|
+
lug.remove();
|
|
499
|
+
lugsPath = temp;
|
|
500
|
+
} catch (e) {
|
|
501
|
+
console.error("Geometry: Failed to unite lug", e);
|
|
502
|
+
lug.remove();
|
|
503
|
+
}
|
|
229
504
|
}
|
|
230
505
|
if (!cutsPath) {
|
|
231
506
|
cutsPath = cut;
|
|
232
507
|
} else {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
508
|
+
try {
|
|
509
|
+
const temp = cutsPath.unite(cut);
|
|
510
|
+
cutsPath.remove();
|
|
511
|
+
cut.remove();
|
|
512
|
+
cutsPath = temp;
|
|
513
|
+
} catch (e) {
|
|
514
|
+
console.error("Geometry: Failed to unite cut", e);
|
|
515
|
+
cut.remove();
|
|
516
|
+
}
|
|
237
517
|
}
|
|
238
518
|
});
|
|
239
519
|
if (lugsPath) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
520
|
+
try {
|
|
521
|
+
const temp = mainShape.unite(lugsPath);
|
|
522
|
+
mainShape.remove();
|
|
523
|
+
lugsPath.remove();
|
|
524
|
+
mainShape = temp;
|
|
525
|
+
} catch (e) {
|
|
526
|
+
console.error("Geometry: Failed to unite lugsPath to mainShape", e);
|
|
527
|
+
}
|
|
244
528
|
}
|
|
245
529
|
if (cutsPath) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
530
|
+
try {
|
|
531
|
+
const temp = mainShape.subtract(cutsPath);
|
|
532
|
+
mainShape.remove();
|
|
533
|
+
cutsPath.remove();
|
|
534
|
+
mainShape = temp;
|
|
535
|
+
} catch (e) {
|
|
536
|
+
console.error("Geometry: Failed to subtract cutsPath from mainShape", e);
|
|
537
|
+
}
|
|
250
538
|
}
|
|
251
539
|
}
|
|
252
540
|
return mainShape;
|
|
@@ -280,13 +568,37 @@ function generateBleedZonePath(options, offset) {
|
|
|
280
568
|
ensurePaper(maxDim, maxDim);
|
|
281
569
|
paper.project.activeLayer.removeChildren();
|
|
282
570
|
const shapeOriginal = getDielineShape(options);
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
571
|
+
let shapeOffset;
|
|
572
|
+
if (options.shape === "custom") {
|
|
573
|
+
const stroker = shapeOriginal.clone();
|
|
574
|
+
stroker.strokeColor = new paper.Color("black");
|
|
575
|
+
stroker.strokeWidth = Math.abs(offset) * 2;
|
|
576
|
+
stroker.strokeJoin = "round";
|
|
577
|
+
stroker.strokeCap = "round";
|
|
578
|
+
let expanded;
|
|
579
|
+
try {
|
|
580
|
+
expanded = stroker.expand({ stroke: true, fill: false, insert: false });
|
|
581
|
+
} catch (e) {
|
|
582
|
+
stroker.remove();
|
|
583
|
+
shapeOffset = shapeOriginal.clone();
|
|
584
|
+
return shapeOffset.pathData;
|
|
585
|
+
}
|
|
586
|
+
stroker.remove();
|
|
587
|
+
if (offset > 0) {
|
|
588
|
+
shapeOffset = shapeOriginal.unite(expanded);
|
|
589
|
+
} else {
|
|
590
|
+
shapeOffset = shapeOriginal.subtract(expanded);
|
|
591
|
+
}
|
|
592
|
+
expanded.remove();
|
|
593
|
+
} else {
|
|
594
|
+
const offsetOptions = {
|
|
595
|
+
...options,
|
|
596
|
+
width: Math.max(0, options.width + offset * 2),
|
|
597
|
+
height: Math.max(0, options.height + offset * 2),
|
|
598
|
+
radius: options.radius === 0 ? 0 : Math.max(0, options.radius + offset)
|
|
599
|
+
};
|
|
600
|
+
shapeOffset = getDielineShape(offsetOptions);
|
|
601
|
+
}
|
|
290
602
|
let bleedZone;
|
|
291
603
|
if (offset > 0) {
|
|
292
604
|
bleedZone = shapeOffset.subtract(shapeOriginal);
|
|
@@ -309,256 +621,301 @@ function getNearestPointOnDieline(point, options) {
|
|
|
309
621
|
shape.remove();
|
|
310
622
|
return result;
|
|
311
623
|
}
|
|
624
|
+
function getPathBounds(pathData) {
|
|
625
|
+
const path = new paper.Path();
|
|
626
|
+
path.pathData = pathData;
|
|
627
|
+
const bounds = path.bounds;
|
|
628
|
+
path.remove();
|
|
629
|
+
return { width: bounds.width, height: bounds.height };
|
|
630
|
+
}
|
|
312
631
|
|
|
313
632
|
// src/dieline.ts
|
|
314
633
|
var DielineTool = class {
|
|
315
|
-
constructor() {
|
|
316
|
-
this.
|
|
317
|
-
this.
|
|
318
|
-
|
|
319
|
-
width: 300,
|
|
320
|
-
height: 300,
|
|
321
|
-
radius: 0,
|
|
322
|
-
offset: 0,
|
|
323
|
-
style: "solid",
|
|
324
|
-
insideColor: "rgba(0,0,0,0)",
|
|
325
|
-
outsideColor: "#ffffff",
|
|
326
|
-
showBleedLines: true
|
|
327
|
-
};
|
|
328
|
-
this.schema = {
|
|
329
|
-
shape: {
|
|
330
|
-
type: "select",
|
|
331
|
-
options: ["rect", "circle", "ellipse"],
|
|
332
|
-
label: "Shape"
|
|
333
|
-
},
|
|
334
|
-
width: { type: "number", min: 10, max: 2e3, label: "Width" },
|
|
335
|
-
height: { type: "number", min: 10, max: 2e3, label: "Height" },
|
|
336
|
-
radius: { type: "number", min: 0, max: 500, label: "Corner Radius" },
|
|
337
|
-
position: { type: "string", label: "Position" },
|
|
338
|
-
// Complex object, simplified for now or need custom handler
|
|
339
|
-
borderLength: { type: "number", min: 0, max: 500, label: "Margin" },
|
|
340
|
-
offset: { type: "number", min: -100, max: 100, label: "Bleed Offset" },
|
|
341
|
-
showBleedLines: { type: "boolean", label: "Show Bleed Lines" },
|
|
342
|
-
style: {
|
|
343
|
-
type: "select",
|
|
344
|
-
options: ["solid", "dashed"],
|
|
345
|
-
label: "Line Style"
|
|
346
|
-
},
|
|
347
|
-
insideColor: { type: "color", label: "Inside Color" },
|
|
348
|
-
outsideColor: { type: "color", label: "Outside Color" }
|
|
634
|
+
constructor(options) {
|
|
635
|
+
this.id = "pooder.kit.dieline";
|
|
636
|
+
this.metadata = {
|
|
637
|
+
name: "DielineTool"
|
|
349
638
|
};
|
|
350
|
-
this.
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
639
|
+
this.shape = "rect";
|
|
640
|
+
this.width = 500;
|
|
641
|
+
this.height = 500;
|
|
642
|
+
this.radius = 0;
|
|
643
|
+
this.offset = 0;
|
|
644
|
+
this.style = "solid";
|
|
645
|
+
this.insideColor = "rgba(0,0,0,0)";
|
|
646
|
+
this.outsideColor = "#ffffff";
|
|
647
|
+
this.showBleedLines = true;
|
|
648
|
+
this.holes = [];
|
|
649
|
+
if (options) {
|
|
650
|
+
Object.assign(this, options);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
activate(context) {
|
|
654
|
+
this.context = context;
|
|
655
|
+
this.canvasService = context.services.get("CanvasService");
|
|
656
|
+
if (!this.canvasService) {
|
|
657
|
+
console.warn("CanvasService not found for DielineTool");
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
const configService = context.services.get("ConfigurationService");
|
|
661
|
+
if (configService) {
|
|
662
|
+
this.shape = configService.get("dieline.shape", this.shape);
|
|
663
|
+
this.width = configService.get("dieline.width", this.width);
|
|
664
|
+
this.height = configService.get("dieline.height", this.height);
|
|
665
|
+
this.radius = configService.get("dieline.radius", this.radius);
|
|
666
|
+
this.borderLength = configService.get(
|
|
667
|
+
"dieline.borderLength",
|
|
668
|
+
this.borderLength
|
|
669
|
+
);
|
|
670
|
+
this.offset = configService.get("dieline.offset", this.offset);
|
|
671
|
+
this.style = configService.get("dieline.style", this.style);
|
|
672
|
+
this.insideColor = configService.get(
|
|
673
|
+
"dieline.insideColor",
|
|
674
|
+
this.insideColor
|
|
675
|
+
);
|
|
676
|
+
this.outsideColor = configService.get(
|
|
677
|
+
"dieline.outsideColor",
|
|
678
|
+
this.outsideColor
|
|
679
|
+
);
|
|
680
|
+
this.showBleedLines = configService.get(
|
|
681
|
+
"dieline.showBleedLines",
|
|
682
|
+
this.showBleedLines
|
|
683
|
+
);
|
|
684
|
+
this.holes = configService.get("dieline.holes", this.holes);
|
|
685
|
+
this.pathData = configService.get("dieline.pathData", this.pathData);
|
|
686
|
+
configService.onAnyChange((e) => {
|
|
687
|
+
if (e.key.startsWith("dieline.")) {
|
|
688
|
+
const prop = e.key.split(".")[1];
|
|
689
|
+
console.log(
|
|
690
|
+
`[DielineTool] Config change detected: ${e.key} -> ${e.value}`
|
|
691
|
+
);
|
|
692
|
+
if (prop && prop in this) {
|
|
693
|
+
this[prop] = e.value;
|
|
694
|
+
this.updateDieline();
|
|
695
|
+
}
|
|
366
696
|
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
this.createLayer();
|
|
700
|
+
this.updateDieline();
|
|
701
|
+
}
|
|
702
|
+
deactivate(context) {
|
|
703
|
+
this.destroyLayer();
|
|
704
|
+
this.canvasService = void 0;
|
|
705
|
+
this.context = void 0;
|
|
706
|
+
}
|
|
707
|
+
contribute() {
|
|
708
|
+
return {
|
|
709
|
+
[ContributionPointIds2.CONFIGURATIONS]: [
|
|
710
|
+
{
|
|
711
|
+
id: "dieline.shape",
|
|
712
|
+
type: "select",
|
|
713
|
+
label: "Shape",
|
|
714
|
+
options: ["rect", "circle", "ellipse", "custom"],
|
|
715
|
+
default: this.shape
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
id: "dieline.width",
|
|
719
|
+
type: "number",
|
|
720
|
+
label: "Width",
|
|
721
|
+
min: 10,
|
|
722
|
+
max: 2e3,
|
|
723
|
+
default: this.width
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
id: "dieline.height",
|
|
727
|
+
type: "number",
|
|
728
|
+
label: "Height",
|
|
729
|
+
min: 10,
|
|
730
|
+
max: 2e3,
|
|
731
|
+
default: this.height
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
id: "dieline.radius",
|
|
735
|
+
type: "number",
|
|
736
|
+
label: "Corner Radius",
|
|
737
|
+
min: 0,
|
|
738
|
+
max: 500,
|
|
739
|
+
default: this.radius
|
|
740
|
+
},
|
|
741
|
+
{
|
|
742
|
+
id: "dieline.position",
|
|
743
|
+
type: "json",
|
|
744
|
+
label: "Position (Normalized)",
|
|
745
|
+
default: this.position
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
id: "dieline.borderLength",
|
|
749
|
+
type: "number",
|
|
750
|
+
label: "Margin",
|
|
751
|
+
min: 0,
|
|
752
|
+
max: 500,
|
|
753
|
+
default: this.borderLength
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
id: "dieline.offset",
|
|
757
|
+
type: "number",
|
|
758
|
+
label: "Bleed Offset",
|
|
759
|
+
min: -100,
|
|
760
|
+
max: 100,
|
|
761
|
+
default: this.offset
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
id: "dieline.showBleedLines",
|
|
765
|
+
type: "boolean",
|
|
766
|
+
label: "Show Bleed Lines",
|
|
767
|
+
default: this.showBleedLines
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
id: "dieline.style",
|
|
771
|
+
type: "select",
|
|
772
|
+
label: "Line Style",
|
|
773
|
+
options: ["solid", "dashed"],
|
|
774
|
+
default: this.style
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
id: "dieline.insideColor",
|
|
778
|
+
type: "color",
|
|
779
|
+
label: "Inside Color",
|
|
780
|
+
default: this.insideColor
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
id: "dieline.outsideColor",
|
|
784
|
+
type: "color",
|
|
785
|
+
label: "Outside Color",
|
|
786
|
+
default: this.outsideColor
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
id: "dieline.holes",
|
|
790
|
+
type: "json",
|
|
791
|
+
label: "Holes",
|
|
792
|
+
default: this.holes
|
|
372
793
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
794
|
+
],
|
|
795
|
+
[ContributionPointIds2.COMMANDS]: [
|
|
796
|
+
{
|
|
797
|
+
command: "reset",
|
|
798
|
+
title: "Reset Dieline",
|
|
799
|
+
handler: () => {
|
|
800
|
+
this.shape = "rect";
|
|
801
|
+
this.width = 300;
|
|
802
|
+
this.height = 300;
|
|
803
|
+
this.radius = 0;
|
|
804
|
+
this.offset = 0;
|
|
805
|
+
this.style = "solid";
|
|
806
|
+
this.insideColor = "rgba(0,0,0,0)";
|
|
807
|
+
this.outsideColor = "#ffffff";
|
|
808
|
+
this.showBleedLines = true;
|
|
809
|
+
this.holes = [];
|
|
810
|
+
this.pathData = void 0;
|
|
811
|
+
this.updateDieline();
|
|
377
812
|
return true;
|
|
378
|
-
|
|
379
|
-
this.options.height = height;
|
|
380
|
-
this.updateDieline(editor);
|
|
381
|
-
return true;
|
|
813
|
+
}
|
|
382
814
|
},
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
type: "number",
|
|
393
|
-
label: "Height",
|
|
394
|
-
min: 10,
|
|
395
|
-
max: 2e3,
|
|
396
|
-
required: true
|
|
815
|
+
{
|
|
816
|
+
command: "setDimensions",
|
|
817
|
+
title: "Set Dimensions",
|
|
818
|
+
handler: (width, height) => {
|
|
819
|
+
if (this.width === width && this.height === height) return true;
|
|
820
|
+
this.width = width;
|
|
821
|
+
this.height = height;
|
|
822
|
+
this.updateDieline();
|
|
823
|
+
return true;
|
|
397
824
|
}
|
|
398
|
-
}
|
|
399
|
-
},
|
|
400
|
-
setShape: {
|
|
401
|
-
execute: (editor, shape) => {
|
|
402
|
-
if (this.options.shape === shape) return true;
|
|
403
|
-
this.options.shape = shape;
|
|
404
|
-
this.updateDieline(editor);
|
|
405
|
-
return true;
|
|
406
825
|
},
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
826
|
+
{
|
|
827
|
+
command: "setShape",
|
|
828
|
+
title: "Set Shape",
|
|
829
|
+
handler: (shape) => {
|
|
830
|
+
if (this.shape === shape) return true;
|
|
831
|
+
this.shape = shape;
|
|
832
|
+
this.updateDieline();
|
|
833
|
+
return true;
|
|
413
834
|
}
|
|
414
|
-
}
|
|
415
|
-
},
|
|
416
|
-
setBleed: {
|
|
417
|
-
execute: (editor, bleed) => {
|
|
418
|
-
if (this.options.offset === bleed) return true;
|
|
419
|
-
this.options.offset = bleed;
|
|
420
|
-
this.updateDieline(editor);
|
|
421
|
-
return true;
|
|
422
835
|
},
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
836
|
+
{
|
|
837
|
+
command: "setBleed",
|
|
838
|
+
title: "Set Bleed",
|
|
839
|
+
handler: (bleed) => {
|
|
840
|
+
if (this.offset === bleed) return true;
|
|
841
|
+
this.offset = bleed;
|
|
842
|
+
this.updateDieline();
|
|
843
|
+
return true;
|
|
844
|
+
}
|
|
845
|
+
},
|
|
846
|
+
{
|
|
847
|
+
command: "setHoles",
|
|
848
|
+
title: "Set Holes",
|
|
849
|
+
handler: (holes) => {
|
|
850
|
+
this.holes = holes;
|
|
851
|
+
this.updateDieline(false);
|
|
852
|
+
return true;
|
|
853
|
+
}
|
|
854
|
+
},
|
|
855
|
+
{
|
|
856
|
+
command: "getGeometry",
|
|
857
|
+
title: "Get Geometry",
|
|
858
|
+
handler: () => {
|
|
859
|
+
return this.getGeometry();
|
|
860
|
+
}
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
command: "exportCutImage",
|
|
864
|
+
title: "Export Cut Image",
|
|
865
|
+
handler: () => {
|
|
866
|
+
return this.exportCutImage();
|
|
867
|
+
}
|
|
868
|
+
},
|
|
869
|
+
{
|
|
870
|
+
command: "detectEdge",
|
|
871
|
+
title: "Detect Edge from Image",
|
|
872
|
+
handler: async (imageUrl, options) => {
|
|
873
|
+
try {
|
|
874
|
+
const pathData = await ImageTracer.trace(imageUrl, options);
|
|
875
|
+
const bounds = getPathBounds(pathData);
|
|
876
|
+
const currentMax = Math.max(this.width, this.height);
|
|
877
|
+
const scale = currentMax / Math.max(bounds.width, bounds.height);
|
|
878
|
+
this.width = bounds.width * scale;
|
|
879
|
+
this.height = bounds.height * scale;
|
|
880
|
+
this.shape = "custom";
|
|
881
|
+
this.pathData = pathData;
|
|
882
|
+
this.updateDieline();
|
|
883
|
+
return pathData;
|
|
884
|
+
} catch (e) {
|
|
885
|
+
console.error("Edge detection failed", e);
|
|
886
|
+
throw e;
|
|
887
|
+
}
|
|
430
888
|
}
|
|
431
889
|
}
|
|
432
|
-
|
|
433
|
-
exportCutImage: {
|
|
434
|
-
execute: (editor) => {
|
|
435
|
-
var _a, _b, _c, _d;
|
|
436
|
-
const { shape, width, height, radius, position } = this.options;
|
|
437
|
-
const canvasW = editor.canvas.width || 800;
|
|
438
|
-
const canvasH = editor.canvas.height || 600;
|
|
439
|
-
const cx = (_a = position == null ? void 0 : position.x) != null ? _a : canvasW / 2;
|
|
440
|
-
const cy = (_b = position == null ? void 0 : position.y) != null ? _b : canvasH / 2;
|
|
441
|
-
const holeTool = editor.getExtension("HoleTool");
|
|
442
|
-
const holes = holeTool ? holeTool.options.holes || [] : [];
|
|
443
|
-
const innerRadius = holeTool ? holeTool.options.innerRadius || 15 : 15;
|
|
444
|
-
const outerRadius = holeTool ? holeTool.options.outerRadius || 25 : 25;
|
|
445
|
-
const holeData = holes.map((h) => ({
|
|
446
|
-
x: h.x,
|
|
447
|
-
y: h.y,
|
|
448
|
-
innerRadius,
|
|
449
|
-
outerRadius
|
|
450
|
-
}));
|
|
451
|
-
const pathData = generateDielinePath({
|
|
452
|
-
shape,
|
|
453
|
-
width,
|
|
454
|
-
height,
|
|
455
|
-
radius,
|
|
456
|
-
x: cx,
|
|
457
|
-
y: cy,
|
|
458
|
-
holes: holeData
|
|
459
|
-
});
|
|
460
|
-
const clipPath = new Path(pathData, {
|
|
461
|
-
left: 0,
|
|
462
|
-
top: 0,
|
|
463
|
-
originX: "left",
|
|
464
|
-
originY: "top",
|
|
465
|
-
absolutePositioned: true
|
|
466
|
-
});
|
|
467
|
-
const layer = this.getLayer(editor, "dieline-overlay");
|
|
468
|
-
const wasVisible = (_c = layer == null ? void 0 : layer.visible) != null ? _c : true;
|
|
469
|
-
if (layer) layer.visible = false;
|
|
470
|
-
const holeMarkers = editor.canvas.getObjects().filter((o) => {
|
|
471
|
-
var _a2;
|
|
472
|
-
return ((_a2 = o.data) == null ? void 0 : _a2.type) === "hole-marker";
|
|
473
|
-
});
|
|
474
|
-
holeMarkers.forEach((o) => o.visible = false);
|
|
475
|
-
const rulerLayer = editor.canvas.getObjects().find((obj) => {
|
|
476
|
-
var _a2;
|
|
477
|
-
return ((_a2 = obj.data) == null ? void 0 : _a2.id) === "ruler-overlay";
|
|
478
|
-
});
|
|
479
|
-
const rulerWasVisible = (_d = rulerLayer == null ? void 0 : rulerLayer.visible) != null ? _d : true;
|
|
480
|
-
if (rulerLayer) rulerLayer.visible = false;
|
|
481
|
-
const originalClip = editor.canvas.clipPath;
|
|
482
|
-
editor.canvas.clipPath = clipPath;
|
|
483
|
-
const bbox = clipPath.getBoundingRect();
|
|
484
|
-
const holeDataRelative = holes.map((h) => ({
|
|
485
|
-
x: h.x - bbox.left,
|
|
486
|
-
y: h.y - bbox.top,
|
|
487
|
-
innerRadius,
|
|
488
|
-
outerRadius
|
|
489
|
-
}));
|
|
490
|
-
const clipPathCorrected = new Path(pathData, {
|
|
491
|
-
absolutePositioned: true,
|
|
492
|
-
left: 0,
|
|
493
|
-
top: 0
|
|
494
|
-
});
|
|
495
|
-
const tempPath = new Path(pathData);
|
|
496
|
-
const tempBounds = tempPath.getBoundingRect();
|
|
497
|
-
clipPathCorrected.set({
|
|
498
|
-
left: tempBounds.left,
|
|
499
|
-
top: tempBounds.top,
|
|
500
|
-
originX: "left",
|
|
501
|
-
originY: "top"
|
|
502
|
-
});
|
|
503
|
-
editor.canvas.clipPath = clipPathCorrected;
|
|
504
|
-
const exportBbox = clipPathCorrected.getBoundingRect();
|
|
505
|
-
const dataURL = editor.canvas.toDataURL({
|
|
506
|
-
format: "png",
|
|
507
|
-
multiplier: 2,
|
|
508
|
-
left: exportBbox.left,
|
|
509
|
-
top: exportBbox.top,
|
|
510
|
-
width: exportBbox.width,
|
|
511
|
-
height: exportBbox.height
|
|
512
|
-
});
|
|
513
|
-
editor.canvas.clipPath = originalClip;
|
|
514
|
-
if (layer) layer.visible = wasVisible;
|
|
515
|
-
if (rulerLayer) rulerLayer.visible = rulerWasVisible;
|
|
516
|
-
holeMarkers.forEach((o) => o.visible = true);
|
|
517
|
-
editor.canvas.requestRenderAll();
|
|
518
|
-
return dataURL;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
890
|
+
]
|
|
521
891
|
};
|
|
522
892
|
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
this.
|
|
526
|
-
}
|
|
527
|
-
onUnmount(editor) {
|
|
528
|
-
this.destroyLayer(editor);
|
|
529
|
-
}
|
|
530
|
-
onUpdate(editor, state) {
|
|
531
|
-
this.updateDieline(editor);
|
|
893
|
+
getLayer() {
|
|
894
|
+
var _a;
|
|
895
|
+
return (_a = this.canvasService) == null ? void 0 : _a.getLayer("dieline-overlay");
|
|
532
896
|
}
|
|
533
|
-
|
|
534
|
-
this.
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
897
|
+
createLayer() {
|
|
898
|
+
if (!this.canvasService) return;
|
|
899
|
+
const width = this.canvasService.canvas.width || 800;
|
|
900
|
+
const height = this.canvasService.canvas.height || 600;
|
|
901
|
+
const layer = this.canvasService.createLayer("dieline-overlay", {
|
|
902
|
+
width,
|
|
903
|
+
height,
|
|
904
|
+
selectable: false,
|
|
905
|
+
evented: false
|
|
540
906
|
});
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
const height = editor.canvas.height || 600;
|
|
547
|
-
layer = new PooderLayer2([], {
|
|
548
|
-
width,
|
|
549
|
-
height,
|
|
550
|
-
selectable: false,
|
|
551
|
-
evented: false,
|
|
552
|
-
data: { id: "dieline-overlay" }
|
|
553
|
-
});
|
|
554
|
-
editor.canvas.add(layer);
|
|
907
|
+
this.canvasService.canvas.bringObjectToFront(layer);
|
|
908
|
+
const userLayer = this.canvasService.getLayer("user");
|
|
909
|
+
if (userLayer) {
|
|
910
|
+
const userIndex = this.canvasService.canvas.getObjects().indexOf(userLayer);
|
|
911
|
+
this.canvasService.canvas.moveObjectTo(layer, userIndex + 1);
|
|
555
912
|
}
|
|
556
|
-
editor.canvas.bringObjectToFront(layer);
|
|
557
913
|
}
|
|
558
|
-
destroyLayer(
|
|
559
|
-
|
|
914
|
+
destroyLayer() {
|
|
915
|
+
if (!this.canvasService) return;
|
|
916
|
+
const layer = this.getLayer();
|
|
560
917
|
if (layer) {
|
|
561
|
-
|
|
918
|
+
this.canvasService.canvas.remove(layer);
|
|
562
919
|
}
|
|
563
920
|
}
|
|
564
921
|
createHatchPattern(color = "rgba(0, 0, 0, 0.3)") {
|
|
@@ -581,8 +938,10 @@ var DielineTool = class {
|
|
|
581
938
|
}
|
|
582
939
|
return new Pattern({ source: canvas, repetition: "repeat" });
|
|
583
940
|
}
|
|
584
|
-
updateDieline(
|
|
585
|
-
|
|
941
|
+
updateDieline(emitEvent = true) {
|
|
942
|
+
if (!this.canvasService) return;
|
|
943
|
+
const layer = this.getLayer();
|
|
944
|
+
if (!layer) return;
|
|
586
945
|
const {
|
|
587
946
|
shape,
|
|
588
947
|
radius,
|
|
@@ -592,33 +951,31 @@ var DielineTool = class {
|
|
|
592
951
|
outsideColor,
|
|
593
952
|
position,
|
|
594
953
|
borderLength,
|
|
595
|
-
showBleedLines
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
const
|
|
954
|
+
showBleedLines,
|
|
955
|
+
holes
|
|
956
|
+
} = this;
|
|
957
|
+
let { width, height } = this;
|
|
958
|
+
const canvasW = this.canvasService.canvas.width || 800;
|
|
959
|
+
const canvasH = this.canvasService.canvas.height || 600;
|
|
600
960
|
if (borderLength && borderLength > 0) {
|
|
601
961
|
width = Math.max(0, canvasW - borderLength * 2);
|
|
602
962
|
height = Math.max(0, canvasH - borderLength * 2);
|
|
603
963
|
}
|
|
604
|
-
const
|
|
605
|
-
const
|
|
606
|
-
const
|
|
607
|
-
if (!layer) return;
|
|
964
|
+
const normalizedPos = position != null ? position : { x: 0.5, y: 0.5 };
|
|
965
|
+
const cx = Coordinate.toAbsolute(normalizedPos.x, canvasW);
|
|
966
|
+
const cy = Coordinate.toAbsolute(normalizedPos.y, canvasH);
|
|
608
967
|
layer.remove(...layer.getObjects());
|
|
609
|
-
const
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
outerRadius
|
|
621
|
-
}));
|
|
968
|
+
const absoluteHoles = (holes || []).map((h) => {
|
|
969
|
+
const p = Coordinate.denormalizePoint(
|
|
970
|
+
{ x: h.x, y: h.y },
|
|
971
|
+
{ width: canvasW, height: canvasH }
|
|
972
|
+
);
|
|
973
|
+
return {
|
|
974
|
+
...h,
|
|
975
|
+
x: p.x,
|
|
976
|
+
y: p.y
|
|
977
|
+
};
|
|
978
|
+
});
|
|
622
979
|
const cutW = Math.max(0, width + offset * 2);
|
|
623
980
|
const cutH = Math.max(0, height + offset * 2);
|
|
624
981
|
const cutR = radius === 0 ? 0 : Math.max(0, radius + offset);
|
|
@@ -631,7 +988,8 @@ var DielineTool = class {
|
|
|
631
988
|
radius: cutR,
|
|
632
989
|
x: cx,
|
|
633
990
|
y: cy,
|
|
634
|
-
holes:
|
|
991
|
+
holes: absoluteHoles,
|
|
992
|
+
pathData: this.pathData
|
|
635
993
|
});
|
|
636
994
|
const mask = new Path(maskPathData, {
|
|
637
995
|
fill: outsideColor,
|
|
@@ -652,7 +1010,8 @@ var DielineTool = class {
|
|
|
652
1010
|
radius: cutR,
|
|
653
1011
|
x: cx,
|
|
654
1012
|
y: cy,
|
|
655
|
-
holes:
|
|
1013
|
+
holes: absoluteHoles,
|
|
1014
|
+
pathData: this.pathData
|
|
656
1015
|
});
|
|
657
1016
|
const insideObj = new Path(productPathData, {
|
|
658
1017
|
fill: insideColor,
|
|
@@ -674,7 +1033,8 @@ var DielineTool = class {
|
|
|
674
1033
|
radius,
|
|
675
1034
|
x: cx,
|
|
676
1035
|
y: cy,
|
|
677
|
-
holes:
|
|
1036
|
+
holes: absoluteHoles,
|
|
1037
|
+
pathData: this.pathData
|
|
678
1038
|
},
|
|
679
1039
|
offset
|
|
680
1040
|
);
|
|
@@ -700,7 +1060,8 @@ var DielineTool = class {
|
|
|
700
1060
|
radius: cutR,
|
|
701
1061
|
x: cx,
|
|
702
1062
|
y: cy,
|
|
703
|
-
holes:
|
|
1063
|
+
holes: absoluteHoles,
|
|
1064
|
+
pathData: this.pathData
|
|
704
1065
|
});
|
|
705
1066
|
const offsetBorderObj = new Path(offsetPathData, {
|
|
706
1067
|
fill: null,
|
|
@@ -723,7 +1084,9 @@ var DielineTool = class {
|
|
|
723
1084
|
radius,
|
|
724
1085
|
x: cx,
|
|
725
1086
|
y: cy,
|
|
726
|
-
holes:
|
|
1087
|
+
holes: absoluteHoles,
|
|
1088
|
+
// FIX: Use absoluteHoles instead of holes
|
|
1089
|
+
pathData: this.pathData
|
|
727
1090
|
});
|
|
728
1091
|
const borderObj = new Path(borderPathData, {
|
|
729
1092
|
fill: "transparent",
|
|
@@ -736,107 +1099,238 @@ var DielineTool = class {
|
|
|
736
1099
|
originY: "top"
|
|
737
1100
|
});
|
|
738
1101
|
layer.add(borderObj);
|
|
739
|
-
|
|
1102
|
+
const userLayer = this.canvasService.getLayer("user");
|
|
1103
|
+
if (layer && userLayer) {
|
|
1104
|
+
const layerIndex = this.canvasService.canvas.getObjects().indexOf(layer);
|
|
1105
|
+
const userIndex = this.canvasService.canvas.getObjects().indexOf(userLayer);
|
|
1106
|
+
if (layerIndex < userIndex) {
|
|
1107
|
+
this.canvasService.canvas.moveObjectTo(layer, userIndex + 1);
|
|
1108
|
+
}
|
|
1109
|
+
} else {
|
|
1110
|
+
this.canvasService.canvas.bringObjectToFront(layer);
|
|
1111
|
+
}
|
|
1112
|
+
const rulerLayer = this.canvasService.getLayer("ruler-overlay");
|
|
1113
|
+
if (rulerLayer) {
|
|
1114
|
+
this.canvasService.canvas.bringObjectToFront(rulerLayer);
|
|
1115
|
+
}
|
|
1116
|
+
layer.dirty = true;
|
|
1117
|
+
this.canvasService.requestRenderAll();
|
|
1118
|
+
if (emitEvent && this.context) {
|
|
1119
|
+
const geometry = this.getGeometry();
|
|
1120
|
+
if (geometry) {
|
|
1121
|
+
this.context.eventBus.emit("dieline:geometry:change", geometry);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
740
1124
|
}
|
|
741
|
-
getGeometry(
|
|
1125
|
+
getGeometry() {
|
|
742
1126
|
var _a, _b;
|
|
743
|
-
|
|
744
|
-
const
|
|
745
|
-
const
|
|
1127
|
+
if (!this.canvasService) return null;
|
|
1128
|
+
const { shape, width, height, radius, position, borderLength, offset } = this;
|
|
1129
|
+
const canvasW = this.canvasService.canvas.width || 800;
|
|
1130
|
+
const canvasH = this.canvasService.canvas.height || 600;
|
|
746
1131
|
let visualWidth = width;
|
|
747
1132
|
let visualHeight = height;
|
|
748
1133
|
if (borderLength && borderLength > 0) {
|
|
749
1134
|
visualWidth = Math.max(0, canvasW - borderLength * 2);
|
|
750
1135
|
visualHeight = Math.max(0, canvasH - borderLength * 2);
|
|
751
1136
|
}
|
|
752
|
-
const cx = (_a = position == null ? void 0 : position.x) != null ? _a : canvasW
|
|
753
|
-
const cy = (_b = position == null ? void 0 : position.y) != null ? _b : canvasH
|
|
1137
|
+
const cx = Coordinate.toAbsolute((_a = position == null ? void 0 : position.x) != null ? _a : 0.5, canvasW);
|
|
1138
|
+
const cy = Coordinate.toAbsolute((_b = position == null ? void 0 : position.y) != null ? _b : 0.5, canvasH);
|
|
754
1139
|
return {
|
|
755
1140
|
shape,
|
|
756
1141
|
x: cx,
|
|
757
1142
|
y: cy,
|
|
758
1143
|
width: visualWidth,
|
|
759
1144
|
height: visualHeight,
|
|
760
|
-
radius
|
|
1145
|
+
radius,
|
|
1146
|
+
offset,
|
|
1147
|
+
borderLength,
|
|
1148
|
+
pathData: this.pathData
|
|
761
1149
|
};
|
|
762
1150
|
}
|
|
1151
|
+
exportCutImage() {
|
|
1152
|
+
var _a, _b, _c, _d;
|
|
1153
|
+
if (!this.canvasService) return null;
|
|
1154
|
+
const canvas = this.canvasService.canvas;
|
|
1155
|
+
const { shape, width, height, radius, position, holes } = this;
|
|
1156
|
+
const canvasW = canvas.width || 800;
|
|
1157
|
+
const canvasH = canvas.height || 600;
|
|
1158
|
+
const cx = Coordinate.toAbsolute((_a = position == null ? void 0 : position.x) != null ? _a : 0.5, canvasW);
|
|
1159
|
+
const cy = Coordinate.toAbsolute((_b = position == null ? void 0 : position.y) != null ? _b : 0.5, canvasH);
|
|
1160
|
+
const absoluteHoles = (holes || []).map((h) => {
|
|
1161
|
+
const p = Coordinate.denormalizePoint(
|
|
1162
|
+
{ x: h.x, y: h.y },
|
|
1163
|
+
{ width: canvasW, height: canvasH }
|
|
1164
|
+
);
|
|
1165
|
+
return {
|
|
1166
|
+
...h,
|
|
1167
|
+
x: p.x,
|
|
1168
|
+
y: p.y
|
|
1169
|
+
};
|
|
1170
|
+
});
|
|
1171
|
+
const pathData = generateDielinePath({
|
|
1172
|
+
shape,
|
|
1173
|
+
width,
|
|
1174
|
+
height,
|
|
1175
|
+
radius,
|
|
1176
|
+
x: cx,
|
|
1177
|
+
y: cy,
|
|
1178
|
+
holes: absoluteHoles,
|
|
1179
|
+
pathData: this.pathData
|
|
1180
|
+
});
|
|
1181
|
+
const clipPath = new Path(pathData, {
|
|
1182
|
+
left: 0,
|
|
1183
|
+
top: 0,
|
|
1184
|
+
originX: "left",
|
|
1185
|
+
originY: "top",
|
|
1186
|
+
absolutePositioned: true
|
|
1187
|
+
});
|
|
1188
|
+
const layer = this.getLayer();
|
|
1189
|
+
const wasVisible = (_c = layer == null ? void 0 : layer.visible) != null ? _c : true;
|
|
1190
|
+
if (layer) layer.visible = false;
|
|
1191
|
+
const holeMarkers = canvas.getObjects().filter((o) => {
|
|
1192
|
+
var _a2;
|
|
1193
|
+
return ((_a2 = o.data) == null ? void 0 : _a2.type) === "hole-marker";
|
|
1194
|
+
});
|
|
1195
|
+
holeMarkers.forEach((o) => o.visible = false);
|
|
1196
|
+
const rulerLayer = canvas.getObjects().find((obj) => {
|
|
1197
|
+
var _a2;
|
|
1198
|
+
return ((_a2 = obj.data) == null ? void 0 : _a2.id) === "ruler-overlay";
|
|
1199
|
+
});
|
|
1200
|
+
const rulerWasVisible = (_d = rulerLayer == null ? void 0 : rulerLayer.visible) != null ? _d : true;
|
|
1201
|
+
if (rulerLayer) rulerLayer.visible = false;
|
|
1202
|
+
const originalClip = canvas.clipPath;
|
|
1203
|
+
canvas.clipPath = clipPath;
|
|
1204
|
+
const bbox = clipPath.getBoundingRect();
|
|
1205
|
+
const clipPathCorrected = new Path(pathData, {
|
|
1206
|
+
absolutePositioned: true,
|
|
1207
|
+
left: 0,
|
|
1208
|
+
top: 0
|
|
1209
|
+
});
|
|
1210
|
+
const tempPath = new Path(pathData);
|
|
1211
|
+
const tempBounds = tempPath.getBoundingRect();
|
|
1212
|
+
clipPathCorrected.set({
|
|
1213
|
+
left: tempBounds.left,
|
|
1214
|
+
top: tempBounds.top,
|
|
1215
|
+
originX: "left",
|
|
1216
|
+
originY: "top"
|
|
1217
|
+
});
|
|
1218
|
+
canvas.clipPath = clipPathCorrected;
|
|
1219
|
+
const exportBbox = clipPathCorrected.getBoundingRect();
|
|
1220
|
+
const dataURL = canvas.toDataURL({
|
|
1221
|
+
format: "png",
|
|
1222
|
+
multiplier: 2,
|
|
1223
|
+
left: exportBbox.left,
|
|
1224
|
+
top: exportBbox.top,
|
|
1225
|
+
width: exportBbox.width,
|
|
1226
|
+
height: exportBbox.height
|
|
1227
|
+
});
|
|
1228
|
+
canvas.clipPath = originalClip;
|
|
1229
|
+
if (layer) layer.visible = wasVisible;
|
|
1230
|
+
if (rulerLayer) rulerLayer.visible = rulerWasVisible;
|
|
1231
|
+
holeMarkers.forEach((o) => o.visible = true);
|
|
1232
|
+
canvas.requestRenderAll();
|
|
1233
|
+
return dataURL;
|
|
1234
|
+
}
|
|
763
1235
|
};
|
|
764
1236
|
|
|
765
1237
|
// src/film.ts
|
|
766
1238
|
import {
|
|
767
|
-
|
|
768
|
-
PooderLayer as PooderLayer3
|
|
1239
|
+
ContributionPointIds as ContributionPointIds3
|
|
769
1240
|
} from "@pooder/core";
|
|
1241
|
+
import { FabricImage as Image3 } from "fabric";
|
|
770
1242
|
var FilmTool = class {
|
|
771
|
-
constructor() {
|
|
772
|
-
this.
|
|
773
|
-
this.
|
|
774
|
-
|
|
775
|
-
opacity: 0.5
|
|
776
|
-
};
|
|
777
|
-
this.schema = {
|
|
778
|
-
url: {
|
|
779
|
-
type: "string",
|
|
780
|
-
label: "Film Image URL"
|
|
781
|
-
},
|
|
782
|
-
opacity: {
|
|
783
|
-
type: "number",
|
|
784
|
-
min: 0,
|
|
785
|
-
max: 1,
|
|
786
|
-
step: 0.1,
|
|
787
|
-
label: "Opacity"
|
|
788
|
-
}
|
|
1243
|
+
constructor(options) {
|
|
1244
|
+
this.id = "pooder.kit.film";
|
|
1245
|
+
this.metadata = {
|
|
1246
|
+
name: "FilmTool"
|
|
789
1247
|
};
|
|
790
|
-
this.
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
1248
|
+
this.url = "";
|
|
1249
|
+
this.opacity = 0.5;
|
|
1250
|
+
if (options) {
|
|
1251
|
+
Object.assign(this, options);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
activate(context) {
|
|
1255
|
+
this.canvasService = context.services.get("CanvasService");
|
|
1256
|
+
if (!this.canvasService) {
|
|
1257
|
+
console.warn("CanvasService not found for FilmTool");
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
const configService = context.services.get("ConfigurationService");
|
|
1261
|
+
if (configService) {
|
|
1262
|
+
this.url = configService.get("film.url", this.url);
|
|
1263
|
+
this.opacity = configService.get("film.opacity", this.opacity);
|
|
1264
|
+
configService.onAnyChange((e) => {
|
|
1265
|
+
if (e.key.startsWith("film.")) {
|
|
1266
|
+
const prop = e.key.split(".")[1];
|
|
1267
|
+
console.log(
|
|
1268
|
+
`[FilmTool] Config change detected: ${e.key} -> ${e.value}`
|
|
1269
|
+
);
|
|
1270
|
+
if (prop && prop in this) {
|
|
1271
|
+
this[prop] = e.value;
|
|
1272
|
+
this.updateFilm();
|
|
812
1273
|
}
|
|
813
1274
|
}
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
this.initLayer();
|
|
1278
|
+
this.updateFilm();
|
|
1279
|
+
}
|
|
1280
|
+
deactivate(context) {
|
|
1281
|
+
if (this.canvasService) {
|
|
1282
|
+
const layer = this.canvasService.getLayer("overlay");
|
|
1283
|
+
if (layer) {
|
|
1284
|
+
const img = this.canvasService.getObject("film-image", "overlay");
|
|
1285
|
+
if (img) {
|
|
1286
|
+
layer.remove(img);
|
|
1287
|
+
this.canvasService.requestRenderAll();
|
|
1288
|
+
}
|
|
814
1289
|
}
|
|
815
|
-
|
|
816
|
-
}
|
|
817
|
-
onMount(editor) {
|
|
818
|
-
this.initLayer(editor);
|
|
819
|
-
this.updateFilm(editor, this.options);
|
|
820
|
-
}
|
|
821
|
-
onUnmount(editor) {
|
|
822
|
-
const layer = editor.getLayer("overlay");
|
|
823
|
-
if (layer) {
|
|
824
|
-
const img = editor.getObject("film-image", "overlay");
|
|
825
|
-
if (img) {
|
|
826
|
-
layer.remove(img);
|
|
827
|
-
editor.canvas.requestRenderAll();
|
|
828
|
-
}
|
|
1290
|
+
this.canvasService = void 0;
|
|
829
1291
|
}
|
|
830
1292
|
}
|
|
831
|
-
|
|
832
|
-
|
|
1293
|
+
contribute() {
|
|
1294
|
+
return {
|
|
1295
|
+
[ContributionPointIds3.CONFIGURATIONS]: [
|
|
1296
|
+
{
|
|
1297
|
+
id: "film.url",
|
|
1298
|
+
type: "string",
|
|
1299
|
+
label: "Film Image URL",
|
|
1300
|
+
default: ""
|
|
1301
|
+
},
|
|
1302
|
+
{
|
|
1303
|
+
id: "film.opacity",
|
|
1304
|
+
type: "number",
|
|
1305
|
+
label: "Opacity",
|
|
1306
|
+
min: 0,
|
|
1307
|
+
max: 1,
|
|
1308
|
+
step: 0.1,
|
|
1309
|
+
default: 0.5
|
|
1310
|
+
}
|
|
1311
|
+
],
|
|
1312
|
+
[ContributionPointIds3.COMMANDS]: [
|
|
1313
|
+
{
|
|
1314
|
+
command: "setFilmImage",
|
|
1315
|
+
title: "Set Film Image",
|
|
1316
|
+
handler: (url, opacity) => {
|
|
1317
|
+
if (this.url === url && this.opacity === opacity) return true;
|
|
1318
|
+
this.url = url;
|
|
1319
|
+
this.opacity = opacity;
|
|
1320
|
+
this.updateFilm();
|
|
1321
|
+
return true;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
]
|
|
1325
|
+
};
|
|
833
1326
|
}
|
|
834
|
-
initLayer(
|
|
835
|
-
|
|
1327
|
+
initLayer() {
|
|
1328
|
+
if (!this.canvasService) return;
|
|
1329
|
+
let overlayLayer = this.canvasService.getLayer("overlay");
|
|
836
1330
|
if (!overlayLayer) {
|
|
837
|
-
const width =
|
|
838
|
-
const height =
|
|
839
|
-
const layer =
|
|
1331
|
+
const width = this.canvasService.canvas.width || 800;
|
|
1332
|
+
const height = this.canvasService.canvas.height || 600;
|
|
1333
|
+
const layer = this.canvasService.createLayer("overlay", {
|
|
840
1334
|
width,
|
|
841
1335
|
height,
|
|
842
1336
|
left: 0,
|
|
@@ -846,33 +1340,30 @@ var FilmTool = class {
|
|
|
846
1340
|
selectable: false,
|
|
847
1341
|
evented: false,
|
|
848
1342
|
subTargetCheck: false,
|
|
849
|
-
interactive: false
|
|
850
|
-
data: {
|
|
851
|
-
id: "overlay"
|
|
852
|
-
}
|
|
1343
|
+
interactive: false
|
|
853
1344
|
});
|
|
854
|
-
|
|
855
|
-
editor.canvas.bringObjectToFront(layer);
|
|
1345
|
+
this.canvasService.canvas.bringObjectToFront(layer);
|
|
856
1346
|
}
|
|
857
1347
|
}
|
|
858
|
-
async updateFilm(
|
|
859
|
-
|
|
1348
|
+
async updateFilm() {
|
|
1349
|
+
if (!this.canvasService) return;
|
|
1350
|
+
const layer = this.canvasService.getLayer("overlay");
|
|
860
1351
|
if (!layer) {
|
|
861
1352
|
console.warn("[FilmTool] Overlay layer not found");
|
|
862
1353
|
return;
|
|
863
1354
|
}
|
|
864
|
-
const { url, opacity } =
|
|
1355
|
+
const { url, opacity } = this;
|
|
865
1356
|
if (!url) {
|
|
866
|
-
const img2 =
|
|
1357
|
+
const img2 = this.canvasService.getObject("film-image", "overlay");
|
|
867
1358
|
if (img2) {
|
|
868
1359
|
layer.remove(img2);
|
|
869
|
-
|
|
1360
|
+
this.canvasService.requestRenderAll();
|
|
870
1361
|
}
|
|
871
1362
|
return;
|
|
872
1363
|
}
|
|
873
|
-
const width =
|
|
874
|
-
const height =
|
|
875
|
-
let img =
|
|
1364
|
+
const width = this.canvasService.canvas.width || 800;
|
|
1365
|
+
const height = this.canvasService.canvas.height || 600;
|
|
1366
|
+
let img = this.canvasService.getObject("film-image", "overlay");
|
|
876
1367
|
try {
|
|
877
1368
|
if (img) {
|
|
878
1369
|
if (img.getSrc() !== url) {
|
|
@@ -880,7 +1371,7 @@ var FilmTool = class {
|
|
|
880
1371
|
}
|
|
881
1372
|
img.set({ opacity });
|
|
882
1373
|
} else {
|
|
883
|
-
img = await
|
|
1374
|
+
img = await Image3.fromURL(url, { crossOrigin: "anonymous" });
|
|
884
1375
|
img.scaleToWidth(width);
|
|
885
1376
|
if (img.getScaledHeight() < height) img.scaleToHeight(height);
|
|
886
1377
|
img.set({
|
|
@@ -895,208 +1386,265 @@ var FilmTool = class {
|
|
|
895
1386
|
});
|
|
896
1387
|
layer.add(img);
|
|
897
1388
|
}
|
|
898
|
-
|
|
1389
|
+
this.canvasService.requestRenderAll();
|
|
899
1390
|
} catch (error) {
|
|
900
1391
|
console.error("[FilmTool] Failed to load film image", url, error);
|
|
901
1392
|
}
|
|
1393
|
+
layer.dirty = true;
|
|
1394
|
+
this.canvasService.requestRenderAll();
|
|
902
1395
|
}
|
|
903
1396
|
};
|
|
904
1397
|
|
|
905
1398
|
// src/hole.ts
|
|
906
1399
|
import {
|
|
907
|
-
|
|
908
|
-
Group,
|
|
909
|
-
Point
|
|
1400
|
+
ContributionPointIds as ContributionPointIds4
|
|
910
1401
|
} from "@pooder/core";
|
|
1402
|
+
import { Circle, Group, Point } from "fabric";
|
|
911
1403
|
var HoleTool = class {
|
|
912
|
-
constructor() {
|
|
913
|
-
this.
|
|
914
|
-
this.
|
|
915
|
-
|
|
916
|
-
outerRadius: 25,
|
|
917
|
-
style: "solid",
|
|
918
|
-
holes: [],
|
|
919
|
-
constraintTarget: "bleed"
|
|
920
|
-
};
|
|
921
|
-
this.schema = {
|
|
922
|
-
innerRadius: {
|
|
923
|
-
type: "number",
|
|
924
|
-
min: 1,
|
|
925
|
-
max: 100,
|
|
926
|
-
label: "Inner Radius"
|
|
927
|
-
},
|
|
928
|
-
outerRadius: {
|
|
929
|
-
type: "number",
|
|
930
|
-
min: 1,
|
|
931
|
-
max: 100,
|
|
932
|
-
label: "Outer Radius"
|
|
933
|
-
},
|
|
934
|
-
style: {
|
|
935
|
-
type: "select",
|
|
936
|
-
options: ["solid", "dashed"],
|
|
937
|
-
label: "Line Style"
|
|
938
|
-
},
|
|
939
|
-
constraintTarget: {
|
|
940
|
-
type: "select",
|
|
941
|
-
options: ["original", "bleed"],
|
|
942
|
-
label: "Constraint Target"
|
|
943
|
-
},
|
|
944
|
-
holes: {
|
|
945
|
-
type: "json",
|
|
946
|
-
label: "Holes"
|
|
947
|
-
}
|
|
1404
|
+
constructor(options) {
|
|
1405
|
+
this.id = "pooder.kit.hole";
|
|
1406
|
+
this.metadata = {
|
|
1407
|
+
name: "HoleTool"
|
|
948
1408
|
};
|
|
1409
|
+
this.innerRadius = 15;
|
|
1410
|
+
this.outerRadius = 25;
|
|
1411
|
+
this.style = "solid";
|
|
1412
|
+
this.holes = [];
|
|
1413
|
+
this.constraintTarget = "bleed";
|
|
1414
|
+
this.isUpdatingConfig = false;
|
|
949
1415
|
this.handleMoving = null;
|
|
950
1416
|
this.handleModified = null;
|
|
951
|
-
this.
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1417
|
+
this.handleDielineChange = null;
|
|
1418
|
+
// Cache geometry to enforce constraints during drag
|
|
1419
|
+
this.currentGeometry = null;
|
|
1420
|
+
if (options) {
|
|
1421
|
+
Object.assign(this, options);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
activate(context) {
|
|
1425
|
+
this.context = context;
|
|
1426
|
+
this.canvasService = context.services.get("CanvasService");
|
|
1427
|
+
if (!this.canvasService) {
|
|
1428
|
+
console.warn("CanvasService not found for HoleTool");
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1431
|
+
const configService = context.services.get(
|
|
1432
|
+
"ConfigurationService"
|
|
1433
|
+
);
|
|
1434
|
+
if (configService) {
|
|
1435
|
+
this.innerRadius = configService.get(
|
|
1436
|
+
"hole.innerRadius",
|
|
1437
|
+
this.innerRadius
|
|
1438
|
+
);
|
|
1439
|
+
this.outerRadius = configService.get(
|
|
1440
|
+
"hole.outerRadius",
|
|
1441
|
+
this.outerRadius
|
|
1442
|
+
);
|
|
1443
|
+
this.style = configService.get("hole.style", this.style);
|
|
1444
|
+
this.constraintTarget = configService.get(
|
|
1445
|
+
"hole.constraintTarget",
|
|
1446
|
+
this.constraintTarget
|
|
1447
|
+
);
|
|
1448
|
+
const dielineHoles = configService.get("dieline.holes", []);
|
|
1449
|
+
if (this.canvasService) {
|
|
1450
|
+
const { width, height } = this.canvasService.canvas;
|
|
1451
|
+
this.holes = dielineHoles.map((h) => {
|
|
1452
|
+
const p = Coordinate.denormalizePoint(h, {
|
|
1453
|
+
width: width || 800,
|
|
1454
|
+
height: height || 600
|
|
1455
|
+
});
|
|
1456
|
+
return { x: p.x, y: p.y };
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
configService.onAnyChange((e) => {
|
|
1460
|
+
if (this.isUpdatingConfig) return;
|
|
1461
|
+
if (e.key.startsWith("hole.")) {
|
|
1462
|
+
const prop = e.key.split(".")[1];
|
|
1463
|
+
if (prop && prop in this) {
|
|
1464
|
+
this[prop] = e.value;
|
|
1465
|
+
this.redraw();
|
|
1466
|
+
this.syncHolesToDieline();
|
|
962
1467
|
}
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
1468
|
+
}
|
|
1469
|
+
if (e.key === "dieline.holes") {
|
|
1470
|
+
const holes = e.value || [];
|
|
1471
|
+
if (this.canvasService) {
|
|
1472
|
+
const { width, height } = this.canvasService.canvas;
|
|
1473
|
+
this.holes = holes.map((h) => {
|
|
1474
|
+
const p = Coordinate.denormalizePoint(h, {
|
|
1475
|
+
width: width || 800,
|
|
1476
|
+
height: height || 600
|
|
1477
|
+
});
|
|
1478
|
+
return { x: p.x, y: p.y };
|
|
1479
|
+
});
|
|
1480
|
+
this.redraw();
|
|
973
1481
|
}
|
|
974
|
-
return true;
|
|
975
1482
|
}
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
this.setup();
|
|
1486
|
+
}
|
|
1487
|
+
deactivate(context) {
|
|
1488
|
+
this.teardown();
|
|
1489
|
+
this.canvasService = void 0;
|
|
1490
|
+
this.context = void 0;
|
|
1491
|
+
}
|
|
1492
|
+
contribute() {
|
|
1493
|
+
return {
|
|
1494
|
+
[ContributionPointIds4.CONFIGURATIONS]: [
|
|
1495
|
+
{
|
|
1496
|
+
id: "hole.innerRadius",
|
|
1497
|
+
type: "number",
|
|
1498
|
+
label: "Inner Radius",
|
|
1499
|
+
min: 1,
|
|
1500
|
+
max: 100,
|
|
1501
|
+
default: 15
|
|
1502
|
+
},
|
|
1503
|
+
{
|
|
1504
|
+
id: "hole.outerRadius",
|
|
1505
|
+
type: "number",
|
|
1506
|
+
label: "Outer Radius",
|
|
1507
|
+
min: 1,
|
|
1508
|
+
max: 100,
|
|
1509
|
+
default: 25
|
|
1510
|
+
},
|
|
1511
|
+
{
|
|
1512
|
+
id: "hole.style",
|
|
1513
|
+
type: "select",
|
|
1514
|
+
label: "Line Style",
|
|
1515
|
+
options: ["solid", "dashed"],
|
|
1516
|
+
default: "solid"
|
|
1517
|
+
},
|
|
1518
|
+
{
|
|
1519
|
+
id: "hole.constraintTarget",
|
|
1520
|
+
type: "select",
|
|
1521
|
+
label: "Constraint Target",
|
|
1522
|
+
options: ["original", "bleed"],
|
|
1523
|
+
default: "bleed"
|
|
1524
|
+
}
|
|
1525
|
+
],
|
|
1526
|
+
[ContributionPointIds4.COMMANDS]: [
|
|
1527
|
+
{
|
|
1528
|
+
command: "resetHoles",
|
|
1529
|
+
title: "Reset Holes",
|
|
1530
|
+
handler: () => {
|
|
1531
|
+
if (!this.canvasService) return false;
|
|
1532
|
+
let defaultPos = { x: this.canvasService.canvas.width / 2, y: 50 };
|
|
1533
|
+
if (this.currentGeometry) {
|
|
1534
|
+
const g = this.currentGeometry;
|
|
1535
|
+
const topCenter = { x: g.x, y: g.y - g.height / 2 };
|
|
1536
|
+
defaultPos = getNearestPointOnDieline(topCenter, {
|
|
1537
|
+
...g,
|
|
1538
|
+
holes: []
|
|
1539
|
+
});
|
|
1540
|
+
}
|
|
1541
|
+
this.innerRadius = 15;
|
|
1542
|
+
this.outerRadius = 25;
|
|
1543
|
+
this.style = "solid";
|
|
1544
|
+
this.holes = [defaultPos];
|
|
1545
|
+
this.redraw();
|
|
1546
|
+
this.syncHolesToDieline();
|
|
1547
|
+
return true;
|
|
985
1548
|
}
|
|
986
|
-
return true;
|
|
987
1549
|
},
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
required: true
|
|
1550
|
+
{
|
|
1551
|
+
command: "addHole",
|
|
1552
|
+
title: "Add Hole",
|
|
1553
|
+
handler: (x, y) => {
|
|
1554
|
+
if (!this.holes) this.holes = [];
|
|
1555
|
+
this.holes.push({ x, y });
|
|
1556
|
+
this.redraw();
|
|
1557
|
+
this.syncHolesToDieline();
|
|
1558
|
+
return true;
|
|
998
1559
|
}
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
command: "clearHoles",
|
|
1563
|
+
title: "Clear Holes",
|
|
1564
|
+
handler: () => {
|
|
1565
|
+
this.holes = [];
|
|
1566
|
+
this.redraw();
|
|
1567
|
+
this.syncHolesToDieline();
|
|
1568
|
+
return true;
|
|
1008
1569
|
}
|
|
1009
|
-
return true;
|
|
1010
1570
|
}
|
|
1011
|
-
|
|
1571
|
+
]
|
|
1012
1572
|
};
|
|
1013
1573
|
}
|
|
1014
|
-
|
|
1015
|
-
this.
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
objects.sort(
|
|
1045
|
-
(a, b) => {
|
|
1046
|
-
var _a, _b, _c, _d;
|
|
1047
|
-
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);
|
|
1048
|
-
}
|
|
1049
|
-
);
|
|
1050
|
-
const newHoles = [];
|
|
1051
|
-
objects.forEach((obj) => {
|
|
1052
|
-
const currentPos = new Point(obj.left, obj.top);
|
|
1053
|
-
const newPos = this.calculateConstrainedPosition(currentPos, geometry);
|
|
1054
|
-
if (currentPos.distanceFrom(newPos) > 0.1) {
|
|
1055
|
-
obj.set({
|
|
1056
|
-
left: newPos.x,
|
|
1057
|
-
top: newPos.y
|
|
1058
|
-
});
|
|
1059
|
-
obj.setCoords();
|
|
1060
|
-
changed = true;
|
|
1574
|
+
setup() {
|
|
1575
|
+
if (!this.canvasService || !this.context) return;
|
|
1576
|
+
const canvas = this.canvasService.canvas;
|
|
1577
|
+
if (!this.handleDielineChange) {
|
|
1578
|
+
this.handleDielineChange = (geometry) => {
|
|
1579
|
+
this.currentGeometry = geometry;
|
|
1580
|
+
const changed = this.enforceConstraints();
|
|
1581
|
+
if (changed) {
|
|
1582
|
+
this.syncHolesToDieline();
|
|
1583
|
+
}
|
|
1584
|
+
};
|
|
1585
|
+
this.context.eventBus.on(
|
|
1586
|
+
"dieline:geometry:change",
|
|
1587
|
+
this.handleDielineChange
|
|
1588
|
+
);
|
|
1589
|
+
}
|
|
1590
|
+
const commandService = this.context.services.get("CommandService");
|
|
1591
|
+
if (commandService) {
|
|
1592
|
+
try {
|
|
1593
|
+
const geometry = commandService.executeCommand("getGeometry");
|
|
1594
|
+
if (geometry) {
|
|
1595
|
+
Promise.resolve(geometry).then((g) => {
|
|
1596
|
+
if (g) {
|
|
1597
|
+
this.currentGeometry = g;
|
|
1598
|
+
this.enforceConstraints();
|
|
1599
|
+
this.initializeHoles();
|
|
1600
|
+
}
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
} catch (e) {
|
|
1061
1604
|
}
|
|
1062
|
-
newHoles.push({ x: obj.left, y: obj.top });
|
|
1063
|
-
});
|
|
1064
|
-
if (changed) {
|
|
1065
|
-
this.options.holes = newHoles;
|
|
1066
|
-
editor.canvas.requestRenderAll();
|
|
1067
1605
|
}
|
|
1068
|
-
}
|
|
1069
|
-
setup(editor) {
|
|
1070
1606
|
if (!this.handleMoving) {
|
|
1071
1607
|
this.handleMoving = (e) => {
|
|
1072
1608
|
var _a;
|
|
1073
1609
|
const target = e.target;
|
|
1074
1610
|
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "hole-marker") return;
|
|
1075
|
-
|
|
1076
|
-
|
|
1611
|
+
if (!this.currentGeometry) return;
|
|
1612
|
+
const effectiveOffset = this.constraintTarget === "original" ? 0 : this.currentGeometry.offset;
|
|
1613
|
+
const constraintGeometry = {
|
|
1614
|
+
...this.currentGeometry,
|
|
1615
|
+
width: Math.max(0, this.currentGeometry.width + effectiveOffset * 2),
|
|
1616
|
+
height: Math.max(
|
|
1617
|
+
0,
|
|
1618
|
+
this.currentGeometry.height + effectiveOffset * 2
|
|
1619
|
+
),
|
|
1620
|
+
radius: Math.max(0, this.currentGeometry.radius + effectiveOffset)
|
|
1621
|
+
};
|
|
1077
1622
|
const p = new Point(target.left, target.top);
|
|
1078
|
-
const newPos = this.calculateConstrainedPosition(p,
|
|
1623
|
+
const newPos = this.calculateConstrainedPosition(p, constraintGeometry);
|
|
1079
1624
|
target.set({
|
|
1080
1625
|
left: newPos.x,
|
|
1081
1626
|
top: newPos.y
|
|
1082
1627
|
});
|
|
1083
1628
|
};
|
|
1084
|
-
|
|
1629
|
+
canvas.on("object:moving", this.handleMoving);
|
|
1085
1630
|
}
|
|
1086
1631
|
if (!this.handleModified) {
|
|
1087
1632
|
this.handleModified = (e) => {
|
|
1088
1633
|
var _a;
|
|
1089
1634
|
const target = e.target;
|
|
1090
1635
|
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "hole-marker") return;
|
|
1091
|
-
this.syncHolesFromCanvas(
|
|
1636
|
+
this.syncHolesFromCanvas();
|
|
1092
1637
|
};
|
|
1093
|
-
|
|
1638
|
+
canvas.on("object:modified", this.handleModified);
|
|
1094
1639
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1640
|
+
this.initializeHoles();
|
|
1641
|
+
}
|
|
1642
|
+
initializeHoles() {
|
|
1643
|
+
if (!this.canvasService) return;
|
|
1644
|
+
if (!this.holes || this.holes.length === 0) {
|
|
1645
|
+
let defaultPos = { x: this.canvasService.canvas.width / 2, y: 50 };
|
|
1646
|
+
if (this.currentGeometry) {
|
|
1647
|
+
const g = this.currentGeometry;
|
|
1100
1648
|
const topCenter = { x: g.x, y: g.y - g.height / 2 };
|
|
1101
1649
|
const snapped = getNearestPointOnDieline(topCenter, {
|
|
1102
1650
|
...g,
|
|
@@ -1104,65 +1652,97 @@ var HoleTool = class {
|
|
|
1104
1652
|
});
|
|
1105
1653
|
defaultPos = snapped;
|
|
1106
1654
|
}
|
|
1107
|
-
|
|
1108
|
-
}
|
|
1109
|
-
this.options = { ...opts };
|
|
1110
|
-
this.redraw(editor);
|
|
1111
|
-
const dielineTool = editor.getExtension("DielineTool");
|
|
1112
|
-
if (dielineTool && dielineTool.updateDieline) {
|
|
1113
|
-
dielineTool.updateDieline(editor);
|
|
1655
|
+
this.holes = [defaultPos];
|
|
1114
1656
|
}
|
|
1657
|
+
this.redraw();
|
|
1658
|
+
this.syncHolesToDieline();
|
|
1115
1659
|
}
|
|
1116
|
-
teardown(
|
|
1660
|
+
teardown() {
|
|
1661
|
+
if (!this.canvasService) return;
|
|
1662
|
+
const canvas = this.canvasService.canvas;
|
|
1117
1663
|
if (this.handleMoving) {
|
|
1118
|
-
|
|
1664
|
+
canvas.off("object:moving", this.handleMoving);
|
|
1119
1665
|
this.handleMoving = null;
|
|
1120
1666
|
}
|
|
1121
1667
|
if (this.handleModified) {
|
|
1122
|
-
|
|
1668
|
+
canvas.off("object:modified", this.handleModified);
|
|
1123
1669
|
this.handleModified = null;
|
|
1124
1670
|
}
|
|
1125
|
-
|
|
1671
|
+
if (this.handleDielineChange && this.context) {
|
|
1672
|
+
this.context.eventBus.off(
|
|
1673
|
+
"dieline:geometry:change",
|
|
1674
|
+
this.handleDielineChange
|
|
1675
|
+
);
|
|
1676
|
+
this.handleDielineChange = null;
|
|
1677
|
+
}
|
|
1678
|
+
const objects = canvas.getObjects().filter((obj) => {
|
|
1126
1679
|
var _a;
|
|
1127
1680
|
return ((_a = obj.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
1128
1681
|
});
|
|
1129
|
-
objects.forEach((obj) =>
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1682
|
+
objects.forEach((obj) => canvas.remove(obj));
|
|
1683
|
+
if (this.context) {
|
|
1684
|
+
const commandService = this.context.services.get("CommandService");
|
|
1685
|
+
if (commandService) {
|
|
1686
|
+
try {
|
|
1687
|
+
commandService.executeCommand("setHoles", []);
|
|
1688
|
+
} catch (e) {
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1138
1691
|
}
|
|
1692
|
+
this.canvasService.requestRenderAll();
|
|
1139
1693
|
}
|
|
1140
|
-
syncHolesFromCanvas(
|
|
1141
|
-
|
|
1694
|
+
syncHolesFromCanvas() {
|
|
1695
|
+
if (!this.canvasService) return;
|
|
1696
|
+
const objects = this.canvasService.canvas.getObjects().filter((obj) => {
|
|
1142
1697
|
var _a;
|
|
1143
1698
|
return ((_a = obj.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
1144
1699
|
});
|
|
1145
1700
|
const holes = objects.map((obj) => ({ x: obj.left, y: obj.top }));
|
|
1146
|
-
this.
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1701
|
+
this.holes = holes;
|
|
1702
|
+
this.syncHolesToDieline();
|
|
1703
|
+
}
|
|
1704
|
+
syncHolesToDieline() {
|
|
1705
|
+
if (!this.context || !this.canvasService) return;
|
|
1706
|
+
const { holes, innerRadius, outerRadius } = this;
|
|
1707
|
+
const currentHoles = holes || [];
|
|
1708
|
+
const width = this.canvasService.canvas.width || 800;
|
|
1709
|
+
const height = this.canvasService.canvas.height || 600;
|
|
1710
|
+
const configService = this.context.services.get(
|
|
1711
|
+
"ConfigurationService"
|
|
1712
|
+
);
|
|
1713
|
+
if (configService) {
|
|
1714
|
+
this.isUpdatingConfig = true;
|
|
1715
|
+
try {
|
|
1716
|
+
const normalizedHoles = currentHoles.map((h) => {
|
|
1717
|
+
const p = Coordinate.normalizePoint(h, { width, height });
|
|
1718
|
+
return {
|
|
1719
|
+
x: p.x,
|
|
1720
|
+
y: p.y,
|
|
1721
|
+
innerRadius,
|
|
1722
|
+
outerRadius
|
|
1723
|
+
};
|
|
1724
|
+
});
|
|
1725
|
+
configService.update("dieline.holes", normalizedHoles);
|
|
1726
|
+
} finally {
|
|
1727
|
+
this.isUpdatingConfig = false;
|
|
1728
|
+
}
|
|
1150
1729
|
}
|
|
1151
1730
|
}
|
|
1152
|
-
redraw(
|
|
1153
|
-
|
|
1731
|
+
redraw() {
|
|
1732
|
+
if (!this.canvasService) return;
|
|
1733
|
+
const canvas = this.canvasService.canvas;
|
|
1154
1734
|
const existing = canvas.getObjects().filter((obj) => {
|
|
1155
1735
|
var _a;
|
|
1156
1736
|
return ((_a = obj.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
1157
1737
|
});
|
|
1158
1738
|
existing.forEach((obj) => canvas.remove(obj));
|
|
1159
|
-
const { innerRadius, outerRadius, style, holes } = this
|
|
1739
|
+
const { innerRadius, outerRadius, style, holes } = this;
|
|
1160
1740
|
if (!holes || holes.length === 0) {
|
|
1161
|
-
|
|
1741
|
+
this.canvasService.requestRenderAll();
|
|
1162
1742
|
return;
|
|
1163
1743
|
}
|
|
1164
1744
|
holes.forEach((hole, index) => {
|
|
1165
|
-
const innerCircle = new
|
|
1745
|
+
const innerCircle = new Circle({
|
|
1166
1746
|
radius: innerRadius,
|
|
1167
1747
|
fill: "transparent",
|
|
1168
1748
|
stroke: "red",
|
|
@@ -1170,7 +1750,7 @@ var HoleTool = class {
|
|
|
1170
1750
|
originX: "center",
|
|
1171
1751
|
originY: "center"
|
|
1172
1752
|
});
|
|
1173
|
-
const outerCircle = new
|
|
1753
|
+
const outerCircle = new Circle({
|
|
1174
1754
|
radius: outerRadius,
|
|
1175
1755
|
fill: "transparent",
|
|
1176
1756
|
stroke: "#666",
|
|
@@ -1216,7 +1796,68 @@ var HoleTool = class {
|
|
|
1216
1796
|
canvas.add(holeGroup);
|
|
1217
1797
|
canvas.bringObjectToFront(holeGroup);
|
|
1218
1798
|
});
|
|
1219
|
-
canvas.
|
|
1799
|
+
const markers = canvas.getObjects().filter((o) => {
|
|
1800
|
+
var _a;
|
|
1801
|
+
return ((_a = o.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
1802
|
+
});
|
|
1803
|
+
markers.forEach((m) => canvas.bringObjectToFront(m));
|
|
1804
|
+
this.canvasService.requestRenderAll();
|
|
1805
|
+
}
|
|
1806
|
+
enforceConstraints() {
|
|
1807
|
+
const geometry = this.currentGeometry;
|
|
1808
|
+
if (!geometry || !this.canvasService) {
|
|
1809
|
+
console.log(
|
|
1810
|
+
"[HoleTool] Skipping enforceConstraints: No geometry or canvas service"
|
|
1811
|
+
);
|
|
1812
|
+
return false;
|
|
1813
|
+
}
|
|
1814
|
+
const effectiveOffset = this.constraintTarget === "original" ? 0 : geometry.offset;
|
|
1815
|
+
const constraintGeometry = {
|
|
1816
|
+
...geometry,
|
|
1817
|
+
width: Math.max(0, geometry.width + effectiveOffset * 2),
|
|
1818
|
+
height: Math.max(0, geometry.height + effectiveOffset * 2),
|
|
1819
|
+
radius: Math.max(0, geometry.radius + effectiveOffset)
|
|
1820
|
+
};
|
|
1821
|
+
const objects = this.canvasService.canvas.getObjects().filter((obj) => {
|
|
1822
|
+
var _a;
|
|
1823
|
+
return ((_a = obj.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
1824
|
+
});
|
|
1825
|
+
console.log(
|
|
1826
|
+
`[HoleTool] Enforcing constraints on ${objects.length} markers`
|
|
1827
|
+
);
|
|
1828
|
+
let changed = false;
|
|
1829
|
+
objects.sort(
|
|
1830
|
+
(a, b) => {
|
|
1831
|
+
var _a, _b, _c, _d;
|
|
1832
|
+
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);
|
|
1833
|
+
}
|
|
1834
|
+
);
|
|
1835
|
+
const newHoles = [];
|
|
1836
|
+
objects.forEach((obj) => {
|
|
1837
|
+
const currentPos = new Point(obj.left, obj.top);
|
|
1838
|
+
const newPos = this.calculateConstrainedPosition(
|
|
1839
|
+
currentPos,
|
|
1840
|
+
constraintGeometry
|
|
1841
|
+
);
|
|
1842
|
+
if (currentPos.distanceFrom(newPos) > 0.1) {
|
|
1843
|
+
console.log(
|
|
1844
|
+
`[HoleTool] Moving hole from (${currentPos.x}, ${currentPos.y}) to (${newPos.x}, ${newPos.y})`
|
|
1845
|
+
);
|
|
1846
|
+
obj.set({
|
|
1847
|
+
left: newPos.x,
|
|
1848
|
+
top: newPos.y
|
|
1849
|
+
});
|
|
1850
|
+
obj.setCoords();
|
|
1851
|
+
changed = true;
|
|
1852
|
+
}
|
|
1853
|
+
newHoles.push({ x: obj.left, y: obj.top });
|
|
1854
|
+
});
|
|
1855
|
+
if (changed) {
|
|
1856
|
+
this.holes = newHoles;
|
|
1857
|
+
this.canvasService.requestRenderAll();
|
|
1858
|
+
return true;
|
|
1859
|
+
}
|
|
1860
|
+
return false;
|
|
1220
1861
|
}
|
|
1221
1862
|
calculateConstrainedPosition(p, g) {
|
|
1222
1863
|
const options = {
|
|
@@ -1241,12 +1882,11 @@ var HoleTool = class {
|
|
|
1241
1882
|
}
|
|
1242
1883
|
let clampedDist = signedDist;
|
|
1243
1884
|
if (signedDist > 0) {
|
|
1244
|
-
clampedDist = Math.min(signedDist, this.
|
|
1885
|
+
clampedDist = Math.min(signedDist, this.innerRadius);
|
|
1245
1886
|
} else {
|
|
1246
|
-
clampedDist = Math.max(signedDist, -this.
|
|
1887
|
+
clampedDist = Math.max(signedDist, -this.outerRadius);
|
|
1247
1888
|
}
|
|
1248
1889
|
if (dist < 1e-3) return nearestP;
|
|
1249
|
-
const dir = v.scalarDivide(dist);
|
|
1250
1890
|
const scale = Math.abs(clampedDist) / (dist || 1);
|
|
1251
1891
|
const offset = v.scalarMultiply(scale);
|
|
1252
1892
|
return nearestP.add(offset);
|
|
@@ -1255,116 +1895,155 @@ var HoleTool = class {
|
|
|
1255
1895
|
|
|
1256
1896
|
// src/image.ts
|
|
1257
1897
|
import {
|
|
1258
|
-
|
|
1259
|
-
PooderLayer as PooderLayer4,
|
|
1260
|
-
util,
|
|
1261
|
-
Point as Point2
|
|
1898
|
+
ContributionPointIds as ContributionPointIds5
|
|
1262
1899
|
} from "@pooder/core";
|
|
1900
|
+
import { FabricImage as Image4, Point as Point2, util } from "fabric";
|
|
1263
1901
|
var ImageTool = class {
|
|
1264
|
-
constructor() {
|
|
1265
|
-
this.
|
|
1266
|
-
this.
|
|
1267
|
-
|
|
1268
|
-
url: "",
|
|
1269
|
-
opacity: 1
|
|
1270
|
-
};
|
|
1271
|
-
this.schema = {
|
|
1272
|
-
url: {
|
|
1273
|
-
type: "string",
|
|
1274
|
-
label: "Image URL"
|
|
1275
|
-
},
|
|
1276
|
-
opacity: {
|
|
1277
|
-
type: "number",
|
|
1278
|
-
min: 0,
|
|
1279
|
-
max: 1,
|
|
1280
|
-
step: 0.1,
|
|
1281
|
-
label: "Opacity"
|
|
1282
|
-
},
|
|
1283
|
-
width: {
|
|
1284
|
-
type: "number",
|
|
1285
|
-
label: "Width",
|
|
1286
|
-
min: 0,
|
|
1287
|
-
max: 5e3
|
|
1288
|
-
},
|
|
1289
|
-
height: {
|
|
1290
|
-
type: "number",
|
|
1291
|
-
label: "Height",
|
|
1292
|
-
min: 0,
|
|
1293
|
-
max: 5e3
|
|
1294
|
-
},
|
|
1295
|
-
angle: {
|
|
1296
|
-
type: "number",
|
|
1297
|
-
label: "Rotation",
|
|
1298
|
-
min: 0,
|
|
1299
|
-
max: 360
|
|
1300
|
-
},
|
|
1301
|
-
left: {
|
|
1302
|
-
type: "number",
|
|
1303
|
-
label: "Left",
|
|
1304
|
-
min: 0,
|
|
1305
|
-
max: 1e3
|
|
1306
|
-
},
|
|
1307
|
-
top: {
|
|
1308
|
-
type: "number",
|
|
1309
|
-
label: "Top",
|
|
1310
|
-
min: 0,
|
|
1311
|
-
max: 1e3
|
|
1312
|
-
}
|
|
1902
|
+
constructor(options) {
|
|
1903
|
+
this.id = "pooder.kit.image";
|
|
1904
|
+
this.metadata = {
|
|
1905
|
+
name: "ImageTool"
|
|
1313
1906
|
};
|
|
1314
|
-
this.
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
this.updateImage(editor, this.options);
|
|
1321
|
-
return true;
|
|
1322
|
-
},
|
|
1323
|
-
schema: {
|
|
1324
|
-
url: {
|
|
1325
|
-
type: "string",
|
|
1326
|
-
label: "Image URL",
|
|
1327
|
-
required: true
|
|
1328
|
-
},
|
|
1329
|
-
opacity: {
|
|
1330
|
-
type: "number",
|
|
1331
|
-
label: "Opacity",
|
|
1332
|
-
min: 0,
|
|
1333
|
-
max: 1,
|
|
1334
|
-
required: true
|
|
1335
|
-
},
|
|
1336
|
-
width: { type: "number", label: "Width" },
|
|
1337
|
-
height: { type: "number", label: "Height" },
|
|
1338
|
-
angle: { type: "number", label: "Angle" },
|
|
1339
|
-
left: { type: "number", label: "Left" },
|
|
1340
|
-
top: { type: "number", label: "Top" }
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
};
|
|
1344
|
-
}
|
|
1345
|
-
onMount(editor) {
|
|
1346
|
-
this.ensureLayer(editor);
|
|
1347
|
-
this.updateImage(editor, this.options);
|
|
1907
|
+
this._loadingUrl = null;
|
|
1908
|
+
this.url = "";
|
|
1909
|
+
this.opacity = 1;
|
|
1910
|
+
if (options) {
|
|
1911
|
+
Object.assign(this, options);
|
|
1912
|
+
}
|
|
1348
1913
|
}
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1914
|
+
activate(context) {
|
|
1915
|
+
this.context = context;
|
|
1916
|
+
this.canvasService = context.services.get("CanvasService");
|
|
1917
|
+
if (!this.canvasService) {
|
|
1918
|
+
console.warn("CanvasService not found for ImageTool");
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1921
|
+
const configService = context.services.get("ConfigurationService");
|
|
1922
|
+
if (configService) {
|
|
1923
|
+
this.url = configService.get("image.url", this.url);
|
|
1924
|
+
this.opacity = configService.get("image.opacity", this.opacity);
|
|
1925
|
+
this.width = configService.get("image.width", this.width);
|
|
1926
|
+
this.height = configService.get("image.height", this.height);
|
|
1927
|
+
this.angle = configService.get("image.angle", this.angle);
|
|
1928
|
+
this.left = configService.get("image.left", this.left);
|
|
1929
|
+
this.top = configService.get("image.top", this.top);
|
|
1930
|
+
configService.onAnyChange((e) => {
|
|
1931
|
+
if (e.key.startsWith("image.")) {
|
|
1932
|
+
const prop = e.key.split(".")[1];
|
|
1933
|
+
console.log(
|
|
1934
|
+
`[ImageTool] Config change detected: ${e.key} -> ${e.value}`
|
|
1935
|
+
);
|
|
1936
|
+
if (prop && prop in this) {
|
|
1937
|
+
this[prop] = e.value;
|
|
1938
|
+
this.updateImage();
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
});
|
|
1942
|
+
}
|
|
1943
|
+
this.ensureLayer();
|
|
1944
|
+
this.updateImage();
|
|
1945
|
+
}
|
|
1946
|
+
deactivate(context) {
|
|
1947
|
+
if (this.canvasService) {
|
|
1948
|
+
const layer = this.canvasService.getLayer("user");
|
|
1949
|
+
if (layer) {
|
|
1950
|
+
const userImage = this.canvasService.getObject("user-image", "user");
|
|
1951
|
+
if (userImage) {
|
|
1952
|
+
layer.remove(userImage);
|
|
1953
|
+
this.canvasService.requestRenderAll();
|
|
1954
|
+
}
|
|
1356
1955
|
}
|
|
1956
|
+
this.canvasService = void 0;
|
|
1957
|
+
this.context = void 0;
|
|
1357
1958
|
}
|
|
1358
1959
|
}
|
|
1359
|
-
|
|
1360
|
-
|
|
1960
|
+
contribute() {
|
|
1961
|
+
return {
|
|
1962
|
+
[ContributionPointIds5.CONFIGURATIONS]: [
|
|
1963
|
+
{
|
|
1964
|
+
id: "image.url",
|
|
1965
|
+
type: "string",
|
|
1966
|
+
label: "Image URL",
|
|
1967
|
+
default: this.url
|
|
1968
|
+
},
|
|
1969
|
+
{
|
|
1970
|
+
id: "image.opacity",
|
|
1971
|
+
type: "number",
|
|
1972
|
+
label: "Opacity",
|
|
1973
|
+
min: 0,
|
|
1974
|
+
max: 1,
|
|
1975
|
+
step: 0.1,
|
|
1976
|
+
default: this.opacity
|
|
1977
|
+
},
|
|
1978
|
+
{
|
|
1979
|
+
id: "image.width",
|
|
1980
|
+
type: "number",
|
|
1981
|
+
label: "Width",
|
|
1982
|
+
min: 0,
|
|
1983
|
+
max: 5e3,
|
|
1984
|
+
default: this.width
|
|
1985
|
+
},
|
|
1986
|
+
{
|
|
1987
|
+
id: "image.height",
|
|
1988
|
+
type: "number",
|
|
1989
|
+
label: "Height",
|
|
1990
|
+
min: 0,
|
|
1991
|
+
max: 5e3,
|
|
1992
|
+
default: this.height
|
|
1993
|
+
},
|
|
1994
|
+
{
|
|
1995
|
+
id: "image.angle",
|
|
1996
|
+
type: "number",
|
|
1997
|
+
label: "Rotation",
|
|
1998
|
+
min: 0,
|
|
1999
|
+
max: 360,
|
|
2000
|
+
default: this.angle
|
|
2001
|
+
},
|
|
2002
|
+
{
|
|
2003
|
+
id: "image.left",
|
|
2004
|
+
type: "number",
|
|
2005
|
+
label: "Left (Normalized)",
|
|
2006
|
+
min: 0,
|
|
2007
|
+
max: 1,
|
|
2008
|
+
default: this.left
|
|
2009
|
+
},
|
|
2010
|
+
{
|
|
2011
|
+
id: "image.top",
|
|
2012
|
+
type: "number",
|
|
2013
|
+
label: "Top (Normalized)",
|
|
2014
|
+
min: 0,
|
|
2015
|
+
max: 1,
|
|
2016
|
+
default: this.top
|
|
2017
|
+
}
|
|
2018
|
+
],
|
|
2019
|
+
[ContributionPointIds5.COMMANDS]: [
|
|
2020
|
+
{
|
|
2021
|
+
command: "setUserImage",
|
|
2022
|
+
title: "Set User Image",
|
|
2023
|
+
handler: (url, opacity, width, height, angle, left, top) => {
|
|
2024
|
+
if (this.url === url && this.opacity === opacity && this.width === width && this.height === height && this.angle === angle && this.left === left && this.top === top)
|
|
2025
|
+
return true;
|
|
2026
|
+
this.url = url;
|
|
2027
|
+
this.opacity = opacity;
|
|
2028
|
+
this.width = width;
|
|
2029
|
+
this.height = height;
|
|
2030
|
+
this.angle = angle;
|
|
2031
|
+
this.left = left;
|
|
2032
|
+
this.top = top;
|
|
2033
|
+
this.updateImage();
|
|
2034
|
+
return true;
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
]
|
|
2038
|
+
};
|
|
1361
2039
|
}
|
|
1362
|
-
ensureLayer(
|
|
1363
|
-
|
|
2040
|
+
ensureLayer() {
|
|
2041
|
+
if (!this.canvasService) return;
|
|
2042
|
+
let userLayer = this.canvasService.getLayer("user");
|
|
1364
2043
|
if (!userLayer) {
|
|
1365
|
-
userLayer =
|
|
1366
|
-
width:
|
|
1367
|
-
height:
|
|
2044
|
+
userLayer = this.canvasService.createLayer("user", {
|
|
2045
|
+
width: this.canvasService.canvas.width,
|
|
2046
|
+
height: this.canvasService.canvas.height,
|
|
1368
2047
|
left: 0,
|
|
1369
2048
|
top: 0,
|
|
1370
2049
|
originX: "left",
|
|
@@ -1372,42 +2051,64 @@ var ImageTool = class {
|
|
|
1372
2051
|
selectable: false,
|
|
1373
2052
|
evented: true,
|
|
1374
2053
|
subTargetCheck: true,
|
|
1375
|
-
interactive: true
|
|
1376
|
-
data: {
|
|
1377
|
-
id: "user"
|
|
1378
|
-
}
|
|
2054
|
+
interactive: true
|
|
1379
2055
|
});
|
|
1380
|
-
|
|
2056
|
+
const dielineLayer = this.canvasService.getLayer("dieline-overlay");
|
|
2057
|
+
if (dielineLayer) {
|
|
2058
|
+
const index = this.canvasService.canvas.getObjects().indexOf(dielineLayer);
|
|
2059
|
+
if (index >= 0) {
|
|
2060
|
+
this.canvasService.canvas.moveObjectTo(userLayer, index);
|
|
2061
|
+
}
|
|
2062
|
+
} else {
|
|
2063
|
+
const bgLayer = this.canvasService.getLayer("background");
|
|
2064
|
+
if (bgLayer) {
|
|
2065
|
+
this.canvasService.canvas.sendObjectToBack(bgLayer);
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
this.canvasService.requestRenderAll();
|
|
1381
2069
|
}
|
|
1382
2070
|
}
|
|
1383
|
-
updateImage(
|
|
2071
|
+
updateImage() {
|
|
1384
2072
|
var _a, _b;
|
|
1385
|
-
|
|
1386
|
-
|
|
2073
|
+
if (!this.canvasService) return;
|
|
2074
|
+
let { url, opacity, width, height, angle, left, top } = this;
|
|
2075
|
+
const layer = this.canvasService.getLayer("user");
|
|
1387
2076
|
if (!layer) {
|
|
1388
2077
|
console.warn("[ImageTool] User layer not found");
|
|
1389
2078
|
return;
|
|
1390
2079
|
}
|
|
1391
|
-
const userImage =
|
|
2080
|
+
const userImage = this.canvasService.getObject("user-image", "user");
|
|
1392
2081
|
if (this._loadingUrl === url) return;
|
|
1393
2082
|
if (userImage) {
|
|
1394
2083
|
const currentSrc = ((_a = userImage.getSrc) == null ? void 0 : _a.call(userImage)) || ((_b = userImage._element) == null ? void 0 : _b.src);
|
|
1395
2084
|
if (currentSrc !== url) {
|
|
1396
|
-
this.loadImage(
|
|
2085
|
+
this.loadImage(layer);
|
|
1397
2086
|
} else {
|
|
1398
2087
|
const updates = {};
|
|
1399
|
-
const
|
|
1400
|
-
const
|
|
2088
|
+
const canvasW = this.canvasService.canvas.width || 800;
|
|
2089
|
+
const canvasH = this.canvasService.canvas.height || 600;
|
|
2090
|
+
const centerX = canvasW / 2;
|
|
2091
|
+
const centerY = canvasH / 2;
|
|
1401
2092
|
if (userImage.opacity !== opacity) updates.opacity = opacity;
|
|
1402
2093
|
if (angle !== void 0 && userImage.angle !== angle)
|
|
1403
2094
|
updates.angle = angle;
|
|
2095
|
+
if (userImage.originX !== "center") {
|
|
2096
|
+
userImage.set({
|
|
2097
|
+
originX: "center",
|
|
2098
|
+
originY: "center",
|
|
2099
|
+
left: userImage.left + userImage.width * userImage.scaleX / 2,
|
|
2100
|
+
top: userImage.top + userImage.height * userImage.scaleY / 2
|
|
2101
|
+
});
|
|
2102
|
+
}
|
|
1404
2103
|
if (left !== void 0) {
|
|
1405
|
-
const
|
|
2104
|
+
const globalLeft = Coordinate.toAbsolute(left, canvasW);
|
|
2105
|
+
const localLeft = globalLeft - centerX;
|
|
1406
2106
|
if (Math.abs(userImage.left - localLeft) > 1)
|
|
1407
2107
|
updates.left = localLeft;
|
|
1408
2108
|
}
|
|
1409
2109
|
if (top !== void 0) {
|
|
1410
|
-
const
|
|
2110
|
+
const globalTop = Coordinate.toAbsolute(top, canvasH);
|
|
2111
|
+
const localTop = globalTop - centerY;
|
|
1411
2112
|
if (Math.abs(userImage.top - localTop) > 1) updates.top = localTop;
|
|
1412
2113
|
}
|
|
1413
2114
|
if (width !== void 0 && userImage.width)
|
|
@@ -1416,44 +2117,145 @@ var ImageTool = class {
|
|
|
1416
2117
|
updates.scaleY = height / userImage.height;
|
|
1417
2118
|
if (Object.keys(updates).length > 0) {
|
|
1418
2119
|
userImage.set(updates);
|
|
1419
|
-
|
|
2120
|
+
layer.dirty = true;
|
|
2121
|
+
this.canvasService.requestRenderAll();
|
|
1420
2122
|
}
|
|
1421
2123
|
}
|
|
1422
2124
|
} else {
|
|
1423
|
-
this.loadImage(
|
|
2125
|
+
this.loadImage(layer);
|
|
1424
2126
|
}
|
|
1425
2127
|
}
|
|
1426
|
-
loadImage(
|
|
1427
|
-
|
|
2128
|
+
loadImage(layer) {
|
|
2129
|
+
if (!this.canvasService) return;
|
|
2130
|
+
const { url } = this;
|
|
2131
|
+
if (!url) return;
|
|
1428
2132
|
this._loadingUrl = url;
|
|
1429
|
-
|
|
2133
|
+
Image4.fromURL(url, { crossOrigin: "anonymous" }).then((image) => {
|
|
2134
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1430
2135
|
if (this._loadingUrl !== url) return;
|
|
1431
2136
|
this._loadingUrl = null;
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
2137
|
+
let { opacity, width, height, angle, left, top } = this;
|
|
2138
|
+
if (this.context) {
|
|
2139
|
+
const configService = this.context.services.get(
|
|
2140
|
+
"ConfigurationService"
|
|
2141
|
+
);
|
|
2142
|
+
const dielineWidth = configService.get("dieline.width");
|
|
2143
|
+
const dielineHeight = configService.get("dieline.height");
|
|
2144
|
+
console.log(
|
|
2145
|
+
"[ImageTool] Dieline config debug:",
|
|
2146
|
+
{
|
|
2147
|
+
widthVal: dielineWidth,
|
|
2148
|
+
heightVal: dielineHeight,
|
|
2149
|
+
// Debug: dump all keys to see what is available
|
|
2150
|
+
allKeys: Array.from(
|
|
2151
|
+
((_a = configService.configValues) == null ? void 0 : _a.keys()) || []
|
|
2152
|
+
)
|
|
2153
|
+
},
|
|
2154
|
+
configService
|
|
2155
|
+
);
|
|
2156
|
+
if (width === void 0 && height === void 0) {
|
|
2157
|
+
const scale = Math.min(
|
|
2158
|
+
dielineWidth / (image.width || 1),
|
|
2159
|
+
dielineHeight / (image.height || 1)
|
|
2160
|
+
);
|
|
2161
|
+
width = (image.width || 1) * scale;
|
|
2162
|
+
height = (image.height || 1) * scale;
|
|
2163
|
+
this.width = width;
|
|
2164
|
+
this.height = height;
|
|
2165
|
+
}
|
|
2166
|
+
if (left === void 0 && top === void 0) {
|
|
2167
|
+
const dielinePos = configService == null ? void 0 : configService.get("dieline.position");
|
|
2168
|
+
if (dielinePos) {
|
|
2169
|
+
this.left = dielinePos.x;
|
|
2170
|
+
this.top = dielinePos.y;
|
|
2171
|
+
} else {
|
|
2172
|
+
this.left = 0.5;
|
|
2173
|
+
this.top = 0.5;
|
|
2174
|
+
}
|
|
2175
|
+
left = this.left;
|
|
2176
|
+
top = this.top;
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
const existingImage = this.canvasService.getObject(
|
|
2180
|
+
"user-image",
|
|
2181
|
+
"user"
|
|
2182
|
+
);
|
|
1435
2183
|
if (existingImage) {
|
|
1436
2184
|
const defaultLeft = existingImage.left;
|
|
1437
2185
|
const defaultTop = existingImage.top;
|
|
1438
2186
|
const defaultAngle = existingImage.angle;
|
|
1439
2187
|
const defaultScaleX = existingImage.scaleX;
|
|
1440
2188
|
const defaultScaleY = existingImage.scaleY;
|
|
2189
|
+
const canvasW = ((_b = this.canvasService) == null ? void 0 : _b.canvas.width) || 800;
|
|
2190
|
+
const canvasH = ((_c = this.canvasService) == null ? void 0 : _c.canvas.height) || 600;
|
|
2191
|
+
const centerX = canvasW / 2;
|
|
2192
|
+
const centerY = canvasH / 2;
|
|
2193
|
+
let targetLeft = left !== void 0 ? left : defaultLeft;
|
|
2194
|
+
let targetTop = top !== void 0 ? top : defaultTop;
|
|
2195
|
+
const configService = (_d = this.context) == null ? void 0 : _d.services.get(
|
|
2196
|
+
"ConfigurationService"
|
|
2197
|
+
);
|
|
2198
|
+
console.log("[ImageTool] Loading EXISTING image...", {
|
|
2199
|
+
canvasW,
|
|
2200
|
+
canvasH,
|
|
2201
|
+
centerX,
|
|
2202
|
+
centerY,
|
|
2203
|
+
incomingLeft: left,
|
|
2204
|
+
incomingTop: top,
|
|
2205
|
+
dielinePos: configService == null ? void 0 : configService.get("dieline.position"),
|
|
2206
|
+
existingImage: !!existingImage
|
|
2207
|
+
});
|
|
2208
|
+
if (left !== void 0) {
|
|
2209
|
+
const globalLeft = Coordinate.toAbsolute(left, canvasW);
|
|
2210
|
+
targetLeft = globalLeft;
|
|
2211
|
+
console.log("[ImageTool] Calculated targetLeft", {
|
|
2212
|
+
globalLeft,
|
|
2213
|
+
targetLeft
|
|
2214
|
+
});
|
|
2215
|
+
}
|
|
2216
|
+
if (top !== void 0) {
|
|
2217
|
+
const globalTop = Coordinate.toAbsolute(top, canvasH);
|
|
2218
|
+
targetTop = globalTop;
|
|
2219
|
+
console.log("[ImageTool] Calculated targetTop", {
|
|
2220
|
+
globalTop,
|
|
2221
|
+
targetTop
|
|
2222
|
+
});
|
|
2223
|
+
}
|
|
1441
2224
|
image.set({
|
|
1442
|
-
|
|
1443
|
-
|
|
2225
|
+
originX: "center",
|
|
2226
|
+
// Use center origin for easier positioning
|
|
2227
|
+
originY: "center",
|
|
2228
|
+
left: targetLeft,
|
|
2229
|
+
top: targetTop,
|
|
1444
2230
|
angle: angle !== void 0 ? angle : defaultAngle,
|
|
1445
2231
|
scaleX: width !== void 0 && image.width ? width / image.width : defaultScaleX,
|
|
1446
2232
|
scaleY: height !== void 0 && image.height ? height / image.height : defaultScaleY
|
|
1447
2233
|
});
|
|
1448
2234
|
layer.remove(existingImage);
|
|
1449
2235
|
} else {
|
|
2236
|
+
image.set({
|
|
2237
|
+
originX: "center",
|
|
2238
|
+
originY: "center"
|
|
2239
|
+
});
|
|
1450
2240
|
if (width !== void 0 && image.width)
|
|
1451
2241
|
image.scaleX = width / image.width;
|
|
1452
2242
|
if (height !== void 0 && image.height)
|
|
1453
2243
|
image.scaleY = height / image.height;
|
|
1454
2244
|
if (angle !== void 0) image.angle = angle;
|
|
1455
|
-
|
|
1456
|
-
|
|
2245
|
+
const canvasW = ((_e = this.canvasService) == null ? void 0 : _e.canvas.width) || 800;
|
|
2246
|
+
const canvasH = ((_f = this.canvasService) == null ? void 0 : _f.canvas.height) || 600;
|
|
2247
|
+
const centerX = canvasW / 2;
|
|
2248
|
+
const centerY = canvasH / 2;
|
|
2249
|
+
if (left !== void 0) {
|
|
2250
|
+
image.left = Coordinate.toAbsolute(left, canvasW);
|
|
2251
|
+
} else {
|
|
2252
|
+
image.left = centerX;
|
|
2253
|
+
}
|
|
2254
|
+
if (top !== void 0) {
|
|
2255
|
+
image.top = Coordinate.toAbsolute(top, canvasH);
|
|
2256
|
+
} else {
|
|
2257
|
+
image.top = centerY;
|
|
2258
|
+
}
|
|
1457
2259
|
}
|
|
1458
2260
|
image.set({
|
|
1459
2261
|
opacity: opacity !== void 0 ? opacity : 1,
|
|
@@ -1463,18 +2265,22 @@ var ImageTool = class {
|
|
|
1463
2265
|
});
|
|
1464
2266
|
layer.add(image);
|
|
1465
2267
|
image.on("modified", (e) => {
|
|
2268
|
+
var _a2, _b2;
|
|
1466
2269
|
const matrix = image.calcTransformMatrix();
|
|
1467
2270
|
const globalPoint = util.transformPoint(new Point2(0, 0), matrix);
|
|
1468
|
-
this.
|
|
1469
|
-
this.
|
|
1470
|
-
this.
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
if (image.
|
|
1474
|
-
|
|
1475
|
-
|
|
2271
|
+
const canvasW = ((_a2 = this.canvasService) == null ? void 0 : _a2.canvas.width) || 800;
|
|
2272
|
+
const canvasH = ((_b2 = this.canvasService) == null ? void 0 : _b2.canvas.height) || 600;
|
|
2273
|
+
this.left = Coordinate.toNormalized(globalPoint.x, canvasW);
|
|
2274
|
+
this.top = Coordinate.toNormalized(globalPoint.y, canvasH);
|
|
2275
|
+
this.angle = e.target.angle;
|
|
2276
|
+
if (image.width) this.width = e.target.width * e.target.scaleX;
|
|
2277
|
+
if (image.height) this.height = e.target.height * e.target.scaleY;
|
|
2278
|
+
if (this.context) {
|
|
2279
|
+
this.context.eventBus.emit("update");
|
|
2280
|
+
}
|
|
1476
2281
|
});
|
|
1477
|
-
|
|
2282
|
+
layer.dirty = true;
|
|
2283
|
+
this.canvasService.requestRenderAll();
|
|
1478
2284
|
}).catch((err) => {
|
|
1479
2285
|
if (this._loadingUrl === url) this._loadingUrl = null;
|
|
1480
2286
|
console.error("Failed to load image", url, err);
|
|
@@ -1484,73 +2290,110 @@ var ImageTool = class {
|
|
|
1484
2290
|
|
|
1485
2291
|
// src/white-ink.ts
|
|
1486
2292
|
import {
|
|
1487
|
-
|
|
1488
|
-
filters,
|
|
1489
|
-
PooderLayer as PooderLayer5
|
|
2293
|
+
ContributionPointIds as ContributionPointIds6
|
|
1490
2294
|
} from "@pooder/core";
|
|
2295
|
+
import { FabricImage as Image5, filters } from "fabric";
|
|
1491
2296
|
var WhiteInkTool = class {
|
|
1492
|
-
constructor() {
|
|
1493
|
-
this.
|
|
1494
|
-
this.
|
|
1495
|
-
|
|
1496
|
-
opacity: 1,
|
|
1497
|
-
enableClip: false
|
|
2297
|
+
constructor(options) {
|
|
2298
|
+
this.id = "pooder.kit.white-ink";
|
|
2299
|
+
this.metadata = {
|
|
2300
|
+
name: "WhiteInkTool"
|
|
1498
2301
|
};
|
|
1499
|
-
this.
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
2302
|
+
this.customMask = "";
|
|
2303
|
+
this.opacity = 1;
|
|
2304
|
+
this.enableClip = false;
|
|
2305
|
+
this._loadingUrl = null;
|
|
2306
|
+
if (options) {
|
|
2307
|
+
Object.assign(this, options);
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
activate(context) {
|
|
2311
|
+
this.canvasService = context.services.get("CanvasService");
|
|
2312
|
+
if (!this.canvasService) {
|
|
2313
|
+
console.warn("CanvasService not found for WhiteInkTool");
|
|
2314
|
+
return;
|
|
2315
|
+
}
|
|
2316
|
+
const configService = context.services.get("ConfigurationService");
|
|
2317
|
+
if (configService) {
|
|
2318
|
+
this.customMask = configService.get(
|
|
2319
|
+
"whiteInk.customMask",
|
|
2320
|
+
this.customMask
|
|
2321
|
+
);
|
|
2322
|
+
this.opacity = configService.get("whiteInk.opacity", this.opacity);
|
|
2323
|
+
this.enableClip = configService.get(
|
|
2324
|
+
"whiteInk.enableClip",
|
|
2325
|
+
this.enableClip
|
|
2326
|
+
);
|
|
2327
|
+
configService.onAnyChange((e) => {
|
|
2328
|
+
if (e.key.startsWith("whiteInk.")) {
|
|
2329
|
+
const prop = e.key.split(".")[1];
|
|
2330
|
+
console.log(
|
|
2331
|
+
`[WhiteInkTool] Config change detected: ${e.key} -> ${e.value}`
|
|
2332
|
+
);
|
|
2333
|
+
if (prop && prop in this) {
|
|
2334
|
+
this[prop] = e.value;
|
|
2335
|
+
this.updateWhiteInk();
|
|
1533
2336
|
}
|
|
1534
2337
|
}
|
|
1535
|
-
}
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
this.setup(editor);
|
|
1540
|
-
this.updateWhiteInk(editor, this.options);
|
|
2338
|
+
});
|
|
2339
|
+
}
|
|
2340
|
+
this.setup();
|
|
2341
|
+
this.updateWhiteInk();
|
|
1541
2342
|
}
|
|
1542
|
-
|
|
1543
|
-
this.teardown(
|
|
2343
|
+
deactivate(context) {
|
|
2344
|
+
this.teardown();
|
|
2345
|
+
this.canvasService = void 0;
|
|
1544
2346
|
}
|
|
1545
|
-
|
|
1546
|
-
|
|
2347
|
+
contribute() {
|
|
2348
|
+
return {
|
|
2349
|
+
[ContributionPointIds6.CONFIGURATIONS]: [
|
|
2350
|
+
{
|
|
2351
|
+
id: "whiteInk.customMask",
|
|
2352
|
+
type: "string",
|
|
2353
|
+
label: "Custom Mask URL",
|
|
2354
|
+
default: ""
|
|
2355
|
+
},
|
|
2356
|
+
{
|
|
2357
|
+
id: "whiteInk.opacity",
|
|
2358
|
+
type: "number",
|
|
2359
|
+
label: "Opacity",
|
|
2360
|
+
min: 0,
|
|
2361
|
+
max: 1,
|
|
2362
|
+
step: 0.01,
|
|
2363
|
+
default: 1
|
|
2364
|
+
},
|
|
2365
|
+
{
|
|
2366
|
+
id: "whiteInk.enableClip",
|
|
2367
|
+
type: "boolean",
|
|
2368
|
+
label: "Enable Clip",
|
|
2369
|
+
default: false
|
|
2370
|
+
}
|
|
2371
|
+
],
|
|
2372
|
+
[ContributionPointIds6.COMMANDS]: [
|
|
2373
|
+
{
|
|
2374
|
+
command: "setWhiteInkImage",
|
|
2375
|
+
title: "Set White Ink Image",
|
|
2376
|
+
handler: (customMask, opacity, enableClip = true) => {
|
|
2377
|
+
if (this.customMask === customMask && this.opacity === opacity && this.enableClip === enableClip)
|
|
2378
|
+
return true;
|
|
2379
|
+
this.customMask = customMask;
|
|
2380
|
+
this.opacity = opacity;
|
|
2381
|
+
this.enableClip = enableClip;
|
|
2382
|
+
this.updateWhiteInk();
|
|
2383
|
+
return true;
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
]
|
|
2387
|
+
};
|
|
1547
2388
|
}
|
|
1548
|
-
setup(
|
|
1549
|
-
|
|
2389
|
+
setup() {
|
|
2390
|
+
if (!this.canvasService) return;
|
|
2391
|
+
const canvas = this.canvasService.canvas;
|
|
2392
|
+
let userLayer = this.canvasService.getLayer("user");
|
|
1550
2393
|
if (!userLayer) {
|
|
1551
|
-
userLayer =
|
|
1552
|
-
width:
|
|
1553
|
-
height:
|
|
2394
|
+
userLayer = this.canvasService.createLayer("user", {
|
|
2395
|
+
width: canvas.width,
|
|
2396
|
+
height: canvas.height,
|
|
1554
2397
|
left: 0,
|
|
1555
2398
|
top: 0,
|
|
1556
2399
|
originX: "left",
|
|
@@ -1558,61 +2401,58 @@ var WhiteInkTool = class {
|
|
|
1558
2401
|
selectable: false,
|
|
1559
2402
|
evented: true,
|
|
1560
2403
|
subTargetCheck: true,
|
|
1561
|
-
interactive: true
|
|
1562
|
-
data: {
|
|
1563
|
-
id: "user"
|
|
1564
|
-
}
|
|
2404
|
+
interactive: true
|
|
1565
2405
|
});
|
|
1566
|
-
|
|
2406
|
+
canvas.add(userLayer);
|
|
1567
2407
|
}
|
|
1568
2408
|
if (!this.syncHandler) {
|
|
1569
2409
|
this.syncHandler = (e) => {
|
|
1570
2410
|
var _a;
|
|
1571
2411
|
const target = e.target;
|
|
1572
2412
|
if (target && ((_a = target.data) == null ? void 0 : _a.id) === "user-image") {
|
|
1573
|
-
this.syncWithUserImage(
|
|
2413
|
+
this.syncWithUserImage();
|
|
1574
2414
|
}
|
|
1575
2415
|
};
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
2416
|
+
canvas.on("object:moving", this.syncHandler);
|
|
2417
|
+
canvas.on("object:scaling", this.syncHandler);
|
|
2418
|
+
canvas.on("object:rotating", this.syncHandler);
|
|
2419
|
+
canvas.on("object:modified", this.syncHandler);
|
|
1580
2420
|
}
|
|
1581
2421
|
}
|
|
1582
|
-
teardown(
|
|
2422
|
+
teardown() {
|
|
2423
|
+
if (!this.canvasService) return;
|
|
2424
|
+
const canvas = this.canvasService.canvas;
|
|
1583
2425
|
if (this.syncHandler) {
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
2426
|
+
canvas.off("object:moving", this.syncHandler);
|
|
2427
|
+
canvas.off("object:scaling", this.syncHandler);
|
|
2428
|
+
canvas.off("object:rotating", this.syncHandler);
|
|
2429
|
+
canvas.off("object:modified", this.syncHandler);
|
|
1588
2430
|
this.syncHandler = void 0;
|
|
1589
2431
|
}
|
|
1590
|
-
const layer =
|
|
2432
|
+
const layer = this.canvasService.getLayer("user");
|
|
1591
2433
|
if (layer) {
|
|
1592
|
-
const whiteInk =
|
|
2434
|
+
const whiteInk = this.canvasService.getObject("white-ink", "user");
|
|
1593
2435
|
if (whiteInk) {
|
|
1594
2436
|
layer.remove(whiteInk);
|
|
1595
2437
|
}
|
|
1596
2438
|
}
|
|
1597
|
-
const userImage =
|
|
2439
|
+
const userImage = this.canvasService.getObject("user-image", "user");
|
|
1598
2440
|
if (userImage && userImage.clipPath) {
|
|
1599
2441
|
userImage.set({ clipPath: void 0 });
|
|
1600
2442
|
}
|
|
1601
|
-
|
|
1602
|
-
}
|
|
1603
|
-
onUpdate(editor, state) {
|
|
1604
|
-
this.updateWhiteInk(editor, this.options);
|
|
2443
|
+
this.canvasService.requestRenderAll();
|
|
1605
2444
|
}
|
|
1606
|
-
updateWhiteInk(
|
|
2445
|
+
updateWhiteInk() {
|
|
1607
2446
|
var _a, _b;
|
|
1608
|
-
|
|
1609
|
-
const
|
|
2447
|
+
if (!this.canvasService) return;
|
|
2448
|
+
const { customMask, opacity, enableClip } = this;
|
|
2449
|
+
const layer = this.canvasService.getLayer("user");
|
|
1610
2450
|
if (!layer) {
|
|
1611
2451
|
console.warn("[WhiteInkTool] User layer not found");
|
|
1612
2452
|
return;
|
|
1613
2453
|
}
|
|
1614
|
-
const whiteInk =
|
|
1615
|
-
const userImage =
|
|
2454
|
+
const whiteInk = this.canvasService.getObject("white-ink", "user");
|
|
2455
|
+
const userImage = this.canvasService.getObject("user-image", "user");
|
|
1616
2456
|
if (!customMask) {
|
|
1617
2457
|
if (whiteInk) {
|
|
1618
2458
|
layer.remove(whiteInk);
|
|
@@ -1620,45 +2460,46 @@ var WhiteInkTool = class {
|
|
|
1620
2460
|
if (userImage && userImage.clipPath) {
|
|
1621
2461
|
userImage.set({ clipPath: void 0 });
|
|
1622
2462
|
}
|
|
1623
|
-
|
|
2463
|
+
layer.dirty = true;
|
|
2464
|
+
this.canvasService.requestRenderAll();
|
|
1624
2465
|
return;
|
|
1625
2466
|
}
|
|
1626
2467
|
if (whiteInk) {
|
|
1627
2468
|
const currentSrc = ((_a = whiteInk.getSrc) == null ? void 0 : _a.call(whiteInk)) || ((_b = whiteInk._element) == null ? void 0 : _b.src);
|
|
1628
2469
|
if (currentSrc !== customMask) {
|
|
1629
|
-
this.loadWhiteInk(
|
|
1630
|
-
editor,
|
|
1631
|
-
layer,
|
|
1632
|
-
customMask,
|
|
1633
|
-
opacity,
|
|
1634
|
-
enableClip,
|
|
1635
|
-
whiteInk
|
|
1636
|
-
);
|
|
2470
|
+
this.loadWhiteInk(layer, customMask, opacity, enableClip, whiteInk);
|
|
1637
2471
|
} else {
|
|
1638
2472
|
if (whiteInk.opacity !== opacity) {
|
|
1639
2473
|
whiteInk.set({ opacity });
|
|
1640
|
-
|
|
2474
|
+
layer.dirty = true;
|
|
2475
|
+
this.canvasService.requestRenderAll();
|
|
1641
2476
|
}
|
|
1642
2477
|
}
|
|
1643
2478
|
} else {
|
|
1644
|
-
this.loadWhiteInk(
|
|
2479
|
+
this.loadWhiteInk(layer, customMask, opacity, enableClip);
|
|
1645
2480
|
}
|
|
1646
2481
|
if (userImage) {
|
|
1647
2482
|
if (enableClip) {
|
|
1648
2483
|
if (!userImage.clipPath) {
|
|
1649
|
-
this.applyClipPath(
|
|
2484
|
+
this.applyClipPath(customMask);
|
|
1650
2485
|
}
|
|
1651
2486
|
} else {
|
|
1652
2487
|
if (userImage.clipPath) {
|
|
1653
2488
|
userImage.set({ clipPath: void 0 });
|
|
1654
|
-
|
|
2489
|
+
layer.dirty = true;
|
|
2490
|
+
this.canvasService.requestRenderAll();
|
|
1655
2491
|
}
|
|
1656
2492
|
}
|
|
1657
2493
|
}
|
|
1658
2494
|
}
|
|
1659
|
-
loadWhiteInk(
|
|
1660
|
-
|
|
2495
|
+
loadWhiteInk(layer, url, opacity, enableClip, oldImage) {
|
|
2496
|
+
if (!this.canvasService) return;
|
|
2497
|
+
if (this._loadingUrl === url) return;
|
|
2498
|
+
this._loadingUrl = url;
|
|
2499
|
+
Image5.fromURL(url, { crossOrigin: "anonymous" }).then((image) => {
|
|
1661
2500
|
var _a;
|
|
2501
|
+
if (this._loadingUrl !== url) return;
|
|
2502
|
+
this._loadingUrl = null;
|
|
1662
2503
|
if (oldImage) {
|
|
1663
2504
|
layer.remove(oldImage);
|
|
1664
2505
|
}
|
|
@@ -1678,26 +2519,29 @@ var WhiteInkTool = class {
|
|
|
1678
2519
|
}
|
|
1679
2520
|
});
|
|
1680
2521
|
layer.add(image);
|
|
1681
|
-
const userImage =
|
|
2522
|
+
const userImage = this.canvasService.getObject("user-image", "user");
|
|
1682
2523
|
if (userImage) {
|
|
1683
2524
|
layer.remove(userImage);
|
|
1684
2525
|
layer.add(userImage);
|
|
1685
2526
|
}
|
|
1686
2527
|
if (enableClip) {
|
|
1687
|
-
this.applyClipPath(
|
|
2528
|
+
this.applyClipPath(url);
|
|
1688
2529
|
} else if (userImage) {
|
|
1689
2530
|
userImage.set({ clipPath: void 0 });
|
|
1690
2531
|
}
|
|
1691
|
-
this.syncWithUserImage(
|
|
1692
|
-
|
|
2532
|
+
this.syncWithUserImage();
|
|
2533
|
+
layer.dirty = true;
|
|
2534
|
+
this.canvasService.requestRenderAll();
|
|
1693
2535
|
}).catch((err) => {
|
|
1694
2536
|
console.error("Failed to load white ink mask", url, err);
|
|
2537
|
+
this._loadingUrl = null;
|
|
1695
2538
|
});
|
|
1696
2539
|
}
|
|
1697
|
-
applyClipPath(
|
|
1698
|
-
|
|
2540
|
+
applyClipPath(url) {
|
|
2541
|
+
if (!this.canvasService) return;
|
|
2542
|
+
const userImage = this.canvasService.getObject("user-image", "user");
|
|
1699
2543
|
if (!userImage) return;
|
|
1700
|
-
|
|
2544
|
+
Image5.fromURL(url, { crossOrigin: "anonymous" }).then((maskImage) => {
|
|
1701
2545
|
maskImage.set({
|
|
1702
2546
|
originX: "center",
|
|
1703
2547
|
originY: "center",
|
|
@@ -1708,14 +2552,17 @@ var WhiteInkTool = class {
|
|
|
1708
2552
|
scaleY: userImage.height / maskImage.height
|
|
1709
2553
|
});
|
|
1710
2554
|
userImage.set({ clipPath: maskImage });
|
|
1711
|
-
|
|
2555
|
+
const layer = this.canvasService.getLayer("user");
|
|
2556
|
+
if (layer) layer.dirty = true;
|
|
2557
|
+
this.canvasService.requestRenderAll();
|
|
1712
2558
|
}).catch((err) => {
|
|
1713
2559
|
console.error("Failed to load clip path", url, err);
|
|
1714
2560
|
});
|
|
1715
2561
|
}
|
|
1716
|
-
syncWithUserImage(
|
|
1717
|
-
|
|
1718
|
-
const
|
|
2562
|
+
syncWithUserImage() {
|
|
2563
|
+
if (!this.canvasService) return;
|
|
2564
|
+
const userImage = this.canvasService.getObject("user-image", "user");
|
|
2565
|
+
const whiteInk = this.canvasService.getObject("white-ink", "user");
|
|
1719
2566
|
if (userImage && whiteInk) {
|
|
1720
2567
|
whiteInk.set({
|
|
1721
2568
|
left: userImage.left,
|
|
@@ -1736,119 +2583,174 @@ var WhiteInkTool = class {
|
|
|
1736
2583
|
|
|
1737
2584
|
// src/ruler.ts
|
|
1738
2585
|
import {
|
|
1739
|
-
|
|
1740
|
-
Rect as Rect3,
|
|
1741
|
-
Line,
|
|
1742
|
-
Text
|
|
2586
|
+
ContributionPointIds as ContributionPointIds7
|
|
1743
2587
|
} from "@pooder/core";
|
|
2588
|
+
import { Rect as Rect2, Line, Text } from "fabric";
|
|
1744
2589
|
var RulerTool = class {
|
|
1745
|
-
constructor() {
|
|
1746
|
-
this.
|
|
1747
|
-
this.
|
|
1748
|
-
|
|
1749
|
-
thickness: 20,
|
|
1750
|
-
backgroundColor: "#f0f0f0",
|
|
1751
|
-
textColor: "#333333",
|
|
1752
|
-
lineColor: "#999999",
|
|
1753
|
-
fontSize: 10
|
|
2590
|
+
constructor(options) {
|
|
2591
|
+
this.id = "pooder.kit.ruler";
|
|
2592
|
+
this.metadata = {
|
|
2593
|
+
name: "RulerTool"
|
|
1754
2594
|
};
|
|
1755
|
-
this.
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
this.
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
2595
|
+
this.unit = "px";
|
|
2596
|
+
this.thickness = 20;
|
|
2597
|
+
this.backgroundColor = "#f0f0f0";
|
|
2598
|
+
this.textColor = "#333333";
|
|
2599
|
+
this.lineColor = "#999999";
|
|
2600
|
+
this.fontSize = 10;
|
|
2601
|
+
if (options) {
|
|
2602
|
+
Object.assign(this, options);
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
activate(context) {
|
|
2606
|
+
this.canvasService = context.services.get("CanvasService");
|
|
2607
|
+
if (!this.canvasService) {
|
|
2608
|
+
console.warn("CanvasService not found for RulerTool");
|
|
2609
|
+
return;
|
|
2610
|
+
}
|
|
2611
|
+
const configService = context.services.get("ConfigurationService");
|
|
2612
|
+
if (configService) {
|
|
2613
|
+
this.unit = configService.get("ruler.unit", this.unit);
|
|
2614
|
+
this.thickness = configService.get("ruler.thickness", this.thickness);
|
|
2615
|
+
this.backgroundColor = configService.get(
|
|
2616
|
+
"ruler.backgroundColor",
|
|
2617
|
+
this.backgroundColor
|
|
2618
|
+
);
|
|
2619
|
+
this.textColor = configService.get("ruler.textColor", this.textColor);
|
|
2620
|
+
this.lineColor = configService.get("ruler.lineColor", this.lineColor);
|
|
2621
|
+
this.fontSize = configService.get("ruler.fontSize", this.fontSize);
|
|
2622
|
+
configService.onAnyChange((e) => {
|
|
2623
|
+
if (e.key.startsWith("ruler.")) {
|
|
2624
|
+
const prop = e.key.split(".")[1];
|
|
2625
|
+
if (prop && prop in this) {
|
|
2626
|
+
this[prop] = e.value;
|
|
2627
|
+
this.updateRuler();
|
|
1781
2628
|
}
|
|
1782
2629
|
}
|
|
1783
|
-
}
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
2630
|
+
});
|
|
2631
|
+
}
|
|
2632
|
+
this.createLayer();
|
|
2633
|
+
this.updateRuler();
|
|
2634
|
+
}
|
|
2635
|
+
deactivate(context) {
|
|
2636
|
+
this.destroyLayer();
|
|
2637
|
+
this.canvasService = void 0;
|
|
2638
|
+
}
|
|
2639
|
+
contribute() {
|
|
2640
|
+
return {
|
|
2641
|
+
[ContributionPointIds7.CONFIGURATIONS]: [
|
|
2642
|
+
{
|
|
2643
|
+
id: "ruler.unit",
|
|
2644
|
+
type: "select",
|
|
2645
|
+
label: "Unit",
|
|
2646
|
+
options: ["px", "mm", "cm", "in"],
|
|
2647
|
+
default: "px"
|
|
2648
|
+
},
|
|
2649
|
+
{
|
|
2650
|
+
id: "ruler.thickness",
|
|
2651
|
+
type: "number",
|
|
2652
|
+
label: "Thickness",
|
|
2653
|
+
min: 10,
|
|
2654
|
+
max: 100,
|
|
2655
|
+
default: 20
|
|
2656
|
+
},
|
|
2657
|
+
{
|
|
2658
|
+
id: "ruler.backgroundColor",
|
|
2659
|
+
type: "color",
|
|
2660
|
+
label: "Background Color",
|
|
2661
|
+
default: "#f0f0f0"
|
|
2662
|
+
},
|
|
2663
|
+
{
|
|
2664
|
+
id: "ruler.textColor",
|
|
2665
|
+
type: "color",
|
|
2666
|
+
label: "Text Color",
|
|
2667
|
+
default: "#333333"
|
|
2668
|
+
},
|
|
2669
|
+
{
|
|
2670
|
+
id: "ruler.lineColor",
|
|
2671
|
+
type: "color",
|
|
2672
|
+
label: "Line Color",
|
|
2673
|
+
default: "#999999"
|
|
2674
|
+
},
|
|
2675
|
+
{
|
|
2676
|
+
id: "ruler.fontSize",
|
|
2677
|
+
type: "number",
|
|
2678
|
+
label: "Font Size",
|
|
2679
|
+
min: 8,
|
|
2680
|
+
max: 24,
|
|
2681
|
+
default: 10
|
|
2682
|
+
}
|
|
2683
|
+
],
|
|
2684
|
+
[ContributionPointIds7.COMMANDS]: [
|
|
2685
|
+
{
|
|
2686
|
+
command: "setUnit",
|
|
2687
|
+
title: "Set Ruler Unit",
|
|
2688
|
+
handler: (unit) => {
|
|
2689
|
+
if (this.unit === unit) return true;
|
|
2690
|
+
this.unit = unit;
|
|
2691
|
+
this.updateRuler();
|
|
1788
2692
|
return true;
|
|
1789
|
-
|
|
1790
|
-
this.updateRuler(editor);
|
|
1791
|
-
return true;
|
|
2693
|
+
}
|
|
1792
2694
|
},
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
2695
|
+
{
|
|
2696
|
+
command: "setTheme",
|
|
2697
|
+
title: "Set Ruler Theme",
|
|
2698
|
+
handler: (theme) => {
|
|
2699
|
+
const oldState = {
|
|
2700
|
+
backgroundColor: this.backgroundColor,
|
|
2701
|
+
textColor: this.textColor,
|
|
2702
|
+
lineColor: this.lineColor,
|
|
2703
|
+
fontSize: this.fontSize,
|
|
2704
|
+
thickness: this.thickness
|
|
2705
|
+
};
|
|
2706
|
+
const newState = { ...oldState, ...theme };
|
|
2707
|
+
if (JSON.stringify(newState) === JSON.stringify(oldState))
|
|
2708
|
+
return true;
|
|
2709
|
+
Object.assign(this, newState);
|
|
2710
|
+
this.updateRuler();
|
|
2711
|
+
return true;
|
|
1798
2712
|
}
|
|
1799
2713
|
}
|
|
1800
|
-
|
|
2714
|
+
]
|
|
1801
2715
|
};
|
|
1802
2716
|
}
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
this.
|
|
1806
|
-
}
|
|
1807
|
-
onUnmount(editor) {
|
|
1808
|
-
this.destroyLayer(editor);
|
|
1809
|
-
}
|
|
1810
|
-
onUpdate(editor, state) {
|
|
1811
|
-
this.updateRuler(editor);
|
|
1812
|
-
}
|
|
1813
|
-
onDestroy(editor) {
|
|
1814
|
-
this.destroyLayer(editor);
|
|
2717
|
+
getLayer() {
|
|
2718
|
+
var _a;
|
|
2719
|
+
return (_a = this.canvasService) == null ? void 0 : _a.getLayer("ruler-overlay");
|
|
1815
2720
|
}
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
2721
|
+
createLayer() {
|
|
2722
|
+
if (!this.canvasService) return;
|
|
2723
|
+
const canvas = this.canvasService.canvas;
|
|
2724
|
+
const width = canvas.width || 800;
|
|
2725
|
+
const height = canvas.height || 600;
|
|
2726
|
+
const layer = this.canvasService.createLayer("ruler-overlay", {
|
|
2727
|
+
width,
|
|
2728
|
+
height,
|
|
2729
|
+
selectable: false,
|
|
2730
|
+
evented: false,
|
|
2731
|
+
left: 0,
|
|
2732
|
+
top: 0,
|
|
2733
|
+
originX: "left",
|
|
2734
|
+
originY: "top"
|
|
1820
2735
|
});
|
|
2736
|
+
canvas.bringObjectToFront(layer);
|
|
1821
2737
|
}
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
const width = editor.canvas.width || 800;
|
|
1826
|
-
const height = editor.canvas.height || 600;
|
|
1827
|
-
layer = new PooderLayer6([], {
|
|
1828
|
-
width,
|
|
1829
|
-
height,
|
|
1830
|
-
selectable: false,
|
|
1831
|
-
evented: false,
|
|
1832
|
-
data: { id: "ruler-overlay" }
|
|
1833
|
-
});
|
|
1834
|
-
editor.canvas.add(layer);
|
|
1835
|
-
}
|
|
1836
|
-
editor.canvas.bringObjectToFront(layer);
|
|
1837
|
-
}
|
|
1838
|
-
destroyLayer(editor) {
|
|
1839
|
-
const layer = this.getLayer(editor);
|
|
2738
|
+
destroyLayer() {
|
|
2739
|
+
if (!this.canvasService) return;
|
|
2740
|
+
const layer = this.getLayer();
|
|
1840
2741
|
if (layer) {
|
|
1841
|
-
|
|
2742
|
+
this.canvasService.canvas.remove(layer);
|
|
1842
2743
|
}
|
|
1843
2744
|
}
|
|
1844
|
-
updateRuler(
|
|
1845
|
-
|
|
2745
|
+
updateRuler() {
|
|
2746
|
+
if (!this.canvasService) return;
|
|
2747
|
+
const layer = this.getLayer();
|
|
1846
2748
|
if (!layer) return;
|
|
1847
2749
|
layer.remove(...layer.getObjects());
|
|
1848
|
-
const { thickness, backgroundColor, lineColor, textColor, fontSize } = this
|
|
1849
|
-
const width =
|
|
1850
|
-
const height =
|
|
1851
|
-
const topBg = new
|
|
2750
|
+
const { thickness, backgroundColor, lineColor, textColor, fontSize } = this;
|
|
2751
|
+
const width = this.canvasService.canvas.width || 800;
|
|
2752
|
+
const height = this.canvasService.canvas.height || 600;
|
|
2753
|
+
const topBg = new Rect2({
|
|
1852
2754
|
left: 0,
|
|
1853
2755
|
top: 0,
|
|
1854
2756
|
width,
|
|
@@ -1857,7 +2759,7 @@ var RulerTool = class {
|
|
|
1857
2759
|
selectable: false,
|
|
1858
2760
|
evented: false
|
|
1859
2761
|
});
|
|
1860
|
-
const leftBg = new
|
|
2762
|
+
const leftBg = new Rect2({
|
|
1861
2763
|
left: 0,
|
|
1862
2764
|
top: 0,
|
|
1863
2765
|
width: thickness,
|
|
@@ -1866,7 +2768,7 @@ var RulerTool = class {
|
|
|
1866
2768
|
selectable: false,
|
|
1867
2769
|
evented: false
|
|
1868
2770
|
});
|
|
1869
|
-
const cornerBg = new
|
|
2771
|
+
const cornerBg = new Rect2({
|
|
1870
2772
|
left: 0,
|
|
1871
2773
|
top: 0,
|
|
1872
2774
|
width: thickness,
|
|
@@ -1877,7 +2779,9 @@ var RulerTool = class {
|
|
|
1877
2779
|
selectable: false,
|
|
1878
2780
|
evented: false
|
|
1879
2781
|
});
|
|
1880
|
-
layer.add(topBg
|
|
2782
|
+
layer.add(topBg);
|
|
2783
|
+
layer.add(leftBg);
|
|
2784
|
+
layer.add(cornerBg);
|
|
1881
2785
|
const step = 100;
|
|
1882
2786
|
const subStep = 10;
|
|
1883
2787
|
const midStep = 50;
|
|
@@ -1935,62 +2839,82 @@ var RulerTool = class {
|
|
|
1935
2839
|
layer.add(text);
|
|
1936
2840
|
}
|
|
1937
2841
|
}
|
|
1938
|
-
|
|
1939
|
-
|
|
2842
|
+
this.canvasService.canvas.bringObjectToFront(layer);
|
|
2843
|
+
this.canvasService.canvas.requestRenderAll();
|
|
1940
2844
|
}
|
|
1941
2845
|
};
|
|
1942
2846
|
|
|
1943
2847
|
// src/mirror.ts
|
|
2848
|
+
import {
|
|
2849
|
+
ContributionPointIds as ContributionPointIds8
|
|
2850
|
+
} from "@pooder/core";
|
|
1944
2851
|
var MirrorTool = class {
|
|
1945
|
-
constructor() {
|
|
1946
|
-
this.
|
|
1947
|
-
this.
|
|
1948
|
-
|
|
2852
|
+
constructor(options) {
|
|
2853
|
+
this.id = "pooder.kit.mirror";
|
|
2854
|
+
this.metadata = {
|
|
2855
|
+
name: "MirrorTool"
|
|
1949
2856
|
};
|
|
1950
|
-
this.
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
execute: (editor) => {
|
|
1959
|
-
this.options.enabled = !this.options.enabled;
|
|
1960
|
-
this.applyMirror(editor, this.options.enabled);
|
|
1961
|
-
return true;
|
|
1962
|
-
}
|
|
1963
|
-
},
|
|
1964
|
-
setMirror: {
|
|
1965
|
-
execute: (editor, enabled) => {
|
|
1966
|
-
if (this.options.enabled === enabled) return true;
|
|
1967
|
-
this.options.enabled = enabled;
|
|
1968
|
-
this.applyMirror(editor, enabled);
|
|
1969
|
-
return true;
|
|
1970
|
-
},
|
|
1971
|
-
schema: {
|
|
1972
|
-
enabled: {
|
|
1973
|
-
type: "boolean",
|
|
1974
|
-
label: "Enabled",
|
|
1975
|
-
required: true
|
|
1976
|
-
}
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
2857
|
+
this.enabled = false;
|
|
2858
|
+
if (options) {
|
|
2859
|
+
Object.assign(this, options);
|
|
2860
|
+
}
|
|
2861
|
+
}
|
|
2862
|
+
toJSON() {
|
|
2863
|
+
return {
|
|
2864
|
+
enabled: this.enabled
|
|
1979
2865
|
};
|
|
1980
2866
|
}
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
2867
|
+
loadFromJSON(json) {
|
|
2868
|
+
this.enabled = json.enabled;
|
|
2869
|
+
}
|
|
2870
|
+
activate(context) {
|
|
2871
|
+
this.canvasService = context.services.get("CanvasService");
|
|
2872
|
+
if (!this.canvasService) {
|
|
2873
|
+
console.warn("CanvasService not found for MirrorTool");
|
|
2874
|
+
return;
|
|
2875
|
+
}
|
|
2876
|
+
const configService = context.services.get("ConfigurationService");
|
|
2877
|
+
if (configService) {
|
|
2878
|
+
this.enabled = configService.get("mirror.enabled", this.enabled);
|
|
2879
|
+
configService.onAnyChange((e) => {
|
|
2880
|
+
if (e.key === "mirror.enabled") {
|
|
2881
|
+
this.applyMirror(e.value);
|
|
2882
|
+
}
|
|
2883
|
+
});
|
|
2884
|
+
}
|
|
2885
|
+
if (this.enabled) {
|
|
2886
|
+
this.applyMirror(true);
|
|
1984
2887
|
}
|
|
1985
2888
|
}
|
|
1986
|
-
|
|
1987
|
-
this.applyMirror(
|
|
2889
|
+
deactivate(context) {
|
|
2890
|
+
this.applyMirror(false);
|
|
2891
|
+
this.canvasService = void 0;
|
|
1988
2892
|
}
|
|
1989
|
-
|
|
1990
|
-
|
|
2893
|
+
contribute() {
|
|
2894
|
+
return {
|
|
2895
|
+
[ContributionPointIds8.CONFIGURATIONS]: [
|
|
2896
|
+
{
|
|
2897
|
+
id: "mirror.enabled",
|
|
2898
|
+
type: "boolean",
|
|
2899
|
+
label: "Enable Mirror",
|
|
2900
|
+
default: false
|
|
2901
|
+
}
|
|
2902
|
+
],
|
|
2903
|
+
[ContributionPointIds8.COMMANDS]: [
|
|
2904
|
+
{
|
|
2905
|
+
command: "setMirror",
|
|
2906
|
+
title: "Set Mirror",
|
|
2907
|
+
handler: (enabled) => {
|
|
2908
|
+
this.applyMirror(enabled);
|
|
2909
|
+
return true;
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
]
|
|
2913
|
+
};
|
|
1991
2914
|
}
|
|
1992
|
-
applyMirror(
|
|
1993
|
-
|
|
2915
|
+
applyMirror(enabled) {
|
|
2916
|
+
if (!this.canvasService) return;
|
|
2917
|
+
const canvas = this.canvasService.canvas;
|
|
1994
2918
|
if (!canvas) return;
|
|
1995
2919
|
const width = canvas.width || 800;
|
|
1996
2920
|
let vpt = canvas.viewportTransform || [1, 0, 0, 1, 0, 0];
|
|
@@ -2001,16 +2925,84 @@ var MirrorTool = class {
|
|
|
2001
2925
|
vpt[4] = width - vpt[4];
|
|
2002
2926
|
canvas.setViewportTransform(vpt);
|
|
2003
2927
|
canvas.requestRenderAll();
|
|
2928
|
+
this.enabled = true;
|
|
2004
2929
|
} else if (!enabled && isFlipped) {
|
|
2005
2930
|
vpt[0] = -vpt[0];
|
|
2006
2931
|
vpt[4] = width - vpt[4];
|
|
2007
2932
|
canvas.setViewportTransform(vpt);
|
|
2008
2933
|
canvas.requestRenderAll();
|
|
2934
|
+
this.enabled = false;
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
};
|
|
2938
|
+
|
|
2939
|
+
// src/CanvasService.ts
|
|
2940
|
+
import { Canvas, Group as Group2 } from "fabric";
|
|
2941
|
+
var CanvasService = class {
|
|
2942
|
+
constructor(el, options) {
|
|
2943
|
+
if (el instanceof Canvas) {
|
|
2944
|
+
this.canvas = el;
|
|
2945
|
+
} else {
|
|
2946
|
+
this.canvas = new Canvas(el, {
|
|
2947
|
+
preserveObjectStacking: true,
|
|
2948
|
+
...options
|
|
2949
|
+
});
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
dispose() {
|
|
2953
|
+
this.canvas.dispose();
|
|
2954
|
+
}
|
|
2955
|
+
/**
|
|
2956
|
+
* Get a layer (Group) by its ID.
|
|
2957
|
+
* We assume layers are Groups directly on the canvas with a data.id property.
|
|
2958
|
+
*/
|
|
2959
|
+
getLayer(id) {
|
|
2960
|
+
return this.canvas.getObjects().find((obj) => {
|
|
2961
|
+
var _a;
|
|
2962
|
+
return ((_a = obj.data) == null ? void 0 : _a.id) === id;
|
|
2963
|
+
});
|
|
2964
|
+
}
|
|
2965
|
+
/**
|
|
2966
|
+
* Create a layer (Group) with the given ID if it doesn't exist.
|
|
2967
|
+
*/
|
|
2968
|
+
createLayer(id, options = {}) {
|
|
2969
|
+
let layer = this.getLayer(id);
|
|
2970
|
+
if (!layer) {
|
|
2971
|
+
const defaultOptions = {
|
|
2972
|
+
selectable: false,
|
|
2973
|
+
evented: false,
|
|
2974
|
+
...options,
|
|
2975
|
+
data: { ...options.data, id }
|
|
2976
|
+
};
|
|
2977
|
+
layer = new Group2([], defaultOptions);
|
|
2978
|
+
this.canvas.add(layer);
|
|
2979
|
+
}
|
|
2980
|
+
return layer;
|
|
2981
|
+
}
|
|
2982
|
+
/**
|
|
2983
|
+
* Find an object by ID, optionally within a specific layer.
|
|
2984
|
+
*/
|
|
2985
|
+
getObject(id, layerId) {
|
|
2986
|
+
if (layerId) {
|
|
2987
|
+
const layer = this.getLayer(layerId);
|
|
2988
|
+
if (!layer) return void 0;
|
|
2989
|
+
return layer.getObjects().find((obj) => {
|
|
2990
|
+
var _a;
|
|
2991
|
+
return ((_a = obj.data) == null ? void 0 : _a.id) === id;
|
|
2992
|
+
});
|
|
2009
2993
|
}
|
|
2994
|
+
return this.canvas.getObjects().find((obj) => {
|
|
2995
|
+
var _a;
|
|
2996
|
+
return ((_a = obj.data) == null ? void 0 : _a.id) === id;
|
|
2997
|
+
});
|
|
2998
|
+
}
|
|
2999
|
+
requestRenderAll() {
|
|
3000
|
+
this.canvas.requestRenderAll();
|
|
2010
3001
|
}
|
|
2011
3002
|
};
|
|
2012
3003
|
export {
|
|
2013
3004
|
BackgroundTool,
|
|
3005
|
+
CanvasService,
|
|
2014
3006
|
DielineTool,
|
|
2015
3007
|
FilmTool,
|
|
2016
3008
|
HoleTool,
|