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