@pooder/kit 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/index.d.mts +246 -134
- package/dist/index.d.ts +246 -134
- package/dist/index.js +2051 -1045
- package/dist/index.mjs +2042 -1050
- package/package.json +3 -2
- package/src/CanvasService.ts +65 -0
- package/src/background.ts +156 -109
- package/src/coordinate.ts +49 -0
- package/src/dieline.ts +536 -336
- package/src/film.ts +120 -89
- package/src/geometry.ts +251 -38
- package/src/hole.ts +422 -286
- package/src/image.ts +374 -174
- package/src/index.ts +1 -0
- package/src/mirror.ts +86 -49
- package/src/ruler.ts +188 -118
- package/src/tracer.ts +372 -0
- package/src/white-ink.ts +186 -142
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ 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,
|
|
@@ -43,118 +44,152 @@ module.exports = __toCommonJS(index_exports);
|
|
|
43
44
|
|
|
44
45
|
// src/background.ts
|
|
45
46
|
var import_core = require("@pooder/core");
|
|
47
|
+
var import_fabric = require("fabric");
|
|
46
48
|
var BackgroundTool = class {
|
|
47
|
-
constructor() {
|
|
48
|
-
this.
|
|
49
|
-
this.
|
|
50
|
-
|
|
51
|
-
url: ""
|
|
49
|
+
constructor(options) {
|
|
50
|
+
this.id = "pooder.kit.background";
|
|
51
|
+
this.metadata = {
|
|
52
|
+
name: "BackgroundTool"
|
|
52
53
|
};
|
|
53
|
-
this.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
+
}
|
|
68
87
|
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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: ""
|
|
78
116
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
117
|
+
],
|
|
118
|
+
[import_core.ContributionPointIds.COMMANDS]: [
|
|
119
|
+
{
|
|
120
|
+
command: "reset",
|
|
121
|
+
title: "Reset Background",
|
|
122
|
+
handler: () => {
|
|
123
|
+
this.updateBackground();
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
86
126
|
},
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
127
|
+
{
|
|
128
|
+
command: "clear",
|
|
129
|
+
title: "Clear Background",
|
|
130
|
+
handler: () => {
|
|
131
|
+
this.color = "transparent";
|
|
132
|
+
this.url = "";
|
|
133
|
+
this.updateBackground();
|
|
134
|
+
return true;
|
|
93
135
|
}
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
setBackgroundImage: {
|
|
97
|
-
execute: (editor, url) => {
|
|
98
|
-
if (this.options.url === url) return true;
|
|
99
|
-
this.options.url = url;
|
|
100
|
-
this.updateBackground(editor, this.options);
|
|
101
|
-
return true;
|
|
102
136
|
},
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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;
|
|
145
|
+
}
|
|
146
|
+
},
|
|
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;
|
|
108
155
|
}
|
|
109
156
|
}
|
|
110
|
-
|
|
157
|
+
]
|
|
111
158
|
};
|
|
112
159
|
}
|
|
113
|
-
initLayer(
|
|
114
|
-
|
|
160
|
+
initLayer() {
|
|
161
|
+
if (!this.canvasService) return;
|
|
162
|
+
let backgroundLayer = this.canvasService.getLayer("background");
|
|
115
163
|
if (!backgroundLayer) {
|
|
116
|
-
backgroundLayer =
|
|
117
|
-
width:
|
|
118
|
-
height:
|
|
164
|
+
backgroundLayer = this.canvasService.createLayer("background", {
|
|
165
|
+
width: this.canvasService.canvas.width,
|
|
166
|
+
height: this.canvasService.canvas.height,
|
|
119
167
|
selectable: false,
|
|
120
|
-
evented: false
|
|
121
|
-
data: {
|
|
122
|
-
id: "background"
|
|
123
|
-
}
|
|
168
|
+
evented: false
|
|
124
169
|
});
|
|
125
|
-
|
|
126
|
-
editor.canvas.sendObjectToBack(backgroundLayer);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
onMount(editor) {
|
|
130
|
-
this.initLayer(editor);
|
|
131
|
-
this.updateBackground(editor, this.options);
|
|
132
|
-
}
|
|
133
|
-
onUnmount(editor) {
|
|
134
|
-
const layer = editor.getLayer("background");
|
|
135
|
-
if (layer) {
|
|
136
|
-
editor.canvas.remove(layer);
|
|
170
|
+
this.canvasService.canvas.sendObjectToBack(backgroundLayer);
|
|
137
171
|
}
|
|
138
172
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
async updateBackground(editor, options) {
|
|
143
|
-
const layer = editor.getLayer("background");
|
|
173
|
+
async updateBackground() {
|
|
174
|
+
if (!this.canvasService) return;
|
|
175
|
+
const layer = this.canvasService.getLayer("background");
|
|
144
176
|
if (!layer) {
|
|
145
177
|
console.warn("[BackgroundTool] Background layer not found");
|
|
146
178
|
return;
|
|
147
179
|
}
|
|
148
|
-
const { color, url } =
|
|
149
|
-
const width =
|
|
150
|
-
const height =
|
|
151
|
-
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
|
+
);
|
|
152
187
|
if (rect) {
|
|
153
188
|
rect.set({
|
|
154
189
|
fill: color
|
|
155
190
|
});
|
|
156
191
|
} else {
|
|
157
|
-
rect = new
|
|
192
|
+
rect = new import_fabric.Rect({
|
|
158
193
|
width,
|
|
159
194
|
height,
|
|
160
195
|
fill: color,
|
|
@@ -167,7 +202,10 @@ var BackgroundTool = class {
|
|
|
167
202
|
layer.add(rect);
|
|
168
203
|
layer.sendObjectToBack(rect);
|
|
169
204
|
}
|
|
170
|
-
let img =
|
|
205
|
+
let img = this.canvasService.getObject(
|
|
206
|
+
"background-image",
|
|
207
|
+
"background"
|
|
208
|
+
);
|
|
171
209
|
try {
|
|
172
210
|
if (img) {
|
|
173
211
|
if (img.getSrc() !== url) {
|
|
@@ -179,7 +217,7 @@ var BackgroundTool = class {
|
|
|
179
217
|
}
|
|
180
218
|
} else {
|
|
181
219
|
if (url) {
|
|
182
|
-
img = await
|
|
220
|
+
img = await import_fabric.FabricImage.fromURL(url, { crossOrigin: "anonymous" });
|
|
183
221
|
img.set({
|
|
184
222
|
originX: "left",
|
|
185
223
|
originY: "top",
|
|
@@ -196,25 +234,253 @@ var BackgroundTool = class {
|
|
|
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,256 +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
|
-
showBleedLines: true
|
|
362
|
-
};
|
|
363
|
-
this.schema = {
|
|
364
|
-
shape: {
|
|
365
|
-
type: "select",
|
|
366
|
-
options: ["rect", "circle", "ellipse"],
|
|
367
|
-
label: "Shape"
|
|
368
|
-
},
|
|
369
|
-
width: { type: "number", min: 10, max: 2e3, label: "Width" },
|
|
370
|
-
height: { type: "number", min: 10, max: 2e3, label: "Height" },
|
|
371
|
-
radius: { type: "number", min: 0, max: 500, label: "Corner Radius" },
|
|
372
|
-
position: { type: "string", label: "Position" },
|
|
373
|
-
// Complex object, simplified for now or need custom handler
|
|
374
|
-
borderLength: { type: "number", min: 0, max: 500, label: "Margin" },
|
|
375
|
-
offset: { type: "number", min: -100, max: 100, label: "Bleed Offset" },
|
|
376
|
-
showBleedLines: { type: "boolean", label: "Show Bleed Lines" },
|
|
377
|
-
style: {
|
|
378
|
-
type: "select",
|
|
379
|
-
options: ["solid", "dashed"],
|
|
380
|
-
label: "Line Style"
|
|
381
|
-
},
|
|
382
|
-
insideColor: { type: "color", label: "Inside Color" },
|
|
383
|
-
outsideColor: { type: "color", label: "Outside Color" }
|
|
674
|
+
constructor(options) {
|
|
675
|
+
this.id = "pooder.kit.dieline";
|
|
676
|
+
this.metadata = {
|
|
677
|
+
name: "DielineTool"
|
|
384
678
|
};
|
|
385
|
-
this.
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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
|
+
}
|
|
401
736
|
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
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
|
|
407
833
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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();
|
|
412
852
|
return true;
|
|
413
|
-
|
|
414
|
-
this.options.height = height;
|
|
415
|
-
this.updateDieline(editor);
|
|
416
|
-
return true;
|
|
853
|
+
}
|
|
417
854
|
},
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
type: "number",
|
|
428
|
-
label: "Height",
|
|
429
|
-
min: 10,
|
|
430
|
-
max: 2e3,
|
|
431
|
-
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;
|
|
432
864
|
}
|
|
433
|
-
}
|
|
434
|
-
},
|
|
435
|
-
setShape: {
|
|
436
|
-
execute: (editor, shape) => {
|
|
437
|
-
if (this.options.shape === shape) return true;
|
|
438
|
-
this.options.shape = shape;
|
|
439
|
-
this.updateDieline(editor);
|
|
440
|
-
return true;
|
|
441
865
|
},
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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;
|
|
448
874
|
}
|
|
449
|
-
}
|
|
450
|
-
},
|
|
451
|
-
setBleed: {
|
|
452
|
-
execute: (editor, bleed) => {
|
|
453
|
-
if (this.options.offset === bleed) return true;
|
|
454
|
-
this.options.offset = bleed;
|
|
455
|
-
this.updateDieline(editor);
|
|
456
|
-
return true;
|
|
457
875
|
},
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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
|
+
}
|
|
465
928
|
}
|
|
466
929
|
}
|
|
467
|
-
|
|
468
|
-
exportCutImage: {
|
|
469
|
-
execute: (editor) => {
|
|
470
|
-
var _a, _b, _c, _d;
|
|
471
|
-
const { shape, width, height, radius, position } = this.options;
|
|
472
|
-
const canvasW = editor.canvas.width || 800;
|
|
473
|
-
const canvasH = editor.canvas.height || 600;
|
|
474
|
-
const cx = (_a = position == null ? void 0 : position.x) != null ? _a : canvasW / 2;
|
|
475
|
-
const cy = (_b = position == null ? void 0 : position.y) != null ? _b : canvasH / 2;
|
|
476
|
-
const holeTool = editor.getExtension("HoleTool");
|
|
477
|
-
const holes = holeTool ? holeTool.options.holes || [] : [];
|
|
478
|
-
const innerRadius = holeTool ? holeTool.options.innerRadius || 15 : 15;
|
|
479
|
-
const outerRadius = holeTool ? holeTool.options.outerRadius || 25 : 25;
|
|
480
|
-
const holeData = holes.map((h) => ({
|
|
481
|
-
x: h.x,
|
|
482
|
-
y: h.y,
|
|
483
|
-
innerRadius,
|
|
484
|
-
outerRadius
|
|
485
|
-
}));
|
|
486
|
-
const pathData = generateDielinePath({
|
|
487
|
-
shape,
|
|
488
|
-
width,
|
|
489
|
-
height,
|
|
490
|
-
radius,
|
|
491
|
-
x: cx,
|
|
492
|
-
y: cy,
|
|
493
|
-
holes: holeData
|
|
494
|
-
});
|
|
495
|
-
const clipPath = new import_core2.Path(pathData, {
|
|
496
|
-
left: 0,
|
|
497
|
-
top: 0,
|
|
498
|
-
originX: "left",
|
|
499
|
-
originY: "top",
|
|
500
|
-
absolutePositioned: true
|
|
501
|
-
});
|
|
502
|
-
const layer = this.getLayer(editor, "dieline-overlay");
|
|
503
|
-
const wasVisible = (_c = layer == null ? void 0 : layer.visible) != null ? _c : true;
|
|
504
|
-
if (layer) layer.visible = false;
|
|
505
|
-
const holeMarkers = editor.canvas.getObjects().filter((o) => {
|
|
506
|
-
var _a2;
|
|
507
|
-
return ((_a2 = o.data) == null ? void 0 : _a2.type) === "hole-marker";
|
|
508
|
-
});
|
|
509
|
-
holeMarkers.forEach((o) => o.visible = false);
|
|
510
|
-
const rulerLayer = editor.canvas.getObjects().find((obj) => {
|
|
511
|
-
var _a2;
|
|
512
|
-
return ((_a2 = obj.data) == null ? void 0 : _a2.id) === "ruler-overlay";
|
|
513
|
-
});
|
|
514
|
-
const rulerWasVisible = (_d = rulerLayer == null ? void 0 : rulerLayer.visible) != null ? _d : true;
|
|
515
|
-
if (rulerLayer) rulerLayer.visible = false;
|
|
516
|
-
const originalClip = editor.canvas.clipPath;
|
|
517
|
-
editor.canvas.clipPath = clipPath;
|
|
518
|
-
const bbox = clipPath.getBoundingRect();
|
|
519
|
-
const holeDataRelative = holes.map((h) => ({
|
|
520
|
-
x: h.x - bbox.left,
|
|
521
|
-
y: h.y - bbox.top,
|
|
522
|
-
innerRadius,
|
|
523
|
-
outerRadius
|
|
524
|
-
}));
|
|
525
|
-
const clipPathCorrected = new import_core2.Path(pathData, {
|
|
526
|
-
absolutePositioned: true,
|
|
527
|
-
left: 0,
|
|
528
|
-
top: 0
|
|
529
|
-
});
|
|
530
|
-
const tempPath = new import_core2.Path(pathData);
|
|
531
|
-
const tempBounds = tempPath.getBoundingRect();
|
|
532
|
-
clipPathCorrected.set({
|
|
533
|
-
left: tempBounds.left,
|
|
534
|
-
top: tempBounds.top,
|
|
535
|
-
originX: "left",
|
|
536
|
-
originY: "top"
|
|
537
|
-
});
|
|
538
|
-
editor.canvas.clipPath = clipPathCorrected;
|
|
539
|
-
const exportBbox = clipPathCorrected.getBoundingRect();
|
|
540
|
-
const dataURL = editor.canvas.toDataURL({
|
|
541
|
-
format: "png",
|
|
542
|
-
multiplier: 2,
|
|
543
|
-
left: exportBbox.left,
|
|
544
|
-
top: exportBbox.top,
|
|
545
|
-
width: exportBbox.width,
|
|
546
|
-
height: exportBbox.height
|
|
547
|
-
});
|
|
548
|
-
editor.canvas.clipPath = originalClip;
|
|
549
|
-
if (layer) layer.visible = wasVisible;
|
|
550
|
-
if (rulerLayer) rulerLayer.visible = rulerWasVisible;
|
|
551
|
-
holeMarkers.forEach((o) => o.visible = true);
|
|
552
|
-
editor.canvas.requestRenderAll();
|
|
553
|
-
return dataURL;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
930
|
+
]
|
|
556
931
|
};
|
|
557
932
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
this.
|
|
561
|
-
}
|
|
562
|
-
onUnmount(editor) {
|
|
563
|
-
this.destroyLayer(editor);
|
|
564
|
-
}
|
|
565
|
-
onUpdate(editor, state) {
|
|
566
|
-
this.updateDieline(editor);
|
|
933
|
+
getLayer() {
|
|
934
|
+
var _a;
|
|
935
|
+
return (_a = this.canvasService) == null ? void 0 : _a.getLayer("dieline-overlay");
|
|
567
936
|
}
|
|
568
|
-
|
|
569
|
-
this.
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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
|
|
575
946
|
});
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
const height = editor.canvas.height || 600;
|
|
582
|
-
layer = new import_core2.PooderLayer([], {
|
|
583
|
-
width,
|
|
584
|
-
height,
|
|
585
|
-
selectable: false,
|
|
586
|
-
evented: false,
|
|
587
|
-
data: { id: "dieline-overlay" }
|
|
588
|
-
});
|
|
589
|
-
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);
|
|
590
952
|
}
|
|
591
|
-
editor.canvas.bringObjectToFront(layer);
|
|
592
953
|
}
|
|
593
|
-
destroyLayer(
|
|
594
|
-
|
|
954
|
+
destroyLayer() {
|
|
955
|
+
if (!this.canvasService) return;
|
|
956
|
+
const layer = this.getLayer();
|
|
595
957
|
if (layer) {
|
|
596
|
-
|
|
958
|
+
this.canvasService.canvas.remove(layer);
|
|
597
959
|
}
|
|
598
960
|
}
|
|
599
961
|
createHatchPattern(color = "rgba(0, 0, 0, 0.3)") {
|
|
@@ -614,10 +976,12 @@ var DielineTool = class {
|
|
|
614
976
|
ctx.lineTo(size, 0);
|
|
615
977
|
ctx.stroke();
|
|
616
978
|
}
|
|
617
|
-
return new
|
|
979
|
+
return new import_fabric2.Pattern({ source: canvas, repetition: "repeat" });
|
|
618
980
|
}
|
|
619
|
-
updateDieline(
|
|
620
|
-
|
|
981
|
+
updateDieline(emitEvent = true) {
|
|
982
|
+
if (!this.canvasService) return;
|
|
983
|
+
const layer = this.getLayer();
|
|
984
|
+
if (!layer) return;
|
|
621
985
|
const {
|
|
622
986
|
shape,
|
|
623
987
|
radius,
|
|
@@ -627,33 +991,31 @@ var DielineTool = class {
|
|
|
627
991
|
outsideColor,
|
|
628
992
|
position,
|
|
629
993
|
borderLength,
|
|
630
|
-
showBleedLines
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
const
|
|
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;
|
|
635
1000
|
if (borderLength && borderLength > 0) {
|
|
636
1001
|
width = Math.max(0, canvasW - borderLength * 2);
|
|
637
1002
|
height = Math.max(0, canvasH - borderLength * 2);
|
|
638
1003
|
}
|
|
639
|
-
const
|
|
640
|
-
const
|
|
641
|
-
const
|
|
642
|
-
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);
|
|
643
1007
|
layer.remove(...layer.getObjects());
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
outerRadius
|
|
656
|
-
}));
|
|
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
|
+
});
|
|
657
1019
|
const cutW = Math.max(0, width + offset * 2);
|
|
658
1020
|
const cutH = Math.max(0, height + offset * 2);
|
|
659
1021
|
const cutR = radius === 0 ? 0 : Math.max(0, radius + offset);
|
|
@@ -666,9 +1028,10 @@ var DielineTool = class {
|
|
|
666
1028
|
radius: cutR,
|
|
667
1029
|
x: cx,
|
|
668
1030
|
y: cy,
|
|
669
|
-
holes:
|
|
1031
|
+
holes: absoluteHoles,
|
|
1032
|
+
pathData: this.pathData
|
|
670
1033
|
});
|
|
671
|
-
const mask = new
|
|
1034
|
+
const mask = new import_fabric2.Path(maskPathData, {
|
|
672
1035
|
fill: outsideColor,
|
|
673
1036
|
stroke: null,
|
|
674
1037
|
selectable: false,
|
|
@@ -687,9 +1050,10 @@ var DielineTool = class {
|
|
|
687
1050
|
radius: cutR,
|
|
688
1051
|
x: cx,
|
|
689
1052
|
y: cy,
|
|
690
|
-
holes:
|
|
1053
|
+
holes: absoluteHoles,
|
|
1054
|
+
pathData: this.pathData
|
|
691
1055
|
});
|
|
692
|
-
const insideObj = new
|
|
1056
|
+
const insideObj = new import_fabric2.Path(productPathData, {
|
|
693
1057
|
fill: insideColor,
|
|
694
1058
|
stroke: null,
|
|
695
1059
|
selectable: false,
|
|
@@ -709,14 +1073,15 @@ var DielineTool = class {
|
|
|
709
1073
|
radius,
|
|
710
1074
|
x: cx,
|
|
711
1075
|
y: cy,
|
|
712
|
-
holes:
|
|
1076
|
+
holes: absoluteHoles,
|
|
1077
|
+
pathData: this.pathData
|
|
713
1078
|
},
|
|
714
1079
|
offset
|
|
715
1080
|
);
|
|
716
1081
|
if (showBleedLines !== false) {
|
|
717
1082
|
const pattern = this.createHatchPattern("red");
|
|
718
1083
|
if (pattern) {
|
|
719
|
-
const bleedObj = new
|
|
1084
|
+
const bleedObj = new import_fabric2.Path(bleedPathData, {
|
|
720
1085
|
fill: pattern,
|
|
721
1086
|
stroke: null,
|
|
722
1087
|
selectable: false,
|
|
@@ -735,9 +1100,10 @@ var DielineTool = class {
|
|
|
735
1100
|
radius: cutR,
|
|
736
1101
|
x: cx,
|
|
737
1102
|
y: cy,
|
|
738
|
-
holes:
|
|
1103
|
+
holes: absoluteHoles,
|
|
1104
|
+
pathData: this.pathData
|
|
739
1105
|
});
|
|
740
|
-
const offsetBorderObj = new
|
|
1106
|
+
const offsetBorderObj = new import_fabric2.Path(offsetPathData, {
|
|
741
1107
|
fill: null,
|
|
742
1108
|
stroke: "#666",
|
|
743
1109
|
// Grey
|
|
@@ -758,9 +1124,11 @@ var DielineTool = class {
|
|
|
758
1124
|
radius,
|
|
759
1125
|
x: cx,
|
|
760
1126
|
y: cy,
|
|
761
|
-
holes:
|
|
1127
|
+
holes: absoluteHoles,
|
|
1128
|
+
// FIX: Use absoluteHoles instead of holes
|
|
1129
|
+
pathData: this.pathData
|
|
762
1130
|
});
|
|
763
|
-
const borderObj = new
|
|
1131
|
+
const borderObj = new import_fabric2.Path(borderPathData, {
|
|
764
1132
|
fill: "transparent",
|
|
765
1133
|
stroke: "red",
|
|
766
1134
|
strokeWidth: 1,
|
|
@@ -771,104 +1139,236 @@ var DielineTool = class {
|
|
|
771
1139
|
originY: "top"
|
|
772
1140
|
});
|
|
773
1141
|
layer.add(borderObj);
|
|
774
|
-
|
|
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
|
+
}
|
|
775
1164
|
}
|
|
776
|
-
getGeometry(
|
|
1165
|
+
getGeometry() {
|
|
777
1166
|
var _a, _b;
|
|
778
|
-
|
|
779
|
-
const
|
|
780
|
-
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;
|
|
781
1171
|
let visualWidth = width;
|
|
782
1172
|
let visualHeight = height;
|
|
783
1173
|
if (borderLength && borderLength > 0) {
|
|
784
1174
|
visualWidth = Math.max(0, canvasW - borderLength * 2);
|
|
785
1175
|
visualHeight = Math.max(0, canvasH - borderLength * 2);
|
|
786
1176
|
}
|
|
787
|
-
const cx = (_a = position == null ? void 0 : position.x) != null ? _a : canvasW
|
|
788
|
-
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);
|
|
789
1179
|
return {
|
|
790
1180
|
shape,
|
|
791
1181
|
x: cx,
|
|
792
1182
|
y: cy,
|
|
793
1183
|
width: visualWidth,
|
|
794
1184
|
height: visualHeight,
|
|
795
|
-
radius
|
|
1185
|
+
radius,
|
|
1186
|
+
offset,
|
|
1187
|
+
borderLength,
|
|
1188
|
+
pathData: this.pathData
|
|
796
1189
|
};
|
|
797
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
|
+
}
|
|
798
1275
|
};
|
|
799
1276
|
|
|
800
1277
|
// src/film.ts
|
|
801
1278
|
var import_core3 = require("@pooder/core");
|
|
1279
|
+
var import_fabric3 = require("fabric");
|
|
802
1280
|
var FilmTool = class {
|
|
803
|
-
constructor() {
|
|
804
|
-
this.
|
|
805
|
-
this.
|
|
806
|
-
|
|
807
|
-
opacity: 0.5
|
|
1281
|
+
constructor(options) {
|
|
1282
|
+
this.id = "pooder.kit.film";
|
|
1283
|
+
this.metadata = {
|
|
1284
|
+
name: "FilmTool"
|
|
808
1285
|
};
|
|
809
|
-
this.
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
type: "string",
|
|
835
|
-
label: "Image URL",
|
|
836
|
-
required: true
|
|
837
|
-
},
|
|
838
|
-
opacity: {
|
|
839
|
-
type: "number",
|
|
840
|
-
label: "Opacity",
|
|
841
|
-
min: 0,
|
|
842
|
-
max: 1,
|
|
843
|
-
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();
|
|
844
1311
|
}
|
|
845
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
|
+
}
|
|
846
1327
|
}
|
|
847
|
-
|
|
848
|
-
}
|
|
849
|
-
onMount(editor) {
|
|
850
|
-
this.initLayer(editor);
|
|
851
|
-
this.updateFilm(editor, this.options);
|
|
852
|
-
}
|
|
853
|
-
onUnmount(editor) {
|
|
854
|
-
const layer = editor.getLayer("overlay");
|
|
855
|
-
if (layer) {
|
|
856
|
-
const img = editor.getObject("film-image", "overlay");
|
|
857
|
-
if (img) {
|
|
858
|
-
layer.remove(img);
|
|
859
|
-
editor.canvas.requestRenderAll();
|
|
860
|
-
}
|
|
1328
|
+
this.canvasService = void 0;
|
|
861
1329
|
}
|
|
862
1330
|
}
|
|
863
|
-
|
|
864
|
-
|
|
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
|
+
};
|
|
865
1364
|
}
|
|
866
|
-
initLayer(
|
|
867
|
-
|
|
1365
|
+
initLayer() {
|
|
1366
|
+
if (!this.canvasService) return;
|
|
1367
|
+
let overlayLayer = this.canvasService.getLayer("overlay");
|
|
868
1368
|
if (!overlayLayer) {
|
|
869
|
-
const width =
|
|
870
|
-
const height =
|
|
871
|
-
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", {
|
|
872
1372
|
width,
|
|
873
1373
|
height,
|
|
874
1374
|
left: 0,
|
|
@@ -878,33 +1378,30 @@ var FilmTool = class {
|
|
|
878
1378
|
selectable: false,
|
|
879
1379
|
evented: false,
|
|
880
1380
|
subTargetCheck: false,
|
|
881
|
-
interactive: false
|
|
882
|
-
data: {
|
|
883
|
-
id: "overlay"
|
|
884
|
-
}
|
|
1381
|
+
interactive: false
|
|
885
1382
|
});
|
|
886
|
-
|
|
887
|
-
editor.canvas.bringObjectToFront(layer);
|
|
1383
|
+
this.canvasService.canvas.bringObjectToFront(layer);
|
|
888
1384
|
}
|
|
889
1385
|
}
|
|
890
|
-
async updateFilm(
|
|
891
|
-
|
|
1386
|
+
async updateFilm() {
|
|
1387
|
+
if (!this.canvasService) return;
|
|
1388
|
+
const layer = this.canvasService.getLayer("overlay");
|
|
892
1389
|
if (!layer) {
|
|
893
1390
|
console.warn("[FilmTool] Overlay layer not found");
|
|
894
1391
|
return;
|
|
895
1392
|
}
|
|
896
|
-
const { url, opacity } =
|
|
1393
|
+
const { url, opacity } = this;
|
|
897
1394
|
if (!url) {
|
|
898
|
-
const img2 =
|
|
1395
|
+
const img2 = this.canvasService.getObject("film-image", "overlay");
|
|
899
1396
|
if (img2) {
|
|
900
1397
|
layer.remove(img2);
|
|
901
|
-
|
|
1398
|
+
this.canvasService.requestRenderAll();
|
|
902
1399
|
}
|
|
903
1400
|
return;
|
|
904
1401
|
}
|
|
905
|
-
const width =
|
|
906
|
-
const height =
|
|
907
|
-
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");
|
|
908
1405
|
try {
|
|
909
1406
|
if (img) {
|
|
910
1407
|
if (img.getSrc() !== url) {
|
|
@@ -912,7 +1409,7 @@ var FilmTool = class {
|
|
|
912
1409
|
}
|
|
913
1410
|
img.set({ opacity });
|
|
914
1411
|
} else {
|
|
915
|
-
img = await
|
|
1412
|
+
img = await import_fabric3.FabricImage.fromURL(url, { crossOrigin: "anonymous" });
|
|
916
1413
|
img.scaleToWidth(width);
|
|
917
1414
|
if (img.getScaledHeight() < height) img.scaleToHeight(height);
|
|
918
1415
|
img.set({
|
|
@@ -927,204 +1424,263 @@ var FilmTool = class {
|
|
|
927
1424
|
});
|
|
928
1425
|
layer.add(img);
|
|
929
1426
|
}
|
|
930
|
-
|
|
1427
|
+
this.canvasService.requestRenderAll();
|
|
931
1428
|
} catch (error) {
|
|
932
1429
|
console.error("[FilmTool] Failed to load film image", url, error);
|
|
933
1430
|
}
|
|
1431
|
+
layer.dirty = true;
|
|
1432
|
+
this.canvasService.requestRenderAll();
|
|
934
1433
|
}
|
|
935
1434
|
};
|
|
936
1435
|
|
|
937
1436
|
// src/hole.ts
|
|
938
1437
|
var import_core4 = require("@pooder/core");
|
|
1438
|
+
var import_fabric4 = require("fabric");
|
|
939
1439
|
var HoleTool = class {
|
|
940
|
-
constructor() {
|
|
941
|
-
this.
|
|
942
|
-
this.
|
|
943
|
-
|
|
944
|
-
outerRadius: 25,
|
|
945
|
-
style: "solid",
|
|
946
|
-
holes: [],
|
|
947
|
-
constraintTarget: "bleed"
|
|
948
|
-
};
|
|
949
|
-
this.schema = {
|
|
950
|
-
innerRadius: {
|
|
951
|
-
type: "number",
|
|
952
|
-
min: 1,
|
|
953
|
-
max: 100,
|
|
954
|
-
label: "Inner Radius"
|
|
955
|
-
},
|
|
956
|
-
outerRadius: {
|
|
957
|
-
type: "number",
|
|
958
|
-
min: 1,
|
|
959
|
-
max: 100,
|
|
960
|
-
label: "Outer Radius"
|
|
961
|
-
},
|
|
962
|
-
style: {
|
|
963
|
-
type: "select",
|
|
964
|
-
options: ["solid", "dashed"],
|
|
965
|
-
label: "Line Style"
|
|
966
|
-
},
|
|
967
|
-
constraintTarget: {
|
|
968
|
-
type: "select",
|
|
969
|
-
options: ["original", "bleed"],
|
|
970
|
-
label: "Constraint Target"
|
|
971
|
-
},
|
|
972
|
-
holes: {
|
|
973
|
-
type: "json",
|
|
974
|
-
label: "Holes"
|
|
975
|
-
}
|
|
1440
|
+
constructor(options) {
|
|
1441
|
+
this.id = "pooder.kit.hole";
|
|
1442
|
+
this.metadata = {
|
|
1443
|
+
name: "HoleTool"
|
|
976
1444
|
};
|
|
1445
|
+
this.innerRadius = 15;
|
|
1446
|
+
this.outerRadius = 25;
|
|
1447
|
+
this.style = "solid";
|
|
1448
|
+
this.holes = [];
|
|
1449
|
+
this.constraintTarget = "bleed";
|
|
1450
|
+
this.isUpdatingConfig = false;
|
|
977
1451
|
this.handleMoving = null;
|
|
978
1452
|
this.handleModified = null;
|
|
979
|
-
this.
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
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();
|
|
990
1503
|
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
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();
|
|
1001
1517
|
}
|
|
1002
|
-
return true;
|
|
1003
1518
|
}
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
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;
|
|
1013
1584
|
}
|
|
1014
|
-
return true;
|
|
1015
1585
|
},
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
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;
|
|
1026
1595
|
}
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1596
|
+
},
|
|
1597
|
+
{
|
|
1598
|
+
command: "clearHoles",
|
|
1599
|
+
title: "Clear Holes",
|
|
1600
|
+
handler: () => {
|
|
1601
|
+
this.holes = [];
|
|
1602
|
+
this.redraw();
|
|
1603
|
+
this.syncHolesToDieline();
|
|
1604
|
+
return true;
|
|
1036
1605
|
}
|
|
1037
|
-
return true;
|
|
1038
1606
|
}
|
|
1039
|
-
|
|
1607
|
+
]
|
|
1040
1608
|
};
|
|
1041
1609
|
}
|
|
1042
|
-
|
|
1043
|
-
this.
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
objects.sort(
|
|
1073
|
-
(a, b) => {
|
|
1074
|
-
var _a, _b, _c, _d;
|
|
1075
|
-
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);
|
|
1076
|
-
}
|
|
1077
|
-
);
|
|
1078
|
-
const newHoles = [];
|
|
1079
|
-
objects.forEach((obj) => {
|
|
1080
|
-
const currentPos = new import_core4.Point(obj.left, obj.top);
|
|
1081
|
-
const newPos = this.calculateConstrainedPosition(currentPos, geometry);
|
|
1082
|
-
if (currentPos.distanceFrom(newPos) > 0.1) {
|
|
1083
|
-
obj.set({
|
|
1084
|
-
left: newPos.x,
|
|
1085
|
-
top: newPos.y
|
|
1086
|
-
});
|
|
1087
|
-
obj.setCoords();
|
|
1088
|
-
changed = true;
|
|
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) {
|
|
1089
1640
|
}
|
|
1090
|
-
newHoles.push({ x: obj.left, y: obj.top });
|
|
1091
|
-
});
|
|
1092
|
-
if (changed) {
|
|
1093
|
-
this.options.holes = newHoles;
|
|
1094
|
-
editor.canvas.requestRenderAll();
|
|
1095
1641
|
}
|
|
1096
|
-
}
|
|
1097
|
-
setup(editor) {
|
|
1098
1642
|
if (!this.handleMoving) {
|
|
1099
1643
|
this.handleMoving = (e) => {
|
|
1100
1644
|
var _a;
|
|
1101
1645
|
const target = e.target;
|
|
1102
1646
|
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "hole-marker") return;
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
const
|
|
1106
|
-
|
|
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);
|
|
1107
1660
|
target.set({
|
|
1108
1661
|
left: newPos.x,
|
|
1109
1662
|
top: newPos.y
|
|
1110
1663
|
});
|
|
1111
1664
|
};
|
|
1112
|
-
|
|
1665
|
+
canvas.on("object:moving", this.handleMoving);
|
|
1113
1666
|
}
|
|
1114
1667
|
if (!this.handleModified) {
|
|
1115
1668
|
this.handleModified = (e) => {
|
|
1116
1669
|
var _a;
|
|
1117
1670
|
const target = e.target;
|
|
1118
1671
|
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "hole-marker") return;
|
|
1119
|
-
this.syncHolesFromCanvas(
|
|
1672
|
+
this.syncHolesFromCanvas();
|
|
1120
1673
|
};
|
|
1121
|
-
|
|
1674
|
+
canvas.on("object:modified", this.handleModified);
|
|
1122
1675
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
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;
|
|
1128
1684
|
const topCenter = { x: g.x, y: g.y - g.height / 2 };
|
|
1129
1685
|
const snapped = getNearestPointOnDieline(topCenter, {
|
|
1130
1686
|
...g,
|
|
@@ -1132,65 +1688,97 @@ var HoleTool = class {
|
|
|
1132
1688
|
});
|
|
1133
1689
|
defaultPos = snapped;
|
|
1134
1690
|
}
|
|
1135
|
-
|
|
1136
|
-
}
|
|
1137
|
-
this.options = { ...opts };
|
|
1138
|
-
this.redraw(editor);
|
|
1139
|
-
const dielineTool = editor.getExtension("DielineTool");
|
|
1140
|
-
if (dielineTool && dielineTool.updateDieline) {
|
|
1141
|
-
dielineTool.updateDieline(editor);
|
|
1691
|
+
this.holes = [defaultPos];
|
|
1142
1692
|
}
|
|
1693
|
+
this.redraw();
|
|
1694
|
+
this.syncHolesToDieline();
|
|
1143
1695
|
}
|
|
1144
|
-
teardown(
|
|
1696
|
+
teardown() {
|
|
1697
|
+
if (!this.canvasService) return;
|
|
1698
|
+
const canvas = this.canvasService.canvas;
|
|
1145
1699
|
if (this.handleMoving) {
|
|
1146
|
-
|
|
1700
|
+
canvas.off("object:moving", this.handleMoving);
|
|
1147
1701
|
this.handleMoving = null;
|
|
1148
1702
|
}
|
|
1149
1703
|
if (this.handleModified) {
|
|
1150
|
-
|
|
1704
|
+
canvas.off("object:modified", this.handleModified);
|
|
1151
1705
|
this.handleModified = null;
|
|
1152
1706
|
}
|
|
1153
|
-
|
|
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) => {
|
|
1154
1715
|
var _a;
|
|
1155
1716
|
return ((_a = obj.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
1156
1717
|
});
|
|
1157
|
-
objects.forEach((obj) =>
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
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
|
+
}
|
|
1166
1727
|
}
|
|
1728
|
+
this.canvasService.requestRenderAll();
|
|
1167
1729
|
}
|
|
1168
|
-
syncHolesFromCanvas(
|
|
1169
|
-
|
|
1730
|
+
syncHolesFromCanvas() {
|
|
1731
|
+
if (!this.canvasService) return;
|
|
1732
|
+
const objects = this.canvasService.canvas.getObjects().filter((obj) => {
|
|
1170
1733
|
var _a;
|
|
1171
1734
|
return ((_a = obj.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
1172
1735
|
});
|
|
1173
1736
|
const holes = objects.map((obj) => ({ x: obj.left, y: obj.top }));
|
|
1174
|
-
this.
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
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
|
+
}
|
|
1178
1765
|
}
|
|
1179
1766
|
}
|
|
1180
|
-
redraw(
|
|
1181
|
-
|
|
1767
|
+
redraw() {
|
|
1768
|
+
if (!this.canvasService) return;
|
|
1769
|
+
const canvas = this.canvasService.canvas;
|
|
1182
1770
|
const existing = canvas.getObjects().filter((obj) => {
|
|
1183
1771
|
var _a;
|
|
1184
1772
|
return ((_a = obj.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
1185
1773
|
});
|
|
1186
1774
|
existing.forEach((obj) => canvas.remove(obj));
|
|
1187
|
-
const { innerRadius, outerRadius, style, holes } = this
|
|
1775
|
+
const { innerRadius, outerRadius, style, holes } = this;
|
|
1188
1776
|
if (!holes || holes.length === 0) {
|
|
1189
|
-
|
|
1777
|
+
this.canvasService.requestRenderAll();
|
|
1190
1778
|
return;
|
|
1191
1779
|
}
|
|
1192
1780
|
holes.forEach((hole, index) => {
|
|
1193
|
-
const innerCircle = new
|
|
1781
|
+
const innerCircle = new import_fabric4.Circle({
|
|
1194
1782
|
radius: innerRadius,
|
|
1195
1783
|
fill: "transparent",
|
|
1196
1784
|
stroke: "red",
|
|
@@ -1198,7 +1786,7 @@ var HoleTool = class {
|
|
|
1198
1786
|
originX: "center",
|
|
1199
1787
|
originY: "center"
|
|
1200
1788
|
});
|
|
1201
|
-
const outerCircle = new
|
|
1789
|
+
const outerCircle = new import_fabric4.Circle({
|
|
1202
1790
|
radius: outerRadius,
|
|
1203
1791
|
fill: "transparent",
|
|
1204
1792
|
stroke: "#666",
|
|
@@ -1207,7 +1795,7 @@ var HoleTool = class {
|
|
|
1207
1795
|
originX: "center",
|
|
1208
1796
|
originY: "center"
|
|
1209
1797
|
});
|
|
1210
|
-
const holeGroup = new
|
|
1798
|
+
const holeGroup = new import_fabric4.Group([outerCircle, innerCircle], {
|
|
1211
1799
|
left: hole.x,
|
|
1212
1800
|
top: hole.y,
|
|
1213
1801
|
originX: "center",
|
|
@@ -1244,7 +1832,68 @@ var HoleTool = class {
|
|
|
1244
1832
|
canvas.add(holeGroup);
|
|
1245
1833
|
canvas.bringObjectToFront(holeGroup);
|
|
1246
1834
|
});
|
|
1247
|
-
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;
|
|
1248
1897
|
}
|
|
1249
1898
|
calculateConstrainedPosition(p, g) {
|
|
1250
1899
|
const options = {
|
|
@@ -1256,10 +1905,10 @@ var HoleTool = class {
|
|
|
1256
1905
|
{ x: p.x, y: p.y },
|
|
1257
1906
|
options
|
|
1258
1907
|
);
|
|
1259
|
-
const nearestP = new
|
|
1908
|
+
const nearestP = new import_fabric4.Point(nearest.x, nearest.y);
|
|
1260
1909
|
const dist = p.distanceFrom(nearestP);
|
|
1261
1910
|
const v = p.subtract(nearestP);
|
|
1262
|
-
const center = new
|
|
1911
|
+
const center = new import_fabric4.Point(g.x, g.y);
|
|
1263
1912
|
const centerToNearest = nearestP.subtract(center);
|
|
1264
1913
|
const distToCenter = p.distanceFrom(center);
|
|
1265
1914
|
const nearestDistToCenter = nearestP.distanceFrom(center);
|
|
@@ -1269,12 +1918,11 @@ var HoleTool = class {
|
|
|
1269
1918
|
}
|
|
1270
1919
|
let clampedDist = signedDist;
|
|
1271
1920
|
if (signedDist > 0) {
|
|
1272
|
-
clampedDist = Math.min(signedDist, this.
|
|
1921
|
+
clampedDist = Math.min(signedDist, this.innerRadius);
|
|
1273
1922
|
} else {
|
|
1274
|
-
clampedDist = Math.max(signedDist, -this.
|
|
1923
|
+
clampedDist = Math.max(signedDist, -this.outerRadius);
|
|
1275
1924
|
}
|
|
1276
1925
|
if (dist < 1e-3) return nearestP;
|
|
1277
|
-
const dir = v.scalarDivide(dist);
|
|
1278
1926
|
const scale = Math.abs(clampedDist) / (dist || 1);
|
|
1279
1927
|
const offset = v.scalarMultiply(scale);
|
|
1280
1928
|
return nearestP.add(offset);
|
|
@@ -1283,111 +1931,153 @@ var HoleTool = class {
|
|
|
1283
1931
|
|
|
1284
1932
|
// src/image.ts
|
|
1285
1933
|
var import_core5 = require("@pooder/core");
|
|
1934
|
+
var import_fabric5 = require("fabric");
|
|
1286
1935
|
var ImageTool = class {
|
|
1287
|
-
constructor() {
|
|
1288
|
-
this.
|
|
1289
|
-
this.
|
|
1290
|
-
|
|
1291
|
-
url: "",
|
|
1292
|
-
opacity: 1
|
|
1293
|
-
};
|
|
1294
|
-
this.schema = {
|
|
1295
|
-
url: {
|
|
1296
|
-
type: "string",
|
|
1297
|
-
label: "Image URL"
|
|
1298
|
-
},
|
|
1299
|
-
opacity: {
|
|
1300
|
-
type: "number",
|
|
1301
|
-
min: 0,
|
|
1302
|
-
max: 1,
|
|
1303
|
-
step: 0.1,
|
|
1304
|
-
label: "Opacity"
|
|
1305
|
-
},
|
|
1306
|
-
width: {
|
|
1307
|
-
type: "number",
|
|
1308
|
-
label: "Width",
|
|
1309
|
-
min: 0,
|
|
1310
|
-
max: 5e3
|
|
1311
|
-
},
|
|
1312
|
-
height: {
|
|
1313
|
-
type: "number",
|
|
1314
|
-
label: "Height",
|
|
1315
|
-
min: 0,
|
|
1316
|
-
max: 5e3
|
|
1317
|
-
},
|
|
1318
|
-
angle: {
|
|
1319
|
-
type: "number",
|
|
1320
|
-
label: "Rotation",
|
|
1321
|
-
min: 0,
|
|
1322
|
-
max: 360
|
|
1323
|
-
},
|
|
1324
|
-
left: {
|
|
1325
|
-
type: "number",
|
|
1326
|
-
label: "Left",
|
|
1327
|
-
min: 0,
|
|
1328
|
-
max: 1e3
|
|
1329
|
-
},
|
|
1330
|
-
top: {
|
|
1331
|
-
type: "number",
|
|
1332
|
-
label: "Top",
|
|
1333
|
-
min: 0,
|
|
1334
|
-
max: 1e3
|
|
1335
|
-
}
|
|
1336
|
-
};
|
|
1337
|
-
this.commands = {
|
|
1338
|
-
setUserImage: {
|
|
1339
|
-
execute: (editor, url, opacity, width, height, angle, left, top) => {
|
|
1340
|
-
if (this.options.url === url && this.options.opacity === opacity && this.options.width === width && this.options.height === height && this.options.angle === angle && this.options.left === left && this.options.top === top)
|
|
1341
|
-
return true;
|
|
1342
|
-
this.options = { url, opacity, width, height, angle, left, top };
|
|
1343
|
-
this.updateImage(editor, this.options);
|
|
1344
|
-
return true;
|
|
1345
|
-
},
|
|
1346
|
-
schema: {
|
|
1347
|
-
url: {
|
|
1348
|
-
type: "string",
|
|
1349
|
-
label: "Image URL",
|
|
1350
|
-
required: true
|
|
1351
|
-
},
|
|
1352
|
-
opacity: {
|
|
1353
|
-
type: "number",
|
|
1354
|
-
label: "Opacity",
|
|
1355
|
-
min: 0,
|
|
1356
|
-
max: 1,
|
|
1357
|
-
required: true
|
|
1358
|
-
},
|
|
1359
|
-
width: { type: "number", label: "Width" },
|
|
1360
|
-
height: { type: "number", label: "Height" },
|
|
1361
|
-
angle: { type: "number", label: "Angle" },
|
|
1362
|
-
left: { type: "number", label: "Left" },
|
|
1363
|
-
top: { type: "number", label: "Top" }
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1936
|
+
constructor(options) {
|
|
1937
|
+
this.id = "pooder.kit.image";
|
|
1938
|
+
this.metadata = {
|
|
1939
|
+
name: "ImageTool"
|
|
1366
1940
|
};
|
|
1941
|
+
this._loadingUrl = null;
|
|
1942
|
+
this.url = "";
|
|
1943
|
+
this.opacity = 1;
|
|
1944
|
+
if (options) {
|
|
1945
|
+
Object.assign(this, options);
|
|
1946
|
+
}
|
|
1367
1947
|
}
|
|
1368
|
-
|
|
1369
|
-
this.
|
|
1370
|
-
this.
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
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();
|
|
1973
|
+
}
|
|
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
|
+
}
|
|
1379
1989
|
}
|
|
1990
|
+
this.canvasService = void 0;
|
|
1991
|
+
this.context = void 0;
|
|
1380
1992
|
}
|
|
1381
1993
|
}
|
|
1382
|
-
|
|
1383
|
-
|
|
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
|
+
};
|
|
1384
2073
|
}
|
|
1385
|
-
ensureLayer(
|
|
1386
|
-
|
|
2074
|
+
ensureLayer() {
|
|
2075
|
+
if (!this.canvasService) return;
|
|
2076
|
+
let userLayer = this.canvasService.getLayer("user");
|
|
1387
2077
|
if (!userLayer) {
|
|
1388
|
-
userLayer =
|
|
1389
|
-
width:
|
|
1390
|
-
height:
|
|
2078
|
+
userLayer = this.canvasService.createLayer("user", {
|
|
2079
|
+
width: this.canvasService.canvas.width,
|
|
2080
|
+
height: this.canvasService.canvas.height,
|
|
1391
2081
|
left: 0,
|
|
1392
2082
|
top: 0,
|
|
1393
2083
|
originX: "left",
|
|
@@ -1395,42 +2085,64 @@ var ImageTool = class {
|
|
|
1395
2085
|
selectable: false,
|
|
1396
2086
|
evented: true,
|
|
1397
2087
|
subTargetCheck: true,
|
|
1398
|
-
interactive: true
|
|
1399
|
-
data: {
|
|
1400
|
-
id: "user"
|
|
1401
|
-
}
|
|
2088
|
+
interactive: true
|
|
1402
2089
|
});
|
|
1403
|
-
|
|
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();
|
|
1404
2103
|
}
|
|
1405
2104
|
}
|
|
1406
|
-
updateImage(
|
|
2105
|
+
updateImage() {
|
|
1407
2106
|
var _a, _b;
|
|
1408
|
-
|
|
1409
|
-
|
|
2107
|
+
if (!this.canvasService) return;
|
|
2108
|
+
let { url, opacity, width, height, angle, left, top } = this;
|
|
2109
|
+
const layer = this.canvasService.getLayer("user");
|
|
1410
2110
|
if (!layer) {
|
|
1411
2111
|
console.warn("[ImageTool] User layer not found");
|
|
1412
2112
|
return;
|
|
1413
2113
|
}
|
|
1414
|
-
const userImage =
|
|
2114
|
+
const userImage = this.canvasService.getObject("user-image", "user");
|
|
1415
2115
|
if (this._loadingUrl === url) return;
|
|
1416
2116
|
if (userImage) {
|
|
1417
2117
|
const currentSrc = ((_a = userImage.getSrc) == null ? void 0 : _a.call(userImage)) || ((_b = userImage._element) == null ? void 0 : _b.src);
|
|
1418
2118
|
if (currentSrc !== url) {
|
|
1419
|
-
this.loadImage(
|
|
2119
|
+
this.loadImage(layer);
|
|
1420
2120
|
} else {
|
|
1421
2121
|
const updates = {};
|
|
1422
|
-
const
|
|
1423
|
-
const
|
|
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;
|
|
1424
2126
|
if (userImage.opacity !== opacity) updates.opacity = opacity;
|
|
1425
2127
|
if (angle !== void 0 && userImage.angle !== angle)
|
|
1426
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
|
+
}
|
|
1427
2137
|
if (left !== void 0) {
|
|
1428
|
-
const
|
|
2138
|
+
const globalLeft = Coordinate.toAbsolute(left, canvasW);
|
|
2139
|
+
const localLeft = globalLeft - centerX;
|
|
1429
2140
|
if (Math.abs(userImage.left - localLeft) > 1)
|
|
1430
2141
|
updates.left = localLeft;
|
|
1431
2142
|
}
|
|
1432
2143
|
if (top !== void 0) {
|
|
1433
|
-
const
|
|
2144
|
+
const globalTop = Coordinate.toAbsolute(top, canvasH);
|
|
2145
|
+
const localTop = globalTop - centerY;
|
|
1434
2146
|
if (Math.abs(userImage.top - localTop) > 1) updates.top = localTop;
|
|
1435
2147
|
}
|
|
1436
2148
|
if (width !== void 0 && userImage.width)
|
|
@@ -1439,44 +2151,145 @@ var ImageTool = class {
|
|
|
1439
2151
|
updates.scaleY = height / userImage.height;
|
|
1440
2152
|
if (Object.keys(updates).length > 0) {
|
|
1441
2153
|
userImage.set(updates);
|
|
1442
|
-
|
|
2154
|
+
layer.dirty = true;
|
|
2155
|
+
this.canvasService.requestRenderAll();
|
|
1443
2156
|
}
|
|
1444
2157
|
}
|
|
1445
2158
|
} else {
|
|
1446
|
-
this.loadImage(
|
|
2159
|
+
this.loadImage(layer);
|
|
1447
2160
|
}
|
|
1448
2161
|
}
|
|
1449
|
-
loadImage(
|
|
1450
|
-
|
|
2162
|
+
loadImage(layer) {
|
|
2163
|
+
if (!this.canvasService) return;
|
|
2164
|
+
const { url } = this;
|
|
2165
|
+
if (!url) return;
|
|
1451
2166
|
this._loadingUrl = url;
|
|
1452
|
-
|
|
2167
|
+
import_fabric5.FabricImage.fromURL(url, { crossOrigin: "anonymous" }).then((image) => {
|
|
2168
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1453
2169
|
if (this._loadingUrl !== url) return;
|
|
1454
2170
|
this._loadingUrl = null;
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
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
|
+
);
|
|
1458
2217
|
if (existingImage) {
|
|
1459
2218
|
const defaultLeft = existingImage.left;
|
|
1460
2219
|
const defaultTop = existingImage.top;
|
|
1461
2220
|
const defaultAngle = existingImage.angle;
|
|
1462
2221
|
const defaultScaleX = existingImage.scaleX;
|
|
1463
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
|
+
}
|
|
1464
2258
|
image.set({
|
|
1465
|
-
|
|
1466
|
-
|
|
2259
|
+
originX: "center",
|
|
2260
|
+
// Use center origin for easier positioning
|
|
2261
|
+
originY: "center",
|
|
2262
|
+
left: targetLeft,
|
|
2263
|
+
top: targetTop,
|
|
1467
2264
|
angle: angle !== void 0 ? angle : defaultAngle,
|
|
1468
2265
|
scaleX: width !== void 0 && image.width ? width / image.width : defaultScaleX,
|
|
1469
2266
|
scaleY: height !== void 0 && image.height ? height / image.height : defaultScaleY
|
|
1470
2267
|
});
|
|
1471
2268
|
layer.remove(existingImage);
|
|
1472
2269
|
} else {
|
|
2270
|
+
image.set({
|
|
2271
|
+
originX: "center",
|
|
2272
|
+
originY: "center"
|
|
2273
|
+
});
|
|
1473
2274
|
if (width !== void 0 && image.width)
|
|
1474
2275
|
image.scaleX = width / image.width;
|
|
1475
2276
|
if (height !== void 0 && image.height)
|
|
1476
2277
|
image.scaleY = height / image.height;
|
|
1477
2278
|
if (angle !== void 0) image.angle = angle;
|
|
1478
|
-
|
|
1479
|
-
|
|
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
|
+
}
|
|
1480
2293
|
}
|
|
1481
2294
|
image.set({
|
|
1482
2295
|
opacity: opacity !== void 0 ? opacity : 1,
|
|
@@ -1486,18 +2299,22 @@ var ImageTool = class {
|
|
|
1486
2299
|
});
|
|
1487
2300
|
layer.add(image);
|
|
1488
2301
|
image.on("modified", (e) => {
|
|
2302
|
+
var _a2, _b2;
|
|
1489
2303
|
const matrix = image.calcTransformMatrix();
|
|
1490
|
-
const globalPoint =
|
|
1491
|
-
this.
|
|
1492
|
-
this.
|
|
1493
|
-
this.
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
if (image.
|
|
1497
|
-
|
|
1498
|
-
|
|
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
|
+
}
|
|
1499
2315
|
});
|
|
1500
|
-
|
|
2316
|
+
layer.dirty = true;
|
|
2317
|
+
this.canvasService.requestRenderAll();
|
|
1501
2318
|
}).catch((err) => {
|
|
1502
2319
|
if (this._loadingUrl === url) this._loadingUrl = null;
|
|
1503
2320
|
console.error("Failed to load image", url, err);
|
|
@@ -1507,69 +2324,108 @@ var ImageTool = class {
|
|
|
1507
2324
|
|
|
1508
2325
|
// src/white-ink.ts
|
|
1509
2326
|
var import_core6 = require("@pooder/core");
|
|
2327
|
+
var import_fabric6 = require("fabric");
|
|
1510
2328
|
var WhiteInkTool = class {
|
|
1511
|
-
constructor() {
|
|
1512
|
-
this.
|
|
1513
|
-
this.
|
|
1514
|
-
|
|
1515
|
-
opacity: 1,
|
|
1516
|
-
enableClip: false
|
|
1517
|
-
};
|
|
1518
|
-
this.schema = {
|
|
1519
|
-
customMask: { type: "string", label: "Custom Mask URL" },
|
|
1520
|
-
opacity: { type: "number", min: 0, max: 1, step: 0.01, label: "Opacity" },
|
|
1521
|
-
enableClip: { type: "boolean", label: "Enable Clip" }
|
|
2329
|
+
constructor(options) {
|
|
2330
|
+
this.id = "pooder.kit.white-ink";
|
|
2331
|
+
this.metadata = {
|
|
2332
|
+
name: "WhiteInkTool"
|
|
1522
2333
|
};
|
|
1523
|
-
this.
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
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();
|
|
1552
2368
|
}
|
|
1553
2369
|
}
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
this.setup(editor);
|
|
1559
|
-
this.updateWhiteInk(editor, this.options);
|
|
2370
|
+
});
|
|
2371
|
+
}
|
|
2372
|
+
this.setup();
|
|
2373
|
+
this.updateWhiteInk();
|
|
1560
2374
|
}
|
|
1561
|
-
|
|
1562
|
-
this.teardown(
|
|
2375
|
+
deactivate(context) {
|
|
2376
|
+
this.teardown();
|
|
2377
|
+
this.canvasService = void 0;
|
|
1563
2378
|
}
|
|
1564
|
-
|
|
1565
|
-
|
|
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
|
+
};
|
|
1566
2420
|
}
|
|
1567
|
-
setup(
|
|
1568
|
-
|
|
2421
|
+
setup() {
|
|
2422
|
+
if (!this.canvasService) return;
|
|
2423
|
+
const canvas = this.canvasService.canvas;
|
|
2424
|
+
let userLayer = this.canvasService.getLayer("user");
|
|
1569
2425
|
if (!userLayer) {
|
|
1570
|
-
userLayer =
|
|
1571
|
-
width:
|
|
1572
|
-
height:
|
|
2426
|
+
userLayer = this.canvasService.createLayer("user", {
|
|
2427
|
+
width: canvas.width,
|
|
2428
|
+
height: canvas.height,
|
|
1573
2429
|
left: 0,
|
|
1574
2430
|
top: 0,
|
|
1575
2431
|
originX: "left",
|
|
@@ -1577,61 +2433,58 @@ var WhiteInkTool = class {
|
|
|
1577
2433
|
selectable: false,
|
|
1578
2434
|
evented: true,
|
|
1579
2435
|
subTargetCheck: true,
|
|
1580
|
-
interactive: true
|
|
1581
|
-
data: {
|
|
1582
|
-
id: "user"
|
|
1583
|
-
}
|
|
2436
|
+
interactive: true
|
|
1584
2437
|
});
|
|
1585
|
-
|
|
2438
|
+
canvas.add(userLayer);
|
|
1586
2439
|
}
|
|
1587
2440
|
if (!this.syncHandler) {
|
|
1588
2441
|
this.syncHandler = (e) => {
|
|
1589
2442
|
var _a;
|
|
1590
2443
|
const target = e.target;
|
|
1591
2444
|
if (target && ((_a = target.data) == null ? void 0 : _a.id) === "user-image") {
|
|
1592
|
-
this.syncWithUserImage(
|
|
2445
|
+
this.syncWithUserImage();
|
|
1593
2446
|
}
|
|
1594
2447
|
};
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
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);
|
|
1599
2452
|
}
|
|
1600
2453
|
}
|
|
1601
|
-
teardown(
|
|
2454
|
+
teardown() {
|
|
2455
|
+
if (!this.canvasService) return;
|
|
2456
|
+
const canvas = this.canvasService.canvas;
|
|
1602
2457
|
if (this.syncHandler) {
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
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);
|
|
1607
2462
|
this.syncHandler = void 0;
|
|
1608
2463
|
}
|
|
1609
|
-
const layer =
|
|
2464
|
+
const layer = this.canvasService.getLayer("user");
|
|
1610
2465
|
if (layer) {
|
|
1611
|
-
const whiteInk =
|
|
2466
|
+
const whiteInk = this.canvasService.getObject("white-ink", "user");
|
|
1612
2467
|
if (whiteInk) {
|
|
1613
2468
|
layer.remove(whiteInk);
|
|
1614
2469
|
}
|
|
1615
2470
|
}
|
|
1616
|
-
const userImage =
|
|
2471
|
+
const userImage = this.canvasService.getObject("user-image", "user");
|
|
1617
2472
|
if (userImage && userImage.clipPath) {
|
|
1618
2473
|
userImage.set({ clipPath: void 0 });
|
|
1619
2474
|
}
|
|
1620
|
-
|
|
2475
|
+
this.canvasService.requestRenderAll();
|
|
1621
2476
|
}
|
|
1622
|
-
|
|
1623
|
-
this.updateWhiteInk(editor, this.options);
|
|
1624
|
-
}
|
|
1625
|
-
updateWhiteInk(editor, opts) {
|
|
2477
|
+
updateWhiteInk() {
|
|
1626
2478
|
var _a, _b;
|
|
1627
|
-
|
|
1628
|
-
const
|
|
2479
|
+
if (!this.canvasService) return;
|
|
2480
|
+
const { customMask, opacity, enableClip } = this;
|
|
2481
|
+
const layer = this.canvasService.getLayer("user");
|
|
1629
2482
|
if (!layer) {
|
|
1630
2483
|
console.warn("[WhiteInkTool] User layer not found");
|
|
1631
2484
|
return;
|
|
1632
2485
|
}
|
|
1633
|
-
const whiteInk =
|
|
1634
|
-
const userImage =
|
|
2486
|
+
const whiteInk = this.canvasService.getObject("white-ink", "user");
|
|
2487
|
+
const userImage = this.canvasService.getObject("user-image", "user");
|
|
1635
2488
|
if (!customMask) {
|
|
1636
2489
|
if (whiteInk) {
|
|
1637
2490
|
layer.remove(whiteInk);
|
|
@@ -1639,50 +2492,51 @@ var WhiteInkTool = class {
|
|
|
1639
2492
|
if (userImage && userImage.clipPath) {
|
|
1640
2493
|
userImage.set({ clipPath: void 0 });
|
|
1641
2494
|
}
|
|
1642
|
-
|
|
2495
|
+
layer.dirty = true;
|
|
2496
|
+
this.canvasService.requestRenderAll();
|
|
1643
2497
|
return;
|
|
1644
2498
|
}
|
|
1645
2499
|
if (whiteInk) {
|
|
1646
2500
|
const currentSrc = ((_a = whiteInk.getSrc) == null ? void 0 : _a.call(whiteInk)) || ((_b = whiteInk._element) == null ? void 0 : _b.src);
|
|
1647
2501
|
if (currentSrc !== customMask) {
|
|
1648
|
-
this.loadWhiteInk(
|
|
1649
|
-
editor,
|
|
1650
|
-
layer,
|
|
1651
|
-
customMask,
|
|
1652
|
-
opacity,
|
|
1653
|
-
enableClip,
|
|
1654
|
-
whiteInk
|
|
1655
|
-
);
|
|
2502
|
+
this.loadWhiteInk(layer, customMask, opacity, enableClip, whiteInk);
|
|
1656
2503
|
} else {
|
|
1657
2504
|
if (whiteInk.opacity !== opacity) {
|
|
1658
2505
|
whiteInk.set({ opacity });
|
|
1659
|
-
|
|
2506
|
+
layer.dirty = true;
|
|
2507
|
+
this.canvasService.requestRenderAll();
|
|
1660
2508
|
}
|
|
1661
2509
|
}
|
|
1662
2510
|
} else {
|
|
1663
|
-
this.loadWhiteInk(
|
|
2511
|
+
this.loadWhiteInk(layer, customMask, opacity, enableClip);
|
|
1664
2512
|
}
|
|
1665
2513
|
if (userImage) {
|
|
1666
2514
|
if (enableClip) {
|
|
1667
2515
|
if (!userImage.clipPath) {
|
|
1668
|
-
this.applyClipPath(
|
|
2516
|
+
this.applyClipPath(customMask);
|
|
1669
2517
|
}
|
|
1670
2518
|
} else {
|
|
1671
2519
|
if (userImage.clipPath) {
|
|
1672
2520
|
userImage.set({ clipPath: void 0 });
|
|
1673
|
-
|
|
2521
|
+
layer.dirty = true;
|
|
2522
|
+
this.canvasService.requestRenderAll();
|
|
1674
2523
|
}
|
|
1675
2524
|
}
|
|
1676
2525
|
}
|
|
1677
2526
|
}
|
|
1678
|
-
loadWhiteInk(
|
|
1679
|
-
|
|
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) => {
|
|
1680
2532
|
var _a;
|
|
2533
|
+
if (this._loadingUrl !== url) return;
|
|
2534
|
+
this._loadingUrl = null;
|
|
1681
2535
|
if (oldImage) {
|
|
1682
2536
|
layer.remove(oldImage);
|
|
1683
2537
|
}
|
|
1684
2538
|
(_a = image.filters) == null ? void 0 : _a.push(
|
|
1685
|
-
new
|
|
2539
|
+
new import_fabric6.filters.BlendColor({
|
|
1686
2540
|
color: "#FFFFFF",
|
|
1687
2541
|
mode: "add"
|
|
1688
2542
|
})
|
|
@@ -1697,26 +2551,29 @@ var WhiteInkTool = class {
|
|
|
1697
2551
|
}
|
|
1698
2552
|
});
|
|
1699
2553
|
layer.add(image);
|
|
1700
|
-
const userImage =
|
|
2554
|
+
const userImage = this.canvasService.getObject("user-image", "user");
|
|
1701
2555
|
if (userImage) {
|
|
1702
2556
|
layer.remove(userImage);
|
|
1703
2557
|
layer.add(userImage);
|
|
1704
2558
|
}
|
|
1705
2559
|
if (enableClip) {
|
|
1706
|
-
this.applyClipPath(
|
|
2560
|
+
this.applyClipPath(url);
|
|
1707
2561
|
} else if (userImage) {
|
|
1708
2562
|
userImage.set({ clipPath: void 0 });
|
|
1709
2563
|
}
|
|
1710
|
-
this.syncWithUserImage(
|
|
1711
|
-
|
|
2564
|
+
this.syncWithUserImage();
|
|
2565
|
+
layer.dirty = true;
|
|
2566
|
+
this.canvasService.requestRenderAll();
|
|
1712
2567
|
}).catch((err) => {
|
|
1713
2568
|
console.error("Failed to load white ink mask", url, err);
|
|
2569
|
+
this._loadingUrl = null;
|
|
1714
2570
|
});
|
|
1715
2571
|
}
|
|
1716
|
-
applyClipPath(
|
|
1717
|
-
|
|
2572
|
+
applyClipPath(url) {
|
|
2573
|
+
if (!this.canvasService) return;
|
|
2574
|
+
const userImage = this.canvasService.getObject("user-image", "user");
|
|
1718
2575
|
if (!userImage) return;
|
|
1719
|
-
|
|
2576
|
+
import_fabric6.FabricImage.fromURL(url, { crossOrigin: "anonymous" }).then((maskImage) => {
|
|
1720
2577
|
maskImage.set({
|
|
1721
2578
|
originX: "center",
|
|
1722
2579
|
originY: "center",
|
|
@@ -1727,14 +2584,17 @@ var WhiteInkTool = class {
|
|
|
1727
2584
|
scaleY: userImage.height / maskImage.height
|
|
1728
2585
|
});
|
|
1729
2586
|
userImage.set({ clipPath: maskImage });
|
|
1730
|
-
|
|
2587
|
+
const layer = this.canvasService.getLayer("user");
|
|
2588
|
+
if (layer) layer.dirty = true;
|
|
2589
|
+
this.canvasService.requestRenderAll();
|
|
1731
2590
|
}).catch((err) => {
|
|
1732
2591
|
console.error("Failed to load clip path", url, err);
|
|
1733
2592
|
});
|
|
1734
2593
|
}
|
|
1735
|
-
syncWithUserImage(
|
|
1736
|
-
|
|
1737
|
-
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");
|
|
1738
2598
|
if (userImage && whiteInk) {
|
|
1739
2599
|
whiteInk.set({
|
|
1740
2600
|
left: userImage.left,
|
|
@@ -1755,114 +2615,172 @@ var WhiteInkTool = class {
|
|
|
1755
2615
|
|
|
1756
2616
|
// src/ruler.ts
|
|
1757
2617
|
var import_core7 = require("@pooder/core");
|
|
2618
|
+
var import_fabric7 = require("fabric");
|
|
1758
2619
|
var RulerTool = class {
|
|
1759
|
-
constructor() {
|
|
1760
|
-
this.
|
|
1761
|
-
this.
|
|
1762
|
-
|
|
1763
|
-
thickness: 20,
|
|
1764
|
-
backgroundColor: "#f0f0f0",
|
|
1765
|
-
textColor: "#333333",
|
|
1766
|
-
lineColor: "#999999",
|
|
1767
|
-
fontSize: 10
|
|
2620
|
+
constructor(options) {
|
|
2621
|
+
this.id = "pooder.kit.ruler";
|
|
2622
|
+
this.metadata = {
|
|
2623
|
+
name: "RulerTool"
|
|
1768
2624
|
};
|
|
1769
|
-
this.
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
this.
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
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();
|
|
1795
2658
|
}
|
|
1796
2659
|
}
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
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();
|
|
1802
2722
|
return true;
|
|
1803
|
-
|
|
1804
|
-
this.updateRuler(editor);
|
|
1805
|
-
return true;
|
|
2723
|
+
}
|
|
1806
2724
|
},
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
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;
|
|
1812
2742
|
}
|
|
1813
2743
|
}
|
|
1814
|
-
|
|
2744
|
+
]
|
|
1815
2745
|
};
|
|
1816
2746
|
}
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
this.
|
|
1820
|
-
}
|
|
1821
|
-
onUnmount(editor) {
|
|
1822
|
-
this.destroyLayer(editor);
|
|
2747
|
+
getLayer() {
|
|
2748
|
+
var _a;
|
|
2749
|
+
return (_a = this.canvasService) == null ? void 0 : _a.getLayer("ruler-overlay");
|
|
1823
2750
|
}
|
|
1824
|
-
|
|
1825
|
-
this.
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
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"
|
|
1834
2765
|
});
|
|
2766
|
+
canvas.bringObjectToFront(layer);
|
|
1835
2767
|
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
const width = editor.canvas.width || 800;
|
|
1840
|
-
const height = editor.canvas.height || 600;
|
|
1841
|
-
layer = new import_core7.PooderLayer([], {
|
|
1842
|
-
width,
|
|
1843
|
-
height,
|
|
1844
|
-
selectable: false,
|
|
1845
|
-
evented: false,
|
|
1846
|
-
data: { id: "ruler-overlay" }
|
|
1847
|
-
});
|
|
1848
|
-
editor.canvas.add(layer);
|
|
1849
|
-
}
|
|
1850
|
-
editor.canvas.bringObjectToFront(layer);
|
|
1851
|
-
}
|
|
1852
|
-
destroyLayer(editor) {
|
|
1853
|
-
const layer = this.getLayer(editor);
|
|
2768
|
+
destroyLayer() {
|
|
2769
|
+
if (!this.canvasService) return;
|
|
2770
|
+
const layer = this.getLayer();
|
|
1854
2771
|
if (layer) {
|
|
1855
|
-
|
|
2772
|
+
this.canvasService.canvas.remove(layer);
|
|
1856
2773
|
}
|
|
1857
2774
|
}
|
|
1858
|
-
updateRuler(
|
|
1859
|
-
|
|
2775
|
+
updateRuler() {
|
|
2776
|
+
if (!this.canvasService) return;
|
|
2777
|
+
const layer = this.getLayer();
|
|
1860
2778
|
if (!layer) return;
|
|
1861
2779
|
layer.remove(...layer.getObjects());
|
|
1862
|
-
const { thickness, backgroundColor, lineColor, textColor, fontSize } = this
|
|
1863
|
-
const width =
|
|
1864
|
-
const height =
|
|
1865
|
-
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({
|
|
1866
2784
|
left: 0,
|
|
1867
2785
|
top: 0,
|
|
1868
2786
|
width,
|
|
@@ -1871,7 +2789,7 @@ var RulerTool = class {
|
|
|
1871
2789
|
selectable: false,
|
|
1872
2790
|
evented: false
|
|
1873
2791
|
});
|
|
1874
|
-
const leftBg = new
|
|
2792
|
+
const leftBg = new import_fabric7.Rect({
|
|
1875
2793
|
left: 0,
|
|
1876
2794
|
top: 0,
|
|
1877
2795
|
width: thickness,
|
|
@@ -1880,7 +2798,7 @@ var RulerTool = class {
|
|
|
1880
2798
|
selectable: false,
|
|
1881
2799
|
evented: false
|
|
1882
2800
|
});
|
|
1883
|
-
const cornerBg = new
|
|
2801
|
+
const cornerBg = new import_fabric7.Rect({
|
|
1884
2802
|
left: 0,
|
|
1885
2803
|
top: 0,
|
|
1886
2804
|
width: thickness,
|
|
@@ -1891,7 +2809,9 @@ var RulerTool = class {
|
|
|
1891
2809
|
selectable: false,
|
|
1892
2810
|
evented: false
|
|
1893
2811
|
});
|
|
1894
|
-
layer.add(topBg
|
|
2812
|
+
layer.add(topBg);
|
|
2813
|
+
layer.add(leftBg);
|
|
2814
|
+
layer.add(cornerBg);
|
|
1895
2815
|
const step = 100;
|
|
1896
2816
|
const subStep = 10;
|
|
1897
2817
|
const midStep = 50;
|
|
@@ -1900,7 +2820,7 @@ var RulerTool = class {
|
|
|
1900
2820
|
let len = thickness * 0.25;
|
|
1901
2821
|
if (x % step === 0) len = thickness * 0.8;
|
|
1902
2822
|
else if (x % midStep === 0) len = thickness * 0.5;
|
|
1903
|
-
const line = new
|
|
2823
|
+
const line = new import_fabric7.Line([x, thickness - len, x, thickness], {
|
|
1904
2824
|
stroke: lineColor,
|
|
1905
2825
|
strokeWidth: 1,
|
|
1906
2826
|
selectable: false,
|
|
@@ -1908,7 +2828,7 @@ var RulerTool = class {
|
|
|
1908
2828
|
});
|
|
1909
2829
|
layer.add(line);
|
|
1910
2830
|
if (x % step === 0) {
|
|
1911
|
-
const text = new
|
|
2831
|
+
const text = new import_fabric7.Text(x.toString(), {
|
|
1912
2832
|
left: x + 2,
|
|
1913
2833
|
top: 2,
|
|
1914
2834
|
fontSize,
|
|
@@ -1925,7 +2845,7 @@ var RulerTool = class {
|
|
|
1925
2845
|
let len = thickness * 0.25;
|
|
1926
2846
|
if (y % step === 0) len = thickness * 0.8;
|
|
1927
2847
|
else if (y % midStep === 0) len = thickness * 0.5;
|
|
1928
|
-
const line = new
|
|
2848
|
+
const line = new import_fabric7.Line([thickness - len, y, thickness, y], {
|
|
1929
2849
|
stroke: lineColor,
|
|
1930
2850
|
strokeWidth: 1,
|
|
1931
2851
|
selectable: false,
|
|
@@ -1933,7 +2853,7 @@ var RulerTool = class {
|
|
|
1933
2853
|
});
|
|
1934
2854
|
layer.add(line);
|
|
1935
2855
|
if (y % step === 0) {
|
|
1936
|
-
const text = new
|
|
2856
|
+
const text = new import_fabric7.Text(y.toString(), {
|
|
1937
2857
|
angle: -90,
|
|
1938
2858
|
left: thickness / 2 - fontSize / 3,
|
|
1939
2859
|
// approximate centering
|
|
@@ -1949,62 +2869,80 @@ var RulerTool = class {
|
|
|
1949
2869
|
layer.add(text);
|
|
1950
2870
|
}
|
|
1951
2871
|
}
|
|
1952
|
-
|
|
1953
|
-
|
|
2872
|
+
this.canvasService.canvas.bringObjectToFront(layer);
|
|
2873
|
+
this.canvasService.canvas.requestRenderAll();
|
|
1954
2874
|
}
|
|
1955
2875
|
};
|
|
1956
2876
|
|
|
1957
2877
|
// src/mirror.ts
|
|
2878
|
+
var import_core8 = require("@pooder/core");
|
|
1958
2879
|
var MirrorTool = class {
|
|
1959
|
-
constructor() {
|
|
1960
|
-
this.
|
|
1961
|
-
this.
|
|
1962
|
-
|
|
2880
|
+
constructor(options) {
|
|
2881
|
+
this.id = "pooder.kit.mirror";
|
|
2882
|
+
this.metadata = {
|
|
2883
|
+
name: "MirrorTool"
|
|
1963
2884
|
};
|
|
1964
|
-
this.
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
execute: (editor) => {
|
|
1973
|
-
this.options.enabled = !this.options.enabled;
|
|
1974
|
-
this.applyMirror(editor, this.options.enabled);
|
|
1975
|
-
return true;
|
|
1976
|
-
}
|
|
1977
|
-
},
|
|
1978
|
-
setMirror: {
|
|
1979
|
-
execute: (editor, enabled) => {
|
|
1980
|
-
if (this.options.enabled === enabled) return true;
|
|
1981
|
-
this.options.enabled = enabled;
|
|
1982
|
-
this.applyMirror(editor, enabled);
|
|
1983
|
-
return true;
|
|
1984
|
-
},
|
|
1985
|
-
schema: {
|
|
1986
|
-
enabled: {
|
|
1987
|
-
type: "boolean",
|
|
1988
|
-
label: "Enabled",
|
|
1989
|
-
required: true
|
|
1990
|
-
}
|
|
1991
|
-
}
|
|
1992
|
-
}
|
|
2885
|
+
this.enabled = false;
|
|
2886
|
+
if (options) {
|
|
2887
|
+
Object.assign(this, options);
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
toJSON() {
|
|
2891
|
+
return {
|
|
2892
|
+
enabled: this.enabled
|
|
1993
2893
|
};
|
|
1994
2894
|
}
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
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);
|
|
1998
2915
|
}
|
|
1999
2916
|
}
|
|
2000
|
-
|
|
2001
|
-
this.applyMirror(
|
|
2917
|
+
deactivate(context) {
|
|
2918
|
+
this.applyMirror(false);
|
|
2919
|
+
this.canvasService = void 0;
|
|
2002
2920
|
}
|
|
2003
|
-
|
|
2004
|
-
|
|
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
|
+
};
|
|
2005
2942
|
}
|
|
2006
|
-
applyMirror(
|
|
2007
|
-
|
|
2943
|
+
applyMirror(enabled) {
|
|
2944
|
+
if (!this.canvasService) return;
|
|
2945
|
+
const canvas = this.canvasService.canvas;
|
|
2008
2946
|
if (!canvas) return;
|
|
2009
2947
|
const width = canvas.width || 800;
|
|
2010
2948
|
let vpt = canvas.viewportTransform || [1, 0, 0, 1, 0, 0];
|
|
@@ -2015,17 +2953,85 @@ var MirrorTool = class {
|
|
|
2015
2953
|
vpt[4] = width - vpt[4];
|
|
2016
2954
|
canvas.setViewportTransform(vpt);
|
|
2017
2955
|
canvas.requestRenderAll();
|
|
2956
|
+
this.enabled = true;
|
|
2018
2957
|
} else if (!enabled && isFlipped) {
|
|
2019
2958
|
vpt[0] = -vpt[0];
|
|
2020
2959
|
vpt[4] = width - vpt[4];
|
|
2021
2960
|
canvas.setViewportTransform(vpt);
|
|
2022
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
|
+
});
|
|
2023
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();
|
|
2024
3029
|
}
|
|
2025
3030
|
};
|
|
2026
3031
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2027
3032
|
0 && (module.exports = {
|
|
2028
3033
|
BackgroundTool,
|
|
3034
|
+
CanvasService,
|
|
2029
3035
|
DielineTool,
|
|
2030
3036
|
FilmTool,
|
|
2031
3037
|
HoleTool,
|