@pooder/kit 3.4.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 +6 -0
- package/dist/index.d.mts +51 -55
- package/dist/index.d.ts +51 -55
- package/dist/index.js +866 -858
- package/dist/index.mjs +865 -857
- 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 -471
- package/src/index.ts +1 -1
- package/src/mirror.ts +128 -128
- package/src/ruler.ts +500 -500
- package/src/tracer.ts +570 -486
- 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, _c, _d, _e;
|
|
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;
|
|
@@ -272,23 +273,37 @@ var ImageTracer = class {
|
|
|
272
273
|
Math.floor(Math.max(width, height) * 0.02)
|
|
273
274
|
);
|
|
274
275
|
const radius = (_b = options.morphologyRadius) != null ? _b : adaptiveRadius;
|
|
275
|
-
|
|
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);
|
|
276
281
|
if (radius > 0) {
|
|
277
|
-
mask = this.
|
|
278
|
-
mask = this.
|
|
279
|
-
|
|
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");
|
|
280
291
|
}
|
|
281
|
-
const allContourPoints = this.traceAllContours(mask,
|
|
292
|
+
const allContourPoints = this.traceAllContours(mask, paddedWidth, paddedHeight);
|
|
282
293
|
if (allContourPoints.length === 0) {
|
|
283
|
-
const w = (
|
|
284
|
-
const h = (
|
|
294
|
+
const w = (_d = options.scaleToWidth) != null ? _d : width;
|
|
295
|
+
const h = (_e = options.scaleToHeight) != null ? _e : height;
|
|
285
296
|
return `M 0 0 L ${w} 0 L ${w} ${h} L 0 ${h} Z`;
|
|
286
297
|
}
|
|
287
298
|
const primaryContour = allContourPoints.sort(
|
|
288
299
|
(a, b) => b.length - a.length
|
|
289
300
|
)[0];
|
|
301
|
+
const unpaddedPoints = primaryContour.map((p) => ({
|
|
302
|
+
x: p.x - padding,
|
|
303
|
+
y: p.y - padding
|
|
304
|
+
}));
|
|
290
305
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
291
|
-
for (const p of
|
|
306
|
+
for (const p of unpaddedPoints) {
|
|
292
307
|
if (p.x < minX) minX = p.x;
|
|
293
308
|
if (p.y < minY) minY = p.y;
|
|
294
309
|
if (p.x > maxX) maxX = p.x;
|
|
@@ -300,95 +315,119 @@ var ImageTracer = class {
|
|
|
300
315
|
width: maxX - minX,
|
|
301
316
|
height: maxY - minY
|
|
302
317
|
};
|
|
303
|
-
let finalPoints =
|
|
318
|
+
let finalPoints = unpaddedPoints;
|
|
304
319
|
if (options.scaleToWidth && options.scaleToHeight) {
|
|
305
320
|
finalPoints = this.scalePoints(
|
|
306
|
-
|
|
321
|
+
unpaddedPoints,
|
|
307
322
|
options.scaleToWidth,
|
|
308
323
|
options.scaleToHeight,
|
|
309
324
|
globalBounds
|
|
310
325
|
);
|
|
311
326
|
}
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
(
|
|
315
|
-
|
|
316
|
-
|
|
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
|
+
}
|
|
317
337
|
}
|
|
318
|
-
static createMask(imageData, threshold) {
|
|
338
|
+
static createMask(imageData, threshold, padding, paddedWidth, paddedHeight) {
|
|
319
339
|
const { width, height, data } = imageData;
|
|
320
|
-
const mask = new Uint8Array(
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
const a = data[idx + 3];
|
|
327
|
-
if (a > threshold && !(r > 240 && g > 240 && b > 240)) {
|
|
328
|
-
mask[i] = 1;
|
|
329
|
-
} else {
|
|
330
|
-
mask[i] = 0;
|
|
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;
|
|
331
346
|
}
|
|
332
347
|
}
|
|
333
|
-
return mask;
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Fast 1D-separable Dilation
|
|
337
|
-
*/
|
|
338
|
-
static dilate(mask, width, height, radius) {
|
|
339
|
-
const horizontal = new Uint8Array(width * height);
|
|
340
348
|
for (let y = 0; y < height; y++) {
|
|
341
|
-
let
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
+
}
|
|
357
365
|
}
|
|
358
366
|
}
|
|
359
|
-
return
|
|
367
|
+
return mask;
|
|
360
368
|
}
|
|
361
369
|
/**
|
|
362
|
-
* Fast
|
|
370
|
+
* Fast circular morphology using a distance-transform inspired separable approach.
|
|
371
|
+
* O(N * R) complexity, where R is the radius.
|
|
363
372
|
*/
|
|
364
|
-
static
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
let
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
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
|
+
);
|
|
374
389
|
}
|
|
375
390
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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;
|
|
388
407
|
}
|
|
389
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;
|
|
390
430
|
}
|
|
391
|
-
return vertical;
|
|
392
431
|
}
|
|
393
432
|
/**
|
|
394
433
|
* Fills internal holes in the binary mask using flood fill from edges.
|
|
@@ -588,6 +627,23 @@ var ImageTracer = class {
|
|
|
588
627
|
const tail = points.slice(1);
|
|
589
628
|
return `M ${head.x} ${head.y} ` + tail.map((p) => `L ${p.x} ${p.y}`).join(" ") + " Z";
|
|
590
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
|
+
}
|
|
591
647
|
};
|
|
592
648
|
|
|
593
649
|
// src/coordinate.ts
|
|
@@ -662,96 +718,45 @@ var Coordinate = class {
|
|
|
662
718
|
};
|
|
663
719
|
|
|
664
720
|
// src/geometry.ts
|
|
665
|
-
var
|
|
666
|
-
function
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
const bottom = y + height / 2;
|
|
675
|
-
switch (hole.anchor) {
|
|
676
|
-
case "top-left":
|
|
677
|
-
bx = left;
|
|
678
|
-
by = top;
|
|
679
|
-
break;
|
|
680
|
-
case "top-center":
|
|
681
|
-
bx = x;
|
|
682
|
-
by = top;
|
|
683
|
-
break;
|
|
684
|
-
case "top-right":
|
|
685
|
-
bx = right;
|
|
686
|
-
by = top;
|
|
687
|
-
break;
|
|
688
|
-
case "center-left":
|
|
689
|
-
bx = left;
|
|
690
|
-
by = y;
|
|
691
|
-
break;
|
|
692
|
-
case "center":
|
|
693
|
-
bx = x;
|
|
694
|
-
by = y;
|
|
695
|
-
break;
|
|
696
|
-
case "center-right":
|
|
697
|
-
bx = right;
|
|
698
|
-
by = y;
|
|
699
|
-
break;
|
|
700
|
-
case "bottom-left":
|
|
701
|
-
bx = left;
|
|
702
|
-
by = bottom;
|
|
703
|
-
break;
|
|
704
|
-
case "bottom-center":
|
|
705
|
-
bx = x;
|
|
706
|
-
by = bottom;
|
|
707
|
-
break;
|
|
708
|
-
case "bottom-right":
|
|
709
|
-
bx = right;
|
|
710
|
-
by = bottom;
|
|
711
|
-
break;
|
|
712
|
-
}
|
|
713
|
-
return {
|
|
714
|
-
x: bx + (hole.offsetX || 0),
|
|
715
|
-
y: by + (hole.offsetY || 0)
|
|
716
|
-
};
|
|
717
|
-
} else if (hole.x !== void 0 && hole.y !== void 0) {
|
|
718
|
-
const { x, width, y, height } = geometry;
|
|
719
|
-
return {
|
|
720
|
-
x: hole.x * width + (x - width / 2) + (hole.offsetX || 0),
|
|
721
|
-
y: hole.y * height + (y - height / 2) + (hole.offsetY || 0)
|
|
722
|
-
};
|
|
723
|
-
}
|
|
724
|
-
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
|
+
};
|
|
725
730
|
}
|
|
726
731
|
function ensurePaper(width, height) {
|
|
727
|
-
if (!
|
|
728
|
-
|
|
732
|
+
if (!import_paper2.default.project) {
|
|
733
|
+
import_paper2.default.setup(new import_paper2.default.Size(width, height));
|
|
729
734
|
} else {
|
|
730
|
-
|
|
735
|
+
import_paper2.default.view.viewSize = new import_paper2.default.Size(width, height);
|
|
731
736
|
}
|
|
732
737
|
}
|
|
733
738
|
function createBaseShape(options) {
|
|
734
739
|
const { shape, width, height, radius, x, y, pathData } = options;
|
|
735
|
-
const center = new
|
|
740
|
+
const center = new import_paper2.default.Point(x, y);
|
|
736
741
|
if (shape === "rect") {
|
|
737
|
-
return new
|
|
742
|
+
return new import_paper2.default.Path.Rectangle({
|
|
738
743
|
point: [x - width / 2, y - height / 2],
|
|
739
744
|
size: [Math.max(0, width), Math.max(0, height)],
|
|
740
745
|
radius: Math.max(0, radius)
|
|
741
746
|
});
|
|
742
747
|
} else if (shape === "circle") {
|
|
743
748
|
const r = Math.min(width, height) / 2;
|
|
744
|
-
return new
|
|
749
|
+
return new import_paper2.default.Path.Circle({
|
|
745
750
|
center,
|
|
746
751
|
radius: Math.max(0, r)
|
|
747
752
|
});
|
|
748
753
|
} else if (shape === "ellipse") {
|
|
749
|
-
return new
|
|
754
|
+
return new import_paper2.default.Path.Ellipse({
|
|
750
755
|
center,
|
|
751
756
|
radius: [Math.max(0, width / 2), Math.max(0, height / 2)]
|
|
752
757
|
});
|
|
753
758
|
} else if (shape === "custom" && pathData) {
|
|
754
|
-
const path = new
|
|
759
|
+
const path = new import_paper2.default.Path();
|
|
755
760
|
path.pathData = pathData;
|
|
756
761
|
path.position = center;
|
|
757
762
|
if (width > 0 && height > 0 && path.bounds.width > 0 && path.bounds.height > 0) {
|
|
@@ -759,89 +764,76 @@ function createBaseShape(options) {
|
|
|
759
764
|
}
|
|
760
765
|
return path;
|
|
761
766
|
} else {
|
|
762
|
-
return new
|
|
767
|
+
return new import_paper2.default.Path.Rectangle({
|
|
763
768
|
point: [x - width / 2, y - height / 2],
|
|
764
769
|
size: [Math.max(0, width), Math.max(0, height)]
|
|
765
770
|
});
|
|
766
771
|
}
|
|
767
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
|
+
}
|
|
768
796
|
function getDielineShape(options) {
|
|
769
797
|
let mainShape = createBaseShape(options);
|
|
770
|
-
const {
|
|
771
|
-
if (
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
const
|
|
776
|
-
const
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
],
|
|
781
|
-
size: [hole.outerRadius * 2, hole.outerRadius * 2]
|
|
782
|
-
}) : new import_paper.default.Path.Circle({
|
|
783
|
-
center,
|
|
784
|
-
radius: hole.outerRadius
|
|
785
|
-
});
|
|
786
|
-
const cut = hole.shape === "square" ? new import_paper.default.Path.Rectangle({
|
|
787
|
-
point: [
|
|
788
|
-
center.x - hole.innerRadius,
|
|
789
|
-
center.y - hole.innerRadius
|
|
790
|
-
],
|
|
791
|
-
size: [hole.innerRadius * 2, hole.innerRadius * 2]
|
|
792
|
-
}) : new import_paper.default.Path.Circle({
|
|
793
|
-
center,
|
|
794
|
-
radius: hole.innerRadius
|
|
795
|
-
});
|
|
796
|
-
if (!lugsPath) {
|
|
797
|
-
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);
|
|
798
808
|
} else {
|
|
809
|
+
subtracts.push(item);
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
if (adds.length > 0) {
|
|
813
|
+
for (const item of adds) {
|
|
799
814
|
try {
|
|
800
|
-
const temp =
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
815
|
+
const temp = mainShape.unite(item);
|
|
816
|
+
mainShape.remove();
|
|
817
|
+
item.remove();
|
|
818
|
+
mainShape = temp;
|
|
804
819
|
} catch (e) {
|
|
805
|
-
console.error("Geometry: Failed to unite
|
|
806
|
-
|
|
820
|
+
console.error("Geometry: Failed to unite feature", e);
|
|
821
|
+
item.remove();
|
|
807
822
|
}
|
|
808
823
|
}
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
824
|
+
}
|
|
825
|
+
if (subtracts.length > 0) {
|
|
826
|
+
for (const item of subtracts) {
|
|
812
827
|
try {
|
|
813
|
-
const temp =
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
828
|
+
const temp = mainShape.subtract(item);
|
|
829
|
+
mainShape.remove();
|
|
830
|
+
item.remove();
|
|
831
|
+
mainShape = temp;
|
|
817
832
|
} catch (e) {
|
|
818
|
-
console.error("Geometry: Failed to
|
|
819
|
-
|
|
833
|
+
console.error("Geometry: Failed to subtract feature", e);
|
|
834
|
+
item.remove();
|
|
820
835
|
}
|
|
821
836
|
}
|
|
822
|
-
});
|
|
823
|
-
if (lugsPath) {
|
|
824
|
-
try {
|
|
825
|
-
const temp = mainShape.unite(lugsPath);
|
|
826
|
-
mainShape.remove();
|
|
827
|
-
lugsPath.remove();
|
|
828
|
-
mainShape = temp;
|
|
829
|
-
} catch (e) {
|
|
830
|
-
console.error("Geometry: Failed to unite lugsPath to mainShape", e);
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
if (cutsPath) {
|
|
834
|
-
try {
|
|
835
|
-
const temp = mainShape.subtract(cutsPath);
|
|
836
|
-
mainShape.remove();
|
|
837
|
-
cutsPath.remove();
|
|
838
|
-
mainShape = temp;
|
|
839
|
-
} catch (e) {
|
|
840
|
-
console.error(
|
|
841
|
-
"Geometry: Failed to subtract cutsPath from mainShape",
|
|
842
|
-
e
|
|
843
|
-
);
|
|
844
|
-
}
|
|
845
837
|
}
|
|
846
838
|
}
|
|
847
839
|
return mainShape;
|
|
@@ -850,7 +842,7 @@ function generateDielinePath(options) {
|
|
|
850
842
|
const paperWidth = options.canvasWidth || options.width * 2 || 2e3;
|
|
851
843
|
const paperHeight = options.canvasHeight || options.height * 2 || 2e3;
|
|
852
844
|
ensurePaper(paperWidth, paperHeight);
|
|
853
|
-
|
|
845
|
+
import_paper2.default.project.activeLayer.removeChildren();
|
|
854
846
|
const mainShape = getDielineShape(options);
|
|
855
847
|
const pathData = mainShape.pathData;
|
|
856
848
|
mainShape.remove();
|
|
@@ -858,9 +850,9 @@ function generateDielinePath(options) {
|
|
|
858
850
|
}
|
|
859
851
|
function generateMaskPath(options) {
|
|
860
852
|
ensurePaper(options.canvasWidth, options.canvasHeight);
|
|
861
|
-
|
|
853
|
+
import_paper2.default.project.activeLayer.removeChildren();
|
|
862
854
|
const { canvasWidth, canvasHeight } = options;
|
|
863
|
-
const maskRect = new
|
|
855
|
+
const maskRect = new import_paper2.default.Path.Rectangle({
|
|
864
856
|
point: [0, 0],
|
|
865
857
|
size: [canvasWidth, canvasHeight]
|
|
866
858
|
});
|
|
@@ -872,43 +864,13 @@ function generateMaskPath(options) {
|
|
|
872
864
|
finalMask.remove();
|
|
873
865
|
return pathData;
|
|
874
866
|
}
|
|
875
|
-
function generateBleedZonePath(
|
|
876
|
-
const paperWidth =
|
|
877
|
-
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;
|
|
878
870
|
ensurePaper(paperWidth, paperHeight);
|
|
879
|
-
|
|
880
|
-
const shapeOriginal = getDielineShape(
|
|
881
|
-
|
|
882
|
-
if (options.shape === "custom") {
|
|
883
|
-
const stroker = shapeOriginal.clone();
|
|
884
|
-
stroker.strokeColor = new import_paper.default.Color("black");
|
|
885
|
-
stroker.strokeWidth = Math.abs(offset) * 2;
|
|
886
|
-
stroker.strokeJoin = "round";
|
|
887
|
-
stroker.strokeCap = "round";
|
|
888
|
-
let expanded;
|
|
889
|
-
try {
|
|
890
|
-
expanded = stroker.expand({ stroke: true, fill: false, insert: false });
|
|
891
|
-
} catch (e) {
|
|
892
|
-
stroker.remove();
|
|
893
|
-
shapeOffset = shapeOriginal.clone();
|
|
894
|
-
return shapeOffset.pathData;
|
|
895
|
-
}
|
|
896
|
-
stroker.remove();
|
|
897
|
-
if (offset > 0) {
|
|
898
|
-
shapeOffset = shapeOriginal.unite(expanded);
|
|
899
|
-
} else {
|
|
900
|
-
shapeOffset = shapeOriginal.subtract(expanded);
|
|
901
|
-
}
|
|
902
|
-
expanded.remove();
|
|
903
|
-
} else {
|
|
904
|
-
const offsetOptions = {
|
|
905
|
-
...options,
|
|
906
|
-
width: Math.max(0, options.width + offset * 2),
|
|
907
|
-
height: Math.max(0, options.height + offset * 2),
|
|
908
|
-
radius: options.radius === 0 ? 0 : Math.max(0, options.radius + offset)
|
|
909
|
-
};
|
|
910
|
-
shapeOffset = getDielineShape(offsetOptions);
|
|
911
|
-
}
|
|
871
|
+
import_paper2.default.project.activeLayer.removeChildren();
|
|
872
|
+
const shapeOriginal = getDielineShape(originalOptions);
|
|
873
|
+
const shapeOffset = getDielineShape(offsetOptions);
|
|
912
874
|
let bleedZone;
|
|
913
875
|
if (offset > 0) {
|
|
914
876
|
bleedZone = shapeOffset.subtract(shapeOriginal);
|
|
@@ -923,16 +885,16 @@ function generateBleedZonePath(options, offset) {
|
|
|
923
885
|
}
|
|
924
886
|
function getNearestPointOnDieline(point, options) {
|
|
925
887
|
ensurePaper(options.width * 2, options.height * 2);
|
|
926
|
-
|
|
888
|
+
import_paper2.default.project.activeLayer.removeChildren();
|
|
927
889
|
const shape = createBaseShape(options);
|
|
928
|
-
const p = new
|
|
890
|
+
const p = new import_paper2.default.Point(point.x, point.y);
|
|
929
891
|
const nearest = shape.getNearestPoint(p);
|
|
930
892
|
const result = { x: nearest.x, y: nearest.y };
|
|
931
893
|
shape.remove();
|
|
932
894
|
return result;
|
|
933
895
|
}
|
|
934
896
|
function getPathBounds(pathData) {
|
|
935
|
-
const path = new
|
|
897
|
+
const path = new import_paper2.default.Path();
|
|
936
898
|
path.pathData = pathData;
|
|
937
899
|
const bounds = path.bounds;
|
|
938
900
|
path.remove();
|
|
@@ -951,20 +913,41 @@ var DielineTool = class {
|
|
|
951
913
|
this.metadata = {
|
|
952
914
|
name: "DielineTool"
|
|
953
915
|
};
|
|
954
|
-
this.
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
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
|
+
};
|
|
966
941
|
if (options) {
|
|
967
|
-
|
|
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);
|
|
968
951
|
}
|
|
969
952
|
}
|
|
970
953
|
activate(context) {
|
|
@@ -976,38 +959,93 @@ var DielineTool = class {
|
|
|
976
959
|
}
|
|
977
960
|
const configService = context.services.get("ConfigurationService");
|
|
978
961
|
if (configService) {
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
);
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
);
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
);
|
|
999
|
-
|
|
1000
|
-
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);
|
|
1001
983
|
configService.onAnyChange((e) => {
|
|
1002
984
|
if (e.key.startsWith("dieline.")) {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
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;
|
|
1010
1047
|
}
|
|
1048
|
+
this.updateDieline();
|
|
1011
1049
|
}
|
|
1012
1050
|
});
|
|
1013
1051
|
}
|
|
@@ -1020,6 +1058,7 @@ var DielineTool = class {
|
|
|
1020
1058
|
this.context = void 0;
|
|
1021
1059
|
}
|
|
1022
1060
|
contribute() {
|
|
1061
|
+
const s = this.state;
|
|
1023
1062
|
return {
|
|
1024
1063
|
[import_core2.ContributionPointIds.CONFIGURATIONS]: [
|
|
1025
1064
|
{
|
|
@@ -1027,14 +1066,14 @@ var DielineTool = class {
|
|
|
1027
1066
|
type: "select",
|
|
1028
1067
|
label: "Unit",
|
|
1029
1068
|
options: ["px", "mm", "cm", "in"],
|
|
1030
|
-
default:
|
|
1069
|
+
default: s.unit
|
|
1031
1070
|
},
|
|
1032
1071
|
{
|
|
1033
1072
|
id: "dieline.shape",
|
|
1034
1073
|
type: "select",
|
|
1035
1074
|
label: "Shape",
|
|
1036
1075
|
options: ["rect", "circle", "ellipse", "custom"],
|
|
1037
|
-
default:
|
|
1076
|
+
default: s.shape
|
|
1038
1077
|
},
|
|
1039
1078
|
{
|
|
1040
1079
|
id: "dieline.width",
|
|
@@ -1042,7 +1081,7 @@ var DielineTool = class {
|
|
|
1042
1081
|
label: "Width",
|
|
1043
1082
|
min: 10,
|
|
1044
1083
|
max: 2e3,
|
|
1045
|
-
default:
|
|
1084
|
+
default: s.width
|
|
1046
1085
|
},
|
|
1047
1086
|
{
|
|
1048
1087
|
id: "dieline.height",
|
|
@@ -1050,7 +1089,7 @@ var DielineTool = class {
|
|
|
1050
1089
|
label: "Height",
|
|
1051
1090
|
min: 10,
|
|
1052
1091
|
max: 2e3,
|
|
1053
|
-
default:
|
|
1092
|
+
default: s.height
|
|
1054
1093
|
},
|
|
1055
1094
|
{
|
|
1056
1095
|
id: "dieline.radius",
|
|
@@ -1058,20 +1097,14 @@ var DielineTool = class {
|
|
|
1058
1097
|
label: "Corner Radius",
|
|
1059
1098
|
min: 0,
|
|
1060
1099
|
max: 500,
|
|
1061
|
-
default:
|
|
1062
|
-
},
|
|
1063
|
-
{
|
|
1064
|
-
id: "dieline.position",
|
|
1065
|
-
type: "json",
|
|
1066
|
-
label: "Position (Normalized)",
|
|
1067
|
-
default: this.radius
|
|
1100
|
+
default: s.radius
|
|
1068
1101
|
},
|
|
1069
1102
|
{
|
|
1070
1103
|
id: "dieline.padding",
|
|
1071
1104
|
type: "select",
|
|
1072
1105
|
label: "View Padding",
|
|
1073
1106
|
options: [0, 10, 20, 40, 60, 100, "2%", "5%", "10%", "15%", "20%"],
|
|
1074
|
-
default:
|
|
1107
|
+
default: s.padding
|
|
1075
1108
|
},
|
|
1076
1109
|
{
|
|
1077
1110
|
id: "dieline.offset",
|
|
@@ -1079,38 +1112,91 @@ var DielineTool = class {
|
|
|
1079
1112
|
label: "Bleed Offset",
|
|
1080
1113
|
min: -100,
|
|
1081
1114
|
max: 100,
|
|
1082
|
-
default:
|
|
1115
|
+
default: s.offset
|
|
1083
1116
|
},
|
|
1084
1117
|
{
|
|
1085
1118
|
id: "dieline.showBleedLines",
|
|
1086
1119
|
type: "boolean",
|
|
1087
1120
|
label: "Show Bleed Lines",
|
|
1088
|
-
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
|
|
1089
1145
|
},
|
|
1090
1146
|
{
|
|
1091
1147
|
id: "dieline.style",
|
|
1092
1148
|
type: "select",
|
|
1093
1149
|
label: "Line Style",
|
|
1094
|
-
options: ["solid", "dashed"],
|
|
1095
|
-
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
|
|
1096
1182
|
},
|
|
1097
1183
|
{
|
|
1098
1184
|
id: "dieline.insideColor",
|
|
1099
1185
|
type: "color",
|
|
1100
1186
|
label: "Inside Color",
|
|
1101
|
-
default:
|
|
1187
|
+
default: s.insideColor
|
|
1102
1188
|
},
|
|
1103
1189
|
{
|
|
1104
1190
|
id: "dieline.outsideColor",
|
|
1105
1191
|
type: "color",
|
|
1106
1192
|
label: "Outside Color",
|
|
1107
|
-
default:
|
|
1193
|
+
default: s.outsideColor
|
|
1108
1194
|
},
|
|
1109
1195
|
{
|
|
1110
|
-
id: "dieline.
|
|
1196
|
+
id: "dieline.features",
|
|
1111
1197
|
type: "json",
|
|
1112
|
-
label: "
|
|
1113
|
-
default:
|
|
1198
|
+
label: "Edge Features",
|
|
1199
|
+
default: s.features
|
|
1114
1200
|
}
|
|
1115
1201
|
],
|
|
1116
1202
|
[import_core2.ContributionPointIds.COMMANDS]: [
|
|
@@ -1132,24 +1218,18 @@ var DielineTool = class {
|
|
|
1132
1218
|
command: "detectEdge",
|
|
1133
1219
|
title: "Detect Edge from Image",
|
|
1134
1220
|
handler: async (imageUrl, options) => {
|
|
1135
|
-
var _a;
|
|
1136
1221
|
try {
|
|
1137
1222
|
const pathData = await ImageTracer.trace(imageUrl, options);
|
|
1138
1223
|
const bounds = getPathBounds(pathData);
|
|
1139
|
-
const currentMax = Math.max(
|
|
1224
|
+
const currentMax = Math.max(s.width, s.height);
|
|
1140
1225
|
const scale = currentMax / Math.max(bounds.width, bounds.height);
|
|
1141
1226
|
const newWidth = bounds.width * scale;
|
|
1142
1227
|
const newHeight = bounds.height * scale;
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
configService.update("dieline.height", newHeight);
|
|
1149
|
-
configService.update("dieline.shape", "custom");
|
|
1150
|
-
configService.update("dieline.pathData", pathData);
|
|
1151
|
-
}
|
|
1152
|
-
return pathData;
|
|
1228
|
+
return {
|
|
1229
|
+
pathData,
|
|
1230
|
+
width: newWidth,
|
|
1231
|
+
height: newHeight
|
|
1232
|
+
};
|
|
1153
1233
|
} catch (e) {
|
|
1154
1234
|
console.error("Edge detection failed", e);
|
|
1155
1235
|
throw e;
|
|
@@ -1208,15 +1288,15 @@ var DielineTool = class {
|
|
|
1208
1288
|
return new import_fabric2.Pattern({ source: canvas, repetition: "repeat" });
|
|
1209
1289
|
}
|
|
1210
1290
|
resolvePadding(containerWidth, containerHeight) {
|
|
1211
|
-
if (typeof this.padding === "number") {
|
|
1212
|
-
return this.padding;
|
|
1291
|
+
if (typeof this.state.padding === "number") {
|
|
1292
|
+
return this.state.padding;
|
|
1213
1293
|
}
|
|
1214
|
-
if (typeof this.padding === "string") {
|
|
1215
|
-
if (this.padding.endsWith("%")) {
|
|
1216
|
-
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;
|
|
1217
1297
|
return Math.min(containerWidth, containerHeight) * percent;
|
|
1218
1298
|
}
|
|
1219
|
-
return parseFloat(this.padding) || 0;
|
|
1299
|
+
return parseFloat(this.state.padding) || 0;
|
|
1220
1300
|
}
|
|
1221
1301
|
return 0;
|
|
1222
1302
|
}
|
|
@@ -1229,14 +1309,14 @@ var DielineTool = class {
|
|
|
1229
1309
|
shape,
|
|
1230
1310
|
radius,
|
|
1231
1311
|
offset,
|
|
1232
|
-
|
|
1312
|
+
mainLine,
|
|
1313
|
+
offsetLine,
|
|
1233
1314
|
insideColor,
|
|
1234
1315
|
outsideColor,
|
|
1235
|
-
position,
|
|
1236
1316
|
showBleedLines,
|
|
1237
|
-
|
|
1238
|
-
} = this;
|
|
1239
|
-
let { width, height } = this;
|
|
1317
|
+
features
|
|
1318
|
+
} = this.state;
|
|
1319
|
+
let { width, height } = this.state;
|
|
1240
1320
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
1241
1321
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
1242
1322
|
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
@@ -1253,40 +1333,27 @@ var DielineTool = class {
|
|
|
1253
1333
|
const visualRadius = radius * scale;
|
|
1254
1334
|
const visualOffset = offset * scale;
|
|
1255
1335
|
layer.remove(...layer.getObjects());
|
|
1256
|
-
const
|
|
1257
|
-
|
|
1258
|
-
y: cy,
|
|
1259
|
-
width: visualWidth,
|
|
1260
|
-
height: visualHeight
|
|
1261
|
-
// Pass scale/unit context if needed by resolveHolePosition (though currently unused there)
|
|
1262
|
-
};
|
|
1263
|
-
const absoluteHoles = (holes || []).map((h) => {
|
|
1264
|
-
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1265
|
-
const offsetScale = unitScale * scale;
|
|
1266
|
-
const hWithPixelOffsets = {
|
|
1267
|
-
...h,
|
|
1268
|
-
offsetX: (h.offsetX || 0) * offsetScale,
|
|
1269
|
-
offsetY: (h.offsetY || 0) * offsetScale
|
|
1270
|
-
};
|
|
1271
|
-
const pos = resolveHolePosition(hWithPixelOffsets, geometryForHoles, {
|
|
1272
|
-
width: canvasW,
|
|
1273
|
-
height: canvasH
|
|
1274
|
-
});
|
|
1336
|
+
const absoluteFeatures = (features || []).map((f) => {
|
|
1337
|
+
const featureScale = scale;
|
|
1275
1338
|
return {
|
|
1276
|
-
...
|
|
1277
|
-
x:
|
|
1278
|
-
y:
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
// Store scaled offsets in the result for consistency, though pos is already resolved
|
|
1283
|
-
offsetX: hWithPixelOffsets.offsetX,
|
|
1284
|
-
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
|
|
1285
1345
|
};
|
|
1286
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
|
+
);
|
|
1287
1353
|
const cutW = Math.max(0, visualWidth + visualOffset * 2);
|
|
1288
1354
|
const cutH = Math.max(0, visualHeight + visualOffset * 2);
|
|
1289
1355
|
const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
1356
|
+
const maskFeatures = visualOffset !== 0 ? offsetFeatures : originalFeatures;
|
|
1290
1357
|
const maskPathData = generateMaskPath({
|
|
1291
1358
|
canvasWidth: canvasW,
|
|
1292
1359
|
canvasHeight: canvasH,
|
|
@@ -1296,8 +1363,8 @@ var DielineTool = class {
|
|
|
1296
1363
|
radius: cutR,
|
|
1297
1364
|
x: cx,
|
|
1298
1365
|
y: cy,
|
|
1299
|
-
|
|
1300
|
-
pathData: this.pathData
|
|
1366
|
+
features: maskFeatures,
|
|
1367
|
+
pathData: this.state.pathData
|
|
1301
1368
|
});
|
|
1302
1369
|
const mask = new import_fabric2.Path(maskPathData, {
|
|
1303
1370
|
fill: outsideColor,
|
|
@@ -1318,8 +1385,9 @@ var DielineTool = class {
|
|
|
1318
1385
|
radius: cutR,
|
|
1319
1386
|
x: cx,
|
|
1320
1387
|
y: cy,
|
|
1321
|
-
|
|
1322
|
-
|
|
1388
|
+
features: maskFeatures,
|
|
1389
|
+
// Use same features as mask for consistency
|
|
1390
|
+
pathData: this.state.pathData,
|
|
1323
1391
|
canvasWidth: canvasW,
|
|
1324
1392
|
canvasHeight: canvasH
|
|
1325
1393
|
});
|
|
@@ -1343,15 +1411,27 @@ var DielineTool = class {
|
|
|
1343
1411
|
radius: visualRadius,
|
|
1344
1412
|
x: cx,
|
|
1345
1413
|
y: cy,
|
|
1346
|
-
|
|
1347
|
-
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,
|
|
1348
1428
|
canvasWidth: canvasW,
|
|
1349
1429
|
canvasHeight: canvasH
|
|
1350
1430
|
},
|
|
1351
1431
|
visualOffset
|
|
1352
1432
|
);
|
|
1353
1433
|
if (showBleedLines !== false) {
|
|
1354
|
-
const pattern = this.createHatchPattern(
|
|
1434
|
+
const pattern = this.createHatchPattern(mainLine.color);
|
|
1355
1435
|
if (pattern) {
|
|
1356
1436
|
const bleedObj = new import_fabric2.Path(bleedPathData, {
|
|
1357
1437
|
fill: pattern,
|
|
@@ -1372,18 +1452,16 @@ var DielineTool = class {
|
|
|
1372
1452
|
radius: cutR,
|
|
1373
1453
|
x: cx,
|
|
1374
1454
|
y: cy,
|
|
1375
|
-
|
|
1376
|
-
pathData: this.pathData,
|
|
1455
|
+
features: offsetFeatures,
|
|
1456
|
+
pathData: this.state.pathData,
|
|
1377
1457
|
canvasWidth: canvasW,
|
|
1378
1458
|
canvasHeight: canvasH
|
|
1379
1459
|
});
|
|
1380
1460
|
const offsetBorderObj = new import_fabric2.Path(offsetPathData, {
|
|
1381
1461
|
fill: null,
|
|
1382
|
-
stroke: "
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
strokeDashArray: [4, 4],
|
|
1386
|
-
// Dashed
|
|
1462
|
+
stroke: offsetLine.style === "hidden" ? null : offsetLine.color,
|
|
1463
|
+
strokeWidth: offsetLine.width,
|
|
1464
|
+
strokeDashArray: offsetLine.style === "dashed" ? [offsetLine.dashLength, offsetLine.dashLength] : void 0,
|
|
1387
1465
|
selectable: false,
|
|
1388
1466
|
evented: false,
|
|
1389
1467
|
originX: "left",
|
|
@@ -1398,16 +1476,16 @@ var DielineTool = class {
|
|
|
1398
1476
|
radius: visualRadius,
|
|
1399
1477
|
x: cx,
|
|
1400
1478
|
y: cy,
|
|
1401
|
-
|
|
1402
|
-
pathData: this.pathData,
|
|
1479
|
+
features: originalFeatures,
|
|
1480
|
+
pathData: this.state.pathData,
|
|
1403
1481
|
canvasWidth: canvasW,
|
|
1404
1482
|
canvasHeight: canvasH
|
|
1405
1483
|
});
|
|
1406
1484
|
const borderObj = new import_fabric2.Path(borderPathData, {
|
|
1407
1485
|
fill: "transparent",
|
|
1408
|
-
stroke: "
|
|
1409
|
-
strokeWidth:
|
|
1410
|
-
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,
|
|
1411
1489
|
selectable: false,
|
|
1412
1490
|
evented: false,
|
|
1413
1491
|
originX: "left",
|
|
@@ -1439,7 +1517,7 @@ var DielineTool = class {
|
|
|
1439
1517
|
}
|
|
1440
1518
|
getGeometry() {
|
|
1441
1519
|
if (!this.canvasService) return null;
|
|
1442
|
-
const { unit, shape, width, height, radius,
|
|
1520
|
+
const { unit, shape, width, height, radius, offset, mainLine, pathData } = this.state;
|
|
1443
1521
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
1444
1522
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
1445
1523
|
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
@@ -1462,16 +1540,17 @@ var DielineTool = class {
|
|
|
1462
1540
|
height: visualHeight,
|
|
1463
1541
|
radius: radius * scale,
|
|
1464
1542
|
offset: offset * scale,
|
|
1465
|
-
// Pass scale to help other tools (like
|
|
1543
|
+
// Pass scale to help other tools (like FeatureTool) convert units
|
|
1466
1544
|
scale,
|
|
1467
|
-
|
|
1545
|
+
strokeWidth: mainLine.width,
|
|
1546
|
+
pathData
|
|
1468
1547
|
};
|
|
1469
1548
|
}
|
|
1470
1549
|
async exportCutImage() {
|
|
1471
1550
|
if (!this.canvasService) return null;
|
|
1472
1551
|
const userLayer = this.canvasService.getLayer("user");
|
|
1473
1552
|
if (!userLayer) return null;
|
|
1474
|
-
const { shape, width, height, radius,
|
|
1553
|
+
const { shape, width, height, radius, features, unit, pathData } = this.state;
|
|
1475
1554
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
1476
1555
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
1477
1556
|
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
@@ -1486,55 +1565,45 @@ var DielineTool = class {
|
|
|
1486
1565
|
const visualWidth = layout.width;
|
|
1487
1566
|
const visualHeight = layout.height;
|
|
1488
1567
|
const visualRadius = radius * scale;
|
|
1489
|
-
const
|
|
1490
|
-
const
|
|
1491
|
-
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1492
|
-
const pos = resolveHolePosition(
|
|
1493
|
-
{
|
|
1494
|
-
...h,
|
|
1495
|
-
offsetX: (h.offsetX || 0) * unitScale * scale,
|
|
1496
|
-
offsetY: (h.offsetY || 0) * unitScale * scale
|
|
1497
|
-
},
|
|
1498
|
-
{ x: cx, y: cy, width: visualWidth, height: visualHeight },
|
|
1499
|
-
{ width: canvasW, height: canvasH }
|
|
1500
|
-
);
|
|
1568
|
+
const absoluteFeatures = (features || []).map((f) => {
|
|
1569
|
+
const featureScale = scale;
|
|
1501
1570
|
return {
|
|
1502
|
-
...
|
|
1503
|
-
x:
|
|
1504
|
-
y:
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
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
|
|
1509
1577
|
};
|
|
1510
1578
|
});
|
|
1511
|
-
const
|
|
1579
|
+
const originalFeatures = absoluteFeatures.filter(
|
|
1580
|
+
(f) => !f.target || f.target === "original" || f.target === "both"
|
|
1581
|
+
);
|
|
1582
|
+
const generatedPathData = generateDielinePath({
|
|
1512
1583
|
shape,
|
|
1513
1584
|
width: visualWidth,
|
|
1514
1585
|
height: visualHeight,
|
|
1515
1586
|
radius: visualRadius,
|
|
1516
1587
|
x: cx,
|
|
1517
1588
|
y: cy,
|
|
1518
|
-
|
|
1519
|
-
pathData
|
|
1589
|
+
features: originalFeatures,
|
|
1590
|
+
pathData,
|
|
1520
1591
|
canvasWidth: canvasW,
|
|
1521
1592
|
canvasHeight: canvasH
|
|
1522
1593
|
});
|
|
1523
1594
|
const clonedLayer = await userLayer.clone();
|
|
1524
|
-
const clipPath = new import_fabric2.Path(
|
|
1595
|
+
const clipPath = new import_fabric2.Path(generatedPathData, {
|
|
1525
1596
|
originX: "left",
|
|
1526
1597
|
originY: "top",
|
|
1527
1598
|
left: 0,
|
|
1528
1599
|
top: 0,
|
|
1529
1600
|
absolutePositioned: true
|
|
1530
|
-
// Important for groups
|
|
1531
1601
|
});
|
|
1532
1602
|
clonedLayer.clipPath = clipPath;
|
|
1533
1603
|
const bounds = clipPath.getBoundingRect();
|
|
1534
1604
|
const dataUrl = clonedLayer.toDataURL({
|
|
1535
1605
|
format: "png",
|
|
1536
1606
|
multiplier: 2,
|
|
1537
|
-
// Better quality
|
|
1538
1607
|
left: bounds.left,
|
|
1539
1608
|
top: bounds.top,
|
|
1540
1609
|
width: bounds.width,
|
|
@@ -1703,22 +1772,20 @@ var FilmTool = class {
|
|
|
1703
1772
|
}
|
|
1704
1773
|
};
|
|
1705
1774
|
|
|
1706
|
-
// src/
|
|
1775
|
+
// src/feature.ts
|
|
1707
1776
|
var import_core4 = require("@pooder/core");
|
|
1708
1777
|
var import_fabric4 = require("fabric");
|
|
1709
|
-
var
|
|
1778
|
+
var FeatureTool = class {
|
|
1710
1779
|
constructor(options) {
|
|
1711
|
-
this.id = "pooder.kit.
|
|
1780
|
+
this.id = "pooder.kit.feature";
|
|
1712
1781
|
this.metadata = {
|
|
1713
|
-
name: "
|
|
1782
|
+
name: "FeatureTool"
|
|
1714
1783
|
};
|
|
1715
|
-
this.
|
|
1716
|
-
this.constraintTarget = "bleed";
|
|
1784
|
+
this.features = [];
|
|
1717
1785
|
this.isUpdatingConfig = false;
|
|
1718
1786
|
this.handleMoving = null;
|
|
1719
1787
|
this.handleModified = null;
|
|
1720
1788
|
this.handleDielineChange = null;
|
|
1721
|
-
// Cache geometry to enforce constraints during drag
|
|
1722
1789
|
this.currentGeometry = null;
|
|
1723
1790
|
if (options) {
|
|
1724
1791
|
Object.assign(this, options);
|
|
@@ -1728,26 +1795,18 @@ var HoleTool = class {
|
|
|
1728
1795
|
this.context = context;
|
|
1729
1796
|
this.canvasService = context.services.get("CanvasService");
|
|
1730
1797
|
if (!this.canvasService) {
|
|
1731
|
-
console.warn("CanvasService not found for
|
|
1798
|
+
console.warn("CanvasService not found for FeatureTool");
|
|
1732
1799
|
return;
|
|
1733
1800
|
}
|
|
1734
1801
|
const configService = context.services.get(
|
|
1735
1802
|
"ConfigurationService"
|
|
1736
1803
|
);
|
|
1737
1804
|
if (configService) {
|
|
1738
|
-
this.
|
|
1739
|
-
"hole.constraintTarget",
|
|
1740
|
-
this.constraintTarget
|
|
1741
|
-
);
|
|
1742
|
-
this.holes = configService.get("dieline.holes", []);
|
|
1805
|
+
this.features = configService.get("dieline.features", []);
|
|
1743
1806
|
configService.onAnyChange((e) => {
|
|
1744
1807
|
if (this.isUpdatingConfig) return;
|
|
1745
|
-
if (e.key === "
|
|
1746
|
-
this.
|
|
1747
|
-
this.enforceConstraints();
|
|
1748
|
-
}
|
|
1749
|
-
if (e.key === "dieline.holes") {
|
|
1750
|
-
this.holes = e.value || [];
|
|
1808
|
+
if (e.key === "dieline.features") {
|
|
1809
|
+
this.features = e.value || [];
|
|
1751
1810
|
this.redraw();
|
|
1752
1811
|
}
|
|
1753
1812
|
});
|
|
@@ -1761,102 +1820,38 @@ var HoleTool = class {
|
|
|
1761
1820
|
}
|
|
1762
1821
|
contribute() {
|
|
1763
1822
|
return {
|
|
1764
|
-
[import_core4.ContributionPointIds.CONFIGURATIONS]: [
|
|
1765
|
-
{
|
|
1766
|
-
id: "hole.constraintTarget",
|
|
1767
|
-
type: "select",
|
|
1768
|
-
label: "Constraint Target",
|
|
1769
|
-
options: ["original", "bleed"],
|
|
1770
|
-
default: "bleed"
|
|
1771
|
-
}
|
|
1772
|
-
],
|
|
1773
1823
|
[import_core4.ContributionPointIds.COMMANDS]: [
|
|
1774
1824
|
{
|
|
1775
|
-
command: "
|
|
1776
|
-
title: "
|
|
1777
|
-
handler: () => {
|
|
1778
|
-
|
|
1779
|
-
if (!this.canvasService) return false;
|
|
1780
|
-
let defaultPos = { x: this.canvasService.canvas.width / 2, y: 50 };
|
|
1781
|
-
if (this.currentGeometry) {
|
|
1782
|
-
const g = this.currentGeometry;
|
|
1783
|
-
const topCenter = { x: g.x, y: g.y - g.height / 2 };
|
|
1784
|
-
defaultPos = getNearestPointOnDieline(topCenter, {
|
|
1785
|
-
...g,
|
|
1786
|
-
holes: []
|
|
1787
|
-
});
|
|
1788
|
-
}
|
|
1789
|
-
const { width, height } = this.canvasService.canvas;
|
|
1790
|
-
const normalizedHole = Coordinate.normalizePoint(defaultPos, {
|
|
1791
|
-
width: width || 800,
|
|
1792
|
-
height: height || 600
|
|
1793
|
-
});
|
|
1794
|
-
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1795
|
-
"ConfigurationService"
|
|
1796
|
-
);
|
|
1797
|
-
if (configService) {
|
|
1798
|
-
configService.update("dieline.holes", [
|
|
1799
|
-
{
|
|
1800
|
-
x: normalizedHole.x,
|
|
1801
|
-
y: normalizedHole.y,
|
|
1802
|
-
innerRadius: 15,
|
|
1803
|
-
outerRadius: 25
|
|
1804
|
-
}
|
|
1805
|
-
]);
|
|
1806
|
-
}
|
|
1807
|
-
return true;
|
|
1825
|
+
command: "addFeature",
|
|
1826
|
+
title: "Add Edge Feature",
|
|
1827
|
+
handler: (type = "subtract") => {
|
|
1828
|
+
return this.addFeature(type);
|
|
1808
1829
|
}
|
|
1809
1830
|
},
|
|
1810
1831
|
{
|
|
1811
1832
|
command: "addHole",
|
|
1812
1833
|
title: "Add Hole",
|
|
1813
|
-
handler: (
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
normalizedX = gw > 0 ? (x - left) / gw : 0.5;
|
|
1823
|
-
normalizedY = gh > 0 ? (y - top) / gh : 0.5;
|
|
1824
|
-
} else {
|
|
1825
|
-
const { width, height } = this.canvasService.canvas;
|
|
1826
|
-
normalizedX = Coordinate.toNormalized(x, width || 800);
|
|
1827
|
-
normalizedY = Coordinate.toNormalized(y, height || 600);
|
|
1828
|
-
}
|
|
1829
|
-
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1830
|
-
"ConfigurationService"
|
|
1831
|
-
);
|
|
1832
|
-
if (configService) {
|
|
1833
|
-
const currentHoles = configService.get("dieline.holes", []);
|
|
1834
|
-
const lastHole = currentHoles[currentHoles.length - 1];
|
|
1835
|
-
const innerRadius = (_b = lastHole == null ? void 0 : lastHole.innerRadius) != null ? _b : 15;
|
|
1836
|
-
const outerRadius = (_c = lastHole == null ? void 0 : lastHole.outerRadius) != null ? _c : 25;
|
|
1837
|
-
const shape = (_d = lastHole == null ? void 0 : lastHole.shape) != null ? _d : "circle";
|
|
1838
|
-
const newHole = {
|
|
1839
|
-
x: normalizedX,
|
|
1840
|
-
y: normalizedY,
|
|
1841
|
-
shape,
|
|
1842
|
-
innerRadius,
|
|
1843
|
-
outerRadius
|
|
1844
|
-
};
|
|
1845
|
-
configService.update("dieline.holes", [...currentHoles, newHole]);
|
|
1846
|
-
}
|
|
1847
|
-
return true;
|
|
1834
|
+
handler: () => {
|
|
1835
|
+
return this.addFeature("subtract");
|
|
1836
|
+
}
|
|
1837
|
+
},
|
|
1838
|
+
{
|
|
1839
|
+
command: "addDoubleLayerHole",
|
|
1840
|
+
title: "Add Double Layer Hole",
|
|
1841
|
+
handler: () => {
|
|
1842
|
+
return this.addDoubleLayerHole();
|
|
1848
1843
|
}
|
|
1849
1844
|
},
|
|
1850
1845
|
{
|
|
1851
|
-
command: "
|
|
1852
|
-
title: "Clear
|
|
1846
|
+
command: "clearFeatures",
|
|
1847
|
+
title: "Clear Features",
|
|
1853
1848
|
handler: () => {
|
|
1854
1849
|
var _a;
|
|
1855
1850
|
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1856
1851
|
"ConfigurationService"
|
|
1857
1852
|
);
|
|
1858
1853
|
if (configService) {
|
|
1859
|
-
configService.update("dieline.
|
|
1854
|
+
configService.update("dieline.features", []);
|
|
1860
1855
|
}
|
|
1861
1856
|
return true;
|
|
1862
1857
|
}
|
|
@@ -1864,6 +1859,88 @@ var HoleTool = class {
|
|
|
1864
1859
|
]
|
|
1865
1860
|
};
|
|
1866
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
|
+
}
|
|
1867
1944
|
setup() {
|
|
1868
1945
|
if (!this.canvasService || !this.context) return;
|
|
1869
1946
|
const canvas = this.canvasService.canvas;
|
|
@@ -1871,10 +1948,7 @@ var HoleTool = class {
|
|
|
1871
1948
|
this.handleDielineChange = (geometry) => {
|
|
1872
1949
|
this.currentGeometry = geometry;
|
|
1873
1950
|
this.redraw();
|
|
1874
|
-
|
|
1875
|
-
if (changed) {
|
|
1876
|
-
this.syncHolesToDieline();
|
|
1877
|
-
}
|
|
1951
|
+
this.enforceConstraints();
|
|
1878
1952
|
};
|
|
1879
1953
|
this.context.eventBus.on(
|
|
1880
1954
|
"dieline:geometry:change",
|
|
@@ -1884,69 +1958,101 @@ var HoleTool = class {
|
|
|
1884
1958
|
const commandService = this.context.services.get("CommandService");
|
|
1885
1959
|
if (commandService) {
|
|
1886
1960
|
try {
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
Promise.resolve(geometry).then((g) => {
|
|
1961
|
+
Promise.resolve(commandService.executeCommand("getGeometry")).then(
|
|
1962
|
+
(g) => {
|
|
1890
1963
|
if (g) {
|
|
1891
1964
|
this.currentGeometry = g;
|
|
1892
|
-
this.
|
|
1893
|
-
this.initializeHoles();
|
|
1965
|
+
this.redraw();
|
|
1894
1966
|
}
|
|
1895
|
-
}
|
|
1896
|
-
|
|
1967
|
+
}
|
|
1968
|
+
);
|
|
1897
1969
|
} catch (e) {
|
|
1898
1970
|
}
|
|
1899
1971
|
}
|
|
1900
1972
|
if (!this.handleMoving) {
|
|
1901
1973
|
this.handleMoving = (e) => {
|
|
1902
|
-
var _a, _b, _c, _d
|
|
1974
|
+
var _a, _b, _c, _d;
|
|
1903
1975
|
const target = e.target;
|
|
1904
|
-
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "
|
|
1976
|
+
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "feature-marker") return;
|
|
1905
1977
|
if (!this.currentGeometry) return;
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
}
|
|
1918
|
-
const
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
constraintGeometry,
|
|
1922
|
-
(_d = holeData == null ? void 0 : holeData.innerRadius) != null ? _d : 15,
|
|
1923
|
-
(_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
|
|
1924
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);
|
|
1925
1999
|
target.set({
|
|
1926
|
-
left:
|
|
1927
|
-
top:
|
|
2000
|
+
left: snapped.x,
|
|
2001
|
+
top: snapped.y
|
|
1928
2002
|
});
|
|
1929
2003
|
};
|
|
1930
2004
|
canvas.on("object:moving", this.handleMoving);
|
|
1931
2005
|
}
|
|
1932
2006
|
if (!this.handleModified) {
|
|
1933
2007
|
this.handleModified = (e) => {
|
|
1934
|
-
var _a;
|
|
2008
|
+
var _a, _b, _c, _d;
|
|
1935
2009
|
const target = e.target;
|
|
1936
|
-
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
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);
|
|
1940
2052
|
}
|
|
1941
2053
|
};
|
|
1942
2054
|
canvas.on("object:modified", this.handleModified);
|
|
1943
2055
|
}
|
|
1944
|
-
this.initializeHoles();
|
|
1945
|
-
}
|
|
1946
|
-
initializeHoles() {
|
|
1947
|
-
if (!this.canvasService) return;
|
|
1948
|
-
this.redraw();
|
|
1949
|
-
this.syncHolesToDieline();
|
|
1950
2056
|
}
|
|
1951
2057
|
teardown() {
|
|
1952
2058
|
if (!this.canvasService) return;
|
|
@@ -1968,357 +2074,259 @@ var HoleTool = class {
|
|
|
1968
2074
|
}
|
|
1969
2075
|
const objects = canvas.getObjects().filter((obj) => {
|
|
1970
2076
|
var _a;
|
|
1971
|
-
return ((_a = obj.data) == null ? void 0 : _a.type) === "
|
|
2077
|
+
return ((_a = obj.data) == null ? void 0 : _a.type) === "feature-marker";
|
|
1972
2078
|
});
|
|
1973
2079
|
objects.forEach((obj) => canvas.remove(obj));
|
|
1974
|
-
if (this.context) {
|
|
1975
|
-
const commandService = this.context.services.get("CommandService");
|
|
1976
|
-
if (commandService) {
|
|
1977
|
-
try {
|
|
1978
|
-
commandService.executeCommand("setHoles", []);
|
|
1979
|
-
} catch (e) {
|
|
1980
|
-
}
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
2080
|
this.canvasService.requestRenderAll();
|
|
1984
2081
|
}
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
var _a;
|
|
1990
|
-
return ((_a = obj.data) == null ? void 0 : _a.type) === "hole-marker" || obj.name === "hole-marker";
|
|
1991
|
-
}
|
|
1992
|
-
);
|
|
1993
|
-
if (objects.length === 0 && this.holes.length > 0) {
|
|
1994
|
-
console.warn("HoleTool: No markers found on canvas to sync from");
|
|
1995
|
-
return;
|
|
1996
|
-
}
|
|
1997
|
-
objects.sort(
|
|
1998
|
-
(a, b) => {
|
|
1999
|
-
var _a, _b, _c, _d;
|
|
2000
|
-
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);
|
|
2001
|
-
}
|
|
2002
|
-
);
|
|
2003
|
-
const newHoles = objects.map((obj, i) => {
|
|
2004
|
-
var _a, _b, _c, _d;
|
|
2005
|
-
const original = this.holes[i];
|
|
2006
|
-
const newAbsX = obj.left;
|
|
2007
|
-
const newAbsY = obj.top;
|
|
2008
|
-
if (isNaN(newAbsX) || isNaN(newAbsY)) {
|
|
2009
|
-
console.error("HoleTool: Invalid marker coordinates", {
|
|
2010
|
-
newAbsX,
|
|
2011
|
-
newAbsY
|
|
2012
|
-
});
|
|
2013
|
-
return original;
|
|
2014
|
-
}
|
|
2015
|
-
const scale = ((_a = this.currentGeometry) == null ? void 0 : _a.scale) || 1;
|
|
2016
|
-
const unit = ((_b = this.currentGeometry) == null ? void 0 : _b.unit) || "mm";
|
|
2017
|
-
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
2018
|
-
if (original && original.anchor && this.currentGeometry) {
|
|
2019
|
-
const { x, y, width, height } = this.currentGeometry;
|
|
2020
|
-
let bx = x;
|
|
2021
|
-
let by = y;
|
|
2022
|
-
const left = x - width / 2;
|
|
2023
|
-
const right = x + width / 2;
|
|
2024
|
-
const top = y - height / 2;
|
|
2025
|
-
const bottom = y + height / 2;
|
|
2026
|
-
switch (original.anchor) {
|
|
2027
|
-
case "top-left":
|
|
2028
|
-
bx = left;
|
|
2029
|
-
by = top;
|
|
2030
|
-
break;
|
|
2031
|
-
case "top-center":
|
|
2032
|
-
bx = x;
|
|
2033
|
-
by = top;
|
|
2034
|
-
break;
|
|
2035
|
-
case "top-right":
|
|
2036
|
-
bx = right;
|
|
2037
|
-
by = top;
|
|
2038
|
-
break;
|
|
2039
|
-
case "center-left":
|
|
2040
|
-
bx = left;
|
|
2041
|
-
by = y;
|
|
2042
|
-
break;
|
|
2043
|
-
case "center":
|
|
2044
|
-
bx = x;
|
|
2045
|
-
by = y;
|
|
2046
|
-
break;
|
|
2047
|
-
case "center-right":
|
|
2048
|
-
bx = right;
|
|
2049
|
-
by = y;
|
|
2050
|
-
break;
|
|
2051
|
-
case "bottom-left":
|
|
2052
|
-
bx = left;
|
|
2053
|
-
by = bottom;
|
|
2054
|
-
break;
|
|
2055
|
-
case "bottom-center":
|
|
2056
|
-
bx = x;
|
|
2057
|
-
by = bottom;
|
|
2058
|
-
break;
|
|
2059
|
-
case "bottom-right":
|
|
2060
|
-
bx = right;
|
|
2061
|
-
by = bottom;
|
|
2062
|
-
break;
|
|
2063
|
-
}
|
|
2064
|
-
return {
|
|
2065
|
-
...original,
|
|
2066
|
-
// Denormalize offset back to physical units (mm)
|
|
2067
|
-
offsetX: (newAbsX - bx) / scale / unitScale,
|
|
2068
|
-
offsetY: (newAbsY - by) / scale / unitScale,
|
|
2069
|
-
// Clear direct coordinates if we use anchor
|
|
2070
|
-
x: void 0,
|
|
2071
|
-
y: void 0,
|
|
2072
|
-
// Ensure other properties are preserved
|
|
2073
|
-
innerRadius: original.innerRadius,
|
|
2074
|
-
outerRadius: original.outerRadius,
|
|
2075
|
-
shape: original.shape || "circle"
|
|
2076
|
-
};
|
|
2077
|
-
}
|
|
2078
|
-
let normalizedX = 0.5;
|
|
2079
|
-
let normalizedY = 0.5;
|
|
2080
|
-
if (this.currentGeometry) {
|
|
2081
|
-
const { x, y, width, height } = this.currentGeometry;
|
|
2082
|
-
const left = x - width / 2;
|
|
2083
|
-
const top = y - height / 2;
|
|
2084
|
-
normalizedX = width > 0 ? (newAbsX - left) / width : 0.5;
|
|
2085
|
-
normalizedY = height > 0 ? (newAbsY - top) / height : 0.5;
|
|
2086
|
-
} else {
|
|
2087
|
-
const { width, height } = this.canvasService.canvas;
|
|
2088
|
-
normalizedX = Coordinate.toNormalized(newAbsX, width || 800);
|
|
2089
|
-
normalizedY = Coordinate.toNormalized(newAbsY, height || 600);
|
|
2090
|
-
}
|
|
2091
|
-
return {
|
|
2092
|
-
...original,
|
|
2093
|
-
x: normalizedX,
|
|
2094
|
-
y: normalizedY,
|
|
2095
|
-
// Clear offsets if we are using direct normalized coordinates
|
|
2096
|
-
offsetX: void 0,
|
|
2097
|
-
offsetY: void 0,
|
|
2098
|
-
// Ensure other properties are preserved
|
|
2099
|
-
innerRadius: (_c = original == null ? void 0 : original.innerRadius) != null ? _c : 15,
|
|
2100
|
-
outerRadius: (_d = original == null ? void 0 : original.outerRadius) != null ? _d : 25,
|
|
2101
|
-
shape: (original == null ? void 0 : original.shape) || "circle"
|
|
2102
|
-
};
|
|
2082
|
+
constrainPosition(p, geometry, limit) {
|
|
2083
|
+
const nearest = getNearestPointOnDieline({ x: p.x, y: p.y }, {
|
|
2084
|
+
...geometry,
|
|
2085
|
+
features: []
|
|
2103
2086
|
});
|
|
2104
|
-
|
|
2105
|
-
|
|
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
|
+
};
|
|
2106
2098
|
}
|
|
2107
|
-
|
|
2108
|
-
|
|
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;
|
|
2109
2121
|
const configService = this.context.services.get(
|
|
2110
2122
|
"ConfigurationService"
|
|
2111
2123
|
);
|
|
2112
2124
|
if (configService) {
|
|
2113
2125
|
this.isUpdatingConfig = true;
|
|
2114
2126
|
try {
|
|
2115
|
-
configService.update("dieline.
|
|
2127
|
+
configService.update("dieline.features", this.features);
|
|
2116
2128
|
} finally {
|
|
2117
2129
|
this.isUpdatingConfig = false;
|
|
2118
2130
|
}
|
|
2119
2131
|
}
|
|
2120
2132
|
}
|
|
2121
2133
|
redraw() {
|
|
2122
|
-
if (!this.canvasService) return;
|
|
2134
|
+
if (!this.canvasService || !this.currentGeometry) return;
|
|
2123
2135
|
const canvas = this.canvasService.canvas;
|
|
2124
|
-
const
|
|
2136
|
+
const geometry = this.currentGeometry;
|
|
2125
2137
|
const existing = canvas.getObjects().filter((obj) => {
|
|
2126
2138
|
var _a;
|
|
2127
|
-
return ((_a = obj.data) == null ? void 0 : _a.type) === "
|
|
2139
|
+
return ((_a = obj.data) == null ? void 0 : _a.type) === "feature-marker";
|
|
2128
2140
|
});
|
|
2129
2141
|
existing.forEach((obj) => canvas.remove(obj));
|
|
2130
|
-
|
|
2131
|
-
if (!holes || holes.length === 0) {
|
|
2142
|
+
if (!this.features || this.features.length === 0) {
|
|
2132
2143
|
this.canvasService.requestRenderAll();
|
|
2133
2144
|
return;
|
|
2134
2145
|
}
|
|
2135
|
-
const
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
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;
|
|
2142
2198
|
};
|
|
2143
|
-
|
|
2144
|
-
const
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
const visualInnerRadius = hole.innerRadius * unitScale * scale;
|
|
2148
|
-
const visualOuterRadius = hole.outerRadius * unitScale * scale;
|
|
2149
|
-
const pos = resolveHolePosition(
|
|
2150
|
-
{
|
|
2151
|
-
...hole,
|
|
2152
|
-
offsetX: (hole.offsetX || 0) * unitScale * scale,
|
|
2153
|
-
offsetY: (hole.offsetY || 0) * unitScale * scale
|
|
2154
|
-
},
|
|
2155
|
-
geometry,
|
|
2156
|
-
{ width: geometry.width, height: geometry.height }
|
|
2157
|
-
// Use geometry dims instead of canvas
|
|
2199
|
+
singles.forEach(({ feature, index }) => {
|
|
2200
|
+
const geometry2 = this.getGeometryForFeature(
|
|
2201
|
+
this.currentGeometry,
|
|
2202
|
+
feature
|
|
2158
2203
|
);
|
|
2159
|
-
const
|
|
2160
|
-
const
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
fill: "transparent",
|
|
2171
|
-
stroke: "red",
|
|
2172
|
-
strokeWidth: 2,
|
|
2173
|
-
originX: "center",
|
|
2174
|
-
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 }
|
|
2175
2215
|
});
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
stroke: "#666",
|
|
2181
|
-
strokeWidth: 1,
|
|
2182
|
-
strokeDashArray: [5, 5],
|
|
2183
|
-
originX: "center",
|
|
2184
|
-
originY: "center"
|
|
2185
|
-
}) : new import_fabric4.Circle({
|
|
2186
|
-
radius: visualOuterRadius,
|
|
2187
|
-
fill: "transparent",
|
|
2188
|
-
stroke: "#666",
|
|
2189
|
-
strokeWidth: 1,
|
|
2190
|
-
strokeDashArray: [5, 5],
|
|
2191
|
-
originX: "center",
|
|
2192
|
-
originY: "center"
|
|
2216
|
+
marker.set("opacity", 0);
|
|
2217
|
+
marker.on("mouseover", () => {
|
|
2218
|
+
marker.set("opacity", 1);
|
|
2219
|
+
canvas.requestRenderAll();
|
|
2193
2220
|
});
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
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, {
|
|
2199
2250
|
selectable: true,
|
|
2200
2251
|
hasControls: false,
|
|
2201
|
-
// Don't allow resizing/rotating
|
|
2202
2252
|
hasBorders: false,
|
|
2203
|
-
subTargetCheck: false,
|
|
2204
|
-
opacity: 0,
|
|
2205
|
-
// Default hidden
|
|
2206
2253
|
hoverCursor: "move",
|
|
2207
|
-
|
|
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
|
+
}
|
|
2208
2268
|
});
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2269
|
+
groupObj.set("opacity", 0);
|
|
2270
|
+
groupObj.on("mouseover", () => {
|
|
2271
|
+
groupObj.set("opacity", 1);
|
|
2212
2272
|
canvas.requestRenderAll();
|
|
2213
2273
|
});
|
|
2214
|
-
|
|
2215
|
-
if (canvas.getActiveObject() !==
|
|
2216
|
-
|
|
2274
|
+
groupObj.on("mouseout", () => {
|
|
2275
|
+
if (canvas.getActiveObject() !== groupObj) {
|
|
2276
|
+
groupObj.set("opacity", 0);
|
|
2217
2277
|
canvas.requestRenderAll();
|
|
2218
2278
|
}
|
|
2219
2279
|
});
|
|
2220
|
-
|
|
2221
|
-
|
|
2280
|
+
groupObj.on("selected", () => {
|
|
2281
|
+
groupObj.set("opacity", 1);
|
|
2222
2282
|
canvas.requestRenderAll();
|
|
2223
2283
|
});
|
|
2224
|
-
|
|
2225
|
-
|
|
2284
|
+
groupObj.on("deselected", () => {
|
|
2285
|
+
groupObj.set("opacity", 0);
|
|
2226
2286
|
canvas.requestRenderAll();
|
|
2227
2287
|
});
|
|
2228
|
-
canvas.add(
|
|
2229
|
-
canvas.bringObjectToFront(
|
|
2288
|
+
canvas.add(groupObj);
|
|
2289
|
+
canvas.bringObjectToFront(groupObj);
|
|
2230
2290
|
});
|
|
2231
|
-
const markers = canvas.getObjects().filter((o) => {
|
|
2232
|
-
var _a;
|
|
2233
|
-
return ((_a = o.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
2234
|
-
});
|
|
2235
|
-
markers.forEach((m) => canvas.bringObjectToFront(m));
|
|
2236
2291
|
this.canvasService.requestRenderAll();
|
|
2237
2292
|
}
|
|
2238
2293
|
enforceConstraints() {
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
}
|
|
2243
|
-
const effectiveOffset = this.constraintTarget === "original" ? 0 : geometry.offset;
|
|
2244
|
-
const constraintGeometry = {
|
|
2245
|
-
...geometry,
|
|
2246
|
-
width: Math.max(0, geometry.width + effectiveOffset * 2),
|
|
2247
|
-
height: Math.max(0, geometry.height + effectiveOffset * 2),
|
|
2248
|
-
radius: Math.max(0, geometry.radius + effectiveOffset)
|
|
2249
|
-
};
|
|
2250
|
-
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) => {
|
|
2251
2297
|
var _a;
|
|
2252
|
-
return ((_a = obj.data) == null ? void 0 : _a.type) === "
|
|
2298
|
+
return ((_a = obj.data) == null ? void 0 : _a.type) === "feature-marker";
|
|
2253
2299
|
});
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
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
|
+
}
|
|
2259
2313
|
}
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
var _a, _b;
|
|
2264
|
-
const currentPos = new import_fabric4.Point(obj.left, obj.top);
|
|
2265
|
-
const holeData = this.holes[i];
|
|
2266
|
-
const scale = geometry.scale || 1;
|
|
2267
|
-
const unit = geometry.unit || "mm";
|
|
2268
|
-
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
2269
|
-
const innerR = ((_a = holeData == null ? void 0 : holeData.innerRadius) != null ? _a : 15) * unitScale * scale;
|
|
2270
|
-
const outerR = ((_b = holeData == null ? void 0 : holeData.outerRadius) != null ? _b : 25) * unitScale * scale;
|
|
2271
|
-
const newPos = this.calculateConstrainedPosition(
|
|
2272
|
-
currentPos,
|
|
2273
|
-
constraintGeometry,
|
|
2274
|
-
innerR,
|
|
2275
|
-
outerR
|
|
2314
|
+
const geometry = this.getGeometryForFeature(
|
|
2315
|
+
this.currentGeometry,
|
|
2316
|
+
feature
|
|
2276
2317
|
);
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
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();
|
|
2285
2328
|
});
|
|
2286
|
-
|
|
2287
|
-
this.syncHolesFromCanvas();
|
|
2288
|
-
return true;
|
|
2289
|
-
}
|
|
2290
|
-
return false;
|
|
2291
|
-
}
|
|
2292
|
-
calculateConstrainedPosition(p, g, innerRadius, outerRadius) {
|
|
2293
|
-
const options = {
|
|
2294
|
-
...g,
|
|
2295
|
-
holes: []
|
|
2296
|
-
// We don't need holes for boundary calculation
|
|
2297
|
-
};
|
|
2298
|
-
const nearest = getNearestPointOnDieline(
|
|
2299
|
-
{ x: p.x, y: p.y },
|
|
2300
|
-
options
|
|
2301
|
-
);
|
|
2302
|
-
const nearestP = new import_fabric4.Point(nearest.x, nearest.y);
|
|
2303
|
-
const dist = p.distanceFrom(nearestP);
|
|
2304
|
-
const v = p.subtract(nearestP);
|
|
2305
|
-
const center = new import_fabric4.Point(g.x, g.y);
|
|
2306
|
-
const distToCenter = p.distanceFrom(center);
|
|
2307
|
-
const nearestDistToCenter = nearestP.distanceFrom(center);
|
|
2308
|
-
let signedDist = dist;
|
|
2309
|
-
if (distToCenter < nearestDistToCenter) {
|
|
2310
|
-
signedDist = -dist;
|
|
2311
|
-
}
|
|
2312
|
-
let clampedDist = signedDist;
|
|
2313
|
-
if (signedDist > 0) {
|
|
2314
|
-
clampedDist = Math.min(signedDist, innerRadius);
|
|
2315
|
-
} else {
|
|
2316
|
-
clampedDist = Math.max(signedDist, -outerRadius);
|
|
2317
|
-
}
|
|
2318
|
-
if (dist < 1e-3) return nearestP;
|
|
2319
|
-
const scale = Math.abs(clampedDist) / (dist || 1);
|
|
2320
|
-
const offset = v.scalarMultiply(scale);
|
|
2321
|
-
return nearestP.add(offset);
|
|
2329
|
+
canvas.requestRenderAll();
|
|
2322
2330
|
}
|
|
2323
2331
|
};
|
|
2324
2332
|
|
|
@@ -3524,8 +3532,8 @@ var CanvasService = class {
|
|
|
3524
3532
|
BackgroundTool,
|
|
3525
3533
|
CanvasService,
|
|
3526
3534
|
DielineTool,
|
|
3535
|
+
FeatureTool,
|
|
3527
3536
|
FilmTool,
|
|
3528
|
-
HoleTool,
|
|
3529
3537
|
ImageTool,
|
|
3530
3538
|
MirrorTool,
|
|
3531
3539
|
RulerTool,
|