@pooder/kit 3.2.0 → 3.4.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 +12 -0
- package/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +387 -158
- package/dist/index.mjs +388 -159
- package/package.json +1 -1
- package/src/dieline.ts +10 -0
- package/src/geometry.ts +37 -11
- package/src/hole.ts +79 -29
- package/src/image.ts +122 -124
- package/src/tracer.ts +278 -164
package/src/image.ts
CHANGED
|
@@ -14,8 +14,7 @@ export interface ImageItem {
|
|
|
14
14
|
id: string;
|
|
15
15
|
url: string;
|
|
16
16
|
opacity: number;
|
|
17
|
-
|
|
18
|
-
height?: number;
|
|
17
|
+
scale?: number;
|
|
19
18
|
angle?: number;
|
|
20
19
|
left?: number;
|
|
21
20
|
top?: number;
|
|
@@ -30,6 +29,7 @@ export class ImageTool implements Extension {
|
|
|
30
29
|
|
|
31
30
|
private items: ImageItem[] = [];
|
|
32
31
|
private objectMap: Map<string, FabricObject> = new Map();
|
|
32
|
+
private loadResolvers: Map<string, () => void> = new Map();
|
|
33
33
|
private canvasService?: CanvasService;
|
|
34
34
|
private context?: ExtensionContext;
|
|
35
35
|
private isUpdatingConfig = false;
|
|
@@ -42,7 +42,9 @@ export class ImageTool implements Extension {
|
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
const configService = context.services.get<ConfigurationService>(
|
|
45
|
+
const configService = context.services.get<ConfigurationService>(
|
|
46
|
+
"ConfigurationService",
|
|
47
|
+
);
|
|
46
48
|
if (configService) {
|
|
47
49
|
// Load initial config
|
|
48
50
|
this.items = configService.get("image.items", []) || [];
|
|
@@ -51,17 +53,8 @@ export class ImageTool implements Extension {
|
|
|
51
53
|
configService.onAnyChange((e: { key: string; value: any }) => {
|
|
52
54
|
if (this.isUpdatingConfig) return;
|
|
53
55
|
|
|
54
|
-
let shouldUpdate = false;
|
|
55
56
|
if (e.key === "image.items") {
|
|
56
57
|
this.items = e.value || [];
|
|
57
|
-
shouldUpdate = true;
|
|
58
|
-
} else if (e.key.startsWith("dieline.") && e.key !== "dieline.holes") {
|
|
59
|
-
// Dieline changes affect image layout/scale
|
|
60
|
-
// Ignore dieline.holes as they don't affect layout and can cause jitter
|
|
61
|
-
shouldUpdate = true;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (shouldUpdate) {
|
|
65
58
|
this.updateImages();
|
|
66
59
|
}
|
|
67
60
|
});
|
|
@@ -100,15 +93,43 @@ export class ImageTool implements Extension {
|
|
|
100
93
|
{
|
|
101
94
|
command: "addImage",
|
|
102
95
|
title: "Add Image",
|
|
103
|
-
handler: (url: string, options?: Partial<ImageItem>) => {
|
|
96
|
+
handler: async (url: string, options?: Partial<ImageItem>) => {
|
|
97
|
+
const id = this.generateId();
|
|
104
98
|
const newItem: ImageItem = {
|
|
105
|
-
id
|
|
99
|
+
id,
|
|
106
100
|
url,
|
|
107
101
|
opacity: 1,
|
|
108
102
|
...options,
|
|
109
103
|
};
|
|
104
|
+
|
|
105
|
+
const promise = new Promise<string>((resolve) => {
|
|
106
|
+
this.loadResolvers.set(id, () => resolve(id));
|
|
107
|
+
});
|
|
108
|
+
|
|
110
109
|
this.updateConfig([...this.items, newItem]);
|
|
111
|
-
return
|
|
110
|
+
return promise;
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
command: "fitImageToArea",
|
|
115
|
+
title: "Fit Image to Area",
|
|
116
|
+
handler: (
|
|
117
|
+
id: string,
|
|
118
|
+
area: { width: number; height: number; left?: number; top?: number },
|
|
119
|
+
) => {
|
|
120
|
+
const item = this.items.find((i) => i.id === id);
|
|
121
|
+
const obj = this.objectMap.get(id);
|
|
122
|
+
if (item && obj && obj.width && obj.height) {
|
|
123
|
+
const scale = Math.max(
|
|
124
|
+
area.width / obj.width,
|
|
125
|
+
area.height / obj.height,
|
|
126
|
+
);
|
|
127
|
+
this.updateImageInConfig(id, {
|
|
128
|
+
scale,
|
|
129
|
+
left: area.left ?? 0.5,
|
|
130
|
+
top: area.top ?? 0.5,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
112
133
|
},
|
|
113
134
|
},
|
|
114
135
|
{
|
|
@@ -178,7 +199,9 @@ export class ImageTool implements Extension {
|
|
|
178
199
|
if (!this.context) return;
|
|
179
200
|
this.isUpdatingConfig = true;
|
|
180
201
|
this.items = newItems;
|
|
181
|
-
const configService = this.context.services.get<ConfigurationService>(
|
|
202
|
+
const configService = this.context.services.get<ConfigurationService>(
|
|
203
|
+
"ConfigurationService",
|
|
204
|
+
);
|
|
182
205
|
if (configService) {
|
|
183
206
|
configService.update("image.items", newItems);
|
|
184
207
|
}
|
|
@@ -187,7 +210,7 @@ export class ImageTool implements Extension {
|
|
|
187
210
|
if (!skipCanvasUpdate) {
|
|
188
211
|
this.updateImages();
|
|
189
212
|
}
|
|
190
|
-
|
|
213
|
+
|
|
191
214
|
// Reset flag after a short delay to allow config propagation
|
|
192
215
|
setTimeout(() => {
|
|
193
216
|
this.isUpdatingConfig = false;
|
|
@@ -214,7 +237,9 @@ export class ImageTool implements Extension {
|
|
|
214
237
|
// Try to insert below dieline-overlay
|
|
215
238
|
const dielineLayer = this.canvasService.getLayer("dieline-overlay");
|
|
216
239
|
if (dielineLayer) {
|
|
217
|
-
const index = this.canvasService.canvas
|
|
240
|
+
const index = this.canvasService.canvas
|
|
241
|
+
.getObjects()
|
|
242
|
+
.indexOf(dielineLayer);
|
|
218
243
|
// If dieline is at 0, move user to 0 (dieline shifts to 1)
|
|
219
244
|
if (index >= 0) {
|
|
220
245
|
this.canvasService.canvas.moveObjectTo(userLayer, index);
|
|
@@ -233,43 +258,13 @@ export class ImageTool implements Extension {
|
|
|
233
258
|
private getLayoutInfo() {
|
|
234
259
|
const canvasW = this.canvasService?.canvas.width || 800;
|
|
235
260
|
const canvasH = this.canvasService?.canvas.height || 600;
|
|
236
|
-
|
|
237
|
-
let layoutScale = 1;
|
|
238
|
-
let layoutOffsetX = 0;
|
|
239
|
-
let layoutOffsetY = 0;
|
|
240
|
-
let visualWidth = canvasW;
|
|
241
|
-
let visualHeight = canvasH;
|
|
242
|
-
let dielinePhysicalWidth = 500;
|
|
243
|
-
let dielinePhysicalHeight = 500;
|
|
244
|
-
|
|
245
|
-
if (this.context) {
|
|
246
|
-
const configService = this.context.services.get<ConfigurationService>("ConfigurationService");
|
|
247
|
-
if (configService) {
|
|
248
|
-
dielinePhysicalWidth = configService.get("dieline.width") || 500;
|
|
249
|
-
dielinePhysicalHeight = configService.get("dieline.height") || 500;
|
|
250
|
-
const padding = configService.get("dieline.padding") || 40;
|
|
251
|
-
|
|
252
|
-
const layout = Coordinate.calculateLayout(
|
|
253
|
-
{ width: canvasW, height: canvasH },
|
|
254
|
-
{ width: dielinePhysicalWidth, height: dielinePhysicalHeight },
|
|
255
|
-
padding
|
|
256
|
-
);
|
|
257
|
-
layoutScale = layout.scale;
|
|
258
|
-
layoutOffsetX = layout.offsetX;
|
|
259
|
-
layoutOffsetY = layout.offsetY;
|
|
260
|
-
visualWidth = layout.width;
|
|
261
|
-
visualHeight = layout.height;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
261
|
|
|
265
262
|
return {
|
|
266
|
-
layoutScale,
|
|
267
|
-
layoutOffsetX,
|
|
268
|
-
layoutOffsetY,
|
|
269
|
-
visualWidth,
|
|
270
|
-
visualHeight,
|
|
271
|
-
dielinePhysicalWidth,
|
|
272
|
-
dielinePhysicalHeight
|
|
263
|
+
layoutScale: 1,
|
|
264
|
+
layoutOffsetX: 0,
|
|
265
|
+
layoutOffsetY: 0,
|
|
266
|
+
visualWidth: canvasW,
|
|
267
|
+
visualHeight: canvasH,
|
|
273
268
|
};
|
|
274
269
|
}
|
|
275
270
|
|
|
@@ -282,7 +277,7 @@ export class ImageTool implements Extension {
|
|
|
282
277
|
}
|
|
283
278
|
|
|
284
279
|
// 1. Remove objects that are no longer in items
|
|
285
|
-
const currentIds = new Set(this.items.map(i => i.id));
|
|
280
|
+
const currentIds = new Set(this.items.map((i) => i.id));
|
|
286
281
|
for (const [id, obj] of this.objectMap) {
|
|
287
282
|
if (!currentIds.has(id)) {
|
|
288
283
|
layer.remove(obj);
|
|
@@ -301,38 +296,38 @@ export class ImageTool implements Extension {
|
|
|
301
296
|
this.loadImage(item, layer, layout);
|
|
302
297
|
} else {
|
|
303
298
|
// Existing object, update properties
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
// Ensure Z-Index order
|
|
307
|
-
// Note: layer.add() appends to end, so if we process in order, they should be roughly correct.
|
|
308
|
-
// However, if we need strict ordering, we might need to verify index.
|
|
309
|
-
// For simplicity, we rely on the fact that if it exists, it's already on canvas.
|
|
310
|
-
// To enforce strict Z-order matching array order:
|
|
311
|
-
// We can check if the object at layer._objects[index] is this object.
|
|
312
|
-
// But Fabric's Group/Layer handling might be complex.
|
|
313
|
-
// A simple way is: remove and re-add if order is wrong, or use moveObjectTo.
|
|
314
|
-
|
|
315
|
-
// Since we are iterating items in order, we can check if the object is at the expected visual index relative to other user images.
|
|
316
|
-
// But for now, let's assume update logic is sufficient.
|
|
317
|
-
// If we want to support reordering, we should probably just `moveTo`
|
|
299
|
+
// We remove and re-add to ensure coordinates are correctly converted
|
|
300
|
+
// from absolute (updateObjectProperties) to relative (layer.add)
|
|
318
301
|
layer.remove(obj);
|
|
319
|
-
|
|
302
|
+
this.updateObjectProperties(obj, item, layout);
|
|
303
|
+
layer.add(obj);
|
|
320
304
|
}
|
|
321
305
|
});
|
|
322
|
-
|
|
306
|
+
|
|
323
307
|
layer.dirty = true;
|
|
324
308
|
this.canvasService.requestRenderAll();
|
|
325
309
|
}
|
|
326
310
|
|
|
327
|
-
private updateObjectProperties(
|
|
328
|
-
|
|
311
|
+
private updateObjectProperties(
|
|
312
|
+
obj: FabricObject,
|
|
313
|
+
item: ImageItem,
|
|
314
|
+
layout: any,
|
|
315
|
+
) {
|
|
316
|
+
const {
|
|
317
|
+
layoutScale,
|
|
318
|
+
layoutOffsetX,
|
|
319
|
+
layoutOffsetY,
|
|
320
|
+
visualWidth,
|
|
321
|
+
visualHeight,
|
|
322
|
+
} = layout;
|
|
329
323
|
const updates: any = {};
|
|
330
324
|
|
|
331
325
|
// Opacity
|
|
332
326
|
if (obj.opacity !== item.opacity) updates.opacity = item.opacity;
|
|
333
|
-
|
|
327
|
+
|
|
334
328
|
// Angle
|
|
335
|
-
if (item.angle !== undefined && obj.angle !== item.angle)
|
|
329
|
+
if (item.angle !== undefined && obj.angle !== item.angle)
|
|
330
|
+
updates.angle = item.angle;
|
|
336
331
|
|
|
337
332
|
// Position (Normalized -> Absolute)
|
|
338
333
|
if (item.left !== undefined) {
|
|
@@ -344,27 +339,27 @@ export class ImageTool implements Extension {
|
|
|
344
339
|
if (Math.abs(obj.top - globalTop) > 1) updates.top = globalTop;
|
|
345
340
|
}
|
|
346
341
|
|
|
347
|
-
// Scale
|
|
348
|
-
if (item.
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
if (Math.abs(obj.scaleY - targetScaleY) > 0.001) updates.scaleY = targetScaleY;
|
|
342
|
+
// Scale
|
|
343
|
+
if (item.scale !== undefined) {
|
|
344
|
+
const targetScale = item.scale * layoutScale;
|
|
345
|
+
if (Math.abs(obj.scaleX - targetScale) > 0.001) {
|
|
346
|
+
updates.scaleX = targetScale;
|
|
347
|
+
updates.scaleY = targetScale;
|
|
348
|
+
}
|
|
355
349
|
}
|
|
356
|
-
|
|
350
|
+
|
|
357
351
|
// Center origin if not set
|
|
358
352
|
if (obj.originX !== "center") {
|
|
359
353
|
updates.originX = "center";
|
|
360
354
|
updates.originY = "center";
|
|
361
355
|
// Adjust position because origin changed (Fabric logic)
|
|
362
|
-
// For simplicity, we just set it, next cycle will fix pos if needed,
|
|
356
|
+
// For simplicity, we just set it, next cycle will fix pos if needed,
|
|
363
357
|
// or we can calculate the shift. Ideally we set origin on creation.
|
|
364
358
|
}
|
|
365
359
|
|
|
366
360
|
if (Object.keys(updates).length > 0) {
|
|
367
361
|
obj.set(updates);
|
|
362
|
+
obj.setCoords();
|
|
368
363
|
}
|
|
369
364
|
}
|
|
370
365
|
|
|
@@ -372,36 +367,29 @@ export class ImageTool implements Extension {
|
|
|
372
367
|
Image.fromURL(item.url, { crossOrigin: "anonymous" })
|
|
373
368
|
.then((image) => {
|
|
374
369
|
// Double check if item still exists
|
|
375
|
-
if (!this.items.find(i => i.id === item.id)) return;
|
|
370
|
+
if (!this.items.find((i) => i.id === item.id)) return;
|
|
376
371
|
|
|
377
372
|
image.set({
|
|
378
373
|
originX: "center",
|
|
379
374
|
originY: "center",
|
|
380
375
|
data: { id: item.id },
|
|
376
|
+
uniformScaling: true,
|
|
377
|
+
lockScalingFlip: true,
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
image.setControlsVisibility({
|
|
381
|
+
mt: false,
|
|
382
|
+
mb: false,
|
|
383
|
+
ml: false,
|
|
384
|
+
mr: false,
|
|
381
385
|
});
|
|
382
386
|
|
|
383
387
|
// Initial Layout
|
|
384
|
-
let {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
const imgAspect = (image.width || 1) / (image.height || 1);
|
|
390
|
-
const dielineAspect = dielinePhysicalWidth / dielinePhysicalHeight;
|
|
391
|
-
|
|
392
|
-
if (imgAspect > dielineAspect) {
|
|
393
|
-
const w = dielinePhysicalWidth;
|
|
394
|
-
width = w;
|
|
395
|
-
height = w / imgAspect;
|
|
396
|
-
} else {
|
|
397
|
-
const h = dielinePhysicalHeight;
|
|
398
|
-
height = h;
|
|
399
|
-
width = h * imgAspect;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// Update item with defaults
|
|
403
|
-
item.width = width;
|
|
404
|
-
item.height = height;
|
|
388
|
+
let { scale, left, top } = item;
|
|
389
|
+
|
|
390
|
+
if (scale === undefined) {
|
|
391
|
+
scale = 1; // Default scale if not provided and not fitted yet
|
|
392
|
+
item.scale = scale;
|
|
405
393
|
}
|
|
406
394
|
|
|
407
395
|
if (left === undefined && top === undefined) {
|
|
@@ -417,6 +405,13 @@ export class ImageTool implements Extension {
|
|
|
417
405
|
layer.add(image);
|
|
418
406
|
this.objectMap.set(item.id, image);
|
|
419
407
|
|
|
408
|
+
// Notify addImage that load is complete
|
|
409
|
+
const resolver = this.loadResolvers.get(item.id);
|
|
410
|
+
if (resolver) {
|
|
411
|
+
resolver();
|
|
412
|
+
this.loadResolvers.delete(item.id);
|
|
413
|
+
}
|
|
414
|
+
|
|
420
415
|
// Bind Events
|
|
421
416
|
image.on("modified", (e: any) => {
|
|
422
417
|
this.handleObjectModified(item.id, image);
|
|
@@ -424,10 +419,10 @@ export class ImageTool implements Extension {
|
|
|
424
419
|
|
|
425
420
|
layer.dirty = true;
|
|
426
421
|
this.canvasService?.requestRenderAll();
|
|
427
|
-
|
|
428
|
-
// Save defaults if we
|
|
429
|
-
if (item.
|
|
430
|
-
|
|
422
|
+
|
|
423
|
+
// Save defaults if we set them
|
|
424
|
+
if (item.scale !== scale || item.left !== left || item.top !== top) {
|
|
425
|
+
this.updateImageInConfig(item.id, { scale, left, top }, true);
|
|
431
426
|
}
|
|
432
427
|
})
|
|
433
428
|
.catch((err) => {
|
|
@@ -437,7 +432,13 @@ export class ImageTool implements Extension {
|
|
|
437
432
|
|
|
438
433
|
private handleObjectModified(id: string, image: FabricObject) {
|
|
439
434
|
const layout = this.getLayoutInfo();
|
|
440
|
-
const {
|
|
435
|
+
const {
|
|
436
|
+
layoutScale,
|
|
437
|
+
layoutOffsetX,
|
|
438
|
+
layoutOffsetY,
|
|
439
|
+
visualWidth,
|
|
440
|
+
visualHeight,
|
|
441
|
+
} = layout;
|
|
441
442
|
|
|
442
443
|
const matrix = image.calcTransformMatrix();
|
|
443
444
|
const globalPoint = util.transformPoint(new Point(0, 0), matrix);
|
|
@@ -449,25 +450,22 @@ export class ImageTool implements Extension {
|
|
|
449
450
|
updates.top = (globalPoint.y - layoutOffsetY) / visualHeight;
|
|
450
451
|
updates.angle = image.angle;
|
|
451
452
|
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
const pixelWidth = image.width * image.scaleX;
|
|
455
|
-
updates.width = pixelWidth / layoutScale;
|
|
456
|
-
}
|
|
457
|
-
if (image.height) {
|
|
458
|
-
const pixelHeight = image.height * image.scaleY;
|
|
459
|
-
updates.height = pixelHeight / layoutScale;
|
|
460
|
-
}
|
|
453
|
+
// Scale
|
|
454
|
+
updates.scale = image.scaleX / layoutScale;
|
|
461
455
|
|
|
462
|
-
this.updateImageInConfig(id, updates);
|
|
456
|
+
this.updateImageInConfig(id, updates, true);
|
|
463
457
|
}
|
|
464
458
|
|
|
465
|
-
private updateImageInConfig(
|
|
466
|
-
|
|
459
|
+
private updateImageInConfig(
|
|
460
|
+
id: string,
|
|
461
|
+
updates: Partial<ImageItem>,
|
|
462
|
+
skipCanvasUpdate = false,
|
|
463
|
+
) {
|
|
464
|
+
const index = this.items.findIndex((i) => i.id === id);
|
|
467
465
|
if (index !== -1) {
|
|
468
466
|
const newItems = [...this.items];
|
|
469
467
|
newItems[index] = { ...newItems[index], ...updates };
|
|
470
|
-
this.updateConfig(newItems,
|
|
468
|
+
this.updateConfig(newItems, skipCanvasUpdate);
|
|
471
469
|
}
|
|
472
470
|
}
|
|
473
471
|
}
|