@pooder/kit 3.3.0 → 3.5.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 +53 -57
- package/dist/index.d.ts +53 -57
- package/dist/index.js +1081 -930
- package/dist/index.mjs +1080 -929
- package/package.json +1 -1
- package/src/CanvasService.ts +65 -65
- package/src/background.ts +230 -230
- package/src/coordinate.ts +106 -106
- package/src/dieline.ts +282 -218
- package/src/feature.ts +724 -0
- package/src/film.ts +194 -194
- package/src/geometry.ts +118 -370
- package/src/image.ts +471 -496
- package/src/index.ts +1 -1
- package/src/mirror.ts +128 -128
- package/src/ruler.ts +500 -500
- package/src/tracer.ts +570 -372
- package/src/white-ink.ts +373 -373
- package/src/hole.ts +0 -786
package/dist/index.js
CHANGED
|
@@ -33,8 +33,8 @@ __export(index_exports, {
|
|
|
33
33
|
BackgroundTool: () => BackgroundTool,
|
|
34
34
|
CanvasService: () => CanvasService,
|
|
35
35
|
DielineTool: () => DielineTool,
|
|
36
|
+
FeatureTool: () => FeatureTool,
|
|
36
37
|
FilmTool: () => FilmTool,
|
|
37
|
-
HoleTool: () => HoleTool,
|
|
38
38
|
ImageTool: () => ImageTool,
|
|
39
39
|
MirrorTool: () => MirrorTool,
|
|
40
40
|
RulerTool: () => RulerTool,
|
|
@@ -248,6 +248,7 @@ var import_core2 = require("@pooder/core");
|
|
|
248
248
|
var import_fabric2 = require("fabric");
|
|
249
249
|
|
|
250
250
|
// src/tracer.ts
|
|
251
|
+
var import_paper = __toESM(require("paper"));
|
|
251
252
|
var ImageTracer = class {
|
|
252
253
|
/**
|
|
253
254
|
* Main entry point: Traces an image URL to an SVG path string.
|
|
@@ -255,7 +256,7 @@ var ImageTracer = class {
|
|
|
255
256
|
* @param options Configuration options.
|
|
256
257
|
*/
|
|
257
258
|
static async trace(imageUrl, options = {}) {
|
|
258
|
-
var _a, _b;
|
|
259
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
259
260
|
const img = await this.loadImage(imageUrl);
|
|
260
261
|
const width = img.width;
|
|
261
262
|
const height = img.height;
|
|
@@ -266,20 +267,250 @@ var ImageTracer = class {
|
|
|
266
267
|
if (!ctx) throw new Error("Could not get 2D context");
|
|
267
268
|
ctx.drawImage(img, 0, 0);
|
|
268
269
|
const imageData = ctx.getImageData(0, 0, width, height);
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
270
|
+
const threshold = (_a = options.threshold) != null ? _a : 10;
|
|
271
|
+
const adaptiveRadius = Math.max(
|
|
272
|
+
5,
|
|
273
|
+
Math.floor(Math.max(width, height) * 0.02)
|
|
274
|
+
);
|
|
275
|
+
const radius = (_b = options.morphologyRadius) != null ? _b : adaptiveRadius;
|
|
276
|
+
const expand = (_c = options.expand) != null ? _c : 0;
|
|
277
|
+
const padding = radius + expand + 2;
|
|
278
|
+
const paddedWidth = width + padding * 2;
|
|
279
|
+
const paddedHeight = height + padding * 2;
|
|
280
|
+
let mask = this.createMask(imageData, threshold, padding, paddedWidth, paddedHeight);
|
|
281
|
+
if (radius > 0) {
|
|
282
|
+
mask = this.circularMorphology(mask, paddedWidth, paddedHeight, radius, "closing");
|
|
283
|
+
mask = this.fillHoles(mask, paddedWidth, paddedHeight);
|
|
284
|
+
const smoothRadius = Math.max(2, Math.floor(radius * 0.3));
|
|
285
|
+
mask = this.circularMorphology(mask, paddedWidth, paddedHeight, smoothRadius, "closing");
|
|
286
|
+
} else {
|
|
287
|
+
mask = this.fillHoles(mask, paddedWidth, paddedHeight);
|
|
288
|
+
}
|
|
289
|
+
if (expand > 0) {
|
|
290
|
+
mask = this.circularMorphology(mask, paddedWidth, paddedHeight, expand, "dilate");
|
|
291
|
+
}
|
|
292
|
+
const allContourPoints = this.traceAllContours(mask, paddedWidth, paddedHeight);
|
|
293
|
+
if (allContourPoints.length === 0) {
|
|
294
|
+
const w = (_d = options.scaleToWidth) != null ? _d : width;
|
|
295
|
+
const h = (_e = options.scaleToHeight) != null ? _e : height;
|
|
296
|
+
return `M 0 0 L ${w} 0 L ${w} ${h} L 0 ${h} Z`;
|
|
297
|
+
}
|
|
298
|
+
const primaryContour = allContourPoints.sort(
|
|
299
|
+
(a, b) => b.length - a.length
|
|
300
|
+
)[0];
|
|
301
|
+
const unpaddedPoints = primaryContour.map((p) => ({
|
|
302
|
+
x: p.x - padding,
|
|
303
|
+
y: p.y - padding
|
|
304
|
+
}));
|
|
305
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
306
|
+
for (const p of unpaddedPoints) {
|
|
307
|
+
if (p.x < minX) minX = p.x;
|
|
308
|
+
if (p.y < minY) minY = p.y;
|
|
309
|
+
if (p.x > maxX) maxX = p.x;
|
|
310
|
+
if (p.y > maxY) maxY = p.y;
|
|
311
|
+
}
|
|
312
|
+
const globalBounds = {
|
|
313
|
+
minX,
|
|
314
|
+
minY,
|
|
315
|
+
width: maxX - minX,
|
|
316
|
+
height: maxY - minY
|
|
317
|
+
};
|
|
318
|
+
let finalPoints = unpaddedPoints;
|
|
319
|
+
if (options.scaleToWidth && options.scaleToHeight) {
|
|
272
320
|
finalPoints = this.scalePoints(
|
|
273
|
-
|
|
321
|
+
unpaddedPoints,
|
|
274
322
|
options.scaleToWidth,
|
|
275
|
-
options.scaleToHeight
|
|
323
|
+
options.scaleToHeight,
|
|
324
|
+
globalBounds
|
|
276
325
|
);
|
|
277
326
|
}
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
(
|
|
281
|
-
|
|
282
|
-
|
|
327
|
+
const useSmoothing = options.smoothing !== false;
|
|
328
|
+
if (useSmoothing) {
|
|
329
|
+
return this.pointsToSVGPaper(finalPoints, (_f = options.simplifyTolerance) != null ? _f : 2.5);
|
|
330
|
+
} else {
|
|
331
|
+
const simplifiedPoints = this.douglasPeucker(
|
|
332
|
+
finalPoints,
|
|
333
|
+
(_g = options.simplifyTolerance) != null ? _g : 2
|
|
334
|
+
);
|
|
335
|
+
return this.pointsToSVG(simplifiedPoints);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
static createMask(imageData, threshold, padding, paddedWidth, paddedHeight) {
|
|
339
|
+
const { width, height, data } = imageData;
|
|
340
|
+
const mask = new Uint8Array(paddedWidth * paddedHeight);
|
|
341
|
+
let hasTransparency = false;
|
|
342
|
+
for (let i = 3; i < data.length; i += 4) {
|
|
343
|
+
if (data[i] < 255) {
|
|
344
|
+
hasTransparency = true;
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
for (let y = 0; y < height; y++) {
|
|
349
|
+
for (let x = 0; x < width; x++) {
|
|
350
|
+
const srcIdx = (y * width + x) * 4;
|
|
351
|
+
const r = data[srcIdx];
|
|
352
|
+
const g = data[srcIdx + 1];
|
|
353
|
+
const b = data[srcIdx + 2];
|
|
354
|
+
const a = data[srcIdx + 3];
|
|
355
|
+
const destIdx = (y + padding) * paddedWidth + (x + padding);
|
|
356
|
+
if (hasTransparency) {
|
|
357
|
+
if (a > threshold) {
|
|
358
|
+
mask[destIdx] = 1;
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
if (!(r > 240 && g > 240 && b > 240)) {
|
|
362
|
+
mask[destIdx] = 1;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return mask;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Fast circular morphology using a distance-transform inspired separable approach.
|
|
371
|
+
* O(N * R) complexity, where R is the radius.
|
|
372
|
+
*/
|
|
373
|
+
static circularMorphology(mask, width, height, radius, op) {
|
|
374
|
+
const dilate = (m, r) => {
|
|
375
|
+
const horizontalDist = new Int32Array(width * height);
|
|
376
|
+
for (let y = 0; y < height; y++) {
|
|
377
|
+
let lastSolid = -r * 2;
|
|
378
|
+
for (let x = 0; x < width; x++) {
|
|
379
|
+
if (m[y * width + x]) lastSolid = x;
|
|
380
|
+
horizontalDist[y * width + x] = x - lastSolid;
|
|
381
|
+
}
|
|
382
|
+
lastSolid = width + r * 2;
|
|
383
|
+
for (let x = width - 1; x >= 0; x--) {
|
|
384
|
+
if (m[y * width + x]) lastSolid = x;
|
|
385
|
+
horizontalDist[y * width + x] = Math.min(
|
|
386
|
+
horizontalDist[y * width + x],
|
|
387
|
+
lastSolid - x
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const result = new Uint8Array(width * height);
|
|
392
|
+
const r2 = r * r;
|
|
393
|
+
for (let x = 0; x < width; x++) {
|
|
394
|
+
for (let y = 0; y < height; y++) {
|
|
395
|
+
let found = false;
|
|
396
|
+
const minY = Math.max(0, y - r);
|
|
397
|
+
const maxY = Math.min(height - 1, y + r);
|
|
398
|
+
for (let dy = minY; dy <= maxY; dy++) {
|
|
399
|
+
const dY = dy - y;
|
|
400
|
+
const hDist = horizontalDist[dy * width + x];
|
|
401
|
+
if (hDist * hDist + dY * dY <= r2) {
|
|
402
|
+
found = true;
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (found) result[y * width + x] = 1;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return result;
|
|
410
|
+
};
|
|
411
|
+
const erode = (m, r) => {
|
|
412
|
+
const inverted = new Uint8Array(m.length);
|
|
413
|
+
for (let i = 0; i < m.length; i++) inverted[i] = m[i] ? 0 : 1;
|
|
414
|
+
const dilatedInverted = dilate(inverted, r);
|
|
415
|
+
const result = new Uint8Array(m.length);
|
|
416
|
+
for (let i = 0; i < m.length; i++) result[i] = dilatedInverted[i] ? 0 : 1;
|
|
417
|
+
return result;
|
|
418
|
+
};
|
|
419
|
+
switch (op) {
|
|
420
|
+
case "dilate":
|
|
421
|
+
return dilate(mask, radius);
|
|
422
|
+
case "erode":
|
|
423
|
+
return erode(mask, radius);
|
|
424
|
+
case "closing":
|
|
425
|
+
return erode(dilate(mask, radius), radius);
|
|
426
|
+
case "opening":
|
|
427
|
+
return dilate(erode(mask, radius), radius);
|
|
428
|
+
default:
|
|
429
|
+
return mask;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Fills internal holes in the binary mask using flood fill from edges.
|
|
434
|
+
*/
|
|
435
|
+
static fillHoles(mask, width, height) {
|
|
436
|
+
const background = new Uint8Array(width * height);
|
|
437
|
+
const queue = [];
|
|
438
|
+
for (let x = 0; x < width; x++) {
|
|
439
|
+
if (mask[x] === 0) {
|
|
440
|
+
background[x] = 1;
|
|
441
|
+
queue.push([x, 0]);
|
|
442
|
+
}
|
|
443
|
+
const lastRow = (height - 1) * width + x;
|
|
444
|
+
if (mask[lastRow] === 0) {
|
|
445
|
+
background[lastRow] = 1;
|
|
446
|
+
queue.push([x, height - 1]);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
for (let y = 1; y < height - 1; y++) {
|
|
450
|
+
if (mask[y * width] === 0) {
|
|
451
|
+
background[y * width] = 1;
|
|
452
|
+
queue.push([0, y]);
|
|
453
|
+
}
|
|
454
|
+
if (mask[y * width + width - 1] === 0) {
|
|
455
|
+
background[y * width + width - 1] = 1;
|
|
456
|
+
queue.push([width - 1, y]);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
const dirs = [
|
|
460
|
+
[0, 1],
|
|
461
|
+
[0, -1],
|
|
462
|
+
[1, 0],
|
|
463
|
+
[-1, 0]
|
|
464
|
+
];
|
|
465
|
+
let head = 0;
|
|
466
|
+
while (head < queue.length) {
|
|
467
|
+
const [cx, cy] = queue[head++];
|
|
468
|
+
for (const [dx, dy] of dirs) {
|
|
469
|
+
const nx = cx + dx;
|
|
470
|
+
const ny = cy + dy;
|
|
471
|
+
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
|
|
472
|
+
const nidx = ny * width + nx;
|
|
473
|
+
if (mask[nidx] === 0 && background[nidx] === 0) {
|
|
474
|
+
background[nidx] = 1;
|
|
475
|
+
queue.push([nx, ny]);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
const filledMask = new Uint8Array(width * height);
|
|
481
|
+
for (let i = 0; i < width * height; i++) {
|
|
482
|
+
filledMask[i] = background[i] === 0 ? 1 : 0;
|
|
483
|
+
}
|
|
484
|
+
return filledMask;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Traces all contours in the mask with optimized start-point detection
|
|
488
|
+
*/
|
|
489
|
+
static traceAllContours(mask, width, height) {
|
|
490
|
+
const visited = new Uint8Array(width * height);
|
|
491
|
+
const allContours = [];
|
|
492
|
+
for (let y = 0; y < height; y++) {
|
|
493
|
+
for (let x = 0; x < width; x++) {
|
|
494
|
+
const idx = y * width + x;
|
|
495
|
+
if (mask[idx] && !visited[idx]) {
|
|
496
|
+
const isLeftEdge = x === 0 || mask[idx - 1] === 0;
|
|
497
|
+
if (isLeftEdge) {
|
|
498
|
+
const contour = this.marchingSquares(
|
|
499
|
+
mask,
|
|
500
|
+
visited,
|
|
501
|
+
x,
|
|
502
|
+
y,
|
|
503
|
+
width,
|
|
504
|
+
height
|
|
505
|
+
);
|
|
506
|
+
if (contour.length > 2) {
|
|
507
|
+
allContours.push(contour);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return allContours;
|
|
283
514
|
}
|
|
284
515
|
static loadImage(url) {
|
|
285
516
|
return new Promise((resolve, reject) => {
|
|
@@ -294,33 +525,11 @@ var ImageTracer = class {
|
|
|
294
525
|
* Moore-Neighbor Tracing Algorithm
|
|
295
526
|
* More robust for irregular shapes than simple Marching Squares walker.
|
|
296
527
|
*/
|
|
297
|
-
static marchingSquares(
|
|
298
|
-
const width = imageData.width;
|
|
299
|
-
const height = imageData.height;
|
|
300
|
-
const data = imageData.data;
|
|
528
|
+
static marchingSquares(mask, visited, startX, startY, width, height) {
|
|
301
529
|
const isSolid = (x, y) => {
|
|
302
530
|
if (x < 0 || x >= width || y < 0 || y >= height) return false;
|
|
303
|
-
|
|
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;
|
|
531
|
+
return mask[y * width + x] === 1;
|
|
311
532
|
};
|
|
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
533
|
const points = [];
|
|
325
534
|
let cx = startX;
|
|
326
535
|
let cy = startY;
|
|
@@ -339,6 +548,7 @@ var ImageTracer = class {
|
|
|
339
548
|
let steps = 0;
|
|
340
549
|
do {
|
|
341
550
|
points.push({ x: cx, y: cy });
|
|
551
|
+
visited[cy * width + cx] = 1;
|
|
342
552
|
let found = false;
|
|
343
553
|
for (let i = 0; i < 8; i++) {
|
|
344
554
|
const idx = (backtrack + 1 + i) % 8;
|
|
@@ -347,16 +557,12 @@ var ImageTracer = class {
|
|
|
347
557
|
if (isSolid(nx, ny)) {
|
|
348
558
|
cx = nx;
|
|
349
559
|
cy = ny;
|
|
350
|
-
backtrack = (idx + 4) % 8;
|
|
351
|
-
backtrack = (idx + 4 + 1) % 8;
|
|
352
560
|
backtrack = (idx + 4 + 1) % 8;
|
|
353
561
|
found = true;
|
|
354
562
|
break;
|
|
355
563
|
}
|
|
356
564
|
}
|
|
357
|
-
if (!found)
|
|
358
|
-
break;
|
|
359
|
-
}
|
|
565
|
+
if (!found) break;
|
|
360
566
|
steps++;
|
|
361
567
|
} while ((cx !== startX || cy !== startY) && steps < maxSteps);
|
|
362
568
|
return points;
|
|
@@ -405,23 +611,14 @@ var ImageTracer = class {
|
|
|
405
611
|
dy = p.y - y;
|
|
406
612
|
return dx * dx + dy * dy;
|
|
407
613
|
}
|
|
408
|
-
static scalePoints(points, targetWidth, targetHeight) {
|
|
614
|
+
static scalePoints(points, targetWidth, targetHeight, bounds) {
|
|
409
615
|
if (points.length === 0) return points;
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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;
|
|
616
|
+
if (bounds.width === 0 || bounds.height === 0) return points;
|
|
617
|
+
const scaleX = targetWidth / bounds.width;
|
|
618
|
+
const scaleY = targetHeight / bounds.height;
|
|
422
619
|
return points.map((p) => ({
|
|
423
|
-
x: (p.x - minX) * scaleX,
|
|
424
|
-
y: (p.y - minY) * scaleY
|
|
620
|
+
x: (p.x - bounds.minX) * scaleX,
|
|
621
|
+
y: (p.y - bounds.minY) * scaleY
|
|
425
622
|
}));
|
|
426
623
|
}
|
|
427
624
|
static pointsToSVG(points) {
|
|
@@ -430,6 +627,23 @@ var ImageTracer = class {
|
|
|
430
627
|
const tail = points.slice(1);
|
|
431
628
|
return `M ${head.x} ${head.y} ` + tail.map((p) => `L ${p.x} ${p.y}`).join(" ") + " Z";
|
|
432
629
|
}
|
|
630
|
+
static ensurePaper() {
|
|
631
|
+
if (!import_paper.default.project) {
|
|
632
|
+
import_paper.default.setup(new import_paper.default.Size(100, 100));
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
static pointsToSVGPaper(points, tolerance) {
|
|
636
|
+
if (points.length < 3) return this.pointsToSVG(points);
|
|
637
|
+
this.ensurePaper();
|
|
638
|
+
const path = new import_paper.default.Path({
|
|
639
|
+
segments: points.map((p) => [p.x, p.y]),
|
|
640
|
+
closed: true
|
|
641
|
+
});
|
|
642
|
+
path.simplify(tolerance);
|
|
643
|
+
const data = path.pathData;
|
|
644
|
+
path.remove();
|
|
645
|
+
return data;
|
|
646
|
+
}
|
|
433
647
|
};
|
|
434
648
|
|
|
435
649
|
// src/coordinate.ts
|
|
@@ -504,96 +718,45 @@ var Coordinate = class {
|
|
|
504
718
|
};
|
|
505
719
|
|
|
506
720
|
// src/geometry.ts
|
|
507
|
-
var
|
|
508
|
-
function
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
const bottom = y + height / 2;
|
|
517
|
-
switch (hole.anchor) {
|
|
518
|
-
case "top-left":
|
|
519
|
-
bx = left;
|
|
520
|
-
by = top;
|
|
521
|
-
break;
|
|
522
|
-
case "top-center":
|
|
523
|
-
bx = x;
|
|
524
|
-
by = top;
|
|
525
|
-
break;
|
|
526
|
-
case "top-right":
|
|
527
|
-
bx = right;
|
|
528
|
-
by = top;
|
|
529
|
-
break;
|
|
530
|
-
case "center-left":
|
|
531
|
-
bx = left;
|
|
532
|
-
by = y;
|
|
533
|
-
break;
|
|
534
|
-
case "center":
|
|
535
|
-
bx = x;
|
|
536
|
-
by = y;
|
|
537
|
-
break;
|
|
538
|
-
case "center-right":
|
|
539
|
-
bx = right;
|
|
540
|
-
by = y;
|
|
541
|
-
break;
|
|
542
|
-
case "bottom-left":
|
|
543
|
-
bx = left;
|
|
544
|
-
by = bottom;
|
|
545
|
-
break;
|
|
546
|
-
case "bottom-center":
|
|
547
|
-
bx = x;
|
|
548
|
-
by = bottom;
|
|
549
|
-
break;
|
|
550
|
-
case "bottom-right":
|
|
551
|
-
bx = right;
|
|
552
|
-
by = bottom;
|
|
553
|
-
break;
|
|
554
|
-
}
|
|
555
|
-
return {
|
|
556
|
-
x: bx + (hole.offsetX || 0),
|
|
557
|
-
y: by + (hole.offsetY || 0)
|
|
558
|
-
};
|
|
559
|
-
} else if (hole.x !== void 0 && hole.y !== void 0) {
|
|
560
|
-
const { x, width, y, height } = geometry;
|
|
561
|
-
return {
|
|
562
|
-
x: hole.x * width + (x - width / 2) + (hole.offsetX || 0),
|
|
563
|
-
y: hole.y * height + (y - height / 2) + (hole.offsetY || 0)
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
return { x: 0, y: 0 };
|
|
721
|
+
var import_paper2 = __toESM(require("paper"));
|
|
722
|
+
function resolveFeaturePosition(feature, geometry) {
|
|
723
|
+
const { x, y, width, height } = geometry;
|
|
724
|
+
const left = x - width / 2;
|
|
725
|
+
const top = y - height / 2;
|
|
726
|
+
return {
|
|
727
|
+
x: left + feature.x * width,
|
|
728
|
+
y: top + feature.y * height
|
|
729
|
+
};
|
|
567
730
|
}
|
|
568
731
|
function ensurePaper(width, height) {
|
|
569
|
-
if (!
|
|
570
|
-
|
|
732
|
+
if (!import_paper2.default.project) {
|
|
733
|
+
import_paper2.default.setup(new import_paper2.default.Size(width, height));
|
|
571
734
|
} else {
|
|
572
|
-
|
|
735
|
+
import_paper2.default.view.viewSize = new import_paper2.default.Size(width, height);
|
|
573
736
|
}
|
|
574
737
|
}
|
|
575
738
|
function createBaseShape(options) {
|
|
576
739
|
const { shape, width, height, radius, x, y, pathData } = options;
|
|
577
|
-
const center = new
|
|
740
|
+
const center = new import_paper2.default.Point(x, y);
|
|
578
741
|
if (shape === "rect") {
|
|
579
|
-
return new
|
|
742
|
+
return new import_paper2.default.Path.Rectangle({
|
|
580
743
|
point: [x - width / 2, y - height / 2],
|
|
581
744
|
size: [Math.max(0, width), Math.max(0, height)],
|
|
582
745
|
radius: Math.max(0, radius)
|
|
583
746
|
});
|
|
584
747
|
} else if (shape === "circle") {
|
|
585
748
|
const r = Math.min(width, height) / 2;
|
|
586
|
-
return new
|
|
749
|
+
return new import_paper2.default.Path.Circle({
|
|
587
750
|
center,
|
|
588
751
|
radius: Math.max(0, r)
|
|
589
752
|
});
|
|
590
753
|
} else if (shape === "ellipse") {
|
|
591
|
-
return new
|
|
754
|
+
return new import_paper2.default.Path.Ellipse({
|
|
592
755
|
center,
|
|
593
756
|
radius: [Math.max(0, width / 2), Math.max(0, height / 2)]
|
|
594
757
|
});
|
|
595
758
|
} else if (shape === "custom" && pathData) {
|
|
596
|
-
const path = new
|
|
759
|
+
const path = new import_paper2.default.Path();
|
|
597
760
|
path.pathData = pathData;
|
|
598
761
|
path.position = center;
|
|
599
762
|
if (width > 0 && height > 0 && path.bounds.width > 0 && path.bounds.height > 0) {
|
|
@@ -601,89 +764,76 @@ function createBaseShape(options) {
|
|
|
601
764
|
}
|
|
602
765
|
return path;
|
|
603
766
|
} else {
|
|
604
|
-
return new
|
|
767
|
+
return new import_paper2.default.Path.Rectangle({
|
|
605
768
|
point: [x - width / 2, y - height / 2],
|
|
606
769
|
size: [Math.max(0, width), Math.max(0, height)]
|
|
607
770
|
});
|
|
608
771
|
}
|
|
609
772
|
}
|
|
773
|
+
function createFeatureItem(feature, center) {
|
|
774
|
+
let item;
|
|
775
|
+
if (feature.shape === "rect") {
|
|
776
|
+
const w = feature.width || 10;
|
|
777
|
+
const h = feature.height || 10;
|
|
778
|
+
const r = feature.radius || 0;
|
|
779
|
+
item = new import_paper2.default.Path.Rectangle({
|
|
780
|
+
point: [center.x - w / 2, center.y - h / 2],
|
|
781
|
+
size: [w, h],
|
|
782
|
+
radius: r
|
|
783
|
+
});
|
|
784
|
+
} else {
|
|
785
|
+
const r = feature.radius || 5;
|
|
786
|
+
item = new import_paper2.default.Path.Circle({
|
|
787
|
+
center,
|
|
788
|
+
radius: r
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
if (feature.rotation) {
|
|
792
|
+
item.rotate(feature.rotation, center);
|
|
793
|
+
}
|
|
794
|
+
return item;
|
|
795
|
+
}
|
|
610
796
|
function getDielineShape(options) {
|
|
611
797
|
let mainShape = createBaseShape(options);
|
|
612
|
-
const {
|
|
613
|
-
if (
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
const
|
|
618
|
-
const
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
],
|
|
623
|
-
size: [hole.outerRadius * 2, hole.outerRadius * 2]
|
|
624
|
-
}) : new import_paper.default.Path.Circle({
|
|
625
|
-
center,
|
|
626
|
-
radius: hole.outerRadius
|
|
627
|
-
});
|
|
628
|
-
const cut = hole.shape === "square" ? new import_paper.default.Path.Rectangle({
|
|
629
|
-
point: [
|
|
630
|
-
center.x - hole.innerRadius,
|
|
631
|
-
center.y - hole.innerRadius
|
|
632
|
-
],
|
|
633
|
-
size: [hole.innerRadius * 2, hole.innerRadius * 2]
|
|
634
|
-
}) : new import_paper.default.Path.Circle({
|
|
635
|
-
center,
|
|
636
|
-
radius: hole.innerRadius
|
|
637
|
-
});
|
|
638
|
-
if (!lugsPath) {
|
|
639
|
-
lugsPath = lug;
|
|
798
|
+
const { features } = options;
|
|
799
|
+
if (features && features.length > 0) {
|
|
800
|
+
const adds = [];
|
|
801
|
+
const subtracts = [];
|
|
802
|
+
features.forEach((f) => {
|
|
803
|
+
const pos = resolveFeaturePosition(f, options);
|
|
804
|
+
const center = new import_paper2.default.Point(pos.x, pos.y);
|
|
805
|
+
const item = createFeatureItem(f, center);
|
|
806
|
+
if (f.operation === "add") {
|
|
807
|
+
adds.push(item);
|
|
640
808
|
} else {
|
|
809
|
+
subtracts.push(item);
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
if (adds.length > 0) {
|
|
813
|
+
for (const item of adds) {
|
|
641
814
|
try {
|
|
642
|
-
const temp =
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
815
|
+
const temp = mainShape.unite(item);
|
|
816
|
+
mainShape.remove();
|
|
817
|
+
item.remove();
|
|
818
|
+
mainShape = temp;
|
|
646
819
|
} catch (e) {
|
|
647
|
-
console.error("Geometry: Failed to unite
|
|
648
|
-
|
|
820
|
+
console.error("Geometry: Failed to unite feature", e);
|
|
821
|
+
item.remove();
|
|
649
822
|
}
|
|
650
823
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
824
|
+
}
|
|
825
|
+
if (subtracts.length > 0) {
|
|
826
|
+
for (const item of subtracts) {
|
|
654
827
|
try {
|
|
655
|
-
const temp =
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
828
|
+
const temp = mainShape.subtract(item);
|
|
829
|
+
mainShape.remove();
|
|
830
|
+
item.remove();
|
|
831
|
+
mainShape = temp;
|
|
659
832
|
} catch (e) {
|
|
660
|
-
console.error("Geometry: Failed to
|
|
661
|
-
|
|
833
|
+
console.error("Geometry: Failed to subtract feature", e);
|
|
834
|
+
item.remove();
|
|
662
835
|
}
|
|
663
836
|
}
|
|
664
|
-
});
|
|
665
|
-
if (lugsPath) {
|
|
666
|
-
try {
|
|
667
|
-
const temp = mainShape.unite(lugsPath);
|
|
668
|
-
mainShape.remove();
|
|
669
|
-
lugsPath.remove();
|
|
670
|
-
mainShape = temp;
|
|
671
|
-
} catch (e) {
|
|
672
|
-
console.error("Geometry: Failed to unite lugsPath to mainShape", e);
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
if (cutsPath) {
|
|
676
|
-
try {
|
|
677
|
-
const temp = mainShape.subtract(cutsPath);
|
|
678
|
-
mainShape.remove();
|
|
679
|
-
cutsPath.remove();
|
|
680
|
-
mainShape = temp;
|
|
681
|
-
} catch (e) {
|
|
682
|
-
console.error(
|
|
683
|
-
"Geometry: Failed to subtract cutsPath from mainShape",
|
|
684
|
-
e
|
|
685
|
-
);
|
|
686
|
-
}
|
|
687
837
|
}
|
|
688
838
|
}
|
|
689
839
|
return mainShape;
|
|
@@ -692,7 +842,7 @@ function generateDielinePath(options) {
|
|
|
692
842
|
const paperWidth = options.canvasWidth || options.width * 2 || 2e3;
|
|
693
843
|
const paperHeight = options.canvasHeight || options.height * 2 || 2e3;
|
|
694
844
|
ensurePaper(paperWidth, paperHeight);
|
|
695
|
-
|
|
845
|
+
import_paper2.default.project.activeLayer.removeChildren();
|
|
696
846
|
const mainShape = getDielineShape(options);
|
|
697
847
|
const pathData = mainShape.pathData;
|
|
698
848
|
mainShape.remove();
|
|
@@ -700,9 +850,9 @@ function generateDielinePath(options) {
|
|
|
700
850
|
}
|
|
701
851
|
function generateMaskPath(options) {
|
|
702
852
|
ensurePaper(options.canvasWidth, options.canvasHeight);
|
|
703
|
-
|
|
853
|
+
import_paper2.default.project.activeLayer.removeChildren();
|
|
704
854
|
const { canvasWidth, canvasHeight } = options;
|
|
705
|
-
const maskRect = new
|
|
855
|
+
const maskRect = new import_paper2.default.Path.Rectangle({
|
|
706
856
|
point: [0, 0],
|
|
707
857
|
size: [canvasWidth, canvasHeight]
|
|
708
858
|
});
|
|
@@ -714,43 +864,13 @@ function generateMaskPath(options) {
|
|
|
714
864
|
finalMask.remove();
|
|
715
865
|
return pathData;
|
|
716
866
|
}
|
|
717
|
-
function generateBleedZonePath(
|
|
718
|
-
const paperWidth =
|
|
719
|
-
const paperHeight =
|
|
867
|
+
function generateBleedZonePath(originalOptions, offsetOptions, offset) {
|
|
868
|
+
const paperWidth = originalOptions.canvasWidth || originalOptions.width * 2 || 2e3;
|
|
869
|
+
const paperHeight = originalOptions.canvasHeight || originalOptions.height * 2 || 2e3;
|
|
720
870
|
ensurePaper(paperWidth, paperHeight);
|
|
721
|
-
|
|
722
|
-
const shapeOriginal = getDielineShape(
|
|
723
|
-
|
|
724
|
-
if (options.shape === "custom") {
|
|
725
|
-
const stroker = shapeOriginal.clone();
|
|
726
|
-
stroker.strokeColor = new import_paper.default.Color("black");
|
|
727
|
-
stroker.strokeWidth = Math.abs(offset) * 2;
|
|
728
|
-
stroker.strokeJoin = "round";
|
|
729
|
-
stroker.strokeCap = "round";
|
|
730
|
-
let expanded;
|
|
731
|
-
try {
|
|
732
|
-
expanded = stroker.expand({ stroke: true, fill: false, insert: false });
|
|
733
|
-
} catch (e) {
|
|
734
|
-
stroker.remove();
|
|
735
|
-
shapeOffset = shapeOriginal.clone();
|
|
736
|
-
return shapeOffset.pathData;
|
|
737
|
-
}
|
|
738
|
-
stroker.remove();
|
|
739
|
-
if (offset > 0) {
|
|
740
|
-
shapeOffset = shapeOriginal.unite(expanded);
|
|
741
|
-
} else {
|
|
742
|
-
shapeOffset = shapeOriginal.subtract(expanded);
|
|
743
|
-
}
|
|
744
|
-
expanded.remove();
|
|
745
|
-
} else {
|
|
746
|
-
const offsetOptions = {
|
|
747
|
-
...options,
|
|
748
|
-
width: Math.max(0, options.width + offset * 2),
|
|
749
|
-
height: Math.max(0, options.height + offset * 2),
|
|
750
|
-
radius: options.radius === 0 ? 0 : Math.max(0, options.radius + offset)
|
|
751
|
-
};
|
|
752
|
-
shapeOffset = getDielineShape(offsetOptions);
|
|
753
|
-
}
|
|
871
|
+
import_paper2.default.project.activeLayer.removeChildren();
|
|
872
|
+
const shapeOriginal = getDielineShape(originalOptions);
|
|
873
|
+
const shapeOffset = getDielineShape(offsetOptions);
|
|
754
874
|
let bleedZone;
|
|
755
875
|
if (offset > 0) {
|
|
756
876
|
bleedZone = shapeOffset.subtract(shapeOriginal);
|
|
@@ -765,16 +885,16 @@ function generateBleedZonePath(options, offset) {
|
|
|
765
885
|
}
|
|
766
886
|
function getNearestPointOnDieline(point, options) {
|
|
767
887
|
ensurePaper(options.width * 2, options.height * 2);
|
|
768
|
-
|
|
888
|
+
import_paper2.default.project.activeLayer.removeChildren();
|
|
769
889
|
const shape = createBaseShape(options);
|
|
770
|
-
const p = new
|
|
890
|
+
const p = new import_paper2.default.Point(point.x, point.y);
|
|
771
891
|
const nearest = shape.getNearestPoint(p);
|
|
772
892
|
const result = { x: nearest.x, y: nearest.y };
|
|
773
893
|
shape.remove();
|
|
774
894
|
return result;
|
|
775
895
|
}
|
|
776
896
|
function getPathBounds(pathData) {
|
|
777
|
-
const path = new
|
|
897
|
+
const path = new import_paper2.default.Path();
|
|
778
898
|
path.pathData = pathData;
|
|
779
899
|
const bounds = path.bounds;
|
|
780
900
|
path.remove();
|
|
@@ -793,20 +913,41 @@ var DielineTool = class {
|
|
|
793
913
|
this.metadata = {
|
|
794
914
|
name: "DielineTool"
|
|
795
915
|
};
|
|
796
|
-
this.
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
916
|
+
this.state = {
|
|
917
|
+
unit: "mm",
|
|
918
|
+
shape: "rect",
|
|
919
|
+
width: 500,
|
|
920
|
+
height: 500,
|
|
921
|
+
radius: 0,
|
|
922
|
+
offset: 0,
|
|
923
|
+
padding: 140,
|
|
924
|
+
mainLine: {
|
|
925
|
+
width: 2.7,
|
|
926
|
+
color: "#FF0000",
|
|
927
|
+
dashLength: 5,
|
|
928
|
+
style: "solid"
|
|
929
|
+
},
|
|
930
|
+
offsetLine: {
|
|
931
|
+
width: 2.7,
|
|
932
|
+
color: "#FF0000",
|
|
933
|
+
dashLength: 5,
|
|
934
|
+
style: "solid"
|
|
935
|
+
},
|
|
936
|
+
insideColor: "rgba(0,0,0,0)",
|
|
937
|
+
outsideColor: "#ffffff",
|
|
938
|
+
showBleedLines: true,
|
|
939
|
+
features: []
|
|
940
|
+
};
|
|
808
941
|
if (options) {
|
|
809
|
-
|
|
942
|
+
if (options.mainLine) {
|
|
943
|
+
Object.assign(this.state.mainLine, options.mainLine);
|
|
944
|
+
delete options.mainLine;
|
|
945
|
+
}
|
|
946
|
+
if (options.offsetLine) {
|
|
947
|
+
Object.assign(this.state.offsetLine, options.offsetLine);
|
|
948
|
+
delete options.offsetLine;
|
|
949
|
+
}
|
|
950
|
+
Object.assign(this.state, options);
|
|
810
951
|
}
|
|
811
952
|
}
|
|
812
953
|
activate(context) {
|
|
@@ -818,38 +959,93 @@ var DielineTool = class {
|
|
|
818
959
|
}
|
|
819
960
|
const configService = context.services.get("ConfigurationService");
|
|
820
961
|
if (configService) {
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
);
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
);
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
);
|
|
841
|
-
|
|
842
|
-
this.pathData = configService.get("dieline.pathData", this.pathData);
|
|
962
|
+
const s = this.state;
|
|
963
|
+
s.unit = configService.get("dieline.unit", s.unit);
|
|
964
|
+
s.shape = configService.get("dieline.shape", s.shape);
|
|
965
|
+
s.width = configService.get("dieline.width", s.width);
|
|
966
|
+
s.height = configService.get("dieline.height", s.height);
|
|
967
|
+
s.radius = configService.get("dieline.radius", s.radius);
|
|
968
|
+
s.padding = configService.get("dieline.padding", s.padding);
|
|
969
|
+
s.offset = configService.get("dieline.offset", s.offset);
|
|
970
|
+
s.mainLine.width = configService.get("dieline.strokeWidth", s.mainLine.width);
|
|
971
|
+
s.mainLine.color = configService.get("dieline.strokeColor", s.mainLine.color);
|
|
972
|
+
s.mainLine.dashLength = configService.get("dieline.dashLength", s.mainLine.dashLength);
|
|
973
|
+
s.mainLine.style = configService.get("dieline.style", s.mainLine.style);
|
|
974
|
+
s.offsetLine.width = configService.get("dieline.offsetStrokeWidth", s.offsetLine.width);
|
|
975
|
+
s.offsetLine.color = configService.get("dieline.offsetStrokeColor", s.offsetLine.color);
|
|
976
|
+
s.offsetLine.dashLength = configService.get("dieline.offsetDashLength", s.offsetLine.dashLength);
|
|
977
|
+
s.offsetLine.style = configService.get("dieline.offsetStyle", s.offsetLine.style);
|
|
978
|
+
s.insideColor = configService.get("dieline.insideColor", s.insideColor);
|
|
979
|
+
s.outsideColor = configService.get("dieline.outsideColor", s.outsideColor);
|
|
980
|
+
s.showBleedLines = configService.get("dieline.showBleedLines", s.showBleedLines);
|
|
981
|
+
s.features = configService.get("dieline.features", s.features);
|
|
982
|
+
s.pathData = configService.get("dieline.pathData", s.pathData);
|
|
843
983
|
configService.onAnyChange((e) => {
|
|
844
984
|
if (e.key.startsWith("dieline.")) {
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
985
|
+
console.log(`[DielineTool] Config change detected: ${e.key} -> ${e.value}`);
|
|
986
|
+
switch (e.key) {
|
|
987
|
+
case "dieline.unit":
|
|
988
|
+
s.unit = e.value;
|
|
989
|
+
break;
|
|
990
|
+
case "dieline.shape":
|
|
991
|
+
s.shape = e.value;
|
|
992
|
+
break;
|
|
993
|
+
case "dieline.width":
|
|
994
|
+
s.width = e.value;
|
|
995
|
+
break;
|
|
996
|
+
case "dieline.height":
|
|
997
|
+
s.height = e.value;
|
|
998
|
+
break;
|
|
999
|
+
case "dieline.radius":
|
|
1000
|
+
s.radius = e.value;
|
|
1001
|
+
break;
|
|
1002
|
+
case "dieline.padding":
|
|
1003
|
+
s.padding = e.value;
|
|
1004
|
+
break;
|
|
1005
|
+
case "dieline.offset":
|
|
1006
|
+
s.offset = e.value;
|
|
1007
|
+
break;
|
|
1008
|
+
case "dieline.strokeWidth":
|
|
1009
|
+
s.mainLine.width = e.value;
|
|
1010
|
+
break;
|
|
1011
|
+
case "dieline.strokeColor":
|
|
1012
|
+
s.mainLine.color = e.value;
|
|
1013
|
+
break;
|
|
1014
|
+
case "dieline.dashLength":
|
|
1015
|
+
s.mainLine.dashLength = e.value;
|
|
1016
|
+
break;
|
|
1017
|
+
case "dieline.style":
|
|
1018
|
+
s.mainLine.style = e.value;
|
|
1019
|
+
break;
|
|
1020
|
+
case "dieline.offsetStrokeWidth":
|
|
1021
|
+
s.offsetLine.width = e.value;
|
|
1022
|
+
break;
|
|
1023
|
+
case "dieline.offsetStrokeColor":
|
|
1024
|
+
s.offsetLine.color = e.value;
|
|
1025
|
+
break;
|
|
1026
|
+
case "dieline.offsetDashLength":
|
|
1027
|
+
s.offsetLine.dashLength = e.value;
|
|
1028
|
+
break;
|
|
1029
|
+
case "dieline.offsetStyle":
|
|
1030
|
+
s.offsetLine.style = e.value;
|
|
1031
|
+
break;
|
|
1032
|
+
case "dieline.insideColor":
|
|
1033
|
+
s.insideColor = e.value;
|
|
1034
|
+
break;
|
|
1035
|
+
case "dieline.outsideColor":
|
|
1036
|
+
s.outsideColor = e.value;
|
|
1037
|
+
break;
|
|
1038
|
+
case "dieline.showBleedLines":
|
|
1039
|
+
s.showBleedLines = e.value;
|
|
1040
|
+
break;
|
|
1041
|
+
case "dieline.features":
|
|
1042
|
+
s.features = e.value;
|
|
1043
|
+
break;
|
|
1044
|
+
case "dieline.pathData":
|
|
1045
|
+
s.pathData = e.value;
|
|
1046
|
+
break;
|
|
852
1047
|
}
|
|
1048
|
+
this.updateDieline();
|
|
853
1049
|
}
|
|
854
1050
|
});
|
|
855
1051
|
}
|
|
@@ -862,6 +1058,7 @@ var DielineTool = class {
|
|
|
862
1058
|
this.context = void 0;
|
|
863
1059
|
}
|
|
864
1060
|
contribute() {
|
|
1061
|
+
const s = this.state;
|
|
865
1062
|
return {
|
|
866
1063
|
[import_core2.ContributionPointIds.CONFIGURATIONS]: [
|
|
867
1064
|
{
|
|
@@ -869,14 +1066,14 @@ var DielineTool = class {
|
|
|
869
1066
|
type: "select",
|
|
870
1067
|
label: "Unit",
|
|
871
1068
|
options: ["px", "mm", "cm", "in"],
|
|
872
|
-
default:
|
|
1069
|
+
default: s.unit
|
|
873
1070
|
},
|
|
874
1071
|
{
|
|
875
1072
|
id: "dieline.shape",
|
|
876
1073
|
type: "select",
|
|
877
1074
|
label: "Shape",
|
|
878
1075
|
options: ["rect", "circle", "ellipse", "custom"],
|
|
879
|
-
default:
|
|
1076
|
+
default: s.shape
|
|
880
1077
|
},
|
|
881
1078
|
{
|
|
882
1079
|
id: "dieline.width",
|
|
@@ -884,7 +1081,7 @@ var DielineTool = class {
|
|
|
884
1081
|
label: "Width",
|
|
885
1082
|
min: 10,
|
|
886
1083
|
max: 2e3,
|
|
887
|
-
default:
|
|
1084
|
+
default: s.width
|
|
888
1085
|
},
|
|
889
1086
|
{
|
|
890
1087
|
id: "dieline.height",
|
|
@@ -892,7 +1089,7 @@ var DielineTool = class {
|
|
|
892
1089
|
label: "Height",
|
|
893
1090
|
min: 10,
|
|
894
1091
|
max: 2e3,
|
|
895
|
-
default:
|
|
1092
|
+
default: s.height
|
|
896
1093
|
},
|
|
897
1094
|
{
|
|
898
1095
|
id: "dieline.radius",
|
|
@@ -900,20 +1097,14 @@ var DielineTool = class {
|
|
|
900
1097
|
label: "Corner Radius",
|
|
901
1098
|
min: 0,
|
|
902
1099
|
max: 500,
|
|
903
|
-
default:
|
|
904
|
-
},
|
|
905
|
-
{
|
|
906
|
-
id: "dieline.position",
|
|
907
|
-
type: "json",
|
|
908
|
-
label: "Position (Normalized)",
|
|
909
|
-
default: this.radius
|
|
1100
|
+
default: s.radius
|
|
910
1101
|
},
|
|
911
1102
|
{
|
|
912
1103
|
id: "dieline.padding",
|
|
913
1104
|
type: "select",
|
|
914
1105
|
label: "View Padding",
|
|
915
1106
|
options: [0, 10, 20, 40, 60, 100, "2%", "5%", "10%", "15%", "20%"],
|
|
916
|
-
default:
|
|
1107
|
+
default: s.padding
|
|
917
1108
|
},
|
|
918
1109
|
{
|
|
919
1110
|
id: "dieline.offset",
|
|
@@ -921,38 +1112,91 @@ var DielineTool = class {
|
|
|
921
1112
|
label: "Bleed Offset",
|
|
922
1113
|
min: -100,
|
|
923
1114
|
max: 100,
|
|
924
|
-
default:
|
|
1115
|
+
default: s.offset
|
|
925
1116
|
},
|
|
926
1117
|
{
|
|
927
1118
|
id: "dieline.showBleedLines",
|
|
928
1119
|
type: "boolean",
|
|
929
1120
|
label: "Show Bleed Lines",
|
|
930
|
-
default:
|
|
1121
|
+
default: s.showBleedLines
|
|
1122
|
+
},
|
|
1123
|
+
{
|
|
1124
|
+
id: "dieline.strokeWidth",
|
|
1125
|
+
type: "number",
|
|
1126
|
+
label: "Line Width",
|
|
1127
|
+
min: 0.1,
|
|
1128
|
+
max: 10,
|
|
1129
|
+
step: 0.1,
|
|
1130
|
+
default: s.mainLine.width
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
id: "dieline.strokeColor",
|
|
1134
|
+
type: "color",
|
|
1135
|
+
label: "Line Color",
|
|
1136
|
+
default: s.mainLine.color
|
|
1137
|
+
},
|
|
1138
|
+
{
|
|
1139
|
+
id: "dieline.dashLength",
|
|
1140
|
+
type: "number",
|
|
1141
|
+
label: "Dash Length",
|
|
1142
|
+
min: 1,
|
|
1143
|
+
max: 50,
|
|
1144
|
+
default: s.mainLine.dashLength
|
|
931
1145
|
},
|
|
932
1146
|
{
|
|
933
1147
|
id: "dieline.style",
|
|
934
1148
|
type: "select",
|
|
935
1149
|
label: "Line Style",
|
|
936
|
-
options: ["solid", "dashed"],
|
|
937
|
-
default:
|
|
1150
|
+
options: ["solid", "dashed", "hidden"],
|
|
1151
|
+
default: s.mainLine.style
|
|
1152
|
+
},
|
|
1153
|
+
{
|
|
1154
|
+
id: "dieline.offsetStrokeWidth",
|
|
1155
|
+
type: "number",
|
|
1156
|
+
label: "Offset Line Width",
|
|
1157
|
+
min: 0.1,
|
|
1158
|
+
max: 10,
|
|
1159
|
+
step: 0.1,
|
|
1160
|
+
default: s.offsetLine.width
|
|
1161
|
+
},
|
|
1162
|
+
{
|
|
1163
|
+
id: "dieline.offsetStrokeColor",
|
|
1164
|
+
type: "color",
|
|
1165
|
+
label: "Offset Line Color",
|
|
1166
|
+
default: s.offsetLine.color
|
|
1167
|
+
},
|
|
1168
|
+
{
|
|
1169
|
+
id: "dieline.offsetDashLength",
|
|
1170
|
+
type: "number",
|
|
1171
|
+
label: "Offset Dash Length",
|
|
1172
|
+
min: 1,
|
|
1173
|
+
max: 50,
|
|
1174
|
+
default: s.offsetLine.dashLength
|
|
1175
|
+
},
|
|
1176
|
+
{
|
|
1177
|
+
id: "dieline.offsetStyle",
|
|
1178
|
+
type: "select",
|
|
1179
|
+
label: "Offset Line Style",
|
|
1180
|
+
options: ["solid", "dashed", "hidden"],
|
|
1181
|
+
default: s.offsetLine.style
|
|
938
1182
|
},
|
|
939
1183
|
{
|
|
940
1184
|
id: "dieline.insideColor",
|
|
941
1185
|
type: "color",
|
|
942
1186
|
label: "Inside Color",
|
|
943
|
-
default:
|
|
1187
|
+
default: s.insideColor
|
|
944
1188
|
},
|
|
945
1189
|
{
|
|
946
1190
|
id: "dieline.outsideColor",
|
|
947
1191
|
type: "color",
|
|
948
1192
|
label: "Outside Color",
|
|
949
|
-
default:
|
|
1193
|
+
default: s.outsideColor
|
|
950
1194
|
},
|
|
951
1195
|
{
|
|
952
|
-
id: "dieline.
|
|
1196
|
+
id: "dieline.features",
|
|
953
1197
|
type: "json",
|
|
954
|
-
label: "
|
|
955
|
-
default:
|
|
1198
|
+
label: "Edge Features",
|
|
1199
|
+
default: s.features
|
|
956
1200
|
}
|
|
957
1201
|
],
|
|
958
1202
|
[import_core2.ContributionPointIds.COMMANDS]: [
|
|
@@ -974,24 +1218,18 @@ var DielineTool = class {
|
|
|
974
1218
|
command: "detectEdge",
|
|
975
1219
|
title: "Detect Edge from Image",
|
|
976
1220
|
handler: async (imageUrl, options) => {
|
|
977
|
-
var _a;
|
|
978
1221
|
try {
|
|
979
1222
|
const pathData = await ImageTracer.trace(imageUrl, options);
|
|
980
1223
|
const bounds = getPathBounds(pathData);
|
|
981
|
-
const currentMax = Math.max(
|
|
1224
|
+
const currentMax = Math.max(s.width, s.height);
|
|
982
1225
|
const scale = currentMax / Math.max(bounds.width, bounds.height);
|
|
983
1226
|
const newWidth = bounds.width * scale;
|
|
984
1227
|
const newHeight = bounds.height * scale;
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
configService.update("dieline.height", newHeight);
|
|
991
|
-
configService.update("dieline.shape", "custom");
|
|
992
|
-
configService.update("dieline.pathData", pathData);
|
|
993
|
-
}
|
|
994
|
-
return pathData;
|
|
1228
|
+
return {
|
|
1229
|
+
pathData,
|
|
1230
|
+
width: newWidth,
|
|
1231
|
+
height: newHeight
|
|
1232
|
+
};
|
|
995
1233
|
} catch (e) {
|
|
996
1234
|
console.error("Edge detection failed", e);
|
|
997
1235
|
throw e;
|
|
@@ -1050,15 +1288,15 @@ var DielineTool = class {
|
|
|
1050
1288
|
return new import_fabric2.Pattern({ source: canvas, repetition: "repeat" });
|
|
1051
1289
|
}
|
|
1052
1290
|
resolvePadding(containerWidth, containerHeight) {
|
|
1053
|
-
if (typeof this.padding === "number") {
|
|
1054
|
-
return this.padding;
|
|
1291
|
+
if (typeof this.state.padding === "number") {
|
|
1292
|
+
return this.state.padding;
|
|
1055
1293
|
}
|
|
1056
|
-
if (typeof this.padding === "string") {
|
|
1057
|
-
if (this.padding.endsWith("%")) {
|
|
1058
|
-
const percent = parseFloat(this.padding) / 100;
|
|
1294
|
+
if (typeof this.state.padding === "string") {
|
|
1295
|
+
if (this.state.padding.endsWith("%")) {
|
|
1296
|
+
const percent = parseFloat(this.state.padding) / 100;
|
|
1059
1297
|
return Math.min(containerWidth, containerHeight) * percent;
|
|
1060
1298
|
}
|
|
1061
|
-
return parseFloat(this.padding) || 0;
|
|
1299
|
+
return parseFloat(this.state.padding) || 0;
|
|
1062
1300
|
}
|
|
1063
1301
|
return 0;
|
|
1064
1302
|
}
|
|
@@ -1071,14 +1309,14 @@ var DielineTool = class {
|
|
|
1071
1309
|
shape,
|
|
1072
1310
|
radius,
|
|
1073
1311
|
offset,
|
|
1074
|
-
|
|
1312
|
+
mainLine,
|
|
1313
|
+
offsetLine,
|
|
1075
1314
|
insideColor,
|
|
1076
1315
|
outsideColor,
|
|
1077
|
-
position,
|
|
1078
1316
|
showBleedLines,
|
|
1079
|
-
|
|
1080
|
-
} = this;
|
|
1081
|
-
let { width, height } = this;
|
|
1317
|
+
features
|
|
1318
|
+
} = this.state;
|
|
1319
|
+
let { width, height } = this.state;
|
|
1082
1320
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
1083
1321
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
1084
1322
|
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
@@ -1095,40 +1333,27 @@ var DielineTool = class {
|
|
|
1095
1333
|
const visualRadius = radius * scale;
|
|
1096
1334
|
const visualOffset = offset * scale;
|
|
1097
1335
|
layer.remove(...layer.getObjects());
|
|
1098
|
-
const
|
|
1099
|
-
|
|
1100
|
-
y: cy,
|
|
1101
|
-
width: visualWidth,
|
|
1102
|
-
height: visualHeight
|
|
1103
|
-
// Pass scale/unit context if needed by resolveHolePosition (though currently unused there)
|
|
1104
|
-
};
|
|
1105
|
-
const absoluteHoles = (holes || []).map((h) => {
|
|
1106
|
-
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1107
|
-
const offsetScale = unitScale * scale;
|
|
1108
|
-
const hWithPixelOffsets = {
|
|
1109
|
-
...h,
|
|
1110
|
-
offsetX: (h.offsetX || 0) * offsetScale,
|
|
1111
|
-
offsetY: (h.offsetY || 0) * offsetScale
|
|
1112
|
-
};
|
|
1113
|
-
const pos = resolveHolePosition(hWithPixelOffsets, geometryForHoles, {
|
|
1114
|
-
width: canvasW,
|
|
1115
|
-
height: canvasH
|
|
1116
|
-
});
|
|
1336
|
+
const absoluteFeatures = (features || []).map((f) => {
|
|
1337
|
+
const featureScale = scale;
|
|
1117
1338
|
return {
|
|
1118
|
-
...
|
|
1119
|
-
x:
|
|
1120
|
-
y:
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
// Store scaled offsets in the result for consistency, though pos is already resolved
|
|
1125
|
-
offsetX: hWithPixelOffsets.offsetX,
|
|
1126
|
-
offsetY: hWithPixelOffsets.offsetY
|
|
1339
|
+
...f,
|
|
1340
|
+
x: f.x,
|
|
1341
|
+
y: f.y,
|
|
1342
|
+
width: (f.width || 0) * featureScale,
|
|
1343
|
+
height: (f.height || 0) * featureScale,
|
|
1344
|
+
radius: (f.radius || 0) * featureScale
|
|
1127
1345
|
};
|
|
1128
1346
|
});
|
|
1347
|
+
const originalFeatures = absoluteFeatures.filter(
|
|
1348
|
+
(f) => !f.target || f.target === "original" || f.target === "both"
|
|
1349
|
+
);
|
|
1350
|
+
const offsetFeatures = absoluteFeatures.filter(
|
|
1351
|
+
(f) => f.target === "offset" || f.target === "both"
|
|
1352
|
+
);
|
|
1129
1353
|
const cutW = Math.max(0, visualWidth + visualOffset * 2);
|
|
1130
1354
|
const cutH = Math.max(0, visualHeight + visualOffset * 2);
|
|
1131
1355
|
const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
1356
|
+
const maskFeatures = visualOffset !== 0 ? offsetFeatures : originalFeatures;
|
|
1132
1357
|
const maskPathData = generateMaskPath({
|
|
1133
1358
|
canvasWidth: canvasW,
|
|
1134
1359
|
canvasHeight: canvasH,
|
|
@@ -1138,8 +1363,8 @@ var DielineTool = class {
|
|
|
1138
1363
|
radius: cutR,
|
|
1139
1364
|
x: cx,
|
|
1140
1365
|
y: cy,
|
|
1141
|
-
|
|
1142
|
-
pathData: this.pathData
|
|
1366
|
+
features: maskFeatures,
|
|
1367
|
+
pathData: this.state.pathData
|
|
1143
1368
|
});
|
|
1144
1369
|
const mask = new import_fabric2.Path(maskPathData, {
|
|
1145
1370
|
fill: outsideColor,
|
|
@@ -1160,8 +1385,9 @@ var DielineTool = class {
|
|
|
1160
1385
|
radius: cutR,
|
|
1161
1386
|
x: cx,
|
|
1162
1387
|
y: cy,
|
|
1163
|
-
|
|
1164
|
-
|
|
1388
|
+
features: maskFeatures,
|
|
1389
|
+
// Use same features as mask for consistency
|
|
1390
|
+
pathData: this.state.pathData,
|
|
1165
1391
|
canvasWidth: canvasW,
|
|
1166
1392
|
canvasHeight: canvasH
|
|
1167
1393
|
});
|
|
@@ -1185,15 +1411,27 @@ var DielineTool = class {
|
|
|
1185
1411
|
radius: visualRadius,
|
|
1186
1412
|
x: cx,
|
|
1187
1413
|
y: cy,
|
|
1188
|
-
|
|
1189
|
-
pathData: this.pathData,
|
|
1414
|
+
features: originalFeatures,
|
|
1415
|
+
pathData: this.state.pathData,
|
|
1416
|
+
canvasWidth: canvasW,
|
|
1417
|
+
canvasHeight: canvasH
|
|
1418
|
+
},
|
|
1419
|
+
{
|
|
1420
|
+
shape,
|
|
1421
|
+
width: cutW,
|
|
1422
|
+
height: cutH,
|
|
1423
|
+
radius: cutR,
|
|
1424
|
+
x: cx,
|
|
1425
|
+
y: cy,
|
|
1426
|
+
features: offsetFeatures,
|
|
1427
|
+
pathData: this.state.pathData,
|
|
1190
1428
|
canvasWidth: canvasW,
|
|
1191
1429
|
canvasHeight: canvasH
|
|
1192
1430
|
},
|
|
1193
1431
|
visualOffset
|
|
1194
1432
|
);
|
|
1195
1433
|
if (showBleedLines !== false) {
|
|
1196
|
-
const pattern = this.createHatchPattern(
|
|
1434
|
+
const pattern = this.createHatchPattern(mainLine.color);
|
|
1197
1435
|
if (pattern) {
|
|
1198
1436
|
const bleedObj = new import_fabric2.Path(bleedPathData, {
|
|
1199
1437
|
fill: pattern,
|
|
@@ -1214,18 +1452,16 @@ var DielineTool = class {
|
|
|
1214
1452
|
radius: cutR,
|
|
1215
1453
|
x: cx,
|
|
1216
1454
|
y: cy,
|
|
1217
|
-
|
|
1218
|
-
pathData: this.pathData,
|
|
1455
|
+
features: offsetFeatures,
|
|
1456
|
+
pathData: this.state.pathData,
|
|
1219
1457
|
canvasWidth: canvasW,
|
|
1220
1458
|
canvasHeight: canvasH
|
|
1221
1459
|
});
|
|
1222
1460
|
const offsetBorderObj = new import_fabric2.Path(offsetPathData, {
|
|
1223
1461
|
fill: null,
|
|
1224
|
-
stroke: "
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
strokeDashArray: [4, 4],
|
|
1228
|
-
// Dashed
|
|
1462
|
+
stroke: offsetLine.style === "hidden" ? null : offsetLine.color,
|
|
1463
|
+
strokeWidth: offsetLine.width,
|
|
1464
|
+
strokeDashArray: offsetLine.style === "dashed" ? [offsetLine.dashLength, offsetLine.dashLength] : void 0,
|
|
1229
1465
|
selectable: false,
|
|
1230
1466
|
evented: false,
|
|
1231
1467
|
originX: "left",
|
|
@@ -1240,16 +1476,16 @@ var DielineTool = class {
|
|
|
1240
1476
|
radius: visualRadius,
|
|
1241
1477
|
x: cx,
|
|
1242
1478
|
y: cy,
|
|
1243
|
-
|
|
1244
|
-
pathData: this.pathData,
|
|
1479
|
+
features: originalFeatures,
|
|
1480
|
+
pathData: this.state.pathData,
|
|
1245
1481
|
canvasWidth: canvasW,
|
|
1246
1482
|
canvasHeight: canvasH
|
|
1247
1483
|
});
|
|
1248
1484
|
const borderObj = new import_fabric2.Path(borderPathData, {
|
|
1249
1485
|
fill: "transparent",
|
|
1250
|
-
stroke: "
|
|
1251
|
-
strokeWidth:
|
|
1252
|
-
strokeDashArray: style === "dashed" ? [
|
|
1486
|
+
stroke: mainLine.style === "hidden" ? null : mainLine.color,
|
|
1487
|
+
strokeWidth: mainLine.width,
|
|
1488
|
+
strokeDashArray: mainLine.style === "dashed" ? [mainLine.dashLength, mainLine.dashLength] : void 0,
|
|
1253
1489
|
selectable: false,
|
|
1254
1490
|
evented: false,
|
|
1255
1491
|
originX: "left",
|
|
@@ -1281,7 +1517,7 @@ var DielineTool = class {
|
|
|
1281
1517
|
}
|
|
1282
1518
|
getGeometry() {
|
|
1283
1519
|
if (!this.canvasService) return null;
|
|
1284
|
-
const { unit, shape, width, height, radius,
|
|
1520
|
+
const { unit, shape, width, height, radius, offset, mainLine, pathData } = this.state;
|
|
1285
1521
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
1286
1522
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
1287
1523
|
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
@@ -1304,16 +1540,17 @@ var DielineTool = class {
|
|
|
1304
1540
|
height: visualHeight,
|
|
1305
1541
|
radius: radius * scale,
|
|
1306
1542
|
offset: offset * scale,
|
|
1307
|
-
// Pass scale to help other tools (like
|
|
1543
|
+
// Pass scale to help other tools (like FeatureTool) convert units
|
|
1308
1544
|
scale,
|
|
1309
|
-
|
|
1545
|
+
strokeWidth: mainLine.width,
|
|
1546
|
+
pathData
|
|
1310
1547
|
};
|
|
1311
1548
|
}
|
|
1312
1549
|
async exportCutImage() {
|
|
1313
1550
|
if (!this.canvasService) return null;
|
|
1314
1551
|
const userLayer = this.canvasService.getLayer("user");
|
|
1315
1552
|
if (!userLayer) return null;
|
|
1316
|
-
const { shape, width, height, radius,
|
|
1553
|
+
const { shape, width, height, radius, features, unit, pathData } = this.state;
|
|
1317
1554
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
1318
1555
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
1319
1556
|
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
@@ -1328,55 +1565,45 @@ var DielineTool = class {
|
|
|
1328
1565
|
const visualWidth = layout.width;
|
|
1329
1566
|
const visualHeight = layout.height;
|
|
1330
1567
|
const visualRadius = radius * scale;
|
|
1331
|
-
const
|
|
1332
|
-
const
|
|
1333
|
-
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1334
|
-
const pos = resolveHolePosition(
|
|
1335
|
-
{
|
|
1336
|
-
...h,
|
|
1337
|
-
offsetX: (h.offsetX || 0) * unitScale * scale,
|
|
1338
|
-
offsetY: (h.offsetY || 0) * unitScale * scale
|
|
1339
|
-
},
|
|
1340
|
-
{ x: cx, y: cy, width: visualWidth, height: visualHeight },
|
|
1341
|
-
{ width: canvasW, height: canvasH }
|
|
1342
|
-
);
|
|
1568
|
+
const absoluteFeatures = (features || []).map((f) => {
|
|
1569
|
+
const featureScale = scale;
|
|
1343
1570
|
return {
|
|
1344
|
-
...
|
|
1345
|
-
x:
|
|
1346
|
-
y:
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
offsetY: (h.offsetY || 0) * unitScale * scale
|
|
1571
|
+
...f,
|
|
1572
|
+
x: f.x,
|
|
1573
|
+
y: f.y,
|
|
1574
|
+
width: (f.width || 0) * featureScale,
|
|
1575
|
+
height: (f.height || 0) * featureScale,
|
|
1576
|
+
radius: (f.radius || 0) * featureScale
|
|
1351
1577
|
};
|
|
1352
1578
|
});
|
|
1353
|
-
const
|
|
1579
|
+
const originalFeatures = absoluteFeatures.filter(
|
|
1580
|
+
(f) => !f.target || f.target === "original" || f.target === "both"
|
|
1581
|
+
);
|
|
1582
|
+
const generatedPathData = generateDielinePath({
|
|
1354
1583
|
shape,
|
|
1355
1584
|
width: visualWidth,
|
|
1356
1585
|
height: visualHeight,
|
|
1357
1586
|
radius: visualRadius,
|
|
1358
1587
|
x: cx,
|
|
1359
1588
|
y: cy,
|
|
1360
|
-
|
|
1361
|
-
pathData
|
|
1589
|
+
features: originalFeatures,
|
|
1590
|
+
pathData,
|
|
1362
1591
|
canvasWidth: canvasW,
|
|
1363
1592
|
canvasHeight: canvasH
|
|
1364
1593
|
});
|
|
1365
1594
|
const clonedLayer = await userLayer.clone();
|
|
1366
|
-
const clipPath = new import_fabric2.Path(
|
|
1595
|
+
const clipPath = new import_fabric2.Path(generatedPathData, {
|
|
1367
1596
|
originX: "left",
|
|
1368
1597
|
originY: "top",
|
|
1369
1598
|
left: 0,
|
|
1370
1599
|
top: 0,
|
|
1371
1600
|
absolutePositioned: true
|
|
1372
|
-
// Important for groups
|
|
1373
1601
|
});
|
|
1374
1602
|
clonedLayer.clipPath = clipPath;
|
|
1375
1603
|
const bounds = clipPath.getBoundingRect();
|
|
1376
1604
|
const dataUrl = clonedLayer.toDataURL({
|
|
1377
1605
|
format: "png",
|
|
1378
1606
|
multiplier: 2,
|
|
1379
|
-
// Better quality
|
|
1380
1607
|
left: bounds.left,
|
|
1381
1608
|
top: bounds.top,
|
|
1382
1609
|
width: bounds.width,
|
|
@@ -1545,22 +1772,20 @@ var FilmTool = class {
|
|
|
1545
1772
|
}
|
|
1546
1773
|
};
|
|
1547
1774
|
|
|
1548
|
-
// src/
|
|
1775
|
+
// src/feature.ts
|
|
1549
1776
|
var import_core4 = require("@pooder/core");
|
|
1550
1777
|
var import_fabric4 = require("fabric");
|
|
1551
|
-
var
|
|
1778
|
+
var FeatureTool = class {
|
|
1552
1779
|
constructor(options) {
|
|
1553
|
-
this.id = "pooder.kit.
|
|
1780
|
+
this.id = "pooder.kit.feature";
|
|
1554
1781
|
this.metadata = {
|
|
1555
|
-
name: "
|
|
1782
|
+
name: "FeatureTool"
|
|
1556
1783
|
};
|
|
1557
|
-
this.
|
|
1558
|
-
this.constraintTarget = "bleed";
|
|
1784
|
+
this.features = [];
|
|
1559
1785
|
this.isUpdatingConfig = false;
|
|
1560
1786
|
this.handleMoving = null;
|
|
1561
1787
|
this.handleModified = null;
|
|
1562
1788
|
this.handleDielineChange = null;
|
|
1563
|
-
// Cache geometry to enforce constraints during drag
|
|
1564
1789
|
this.currentGeometry = null;
|
|
1565
1790
|
if (options) {
|
|
1566
1791
|
Object.assign(this, options);
|
|
@@ -1570,26 +1795,18 @@ var HoleTool = class {
|
|
|
1570
1795
|
this.context = context;
|
|
1571
1796
|
this.canvasService = context.services.get("CanvasService");
|
|
1572
1797
|
if (!this.canvasService) {
|
|
1573
|
-
console.warn("CanvasService not found for
|
|
1798
|
+
console.warn("CanvasService not found for FeatureTool");
|
|
1574
1799
|
return;
|
|
1575
1800
|
}
|
|
1576
1801
|
const configService = context.services.get(
|
|
1577
1802
|
"ConfigurationService"
|
|
1578
1803
|
);
|
|
1579
1804
|
if (configService) {
|
|
1580
|
-
this.
|
|
1581
|
-
"hole.constraintTarget",
|
|
1582
|
-
this.constraintTarget
|
|
1583
|
-
);
|
|
1584
|
-
this.holes = configService.get("dieline.holes", []);
|
|
1805
|
+
this.features = configService.get("dieline.features", []);
|
|
1585
1806
|
configService.onAnyChange((e) => {
|
|
1586
1807
|
if (this.isUpdatingConfig) return;
|
|
1587
|
-
if (e.key === "
|
|
1588
|
-
this.
|
|
1589
|
-
this.enforceConstraints();
|
|
1590
|
-
}
|
|
1591
|
-
if (e.key === "dieline.holes") {
|
|
1592
|
-
this.holes = e.value || [];
|
|
1808
|
+
if (e.key === "dieline.features") {
|
|
1809
|
+
this.features = e.value || [];
|
|
1593
1810
|
this.redraw();
|
|
1594
1811
|
}
|
|
1595
1812
|
});
|
|
@@ -1603,102 +1820,38 @@ var HoleTool = class {
|
|
|
1603
1820
|
}
|
|
1604
1821
|
contribute() {
|
|
1605
1822
|
return {
|
|
1606
|
-
[import_core4.ContributionPointIds.CONFIGURATIONS]: [
|
|
1607
|
-
{
|
|
1608
|
-
id: "hole.constraintTarget",
|
|
1609
|
-
type: "select",
|
|
1610
|
-
label: "Constraint Target",
|
|
1611
|
-
options: ["original", "bleed"],
|
|
1612
|
-
default: "bleed"
|
|
1613
|
-
}
|
|
1614
|
-
],
|
|
1615
1823
|
[import_core4.ContributionPointIds.COMMANDS]: [
|
|
1616
1824
|
{
|
|
1617
|
-
command: "
|
|
1618
|
-
title: "
|
|
1619
|
-
handler: () => {
|
|
1620
|
-
|
|
1621
|
-
if (!this.canvasService) return false;
|
|
1622
|
-
let defaultPos = { x: this.canvasService.canvas.width / 2, y: 50 };
|
|
1623
|
-
if (this.currentGeometry) {
|
|
1624
|
-
const g = this.currentGeometry;
|
|
1625
|
-
const topCenter = { x: g.x, y: g.y - g.height / 2 };
|
|
1626
|
-
defaultPos = getNearestPointOnDieline(topCenter, {
|
|
1627
|
-
...g,
|
|
1628
|
-
holes: []
|
|
1629
|
-
});
|
|
1630
|
-
}
|
|
1631
|
-
const { width, height } = this.canvasService.canvas;
|
|
1632
|
-
const normalizedHole = Coordinate.normalizePoint(defaultPos, {
|
|
1633
|
-
width: width || 800,
|
|
1634
|
-
height: height || 600
|
|
1635
|
-
});
|
|
1636
|
-
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1637
|
-
"ConfigurationService"
|
|
1638
|
-
);
|
|
1639
|
-
if (configService) {
|
|
1640
|
-
configService.update("dieline.holes", [
|
|
1641
|
-
{
|
|
1642
|
-
x: normalizedHole.x,
|
|
1643
|
-
y: normalizedHole.y,
|
|
1644
|
-
innerRadius: 15,
|
|
1645
|
-
outerRadius: 25
|
|
1646
|
-
}
|
|
1647
|
-
]);
|
|
1648
|
-
}
|
|
1649
|
-
return true;
|
|
1825
|
+
command: "addFeature",
|
|
1826
|
+
title: "Add Edge Feature",
|
|
1827
|
+
handler: (type = "subtract") => {
|
|
1828
|
+
return this.addFeature(type);
|
|
1650
1829
|
}
|
|
1651
1830
|
},
|
|
1652
1831
|
{
|
|
1653
1832
|
command: "addHole",
|
|
1654
1833
|
title: "Add Hole",
|
|
1655
|
-
handler: (
|
|
1656
|
-
|
|
1657
|
-
if (!this.canvasService) return false;
|
|
1658
|
-
let normalizedX = 0.5;
|
|
1659
|
-
let normalizedY = 0.5;
|
|
1660
|
-
if (this.currentGeometry) {
|
|
1661
|
-
const { x: gx, y: gy, width: gw, height: gh } = this.currentGeometry;
|
|
1662
|
-
const left = gx - gw / 2;
|
|
1663
|
-
const top = gy - gh / 2;
|
|
1664
|
-
normalizedX = gw > 0 ? (x - left) / gw : 0.5;
|
|
1665
|
-
normalizedY = gh > 0 ? (y - top) / gh : 0.5;
|
|
1666
|
-
} else {
|
|
1667
|
-
const { width, height } = this.canvasService.canvas;
|
|
1668
|
-
normalizedX = Coordinate.toNormalized(x, width || 800);
|
|
1669
|
-
normalizedY = Coordinate.toNormalized(y, height || 600);
|
|
1670
|
-
}
|
|
1671
|
-
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1672
|
-
"ConfigurationService"
|
|
1673
|
-
);
|
|
1674
|
-
if (configService) {
|
|
1675
|
-
const currentHoles = configService.get("dieline.holes", []);
|
|
1676
|
-
const lastHole = currentHoles[currentHoles.length - 1];
|
|
1677
|
-
const innerRadius = (_b = lastHole == null ? void 0 : lastHole.innerRadius) != null ? _b : 15;
|
|
1678
|
-
const outerRadius = (_c = lastHole == null ? void 0 : lastHole.outerRadius) != null ? _c : 25;
|
|
1679
|
-
const shape = (_d = lastHole == null ? void 0 : lastHole.shape) != null ? _d : "circle";
|
|
1680
|
-
const newHole = {
|
|
1681
|
-
x: normalizedX,
|
|
1682
|
-
y: normalizedY,
|
|
1683
|
-
shape,
|
|
1684
|
-
innerRadius,
|
|
1685
|
-
outerRadius
|
|
1686
|
-
};
|
|
1687
|
-
configService.update("dieline.holes", [...currentHoles, newHole]);
|
|
1688
|
-
}
|
|
1689
|
-
return true;
|
|
1834
|
+
handler: () => {
|
|
1835
|
+
return this.addFeature("subtract");
|
|
1690
1836
|
}
|
|
1691
1837
|
},
|
|
1692
1838
|
{
|
|
1693
|
-
command: "
|
|
1694
|
-
title: "
|
|
1839
|
+
command: "addDoubleLayerHole",
|
|
1840
|
+
title: "Add Double Layer Hole",
|
|
1841
|
+
handler: () => {
|
|
1842
|
+
return this.addDoubleLayerHole();
|
|
1843
|
+
}
|
|
1844
|
+
},
|
|
1845
|
+
{
|
|
1846
|
+
command: "clearFeatures",
|
|
1847
|
+
title: "Clear Features",
|
|
1695
1848
|
handler: () => {
|
|
1696
1849
|
var _a;
|
|
1697
1850
|
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1698
1851
|
"ConfigurationService"
|
|
1699
1852
|
);
|
|
1700
1853
|
if (configService) {
|
|
1701
|
-
configService.update("dieline.
|
|
1854
|
+
configService.update("dieline.features", []);
|
|
1702
1855
|
}
|
|
1703
1856
|
return true;
|
|
1704
1857
|
}
|
|
@@ -1706,6 +1859,88 @@ var HoleTool = class {
|
|
|
1706
1859
|
]
|
|
1707
1860
|
};
|
|
1708
1861
|
}
|
|
1862
|
+
addFeature(type) {
|
|
1863
|
+
var _a;
|
|
1864
|
+
if (!this.canvasService) return false;
|
|
1865
|
+
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1866
|
+
"ConfigurationService"
|
|
1867
|
+
);
|
|
1868
|
+
const unit = (configService == null ? void 0 : configService.get("dieline.unit", "mm")) || "mm";
|
|
1869
|
+
const defaultSize = Coordinate.convertUnit(10, "mm", unit);
|
|
1870
|
+
const newFeature = {
|
|
1871
|
+
id: Date.now().toString(),
|
|
1872
|
+
operation: type,
|
|
1873
|
+
target: "original",
|
|
1874
|
+
shape: "rect",
|
|
1875
|
+
x: 0.5,
|
|
1876
|
+
y: 0,
|
|
1877
|
+
// Top edge
|
|
1878
|
+
width: defaultSize,
|
|
1879
|
+
height: defaultSize,
|
|
1880
|
+
rotation: 0
|
|
1881
|
+
};
|
|
1882
|
+
if (configService) {
|
|
1883
|
+
const current = configService.get(
|
|
1884
|
+
"dieline.features",
|
|
1885
|
+
[]
|
|
1886
|
+
);
|
|
1887
|
+
configService.update("dieline.features", [...current, newFeature]);
|
|
1888
|
+
}
|
|
1889
|
+
return true;
|
|
1890
|
+
}
|
|
1891
|
+
addDoubleLayerHole() {
|
|
1892
|
+
var _a;
|
|
1893
|
+
if (!this.canvasService) return false;
|
|
1894
|
+
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1895
|
+
"ConfigurationService"
|
|
1896
|
+
);
|
|
1897
|
+
const unit = (configService == null ? void 0 : configService.get("dieline.unit", "mm")) || "mm";
|
|
1898
|
+
const lugRadius = Coordinate.convertUnit(20, "mm", unit);
|
|
1899
|
+
const holeRadius = Coordinate.convertUnit(15, "mm", unit);
|
|
1900
|
+
const groupId = Date.now().toString();
|
|
1901
|
+
const timestamp = Date.now();
|
|
1902
|
+
const lug = {
|
|
1903
|
+
id: `${timestamp}-lug`,
|
|
1904
|
+
groupId,
|
|
1905
|
+
operation: "add",
|
|
1906
|
+
shape: "circle",
|
|
1907
|
+
x: 0.5,
|
|
1908
|
+
y: 0,
|
|
1909
|
+
radius: lugRadius,
|
|
1910
|
+
// 20mm
|
|
1911
|
+
rotation: 0
|
|
1912
|
+
};
|
|
1913
|
+
const hole = {
|
|
1914
|
+
id: `${timestamp}-hole`,
|
|
1915
|
+
groupId,
|
|
1916
|
+
operation: "subtract",
|
|
1917
|
+
shape: "circle",
|
|
1918
|
+
x: 0.5,
|
|
1919
|
+
y: 0,
|
|
1920
|
+
radius: holeRadius,
|
|
1921
|
+
// 15mm
|
|
1922
|
+
rotation: 0
|
|
1923
|
+
};
|
|
1924
|
+
if (configService) {
|
|
1925
|
+
const current = configService.get(
|
|
1926
|
+
"dieline.features",
|
|
1927
|
+
[]
|
|
1928
|
+
);
|
|
1929
|
+
configService.update("dieline.features", [...current, lug, hole]);
|
|
1930
|
+
}
|
|
1931
|
+
return true;
|
|
1932
|
+
}
|
|
1933
|
+
getGeometryForFeature(geometry, feature) {
|
|
1934
|
+
if ((feature == null ? void 0 : feature.target) === "offset" && geometry.offset !== 0) {
|
|
1935
|
+
return {
|
|
1936
|
+
...geometry,
|
|
1937
|
+
width: geometry.width + geometry.offset * 2,
|
|
1938
|
+
height: geometry.height + geometry.offset * 2,
|
|
1939
|
+
radius: geometry.radius === 0 ? 0 : Math.max(0, geometry.radius + geometry.offset)
|
|
1940
|
+
};
|
|
1941
|
+
}
|
|
1942
|
+
return geometry;
|
|
1943
|
+
}
|
|
1709
1944
|
setup() {
|
|
1710
1945
|
if (!this.canvasService || !this.context) return;
|
|
1711
1946
|
const canvas = this.canvasService.canvas;
|
|
@@ -1713,10 +1948,7 @@ var HoleTool = class {
|
|
|
1713
1948
|
this.handleDielineChange = (geometry) => {
|
|
1714
1949
|
this.currentGeometry = geometry;
|
|
1715
1950
|
this.redraw();
|
|
1716
|
-
|
|
1717
|
-
if (changed) {
|
|
1718
|
-
this.syncHolesToDieline();
|
|
1719
|
-
}
|
|
1951
|
+
this.enforceConstraints();
|
|
1720
1952
|
};
|
|
1721
1953
|
this.context.eventBus.on(
|
|
1722
1954
|
"dieline:geometry:change",
|
|
@@ -1726,69 +1958,101 @@ var HoleTool = class {
|
|
|
1726
1958
|
const commandService = this.context.services.get("CommandService");
|
|
1727
1959
|
if (commandService) {
|
|
1728
1960
|
try {
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
Promise.resolve(geometry).then((g) => {
|
|
1961
|
+
Promise.resolve(commandService.executeCommand("getGeometry")).then(
|
|
1962
|
+
(g) => {
|
|
1732
1963
|
if (g) {
|
|
1733
1964
|
this.currentGeometry = g;
|
|
1734
|
-
this.
|
|
1735
|
-
this.initializeHoles();
|
|
1965
|
+
this.redraw();
|
|
1736
1966
|
}
|
|
1737
|
-
}
|
|
1738
|
-
|
|
1967
|
+
}
|
|
1968
|
+
);
|
|
1739
1969
|
} catch (e) {
|
|
1740
1970
|
}
|
|
1741
1971
|
}
|
|
1742
1972
|
if (!this.handleMoving) {
|
|
1743
1973
|
this.handleMoving = (e) => {
|
|
1744
|
-
var _a, _b, _c, _d
|
|
1974
|
+
var _a, _b, _c, _d;
|
|
1745
1975
|
const target = e.target;
|
|
1746
|
-
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "
|
|
1976
|
+
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "feature-marker") return;
|
|
1747
1977
|
if (!this.currentGeometry) return;
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
}
|
|
1760
|
-
const
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
constraintGeometry,
|
|
1764
|
-
(_d = holeData == null ? void 0 : holeData.innerRadius) != null ? _d : 15,
|
|
1765
|
-
(_e = holeData == null ? void 0 : holeData.outerRadius) != null ? _e : 25
|
|
1978
|
+
let feature;
|
|
1979
|
+
if ((_b = target.data) == null ? void 0 : _b.isGroup) {
|
|
1980
|
+
const indices = (_c = target.data) == null ? void 0 : _c.indices;
|
|
1981
|
+
if (indices && indices.length > 0) {
|
|
1982
|
+
feature = this.features[indices[0]];
|
|
1983
|
+
}
|
|
1984
|
+
} else {
|
|
1985
|
+
const index = (_d = target.data) == null ? void 0 : _d.index;
|
|
1986
|
+
if (index !== void 0) {
|
|
1987
|
+
feature = this.features[index];
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
const geometry = this.getGeometryForFeature(
|
|
1991
|
+
this.currentGeometry,
|
|
1992
|
+
feature
|
|
1766
1993
|
);
|
|
1994
|
+
const p = new import_fabric4.Point(target.left, target.top);
|
|
1995
|
+
const markerStrokeWidth = (target.strokeWidth || 2) * (target.scaleX || 1);
|
|
1996
|
+
const minDim = Math.min(target.getScaledWidth(), target.getScaledHeight());
|
|
1997
|
+
const limit = Math.max(0, minDim / 2 - markerStrokeWidth);
|
|
1998
|
+
const snapped = this.constrainPosition(p, geometry, limit);
|
|
1767
1999
|
target.set({
|
|
1768
|
-
left:
|
|
1769
|
-
top:
|
|
2000
|
+
left: snapped.x,
|
|
2001
|
+
top: snapped.y
|
|
1770
2002
|
});
|
|
1771
2003
|
};
|
|
1772
2004
|
canvas.on("object:moving", this.handleMoving);
|
|
1773
2005
|
}
|
|
1774
2006
|
if (!this.handleModified) {
|
|
1775
2007
|
this.handleModified = (e) => {
|
|
1776
|
-
var _a;
|
|
2008
|
+
var _a, _b, _c, _d;
|
|
1777
2009
|
const target = e.target;
|
|
1778
|
-
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
2010
|
+
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "feature-marker") return;
|
|
2011
|
+
if ((_b = target.data) == null ? void 0 : _b.isGroup) {
|
|
2012
|
+
const groupObj = target;
|
|
2013
|
+
const indices = (_c = groupObj.data) == null ? void 0 : _c.indices;
|
|
2014
|
+
if (!indices) return;
|
|
2015
|
+
const groupCenter = new import_fabric4.Point(groupObj.left, groupObj.top);
|
|
2016
|
+
const newFeatures = [...this.features];
|
|
2017
|
+
const { x, y } = this.currentGeometry;
|
|
2018
|
+
groupObj.getObjects().forEach((child, i) => {
|
|
2019
|
+
const originalIndex = indices[i];
|
|
2020
|
+
const feature = this.features[originalIndex];
|
|
2021
|
+
const geometry = this.getGeometryForFeature(
|
|
2022
|
+
this.currentGeometry,
|
|
2023
|
+
feature
|
|
2024
|
+
);
|
|
2025
|
+
const { width, height } = geometry;
|
|
2026
|
+
const layoutLeft = x - width / 2;
|
|
2027
|
+
const layoutTop = y - height / 2;
|
|
2028
|
+
const absX = groupCenter.x + (child.left || 0);
|
|
2029
|
+
const absY = groupCenter.y + (child.top || 0);
|
|
2030
|
+
const normalizedX = width > 0 ? (absX - layoutLeft) / width : 0.5;
|
|
2031
|
+
const normalizedY = height > 0 ? (absY - layoutTop) / height : 0.5;
|
|
2032
|
+
newFeatures[originalIndex] = {
|
|
2033
|
+
...newFeatures[originalIndex],
|
|
2034
|
+
x: normalizedX,
|
|
2035
|
+
y: normalizedY
|
|
2036
|
+
};
|
|
2037
|
+
});
|
|
2038
|
+
this.features = newFeatures;
|
|
2039
|
+
const configService = (_d = this.context) == null ? void 0 : _d.services.get(
|
|
2040
|
+
"ConfigurationService"
|
|
2041
|
+
);
|
|
2042
|
+
if (configService) {
|
|
2043
|
+
this.isUpdatingConfig = true;
|
|
2044
|
+
try {
|
|
2045
|
+
configService.update("dieline.features", this.features);
|
|
2046
|
+
} finally {
|
|
2047
|
+
this.isUpdatingConfig = false;
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
} else {
|
|
2051
|
+
this.syncFeatureFromCanvas(target);
|
|
1782
2052
|
}
|
|
1783
2053
|
};
|
|
1784
2054
|
canvas.on("object:modified", this.handleModified);
|
|
1785
2055
|
}
|
|
1786
|
-
this.initializeHoles();
|
|
1787
|
-
}
|
|
1788
|
-
initializeHoles() {
|
|
1789
|
-
if (!this.canvasService) return;
|
|
1790
|
-
this.redraw();
|
|
1791
|
-
this.syncHolesToDieline();
|
|
1792
2056
|
}
|
|
1793
2057
|
teardown() {
|
|
1794
2058
|
if (!this.canvasService) return;
|
|
@@ -1810,357 +2074,259 @@ var HoleTool = class {
|
|
|
1810
2074
|
}
|
|
1811
2075
|
const objects = canvas.getObjects().filter((obj) => {
|
|
1812
2076
|
var _a;
|
|
1813
|
-
return ((_a = obj.data) == null ? void 0 : _a.type) === "
|
|
2077
|
+
return ((_a = obj.data) == null ? void 0 : _a.type) === "feature-marker";
|
|
1814
2078
|
});
|
|
1815
2079
|
objects.forEach((obj) => canvas.remove(obj));
|
|
1816
|
-
if (this.context) {
|
|
1817
|
-
const commandService = this.context.services.get("CommandService");
|
|
1818
|
-
if (commandService) {
|
|
1819
|
-
try {
|
|
1820
|
-
commandService.executeCommand("setHoles", []);
|
|
1821
|
-
} catch (e) {
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
2080
|
this.canvasService.requestRenderAll();
|
|
1826
2081
|
}
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
var _a;
|
|
1832
|
-
return ((_a = obj.data) == null ? void 0 : _a.type) === "hole-marker" || obj.name === "hole-marker";
|
|
1833
|
-
}
|
|
1834
|
-
);
|
|
1835
|
-
if (objects.length === 0 && this.holes.length > 0) {
|
|
1836
|
-
console.warn("HoleTool: No markers found on canvas to sync from");
|
|
1837
|
-
return;
|
|
1838
|
-
}
|
|
1839
|
-
objects.sort(
|
|
1840
|
-
(a, b) => {
|
|
1841
|
-
var _a, _b, _c, _d;
|
|
1842
|
-
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);
|
|
1843
|
-
}
|
|
1844
|
-
);
|
|
1845
|
-
const newHoles = objects.map((obj, i) => {
|
|
1846
|
-
var _a, _b, _c, _d;
|
|
1847
|
-
const original = this.holes[i];
|
|
1848
|
-
const newAbsX = obj.left;
|
|
1849
|
-
const newAbsY = obj.top;
|
|
1850
|
-
if (isNaN(newAbsX) || isNaN(newAbsY)) {
|
|
1851
|
-
console.error("HoleTool: Invalid marker coordinates", {
|
|
1852
|
-
newAbsX,
|
|
1853
|
-
newAbsY
|
|
1854
|
-
});
|
|
1855
|
-
return original;
|
|
1856
|
-
}
|
|
1857
|
-
const scale = ((_a = this.currentGeometry) == null ? void 0 : _a.scale) || 1;
|
|
1858
|
-
const unit = ((_b = this.currentGeometry) == null ? void 0 : _b.unit) || "mm";
|
|
1859
|
-
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1860
|
-
if (original && original.anchor && this.currentGeometry) {
|
|
1861
|
-
const { x, y, width, height } = this.currentGeometry;
|
|
1862
|
-
let bx = x;
|
|
1863
|
-
let by = y;
|
|
1864
|
-
const left = x - width / 2;
|
|
1865
|
-
const right = x + width / 2;
|
|
1866
|
-
const top = y - height / 2;
|
|
1867
|
-
const bottom = y + height / 2;
|
|
1868
|
-
switch (original.anchor) {
|
|
1869
|
-
case "top-left":
|
|
1870
|
-
bx = left;
|
|
1871
|
-
by = top;
|
|
1872
|
-
break;
|
|
1873
|
-
case "top-center":
|
|
1874
|
-
bx = x;
|
|
1875
|
-
by = top;
|
|
1876
|
-
break;
|
|
1877
|
-
case "top-right":
|
|
1878
|
-
bx = right;
|
|
1879
|
-
by = top;
|
|
1880
|
-
break;
|
|
1881
|
-
case "center-left":
|
|
1882
|
-
bx = left;
|
|
1883
|
-
by = y;
|
|
1884
|
-
break;
|
|
1885
|
-
case "center":
|
|
1886
|
-
bx = x;
|
|
1887
|
-
by = y;
|
|
1888
|
-
break;
|
|
1889
|
-
case "center-right":
|
|
1890
|
-
bx = right;
|
|
1891
|
-
by = y;
|
|
1892
|
-
break;
|
|
1893
|
-
case "bottom-left":
|
|
1894
|
-
bx = left;
|
|
1895
|
-
by = bottom;
|
|
1896
|
-
break;
|
|
1897
|
-
case "bottom-center":
|
|
1898
|
-
bx = x;
|
|
1899
|
-
by = bottom;
|
|
1900
|
-
break;
|
|
1901
|
-
case "bottom-right":
|
|
1902
|
-
bx = right;
|
|
1903
|
-
by = bottom;
|
|
1904
|
-
break;
|
|
1905
|
-
}
|
|
1906
|
-
return {
|
|
1907
|
-
...original,
|
|
1908
|
-
// Denormalize offset back to physical units (mm)
|
|
1909
|
-
offsetX: (newAbsX - bx) / scale / unitScale,
|
|
1910
|
-
offsetY: (newAbsY - by) / scale / unitScale,
|
|
1911
|
-
// Clear direct coordinates if we use anchor
|
|
1912
|
-
x: void 0,
|
|
1913
|
-
y: void 0,
|
|
1914
|
-
// Ensure other properties are preserved
|
|
1915
|
-
innerRadius: original.innerRadius,
|
|
1916
|
-
outerRadius: original.outerRadius,
|
|
1917
|
-
shape: original.shape || "circle"
|
|
1918
|
-
};
|
|
1919
|
-
}
|
|
1920
|
-
let normalizedX = 0.5;
|
|
1921
|
-
let normalizedY = 0.5;
|
|
1922
|
-
if (this.currentGeometry) {
|
|
1923
|
-
const { x, y, width, height } = this.currentGeometry;
|
|
1924
|
-
const left = x - width / 2;
|
|
1925
|
-
const top = y - height / 2;
|
|
1926
|
-
normalizedX = width > 0 ? (newAbsX - left) / width : 0.5;
|
|
1927
|
-
normalizedY = height > 0 ? (newAbsY - top) / height : 0.5;
|
|
1928
|
-
} else {
|
|
1929
|
-
const { width, height } = this.canvasService.canvas;
|
|
1930
|
-
normalizedX = Coordinate.toNormalized(newAbsX, width || 800);
|
|
1931
|
-
normalizedY = Coordinate.toNormalized(newAbsY, height || 600);
|
|
1932
|
-
}
|
|
1933
|
-
return {
|
|
1934
|
-
...original,
|
|
1935
|
-
x: normalizedX,
|
|
1936
|
-
y: normalizedY,
|
|
1937
|
-
// Clear offsets if we are using direct normalized coordinates
|
|
1938
|
-
offsetX: void 0,
|
|
1939
|
-
offsetY: void 0,
|
|
1940
|
-
// Ensure other properties are preserved
|
|
1941
|
-
innerRadius: (_c = original == null ? void 0 : original.innerRadius) != null ? _c : 15,
|
|
1942
|
-
outerRadius: (_d = original == null ? void 0 : original.outerRadius) != null ? _d : 25,
|
|
1943
|
-
shape: (original == null ? void 0 : original.shape) || "circle"
|
|
1944
|
-
};
|
|
2082
|
+
constrainPosition(p, geometry, limit) {
|
|
2083
|
+
const nearest = getNearestPointOnDieline({ x: p.x, y: p.y }, {
|
|
2084
|
+
...geometry,
|
|
2085
|
+
features: []
|
|
1945
2086
|
});
|
|
1946
|
-
|
|
1947
|
-
|
|
2087
|
+
const dx = p.x - nearest.x;
|
|
2088
|
+
const dy = p.y - nearest.y;
|
|
2089
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
2090
|
+
if (dist <= limit) {
|
|
2091
|
+
return { x: p.x, y: p.y };
|
|
2092
|
+
}
|
|
2093
|
+
const scale = limit / dist;
|
|
2094
|
+
return {
|
|
2095
|
+
x: nearest.x + dx * scale,
|
|
2096
|
+
y: nearest.y + dy * scale
|
|
2097
|
+
};
|
|
1948
2098
|
}
|
|
1949
|
-
|
|
1950
|
-
|
|
2099
|
+
syncFeatureFromCanvas(target) {
|
|
2100
|
+
var _a;
|
|
2101
|
+
if (!this.currentGeometry || !this.context) return;
|
|
2102
|
+
const index = (_a = target.data) == null ? void 0 : _a.index;
|
|
2103
|
+
if (index === void 0 || index < 0 || index >= this.features.length)
|
|
2104
|
+
return;
|
|
2105
|
+
const feature = this.features[index];
|
|
2106
|
+
const geometry = this.getGeometryForFeature(this.currentGeometry, feature);
|
|
2107
|
+
const { width, height, x, y } = geometry;
|
|
2108
|
+
const left = x - width / 2;
|
|
2109
|
+
const top = y - height / 2;
|
|
2110
|
+
const normalizedX = width > 0 ? (target.left - left) / width : 0.5;
|
|
2111
|
+
const normalizedY = height > 0 ? (target.top - top) / height : 0.5;
|
|
2112
|
+
const updatedFeature = {
|
|
2113
|
+
...feature,
|
|
2114
|
+
x: normalizedX,
|
|
2115
|
+
y: normalizedY
|
|
2116
|
+
// Could also update rotation if we allowed rotating markers
|
|
2117
|
+
};
|
|
2118
|
+
const newFeatures = [...this.features];
|
|
2119
|
+
newFeatures[index] = updatedFeature;
|
|
2120
|
+
this.features = newFeatures;
|
|
1951
2121
|
const configService = this.context.services.get(
|
|
1952
2122
|
"ConfigurationService"
|
|
1953
2123
|
);
|
|
1954
2124
|
if (configService) {
|
|
1955
2125
|
this.isUpdatingConfig = true;
|
|
1956
2126
|
try {
|
|
1957
|
-
configService.update("dieline.
|
|
2127
|
+
configService.update("dieline.features", this.features);
|
|
1958
2128
|
} finally {
|
|
1959
2129
|
this.isUpdatingConfig = false;
|
|
1960
2130
|
}
|
|
1961
2131
|
}
|
|
1962
2132
|
}
|
|
1963
2133
|
redraw() {
|
|
1964
|
-
if (!this.canvasService) return;
|
|
2134
|
+
if (!this.canvasService || !this.currentGeometry) return;
|
|
1965
2135
|
const canvas = this.canvasService.canvas;
|
|
1966
|
-
const
|
|
2136
|
+
const geometry = this.currentGeometry;
|
|
1967
2137
|
const existing = canvas.getObjects().filter((obj) => {
|
|
1968
2138
|
var _a;
|
|
1969
|
-
return ((_a = obj.data) == null ? void 0 : _a.type) === "
|
|
2139
|
+
return ((_a = obj.data) == null ? void 0 : _a.type) === "feature-marker";
|
|
1970
2140
|
});
|
|
1971
2141
|
existing.forEach((obj) => canvas.remove(obj));
|
|
1972
|
-
|
|
1973
|
-
if (!holes || holes.length === 0) {
|
|
2142
|
+
if (!this.features || this.features.length === 0) {
|
|
1974
2143
|
this.canvasService.requestRenderAll();
|
|
1975
2144
|
return;
|
|
1976
2145
|
}
|
|
1977
|
-
const
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
2146
|
+
const scale = geometry.scale || 1;
|
|
2147
|
+
const finalScale = scale;
|
|
2148
|
+
const groups = {};
|
|
2149
|
+
const singles = [];
|
|
2150
|
+
this.features.forEach((f, i) => {
|
|
2151
|
+
if (f.groupId) {
|
|
2152
|
+
if (!groups[f.groupId]) groups[f.groupId] = [];
|
|
2153
|
+
groups[f.groupId].push({ feature: f, index: i });
|
|
2154
|
+
} else {
|
|
2155
|
+
singles.push({ feature: f, index: i });
|
|
2156
|
+
}
|
|
2157
|
+
});
|
|
2158
|
+
const createMarkerShape = (feature, pos) => {
|
|
2159
|
+
const featureScale = scale;
|
|
2160
|
+
const visualWidth = (feature.width || 10) * featureScale;
|
|
2161
|
+
const visualHeight = (feature.height || 10) * featureScale;
|
|
2162
|
+
const visualRadius = (feature.radius || 0) * featureScale;
|
|
2163
|
+
const color = feature.color || (feature.operation === "add" ? "#00FF00" : "#FF0000");
|
|
2164
|
+
const strokeDash = feature.strokeDash || (feature.operation === "subtract" ? [4, 4] : void 0);
|
|
2165
|
+
let shape;
|
|
2166
|
+
if (feature.shape === "rect") {
|
|
2167
|
+
shape = new import_fabric4.Rect({
|
|
2168
|
+
width: visualWidth,
|
|
2169
|
+
height: visualHeight,
|
|
2170
|
+
rx: visualRadius,
|
|
2171
|
+
ry: visualRadius,
|
|
2172
|
+
fill: "transparent",
|
|
2173
|
+
stroke: color,
|
|
2174
|
+
strokeWidth: 2,
|
|
2175
|
+
strokeDashArray: strokeDash,
|
|
2176
|
+
originX: "center",
|
|
2177
|
+
originY: "center",
|
|
2178
|
+
left: pos.x,
|
|
2179
|
+
top: pos.y
|
|
2180
|
+
});
|
|
2181
|
+
} else {
|
|
2182
|
+
shape = new import_fabric4.Circle({
|
|
2183
|
+
radius: visualRadius || 5 * finalScale,
|
|
2184
|
+
fill: "transparent",
|
|
2185
|
+
stroke: color,
|
|
2186
|
+
strokeWidth: 2,
|
|
2187
|
+
strokeDashArray: strokeDash,
|
|
2188
|
+
originX: "center",
|
|
2189
|
+
originY: "center",
|
|
2190
|
+
left: pos.x,
|
|
2191
|
+
top: pos.y
|
|
2192
|
+
});
|
|
2193
|
+
}
|
|
2194
|
+
if (feature.rotation) {
|
|
2195
|
+
shape.rotate(feature.rotation);
|
|
2196
|
+
}
|
|
2197
|
+
return shape;
|
|
1984
2198
|
};
|
|
1985
|
-
|
|
1986
|
-
const
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
const visualInnerRadius = hole.innerRadius * unitScale * scale;
|
|
1990
|
-
const visualOuterRadius = hole.outerRadius * unitScale * scale;
|
|
1991
|
-
const pos = resolveHolePosition(
|
|
1992
|
-
{
|
|
1993
|
-
...hole,
|
|
1994
|
-
offsetX: (hole.offsetX || 0) * unitScale * scale,
|
|
1995
|
-
offsetY: (hole.offsetY || 0) * unitScale * scale
|
|
1996
|
-
},
|
|
1997
|
-
geometry,
|
|
1998
|
-
{ width: geometry.width, height: geometry.height }
|
|
1999
|
-
// Use geometry dims instead of canvas
|
|
2199
|
+
singles.forEach(({ feature, index }) => {
|
|
2200
|
+
const geometry2 = this.getGeometryForFeature(
|
|
2201
|
+
this.currentGeometry,
|
|
2202
|
+
feature
|
|
2000
2203
|
);
|
|
2001
|
-
const
|
|
2002
|
-
const
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
fill: "transparent",
|
|
2013
|
-
stroke: "red",
|
|
2014
|
-
strokeWidth: 2,
|
|
2015
|
-
originX: "center",
|
|
2016
|
-
originY: "center"
|
|
2204
|
+
const pos = resolveFeaturePosition(feature, geometry2);
|
|
2205
|
+
const marker = createMarkerShape(feature, pos);
|
|
2206
|
+
marker.set({
|
|
2207
|
+
selectable: true,
|
|
2208
|
+
hasControls: false,
|
|
2209
|
+
hasBorders: false,
|
|
2210
|
+
hoverCursor: "move",
|
|
2211
|
+
lockRotation: true,
|
|
2212
|
+
lockScalingX: true,
|
|
2213
|
+
lockScalingY: true,
|
|
2214
|
+
data: { type: "feature-marker", index, isGroup: false }
|
|
2017
2215
|
});
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
stroke: "#666",
|
|
2023
|
-
strokeWidth: 1,
|
|
2024
|
-
strokeDashArray: [5, 5],
|
|
2025
|
-
originX: "center",
|
|
2026
|
-
originY: "center"
|
|
2027
|
-
}) : new import_fabric4.Circle({
|
|
2028
|
-
radius: visualOuterRadius,
|
|
2029
|
-
fill: "transparent",
|
|
2030
|
-
stroke: "#666",
|
|
2031
|
-
strokeWidth: 1,
|
|
2032
|
-
strokeDashArray: [5, 5],
|
|
2033
|
-
originX: "center",
|
|
2034
|
-
originY: "center"
|
|
2216
|
+
marker.set("opacity", 0);
|
|
2217
|
+
marker.on("mouseover", () => {
|
|
2218
|
+
marker.set("opacity", 1);
|
|
2219
|
+
canvas.requestRenderAll();
|
|
2035
2220
|
});
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2221
|
+
marker.on("mouseout", () => {
|
|
2222
|
+
if (canvas.getActiveObject() !== marker) {
|
|
2223
|
+
marker.set("opacity", 0);
|
|
2224
|
+
canvas.requestRenderAll();
|
|
2225
|
+
}
|
|
2226
|
+
});
|
|
2227
|
+
marker.on("selected", () => {
|
|
2228
|
+
marker.set("opacity", 1);
|
|
2229
|
+
canvas.requestRenderAll();
|
|
2230
|
+
});
|
|
2231
|
+
marker.on("deselected", () => {
|
|
2232
|
+
marker.set("opacity", 0);
|
|
2233
|
+
canvas.requestRenderAll();
|
|
2234
|
+
});
|
|
2235
|
+
canvas.add(marker);
|
|
2236
|
+
canvas.bringObjectToFront(marker);
|
|
2237
|
+
});
|
|
2238
|
+
Object.keys(groups).forEach((groupId) => {
|
|
2239
|
+
const members = groups[groupId];
|
|
2240
|
+
if (members.length === 0) return;
|
|
2241
|
+
const shapes = members.map(({ feature }) => {
|
|
2242
|
+
const geometry2 = this.getGeometryForFeature(
|
|
2243
|
+
this.currentGeometry,
|
|
2244
|
+
feature
|
|
2245
|
+
);
|
|
2246
|
+
const pos = resolveFeaturePosition(feature, geometry2);
|
|
2247
|
+
return createMarkerShape(feature, pos);
|
|
2248
|
+
});
|
|
2249
|
+
const groupObj = new import_fabric4.Group(shapes, {
|
|
2041
2250
|
selectable: true,
|
|
2042
2251
|
hasControls: false,
|
|
2043
|
-
// Don't allow resizing/rotating
|
|
2044
2252
|
hasBorders: false,
|
|
2045
|
-
subTargetCheck: false,
|
|
2046
|
-
opacity: 0,
|
|
2047
|
-
// Default hidden
|
|
2048
2253
|
hoverCursor: "move",
|
|
2049
|
-
|
|
2254
|
+
lockRotation: true,
|
|
2255
|
+
lockScalingX: true,
|
|
2256
|
+
lockScalingY: true,
|
|
2257
|
+
subTargetCheck: true,
|
|
2258
|
+
// Allow events to pass through if needed, but we treat as one
|
|
2259
|
+
interactive: false,
|
|
2260
|
+
// Children not interactive
|
|
2261
|
+
// @ts-ignore
|
|
2262
|
+
data: {
|
|
2263
|
+
type: "feature-marker",
|
|
2264
|
+
isGroup: true,
|
|
2265
|
+
groupId,
|
|
2266
|
+
indices: members.map((m) => m.index)
|
|
2267
|
+
}
|
|
2050
2268
|
});
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2269
|
+
groupObj.set("opacity", 0);
|
|
2270
|
+
groupObj.on("mouseover", () => {
|
|
2271
|
+
groupObj.set("opacity", 1);
|
|
2054
2272
|
canvas.requestRenderAll();
|
|
2055
2273
|
});
|
|
2056
|
-
|
|
2057
|
-
if (canvas.getActiveObject() !==
|
|
2058
|
-
|
|
2274
|
+
groupObj.on("mouseout", () => {
|
|
2275
|
+
if (canvas.getActiveObject() !== groupObj) {
|
|
2276
|
+
groupObj.set("opacity", 0);
|
|
2059
2277
|
canvas.requestRenderAll();
|
|
2060
2278
|
}
|
|
2061
2279
|
});
|
|
2062
|
-
|
|
2063
|
-
|
|
2280
|
+
groupObj.on("selected", () => {
|
|
2281
|
+
groupObj.set("opacity", 1);
|
|
2064
2282
|
canvas.requestRenderAll();
|
|
2065
2283
|
});
|
|
2066
|
-
|
|
2067
|
-
|
|
2284
|
+
groupObj.on("deselected", () => {
|
|
2285
|
+
groupObj.set("opacity", 0);
|
|
2068
2286
|
canvas.requestRenderAll();
|
|
2069
2287
|
});
|
|
2070
|
-
canvas.add(
|
|
2071
|
-
canvas.bringObjectToFront(
|
|
2072
|
-
});
|
|
2073
|
-
const markers = canvas.getObjects().filter((o) => {
|
|
2074
|
-
var _a;
|
|
2075
|
-
return ((_a = o.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
2288
|
+
canvas.add(groupObj);
|
|
2289
|
+
canvas.bringObjectToFront(groupObj);
|
|
2076
2290
|
});
|
|
2077
|
-
markers.forEach((m) => canvas.bringObjectToFront(m));
|
|
2078
2291
|
this.canvasService.requestRenderAll();
|
|
2079
2292
|
}
|
|
2080
2293
|
enforceConstraints() {
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
}
|
|
2085
|
-
const effectiveOffset = this.constraintTarget === "original" ? 0 : geometry.offset;
|
|
2086
|
-
const constraintGeometry = {
|
|
2087
|
-
...geometry,
|
|
2088
|
-
width: Math.max(0, geometry.width + effectiveOffset * 2),
|
|
2089
|
-
height: Math.max(0, geometry.height + effectiveOffset * 2),
|
|
2090
|
-
radius: Math.max(0, geometry.radius + effectiveOffset)
|
|
2091
|
-
};
|
|
2092
|
-
const objects = this.canvasService.canvas.getObjects().filter((obj) => {
|
|
2294
|
+
if (!this.canvasService || !this.currentGeometry) return;
|
|
2295
|
+
const canvas = this.canvasService.canvas;
|
|
2296
|
+
const markers = canvas.getObjects().filter((obj) => {
|
|
2093
2297
|
var _a;
|
|
2094
|
-
return ((_a = obj.data) == null ? void 0 : _a.type) === "
|
|
2298
|
+
return ((_a = obj.data) == null ? void 0 : _a.type) === "feature-marker";
|
|
2095
2299
|
});
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2300
|
+
markers.forEach((marker) => {
|
|
2301
|
+
var _a, _b, _c;
|
|
2302
|
+
let feature;
|
|
2303
|
+
if ((_a = marker.data) == null ? void 0 : _a.isGroup) {
|
|
2304
|
+
const indices = (_b = marker.data) == null ? void 0 : _b.indices;
|
|
2305
|
+
if (indices && indices.length > 0) {
|
|
2306
|
+
feature = this.features[indices[0]];
|
|
2307
|
+
}
|
|
2308
|
+
} else {
|
|
2309
|
+
const index = (_c = marker.data) == null ? void 0 : _c.index;
|
|
2310
|
+
if (index !== void 0) {
|
|
2311
|
+
feature = this.features[index];
|
|
2312
|
+
}
|
|
2101
2313
|
}
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
var _a, _b;
|
|
2106
|
-
const currentPos = new import_fabric4.Point(obj.left, obj.top);
|
|
2107
|
-
const holeData = this.holes[i];
|
|
2108
|
-
const scale = geometry.scale || 1;
|
|
2109
|
-
const unit = geometry.unit || "mm";
|
|
2110
|
-
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
2111
|
-
const innerR = ((_a = holeData == null ? void 0 : holeData.innerRadius) != null ? _a : 15) * unitScale * scale;
|
|
2112
|
-
const outerR = ((_b = holeData == null ? void 0 : holeData.outerRadius) != null ? _b : 25) * unitScale * scale;
|
|
2113
|
-
const newPos = this.calculateConstrainedPosition(
|
|
2114
|
-
currentPos,
|
|
2115
|
-
constraintGeometry,
|
|
2116
|
-
innerR,
|
|
2117
|
-
outerR
|
|
2314
|
+
const geometry = this.getGeometryForFeature(
|
|
2315
|
+
this.currentGeometry,
|
|
2316
|
+
feature
|
|
2118
2317
|
);
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2318
|
+
const markerStrokeWidth = (marker.strokeWidth || 2) * (marker.scaleX || 1);
|
|
2319
|
+
const minDim = Math.min(marker.getScaledWidth(), marker.getScaledHeight());
|
|
2320
|
+
const limit = Math.max(0, minDim / 2 - markerStrokeWidth);
|
|
2321
|
+
const snapped = this.constrainPosition(
|
|
2322
|
+
new import_fabric4.Point(marker.left, marker.top),
|
|
2323
|
+
geometry,
|
|
2324
|
+
limit
|
|
2325
|
+
);
|
|
2326
|
+
marker.set({ left: snapped.x, top: snapped.y });
|
|
2327
|
+
marker.setCoords();
|
|
2127
2328
|
});
|
|
2128
|
-
|
|
2129
|
-
this.syncHolesFromCanvas();
|
|
2130
|
-
return true;
|
|
2131
|
-
}
|
|
2132
|
-
return false;
|
|
2133
|
-
}
|
|
2134
|
-
calculateConstrainedPosition(p, g, innerRadius, outerRadius) {
|
|
2135
|
-
const options = {
|
|
2136
|
-
...g,
|
|
2137
|
-
holes: []
|
|
2138
|
-
// We don't need holes for boundary calculation
|
|
2139
|
-
};
|
|
2140
|
-
const nearest = getNearestPointOnDieline(
|
|
2141
|
-
{ x: p.x, y: p.y },
|
|
2142
|
-
options
|
|
2143
|
-
);
|
|
2144
|
-
const nearestP = new import_fabric4.Point(nearest.x, nearest.y);
|
|
2145
|
-
const dist = p.distanceFrom(nearestP);
|
|
2146
|
-
const v = p.subtract(nearestP);
|
|
2147
|
-
const center = new import_fabric4.Point(g.x, g.y);
|
|
2148
|
-
const distToCenter = p.distanceFrom(center);
|
|
2149
|
-
const nearestDistToCenter = nearestP.distanceFrom(center);
|
|
2150
|
-
let signedDist = dist;
|
|
2151
|
-
if (distToCenter < nearestDistToCenter) {
|
|
2152
|
-
signedDist = -dist;
|
|
2153
|
-
}
|
|
2154
|
-
let clampedDist = signedDist;
|
|
2155
|
-
if (signedDist > 0) {
|
|
2156
|
-
clampedDist = Math.min(signedDist, innerRadius);
|
|
2157
|
-
} else {
|
|
2158
|
-
clampedDist = Math.max(signedDist, -outerRadius);
|
|
2159
|
-
}
|
|
2160
|
-
if (dist < 1e-3) return nearestP;
|
|
2161
|
-
const scale = Math.abs(clampedDist) / (dist || 1);
|
|
2162
|
-
const offset = v.scalarMultiply(scale);
|
|
2163
|
-
return nearestP.add(offset);
|
|
2329
|
+
canvas.requestRenderAll();
|
|
2164
2330
|
}
|
|
2165
2331
|
};
|
|
2166
2332
|
|
|
@@ -2175,6 +2341,7 @@ var ImageTool = class {
|
|
|
2175
2341
|
};
|
|
2176
2342
|
this.items = [];
|
|
2177
2343
|
this.objectMap = /* @__PURE__ */ new Map();
|
|
2344
|
+
this.loadResolvers = /* @__PURE__ */ new Map();
|
|
2178
2345
|
this.isUpdatingConfig = false;
|
|
2179
2346
|
}
|
|
2180
2347
|
activate(context) {
|
|
@@ -2184,19 +2351,15 @@ var ImageTool = class {
|
|
|
2184
2351
|
console.warn("CanvasService not found for ImageTool");
|
|
2185
2352
|
return;
|
|
2186
2353
|
}
|
|
2187
|
-
const configService = context.services.get(
|
|
2354
|
+
const configService = context.services.get(
|
|
2355
|
+
"ConfigurationService"
|
|
2356
|
+
);
|
|
2188
2357
|
if (configService) {
|
|
2189
2358
|
this.items = configService.get("image.items", []) || [];
|
|
2190
2359
|
configService.onAnyChange((e) => {
|
|
2191
2360
|
if (this.isUpdatingConfig) return;
|
|
2192
|
-
let shouldUpdate = false;
|
|
2193
2361
|
if (e.key === "image.items") {
|
|
2194
2362
|
this.items = e.value || [];
|
|
2195
|
-
shouldUpdate = true;
|
|
2196
|
-
} else if (e.key.startsWith("dieline.") && e.key !== "dieline.holes") {
|
|
2197
|
-
shouldUpdate = true;
|
|
2198
|
-
}
|
|
2199
|
-
if (shouldUpdate) {
|
|
2200
2363
|
this.updateImages();
|
|
2201
2364
|
}
|
|
2202
2365
|
});
|
|
@@ -2232,15 +2395,39 @@ var ImageTool = class {
|
|
|
2232
2395
|
{
|
|
2233
2396
|
command: "addImage",
|
|
2234
2397
|
title: "Add Image",
|
|
2235
|
-
handler: (url, options) => {
|
|
2398
|
+
handler: async (url, options) => {
|
|
2399
|
+
const id = this.generateId();
|
|
2236
2400
|
const newItem = {
|
|
2237
|
-
id
|
|
2401
|
+
id,
|
|
2238
2402
|
url,
|
|
2239
2403
|
opacity: 1,
|
|
2240
2404
|
...options
|
|
2241
2405
|
};
|
|
2406
|
+
const promise = new Promise((resolve) => {
|
|
2407
|
+
this.loadResolvers.set(id, () => resolve(id));
|
|
2408
|
+
});
|
|
2242
2409
|
this.updateConfig([...this.items, newItem]);
|
|
2243
|
-
return
|
|
2410
|
+
return promise;
|
|
2411
|
+
}
|
|
2412
|
+
},
|
|
2413
|
+
{
|
|
2414
|
+
command: "fitImageToArea",
|
|
2415
|
+
title: "Fit Image to Area",
|
|
2416
|
+
handler: (id, area) => {
|
|
2417
|
+
var _a, _b;
|
|
2418
|
+
const item = this.items.find((i) => i.id === id);
|
|
2419
|
+
const obj = this.objectMap.get(id);
|
|
2420
|
+
if (item && obj && obj.width && obj.height) {
|
|
2421
|
+
const scale = Math.max(
|
|
2422
|
+
area.width / obj.width,
|
|
2423
|
+
area.height / obj.height
|
|
2424
|
+
);
|
|
2425
|
+
this.updateImageInConfig(id, {
|
|
2426
|
+
scale,
|
|
2427
|
+
left: (_a = area.left) != null ? _a : 0.5,
|
|
2428
|
+
top: (_b = area.top) != null ? _b : 0.5
|
|
2429
|
+
});
|
|
2430
|
+
}
|
|
2244
2431
|
}
|
|
2245
2432
|
},
|
|
2246
2433
|
{
|
|
@@ -2308,7 +2495,9 @@ var ImageTool = class {
|
|
|
2308
2495
|
if (!this.context) return;
|
|
2309
2496
|
this.isUpdatingConfig = true;
|
|
2310
2497
|
this.items = newItems;
|
|
2311
|
-
const configService = this.context.services.get(
|
|
2498
|
+
const configService = this.context.services.get(
|
|
2499
|
+
"ConfigurationService"
|
|
2500
|
+
);
|
|
2312
2501
|
if (configService) {
|
|
2313
2502
|
configService.update("image.items", newItems);
|
|
2314
2503
|
}
|
|
@@ -2354,53 +2543,12 @@ var ImageTool = class {
|
|
|
2354
2543
|
var _a, _b;
|
|
2355
2544
|
const canvasW = ((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 800;
|
|
2356
2545
|
const canvasH = ((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 600;
|
|
2357
|
-
let layoutScale = 1;
|
|
2358
|
-
let layoutOffsetX = 0;
|
|
2359
|
-
let layoutOffsetY = 0;
|
|
2360
|
-
let visualWidth = canvasW;
|
|
2361
|
-
let visualHeight = canvasH;
|
|
2362
|
-
let dielinePhysicalWidth = 500;
|
|
2363
|
-
let dielinePhysicalHeight = 500;
|
|
2364
|
-
let bleedOffset = 0;
|
|
2365
|
-
if (this.context) {
|
|
2366
|
-
const configService = this.context.services.get("ConfigurationService");
|
|
2367
|
-
if (configService) {
|
|
2368
|
-
dielinePhysicalWidth = configService.get("dieline.width") || 500;
|
|
2369
|
-
dielinePhysicalHeight = configService.get("dieline.height") || 500;
|
|
2370
|
-
bleedOffset = configService.get("dieline.offset") || 0;
|
|
2371
|
-
const paddingValue = configService.get("dieline.padding") || 40;
|
|
2372
|
-
let padding = 0;
|
|
2373
|
-
if (typeof paddingValue === "number") {
|
|
2374
|
-
padding = paddingValue;
|
|
2375
|
-
} else if (typeof paddingValue === "string") {
|
|
2376
|
-
if (paddingValue.endsWith("%")) {
|
|
2377
|
-
const percent = parseFloat(paddingValue) / 100;
|
|
2378
|
-
padding = Math.min(canvasW, canvasH) * percent;
|
|
2379
|
-
} else {
|
|
2380
|
-
padding = parseFloat(paddingValue) || 0;
|
|
2381
|
-
}
|
|
2382
|
-
}
|
|
2383
|
-
const layout = Coordinate.calculateLayout(
|
|
2384
|
-
{ width: canvasW, height: canvasH },
|
|
2385
|
-
{ width: dielinePhysicalWidth, height: dielinePhysicalHeight },
|
|
2386
|
-
padding
|
|
2387
|
-
);
|
|
2388
|
-
layoutScale = layout.scale;
|
|
2389
|
-
layoutOffsetX = layout.offsetX;
|
|
2390
|
-
layoutOffsetY = layout.offsetY;
|
|
2391
|
-
visualWidth = layout.width;
|
|
2392
|
-
visualHeight = layout.height;
|
|
2393
|
-
}
|
|
2394
|
-
}
|
|
2395
2546
|
return {
|
|
2396
|
-
layoutScale,
|
|
2397
|
-
layoutOffsetX,
|
|
2398
|
-
layoutOffsetY,
|
|
2399
|
-
visualWidth,
|
|
2400
|
-
visualHeight
|
|
2401
|
-
dielinePhysicalWidth,
|
|
2402
|
-
dielinePhysicalHeight,
|
|
2403
|
-
bleedOffset
|
|
2547
|
+
layoutScale: 1,
|
|
2548
|
+
layoutOffsetX: 0,
|
|
2549
|
+
layoutOffsetY: 0,
|
|
2550
|
+
visualWidth: canvasW,
|
|
2551
|
+
visualHeight: canvasH
|
|
2404
2552
|
};
|
|
2405
2553
|
}
|
|
2406
2554
|
updateImages() {
|
|
@@ -2423,8 +2571,8 @@ var ImageTool = class {
|
|
|
2423
2571
|
if (!obj) {
|
|
2424
2572
|
this.loadImage(item, layer, layout);
|
|
2425
2573
|
} else {
|
|
2426
|
-
this.updateObjectProperties(obj, item, layout);
|
|
2427
2574
|
layer.remove(obj);
|
|
2575
|
+
this.updateObjectProperties(obj, item, layout);
|
|
2428
2576
|
layer.add(obj);
|
|
2429
2577
|
}
|
|
2430
2578
|
});
|
|
@@ -2432,10 +2580,17 @@ var ImageTool = class {
|
|
|
2432
2580
|
this.canvasService.requestRenderAll();
|
|
2433
2581
|
}
|
|
2434
2582
|
updateObjectProperties(obj, item, layout) {
|
|
2435
|
-
const {
|
|
2583
|
+
const {
|
|
2584
|
+
layoutScale,
|
|
2585
|
+
layoutOffsetX,
|
|
2586
|
+
layoutOffsetY,
|
|
2587
|
+
visualWidth,
|
|
2588
|
+
visualHeight
|
|
2589
|
+
} = layout;
|
|
2436
2590
|
const updates = {};
|
|
2437
2591
|
if (obj.opacity !== item.opacity) updates.opacity = item.opacity;
|
|
2438
|
-
if (item.angle !== void 0 && obj.angle !== item.angle)
|
|
2592
|
+
if (item.angle !== void 0 && obj.angle !== item.angle)
|
|
2593
|
+
updates.angle = item.angle;
|
|
2439
2594
|
if (item.left !== void 0) {
|
|
2440
2595
|
const globalLeft = layoutOffsetX + item.left * visualWidth;
|
|
2441
2596
|
if (Math.abs(obj.left - globalLeft) > 1) updates.left = globalLeft;
|
|
@@ -2444,13 +2599,12 @@ var ImageTool = class {
|
|
|
2444
2599
|
const globalTop = layoutOffsetY + item.top * visualHeight;
|
|
2445
2600
|
if (Math.abs(obj.top - globalTop) > 1) updates.top = globalTop;
|
|
2446
2601
|
}
|
|
2447
|
-
if (item.
|
|
2448
|
-
const
|
|
2449
|
-
if (Math.abs(obj.scaleX -
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
if (Math.abs(obj.scaleY - targetScaleY) > 1e-3) updates.scaleY = targetScaleY;
|
|
2602
|
+
if (item.scale !== void 0) {
|
|
2603
|
+
const targetScale = item.scale * layoutScale;
|
|
2604
|
+
if (Math.abs(obj.scaleX - targetScale) > 1e-3) {
|
|
2605
|
+
updates.scaleX = targetScale;
|
|
2606
|
+
updates.scaleY = targetScale;
|
|
2607
|
+
}
|
|
2454
2608
|
}
|
|
2455
2609
|
if (obj.originX !== "center") {
|
|
2456
2610
|
updates.originX = "center";
|
|
@@ -2458,6 +2612,7 @@ var ImageTool = class {
|
|
|
2458
2612
|
}
|
|
2459
2613
|
if (Object.keys(updates).length > 0) {
|
|
2460
2614
|
obj.set(updates);
|
|
2615
|
+
obj.setCoords();
|
|
2461
2616
|
}
|
|
2462
2617
|
}
|
|
2463
2618
|
loadImage(item, layer, layout) {
|
|
@@ -2477,18 +2632,10 @@ var ImageTool = class {
|
|
|
2477
2632
|
ml: false,
|
|
2478
2633
|
mr: false
|
|
2479
2634
|
});
|
|
2480
|
-
let {
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
const targetHeight = dielinePhysicalHeight + 2 * bleedOffset;
|
|
2485
|
-
const targetMax = Math.max(targetWidth, targetHeight);
|
|
2486
|
-
const imageMax = Math.max(image.width || 1, image.height || 1);
|
|
2487
|
-
const scale = targetMax / imageMax;
|
|
2488
|
-
width = (image.width || 1) * scale;
|
|
2489
|
-
height = (image.height || 1) * scale;
|
|
2490
|
-
item.width = width;
|
|
2491
|
-
item.height = height;
|
|
2635
|
+
let { scale, left, top } = item;
|
|
2636
|
+
if (scale === void 0) {
|
|
2637
|
+
scale = 1;
|
|
2638
|
+
item.scale = scale;
|
|
2492
2639
|
}
|
|
2493
2640
|
if (left === void 0 && top === void 0) {
|
|
2494
2641
|
left = 0.5;
|
|
@@ -2499,13 +2646,18 @@ var ImageTool = class {
|
|
|
2499
2646
|
this.updateObjectProperties(image, item, layout);
|
|
2500
2647
|
layer.add(image);
|
|
2501
2648
|
this.objectMap.set(item.id, image);
|
|
2649
|
+
const resolver = this.loadResolvers.get(item.id);
|
|
2650
|
+
if (resolver) {
|
|
2651
|
+
resolver();
|
|
2652
|
+
this.loadResolvers.delete(item.id);
|
|
2653
|
+
}
|
|
2502
2654
|
image.on("modified", (e) => {
|
|
2503
2655
|
this.handleObjectModified(item.id, image);
|
|
2504
2656
|
});
|
|
2505
2657
|
layer.dirty = true;
|
|
2506
2658
|
(_a = this.canvasService) == null ? void 0 : _a.requestRenderAll();
|
|
2507
|
-
if (item.
|
|
2508
|
-
this.updateImageInConfig(item.id, {
|
|
2659
|
+
if (item.scale !== scale || item.left !== left || item.top !== top) {
|
|
2660
|
+
this.updateImageInConfig(item.id, { scale, left, top }, true);
|
|
2509
2661
|
}
|
|
2510
2662
|
}).catch((err) => {
|
|
2511
2663
|
console.error("Failed to load image", item.url, err);
|
|
@@ -2513,29 +2665,28 @@ var ImageTool = class {
|
|
|
2513
2665
|
}
|
|
2514
2666
|
handleObjectModified(id, image) {
|
|
2515
2667
|
const layout = this.getLayoutInfo();
|
|
2516
|
-
const {
|
|
2668
|
+
const {
|
|
2669
|
+
layoutScale,
|
|
2670
|
+
layoutOffsetX,
|
|
2671
|
+
layoutOffsetY,
|
|
2672
|
+
visualWidth,
|
|
2673
|
+
visualHeight
|
|
2674
|
+
} = layout;
|
|
2517
2675
|
const matrix = image.calcTransformMatrix();
|
|
2518
2676
|
const globalPoint = import_fabric5.util.transformPoint(new import_fabric5.Point(0, 0), matrix);
|
|
2519
2677
|
const updates = {};
|
|
2520
2678
|
updates.left = (globalPoint.x - layoutOffsetX) / visualWidth;
|
|
2521
2679
|
updates.top = (globalPoint.y - layoutOffsetY) / visualHeight;
|
|
2522
2680
|
updates.angle = image.angle;
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
updates.width = pixelWidth / layoutScale;
|
|
2526
|
-
}
|
|
2527
|
-
if (image.height) {
|
|
2528
|
-
const pixelHeight = image.height * image.scaleY;
|
|
2529
|
-
updates.height = pixelHeight / layoutScale;
|
|
2530
|
-
}
|
|
2531
|
-
this.updateImageInConfig(id, updates);
|
|
2681
|
+
updates.scale = image.scaleX / layoutScale;
|
|
2682
|
+
this.updateImageInConfig(id, updates, true);
|
|
2532
2683
|
}
|
|
2533
|
-
updateImageInConfig(id, updates) {
|
|
2684
|
+
updateImageInConfig(id, updates, skipCanvasUpdate = false) {
|
|
2534
2685
|
const index = this.items.findIndex((i) => i.id === id);
|
|
2535
2686
|
if (index !== -1) {
|
|
2536
2687
|
const newItems = [...this.items];
|
|
2537
2688
|
newItems[index] = { ...newItems[index], ...updates };
|
|
2538
|
-
this.updateConfig(newItems,
|
|
2689
|
+
this.updateConfig(newItems, skipCanvasUpdate);
|
|
2539
2690
|
}
|
|
2540
2691
|
}
|
|
2541
2692
|
};
|
|
@@ -3381,8 +3532,8 @@ var CanvasService = class {
|
|
|
3381
3532
|
BackgroundTool,
|
|
3382
3533
|
CanvasService,
|
|
3383
3534
|
DielineTool,
|
|
3535
|
+
FeatureTool,
|
|
3384
3536
|
FilmTool,
|
|
3385
|
-
HoleTool,
|
|
3386
3537
|
ImageTool,
|
|
3387
3538
|
MirrorTool,
|
|
3388
3539
|
RulerTool,
|