@pooder/kit 3.4.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/index.d.mts +62 -56
- package/dist/index.d.ts +62 -56
- package/dist/index.js +978 -862
- package/dist/index.mjs +977 -861
- package/package.json +2 -2
- package/src/CanvasService.ts +25 -1
- package/src/background.ts +230 -230
- package/src/coordinate.ts +106 -106
- package/src/dieline.ts +272 -218
- package/src/feature.ts +767 -0
- package/src/film.ts +194 -194
- package/src/geometry.ts +172 -375
- package/src/image.ts +512 -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.mjs
CHANGED
|
@@ -208,6 +208,7 @@ import {
|
|
|
208
208
|
import { Path, Pattern } from "fabric";
|
|
209
209
|
|
|
210
210
|
// src/tracer.ts
|
|
211
|
+
import paper from "paper";
|
|
211
212
|
var ImageTracer = class {
|
|
212
213
|
/**
|
|
213
214
|
* Main entry point: Traces an image URL to an SVG path string.
|
|
@@ -215,7 +216,7 @@ var ImageTracer = class {
|
|
|
215
216
|
* @param options Configuration options.
|
|
216
217
|
*/
|
|
217
218
|
static async trace(imageUrl, options = {}) {
|
|
218
|
-
var _a, _b, _c, _d, _e;
|
|
219
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
219
220
|
const img = await this.loadImage(imageUrl);
|
|
220
221
|
const width = img.width;
|
|
221
222
|
const height = img.height;
|
|
@@ -232,23 +233,37 @@ var ImageTracer = class {
|
|
|
232
233
|
Math.floor(Math.max(width, height) * 0.02)
|
|
233
234
|
);
|
|
234
235
|
const radius = (_b = options.morphologyRadius) != null ? _b : adaptiveRadius;
|
|
235
|
-
|
|
236
|
+
const expand = (_c = options.expand) != null ? _c : 0;
|
|
237
|
+
const padding = radius + expand + 2;
|
|
238
|
+
const paddedWidth = width + padding * 2;
|
|
239
|
+
const paddedHeight = height + padding * 2;
|
|
240
|
+
let mask = this.createMask(imageData, threshold, padding, paddedWidth, paddedHeight);
|
|
236
241
|
if (radius > 0) {
|
|
237
|
-
mask = this.
|
|
238
|
-
mask = this.
|
|
239
|
-
|
|
242
|
+
mask = this.circularMorphology(mask, paddedWidth, paddedHeight, radius, "closing");
|
|
243
|
+
mask = this.fillHoles(mask, paddedWidth, paddedHeight);
|
|
244
|
+
const smoothRadius = Math.max(2, Math.floor(radius * 0.3));
|
|
245
|
+
mask = this.circularMorphology(mask, paddedWidth, paddedHeight, smoothRadius, "closing");
|
|
246
|
+
} else {
|
|
247
|
+
mask = this.fillHoles(mask, paddedWidth, paddedHeight);
|
|
248
|
+
}
|
|
249
|
+
if (expand > 0) {
|
|
250
|
+
mask = this.circularMorphology(mask, paddedWidth, paddedHeight, expand, "dilate");
|
|
240
251
|
}
|
|
241
|
-
const allContourPoints = this.traceAllContours(mask,
|
|
252
|
+
const allContourPoints = this.traceAllContours(mask, paddedWidth, paddedHeight);
|
|
242
253
|
if (allContourPoints.length === 0) {
|
|
243
|
-
const w = (
|
|
244
|
-
const h = (
|
|
254
|
+
const w = (_d = options.scaleToWidth) != null ? _d : width;
|
|
255
|
+
const h = (_e = options.scaleToHeight) != null ? _e : height;
|
|
245
256
|
return `M 0 0 L ${w} 0 L ${w} ${h} L 0 ${h} Z`;
|
|
246
257
|
}
|
|
247
258
|
const primaryContour = allContourPoints.sort(
|
|
248
259
|
(a, b) => b.length - a.length
|
|
249
260
|
)[0];
|
|
261
|
+
const unpaddedPoints = primaryContour.map((p) => ({
|
|
262
|
+
x: p.x - padding,
|
|
263
|
+
y: p.y - padding
|
|
264
|
+
}));
|
|
250
265
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
251
|
-
for (const p of
|
|
266
|
+
for (const p of unpaddedPoints) {
|
|
252
267
|
if (p.x < minX) minX = p.x;
|
|
253
268
|
if (p.y < minY) minY = p.y;
|
|
254
269
|
if (p.x > maxX) maxX = p.x;
|
|
@@ -260,95 +275,119 @@ var ImageTracer = class {
|
|
|
260
275
|
width: maxX - minX,
|
|
261
276
|
height: maxY - minY
|
|
262
277
|
};
|
|
263
|
-
let finalPoints =
|
|
278
|
+
let finalPoints = unpaddedPoints;
|
|
264
279
|
if (options.scaleToWidth && options.scaleToHeight) {
|
|
265
280
|
finalPoints = this.scalePoints(
|
|
266
|
-
|
|
281
|
+
unpaddedPoints,
|
|
267
282
|
options.scaleToWidth,
|
|
268
283
|
options.scaleToHeight,
|
|
269
284
|
globalBounds
|
|
270
285
|
);
|
|
271
286
|
}
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
(
|
|
275
|
-
|
|
276
|
-
|
|
287
|
+
const useSmoothing = options.smoothing !== false;
|
|
288
|
+
if (useSmoothing) {
|
|
289
|
+
return this.pointsToSVGPaper(finalPoints, (_f = options.simplifyTolerance) != null ? _f : 2.5);
|
|
290
|
+
} else {
|
|
291
|
+
const simplifiedPoints = this.douglasPeucker(
|
|
292
|
+
finalPoints,
|
|
293
|
+
(_g = options.simplifyTolerance) != null ? _g : 2
|
|
294
|
+
);
|
|
295
|
+
return this.pointsToSVG(simplifiedPoints);
|
|
296
|
+
}
|
|
277
297
|
}
|
|
278
|
-
static createMask(imageData, threshold) {
|
|
298
|
+
static createMask(imageData, threshold, padding, paddedWidth, paddedHeight) {
|
|
279
299
|
const { width, height, data } = imageData;
|
|
280
|
-
const mask = new Uint8Array(
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const a = data[idx + 3];
|
|
287
|
-
if (a > threshold && !(r > 240 && g > 240 && b > 240)) {
|
|
288
|
-
mask[i] = 1;
|
|
289
|
-
} else {
|
|
290
|
-
mask[i] = 0;
|
|
300
|
+
const mask = new Uint8Array(paddedWidth * paddedHeight);
|
|
301
|
+
let hasTransparency = false;
|
|
302
|
+
for (let i = 3; i < data.length; i += 4) {
|
|
303
|
+
if (data[i] < 255) {
|
|
304
|
+
hasTransparency = true;
|
|
305
|
+
break;
|
|
291
306
|
}
|
|
292
307
|
}
|
|
293
|
-
return mask;
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Fast 1D-separable Dilation
|
|
297
|
-
*/
|
|
298
|
-
static dilate(mask, width, height, radius) {
|
|
299
|
-
const horizontal = new Uint8Array(width * height);
|
|
300
308
|
for (let y = 0; y < height; y++) {
|
|
301
|
-
let
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
309
|
+
for (let x = 0; x < width; x++) {
|
|
310
|
+
const srcIdx = (y * width + x) * 4;
|
|
311
|
+
const r = data[srcIdx];
|
|
312
|
+
const g = data[srcIdx + 1];
|
|
313
|
+
const b = data[srcIdx + 2];
|
|
314
|
+
const a = data[srcIdx + 3];
|
|
315
|
+
const destIdx = (y + padding) * paddedWidth + (x + padding);
|
|
316
|
+
if (hasTransparency) {
|
|
317
|
+
if (a > threshold) {
|
|
318
|
+
mask[destIdx] = 1;
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
if (!(r > 240 && g > 240 && b > 240)) {
|
|
322
|
+
mask[destIdx] = 1;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
317
325
|
}
|
|
318
326
|
}
|
|
319
|
-
return
|
|
327
|
+
return mask;
|
|
320
328
|
}
|
|
321
329
|
/**
|
|
322
|
-
* Fast
|
|
330
|
+
* Fast circular morphology using a distance-transform inspired separable approach.
|
|
331
|
+
* O(N * R) complexity, where R is the radius.
|
|
323
332
|
*/
|
|
324
|
-
static
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
let
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
333
|
+
static circularMorphology(mask, width, height, radius, op) {
|
|
334
|
+
const dilate = (m, r) => {
|
|
335
|
+
const horizontalDist = new Int32Array(width * height);
|
|
336
|
+
for (let y = 0; y < height; y++) {
|
|
337
|
+
let lastSolid = -r * 2;
|
|
338
|
+
for (let x = 0; x < width; x++) {
|
|
339
|
+
if (m[y * width + x]) lastSolid = x;
|
|
340
|
+
horizontalDist[y * width + x] = x - lastSolid;
|
|
341
|
+
}
|
|
342
|
+
lastSolid = width + r * 2;
|
|
343
|
+
for (let x = width - 1; x >= 0; x--) {
|
|
344
|
+
if (m[y * width + x]) lastSolid = x;
|
|
345
|
+
horizontalDist[y * width + x] = Math.min(
|
|
346
|
+
horizontalDist[y * width + x],
|
|
347
|
+
lastSolid - x
|
|
348
|
+
);
|
|
334
349
|
}
|
|
335
350
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
351
|
+
const result = new Uint8Array(width * height);
|
|
352
|
+
const r2 = r * r;
|
|
353
|
+
for (let x = 0; x < width; x++) {
|
|
354
|
+
for (let y = 0; y < height; y++) {
|
|
355
|
+
let found = false;
|
|
356
|
+
const minY = Math.max(0, y - r);
|
|
357
|
+
const maxY = Math.min(height - 1, y + r);
|
|
358
|
+
for (let dy = minY; dy <= maxY; dy++) {
|
|
359
|
+
const dY = dy - y;
|
|
360
|
+
const hDist = horizontalDist[dy * width + x];
|
|
361
|
+
if (hDist * hDist + dY * dY <= r2) {
|
|
362
|
+
found = true;
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (found) result[y * width + x] = 1;
|
|
348
367
|
}
|
|
349
368
|
}
|
|
369
|
+
return result;
|
|
370
|
+
};
|
|
371
|
+
const erode = (m, r) => {
|
|
372
|
+
const inverted = new Uint8Array(m.length);
|
|
373
|
+
for (let i = 0; i < m.length; i++) inverted[i] = m[i] ? 0 : 1;
|
|
374
|
+
const dilatedInverted = dilate(inverted, r);
|
|
375
|
+
const result = new Uint8Array(m.length);
|
|
376
|
+
for (let i = 0; i < m.length; i++) result[i] = dilatedInverted[i] ? 0 : 1;
|
|
377
|
+
return result;
|
|
378
|
+
};
|
|
379
|
+
switch (op) {
|
|
380
|
+
case "dilate":
|
|
381
|
+
return dilate(mask, radius);
|
|
382
|
+
case "erode":
|
|
383
|
+
return erode(mask, radius);
|
|
384
|
+
case "closing":
|
|
385
|
+
return erode(dilate(mask, radius), radius);
|
|
386
|
+
case "opening":
|
|
387
|
+
return dilate(erode(mask, radius), radius);
|
|
388
|
+
default:
|
|
389
|
+
return mask;
|
|
350
390
|
}
|
|
351
|
-
return vertical;
|
|
352
391
|
}
|
|
353
392
|
/**
|
|
354
393
|
* Fills internal holes in the binary mask using flood fill from edges.
|
|
@@ -548,6 +587,23 @@ var ImageTracer = class {
|
|
|
548
587
|
const tail = points.slice(1);
|
|
549
588
|
return `M ${head.x} ${head.y} ` + tail.map((p) => `L ${p.x} ${p.y}`).join(" ") + " Z";
|
|
550
589
|
}
|
|
590
|
+
static ensurePaper() {
|
|
591
|
+
if (!paper.project) {
|
|
592
|
+
paper.setup(new paper.Size(100, 100));
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
static pointsToSVGPaper(points, tolerance) {
|
|
596
|
+
if (points.length < 3) return this.pointsToSVG(points);
|
|
597
|
+
this.ensurePaper();
|
|
598
|
+
const path = new paper.Path({
|
|
599
|
+
segments: points.map((p) => [p.x, p.y]),
|
|
600
|
+
closed: true
|
|
601
|
+
});
|
|
602
|
+
path.simplify(tolerance);
|
|
603
|
+
const data = path.pathData;
|
|
604
|
+
path.remove();
|
|
605
|
+
return data;
|
|
606
|
+
}
|
|
551
607
|
};
|
|
552
608
|
|
|
553
609
|
// src/coordinate.ts
|
|
@@ -622,96 +678,45 @@ var Coordinate = class {
|
|
|
622
678
|
};
|
|
623
679
|
|
|
624
680
|
// src/geometry.ts
|
|
625
|
-
import
|
|
626
|
-
function
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
const bottom = y + height / 2;
|
|
635
|
-
switch (hole.anchor) {
|
|
636
|
-
case "top-left":
|
|
637
|
-
bx = left;
|
|
638
|
-
by = top;
|
|
639
|
-
break;
|
|
640
|
-
case "top-center":
|
|
641
|
-
bx = x;
|
|
642
|
-
by = top;
|
|
643
|
-
break;
|
|
644
|
-
case "top-right":
|
|
645
|
-
bx = right;
|
|
646
|
-
by = top;
|
|
647
|
-
break;
|
|
648
|
-
case "center-left":
|
|
649
|
-
bx = left;
|
|
650
|
-
by = y;
|
|
651
|
-
break;
|
|
652
|
-
case "center":
|
|
653
|
-
bx = x;
|
|
654
|
-
by = y;
|
|
655
|
-
break;
|
|
656
|
-
case "center-right":
|
|
657
|
-
bx = right;
|
|
658
|
-
by = y;
|
|
659
|
-
break;
|
|
660
|
-
case "bottom-left":
|
|
661
|
-
bx = left;
|
|
662
|
-
by = bottom;
|
|
663
|
-
break;
|
|
664
|
-
case "bottom-center":
|
|
665
|
-
bx = x;
|
|
666
|
-
by = bottom;
|
|
667
|
-
break;
|
|
668
|
-
case "bottom-right":
|
|
669
|
-
bx = right;
|
|
670
|
-
by = bottom;
|
|
671
|
-
break;
|
|
672
|
-
}
|
|
673
|
-
return {
|
|
674
|
-
x: bx + (hole.offsetX || 0),
|
|
675
|
-
y: by + (hole.offsetY || 0)
|
|
676
|
-
};
|
|
677
|
-
} else if (hole.x !== void 0 && hole.y !== void 0) {
|
|
678
|
-
const { x, width, y, height } = geometry;
|
|
679
|
-
return {
|
|
680
|
-
x: hole.x * width + (x - width / 2) + (hole.offsetX || 0),
|
|
681
|
-
y: hole.y * height + (y - height / 2) + (hole.offsetY || 0)
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
return { x: 0, y: 0 };
|
|
681
|
+
import paper2 from "paper";
|
|
682
|
+
function resolveFeaturePosition(feature, geometry) {
|
|
683
|
+
const { x, y, width, height } = geometry;
|
|
684
|
+
const left = x - width / 2;
|
|
685
|
+
const top = y - height / 2;
|
|
686
|
+
return {
|
|
687
|
+
x: left + feature.x * width,
|
|
688
|
+
y: top + feature.y * height
|
|
689
|
+
};
|
|
685
690
|
}
|
|
686
691
|
function ensurePaper(width, height) {
|
|
687
|
-
if (!
|
|
688
|
-
|
|
692
|
+
if (!paper2.project) {
|
|
693
|
+
paper2.setup(new paper2.Size(width, height));
|
|
689
694
|
} else {
|
|
690
|
-
|
|
695
|
+
paper2.view.viewSize = new paper2.Size(width, height);
|
|
691
696
|
}
|
|
692
697
|
}
|
|
693
698
|
function createBaseShape(options) {
|
|
694
699
|
const { shape, width, height, radius, x, y, pathData } = options;
|
|
695
|
-
const center = new
|
|
700
|
+
const center = new paper2.Point(x, y);
|
|
696
701
|
if (shape === "rect") {
|
|
697
|
-
return new
|
|
702
|
+
return new paper2.Path.Rectangle({
|
|
698
703
|
point: [x - width / 2, y - height / 2],
|
|
699
704
|
size: [Math.max(0, width), Math.max(0, height)],
|
|
700
705
|
radius: Math.max(0, radius)
|
|
701
706
|
});
|
|
702
707
|
} else if (shape === "circle") {
|
|
703
708
|
const r = Math.min(width, height) / 2;
|
|
704
|
-
return new
|
|
709
|
+
return new paper2.Path.Circle({
|
|
705
710
|
center,
|
|
706
711
|
radius: Math.max(0, r)
|
|
707
712
|
});
|
|
708
713
|
} else if (shape === "ellipse") {
|
|
709
|
-
return new
|
|
714
|
+
return new paper2.Path.Ellipse({
|
|
710
715
|
center,
|
|
711
716
|
radius: [Math.max(0, width / 2), Math.max(0, height / 2)]
|
|
712
717
|
});
|
|
713
718
|
} else if (shape === "custom" && pathData) {
|
|
714
|
-
const path = new
|
|
719
|
+
const path = new paper2.Path();
|
|
715
720
|
path.pathData = pathData;
|
|
716
721
|
path.position = center;
|
|
717
722
|
if (width > 0 && height > 0 && path.bounds.width > 0 && path.bounds.height > 0) {
|
|
@@ -719,112 +724,131 @@ function createBaseShape(options) {
|
|
|
719
724
|
}
|
|
720
725
|
return path;
|
|
721
726
|
} else {
|
|
722
|
-
return new
|
|
727
|
+
return new paper2.Path.Rectangle({
|
|
723
728
|
point: [x - width / 2, y - height / 2],
|
|
724
729
|
size: [Math.max(0, width), Math.max(0, height)]
|
|
725
730
|
});
|
|
726
731
|
}
|
|
727
732
|
}
|
|
728
|
-
function
|
|
733
|
+
function createFeatureItem(feature, center) {
|
|
734
|
+
let item;
|
|
735
|
+
if (feature.shape === "rect") {
|
|
736
|
+
const w = feature.width || 10;
|
|
737
|
+
const h = feature.height || 10;
|
|
738
|
+
const r = feature.radius || 0;
|
|
739
|
+
item = new paper2.Path.Rectangle({
|
|
740
|
+
point: [center.x - w / 2, center.y - h / 2],
|
|
741
|
+
size: [w, h],
|
|
742
|
+
radius: r
|
|
743
|
+
});
|
|
744
|
+
} else {
|
|
745
|
+
const r = feature.radius || 5;
|
|
746
|
+
item = new paper2.Path.Circle({
|
|
747
|
+
center,
|
|
748
|
+
radius: r
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
if (feature.rotation) {
|
|
752
|
+
item.rotate(feature.rotation, center);
|
|
753
|
+
}
|
|
754
|
+
return item;
|
|
755
|
+
}
|
|
756
|
+
function getPerimeterShape(options) {
|
|
729
757
|
let mainShape = createBaseShape(options);
|
|
730
|
-
const {
|
|
731
|
-
if (
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
center,
|
|
744
|
-
radius: hole.outerRadius
|
|
745
|
-
});
|
|
746
|
-
const cut = hole.shape === "square" ? new paper.Path.Rectangle({
|
|
747
|
-
point: [
|
|
748
|
-
center.x - hole.innerRadius,
|
|
749
|
-
center.y - hole.innerRadius
|
|
750
|
-
],
|
|
751
|
-
size: [hole.innerRadius * 2, hole.innerRadius * 2]
|
|
752
|
-
}) : new paper.Path.Circle({
|
|
753
|
-
center,
|
|
754
|
-
radius: hole.innerRadius
|
|
755
|
-
});
|
|
756
|
-
if (!lugsPath) {
|
|
757
|
-
lugsPath = lug;
|
|
758
|
+
const { features } = options;
|
|
759
|
+
if (features && features.length > 0) {
|
|
760
|
+
const edgeFeatures = features.filter(
|
|
761
|
+
(f) => !f.placement || f.placement === "edge"
|
|
762
|
+
);
|
|
763
|
+
const adds = [];
|
|
764
|
+
const subtracts = [];
|
|
765
|
+
edgeFeatures.forEach((f) => {
|
|
766
|
+
const pos = resolveFeaturePosition(f, options);
|
|
767
|
+
const center = new paper2.Point(pos.x, pos.y);
|
|
768
|
+
const item = createFeatureItem(f, center);
|
|
769
|
+
if (f.operation === "add") {
|
|
770
|
+
adds.push(item);
|
|
758
771
|
} else {
|
|
772
|
+
subtracts.push(item);
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
if (adds.length > 0) {
|
|
776
|
+
for (const item of adds) {
|
|
759
777
|
try {
|
|
760
|
-
const temp =
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
778
|
+
const temp = mainShape.unite(item);
|
|
779
|
+
mainShape.remove();
|
|
780
|
+
item.remove();
|
|
781
|
+
mainShape = temp;
|
|
764
782
|
} catch (e) {
|
|
765
|
-
console.error("Geometry: Failed to unite
|
|
766
|
-
|
|
783
|
+
console.error("Geometry: Failed to unite feature", e);
|
|
784
|
+
item.remove();
|
|
767
785
|
}
|
|
768
786
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
787
|
+
}
|
|
788
|
+
if (subtracts.length > 0) {
|
|
789
|
+
for (const item of subtracts) {
|
|
772
790
|
try {
|
|
773
|
-
const temp =
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
791
|
+
const temp = mainShape.subtract(item);
|
|
792
|
+
mainShape.remove();
|
|
793
|
+
item.remove();
|
|
794
|
+
mainShape = temp;
|
|
777
795
|
} catch (e) {
|
|
778
|
-
console.error("Geometry: Failed to
|
|
779
|
-
|
|
796
|
+
console.error("Geometry: Failed to subtract feature", e);
|
|
797
|
+
item.remove();
|
|
780
798
|
}
|
|
781
799
|
}
|
|
782
|
-
});
|
|
783
|
-
if (lugsPath) {
|
|
784
|
-
try {
|
|
785
|
-
const temp = mainShape.unite(lugsPath);
|
|
786
|
-
mainShape.remove();
|
|
787
|
-
lugsPath.remove();
|
|
788
|
-
mainShape = temp;
|
|
789
|
-
} catch (e) {
|
|
790
|
-
console.error("Geometry: Failed to unite lugsPath to mainShape", e);
|
|
791
|
-
}
|
|
792
800
|
}
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
801
|
+
}
|
|
802
|
+
return mainShape;
|
|
803
|
+
}
|
|
804
|
+
function applySurfaceFeatures(shape, features, options) {
|
|
805
|
+
const internalFeatures = features.filter((f) => f.placement === "internal");
|
|
806
|
+
if (internalFeatures.length === 0) return shape;
|
|
807
|
+
let result = shape;
|
|
808
|
+
for (const f of internalFeatures) {
|
|
809
|
+
const pos = resolveFeaturePosition(f, options);
|
|
810
|
+
const center = new paper2.Point(pos.x, pos.y);
|
|
811
|
+
const item = createFeatureItem(f, center);
|
|
812
|
+
try {
|
|
813
|
+
if (f.operation === "add") {
|
|
814
|
+
const temp = result.unite(item);
|
|
815
|
+
result.remove();
|
|
816
|
+
item.remove();
|
|
817
|
+
result = temp;
|
|
818
|
+
} else {
|
|
819
|
+
const temp = result.subtract(item);
|
|
820
|
+
result.remove();
|
|
821
|
+
item.remove();
|
|
822
|
+
result = temp;
|
|
804
823
|
}
|
|
824
|
+
} catch (e) {
|
|
825
|
+
console.error("Geometry: Failed to apply surface feature", e);
|
|
826
|
+
item.remove();
|
|
805
827
|
}
|
|
806
828
|
}
|
|
807
|
-
return
|
|
829
|
+
return result;
|
|
808
830
|
}
|
|
809
831
|
function generateDielinePath(options) {
|
|
810
832
|
const paperWidth = options.canvasWidth || options.width * 2 || 2e3;
|
|
811
833
|
const paperHeight = options.canvasHeight || options.height * 2 || 2e3;
|
|
812
834
|
ensurePaper(paperWidth, paperHeight);
|
|
813
|
-
|
|
814
|
-
const
|
|
815
|
-
const
|
|
816
|
-
|
|
835
|
+
paper2.project.activeLayer.removeChildren();
|
|
836
|
+
const perimeter = getPerimeterShape(options);
|
|
837
|
+
const finalShape = applySurfaceFeatures(perimeter, options.features, options);
|
|
838
|
+
const pathData = finalShape.pathData;
|
|
839
|
+
finalShape.remove();
|
|
817
840
|
return pathData;
|
|
818
841
|
}
|
|
819
842
|
function generateMaskPath(options) {
|
|
820
843
|
ensurePaper(options.canvasWidth, options.canvasHeight);
|
|
821
|
-
|
|
844
|
+
paper2.project.activeLayer.removeChildren();
|
|
822
845
|
const { canvasWidth, canvasHeight } = options;
|
|
823
|
-
const maskRect = new
|
|
846
|
+
const maskRect = new paper2.Path.Rectangle({
|
|
824
847
|
point: [0, 0],
|
|
825
848
|
size: [canvasWidth, canvasHeight]
|
|
826
849
|
});
|
|
827
|
-
const
|
|
850
|
+
const perimeter = getPerimeterShape(options);
|
|
851
|
+
const mainShape = applySurfaceFeatures(perimeter, options.features, options);
|
|
828
852
|
const finalMask = maskRect.subtract(mainShape);
|
|
829
853
|
maskRect.remove();
|
|
830
854
|
mainShape.remove();
|
|
@@ -832,43 +856,15 @@ function generateMaskPath(options) {
|
|
|
832
856
|
finalMask.remove();
|
|
833
857
|
return pathData;
|
|
834
858
|
}
|
|
835
|
-
function generateBleedZonePath(
|
|
836
|
-
const paperWidth =
|
|
837
|
-
const paperHeight =
|
|
859
|
+
function generateBleedZonePath(originalOptions, offsetOptions, offset) {
|
|
860
|
+
const paperWidth = originalOptions.canvasWidth || originalOptions.width * 2 || 2e3;
|
|
861
|
+
const paperHeight = originalOptions.canvasHeight || originalOptions.height * 2 || 2e3;
|
|
838
862
|
ensurePaper(paperWidth, paperHeight);
|
|
839
|
-
|
|
840
|
-
const
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
stroker.strokeColor = new paper.Color("black");
|
|
845
|
-
stroker.strokeWidth = Math.abs(offset) * 2;
|
|
846
|
-
stroker.strokeJoin = "round";
|
|
847
|
-
stroker.strokeCap = "round";
|
|
848
|
-
let expanded;
|
|
849
|
-
try {
|
|
850
|
-
expanded = stroker.expand({ stroke: true, fill: false, insert: false });
|
|
851
|
-
} catch (e) {
|
|
852
|
-
stroker.remove();
|
|
853
|
-
shapeOffset = shapeOriginal.clone();
|
|
854
|
-
return shapeOffset.pathData;
|
|
855
|
-
}
|
|
856
|
-
stroker.remove();
|
|
857
|
-
if (offset > 0) {
|
|
858
|
-
shapeOffset = shapeOriginal.unite(expanded);
|
|
859
|
-
} else {
|
|
860
|
-
shapeOffset = shapeOriginal.subtract(expanded);
|
|
861
|
-
}
|
|
862
|
-
expanded.remove();
|
|
863
|
-
} else {
|
|
864
|
-
const offsetOptions = {
|
|
865
|
-
...options,
|
|
866
|
-
width: Math.max(0, options.width + offset * 2),
|
|
867
|
-
height: Math.max(0, options.height + offset * 2),
|
|
868
|
-
radius: options.radius === 0 ? 0 : Math.max(0, options.radius + offset)
|
|
869
|
-
};
|
|
870
|
-
shapeOffset = getDielineShape(offsetOptions);
|
|
871
|
-
}
|
|
863
|
+
paper2.project.activeLayer.removeChildren();
|
|
864
|
+
const pOriginal = getPerimeterShape(originalOptions);
|
|
865
|
+
const shapeOriginal = applySurfaceFeatures(pOriginal, originalOptions.features, originalOptions);
|
|
866
|
+
const pOffset = getPerimeterShape(offsetOptions);
|
|
867
|
+
const shapeOffset = applySurfaceFeatures(pOffset, offsetOptions.features, offsetOptions);
|
|
872
868
|
let bleedZone;
|
|
873
869
|
if (offset > 0) {
|
|
874
870
|
bleedZone = shapeOffset.subtract(shapeOriginal);
|
|
@@ -883,16 +879,16 @@ function generateBleedZonePath(options, offset) {
|
|
|
883
879
|
}
|
|
884
880
|
function getNearestPointOnDieline(point, options) {
|
|
885
881
|
ensurePaper(options.width * 2, options.height * 2);
|
|
886
|
-
|
|
882
|
+
paper2.project.activeLayer.removeChildren();
|
|
887
883
|
const shape = createBaseShape(options);
|
|
888
|
-
const p = new
|
|
884
|
+
const p = new paper2.Point(point.x, point.y);
|
|
889
885
|
const nearest = shape.getNearestPoint(p);
|
|
890
886
|
const result = { x: nearest.x, y: nearest.y };
|
|
891
887
|
shape.remove();
|
|
892
888
|
return result;
|
|
893
889
|
}
|
|
894
890
|
function getPathBounds(pathData) {
|
|
895
|
-
const path = new
|
|
891
|
+
const path = new paper2.Path();
|
|
896
892
|
path.pathData = pathData;
|
|
897
893
|
const bounds = path.bounds;
|
|
898
894
|
path.remove();
|
|
@@ -911,20 +907,41 @@ var DielineTool = class {
|
|
|
911
907
|
this.metadata = {
|
|
912
908
|
name: "DielineTool"
|
|
913
909
|
};
|
|
914
|
-
this.
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
910
|
+
this.state = {
|
|
911
|
+
unit: "mm",
|
|
912
|
+
shape: "rect",
|
|
913
|
+
width: 500,
|
|
914
|
+
height: 500,
|
|
915
|
+
radius: 0,
|
|
916
|
+
offset: 0,
|
|
917
|
+
padding: 140,
|
|
918
|
+
mainLine: {
|
|
919
|
+
width: 2.7,
|
|
920
|
+
color: "#FF0000",
|
|
921
|
+
dashLength: 5,
|
|
922
|
+
style: "solid"
|
|
923
|
+
},
|
|
924
|
+
offsetLine: {
|
|
925
|
+
width: 2.7,
|
|
926
|
+
color: "#FF0000",
|
|
927
|
+
dashLength: 5,
|
|
928
|
+
style: "solid"
|
|
929
|
+
},
|
|
930
|
+
insideColor: "rgba(0,0,0,0)",
|
|
931
|
+
outsideColor: "#ffffff",
|
|
932
|
+
showBleedLines: true,
|
|
933
|
+
features: []
|
|
934
|
+
};
|
|
926
935
|
if (options) {
|
|
927
|
-
|
|
936
|
+
if (options.mainLine) {
|
|
937
|
+
Object.assign(this.state.mainLine, options.mainLine);
|
|
938
|
+
delete options.mainLine;
|
|
939
|
+
}
|
|
940
|
+
if (options.offsetLine) {
|
|
941
|
+
Object.assign(this.state.offsetLine, options.offsetLine);
|
|
942
|
+
delete options.offsetLine;
|
|
943
|
+
}
|
|
944
|
+
Object.assign(this.state, options);
|
|
928
945
|
}
|
|
929
946
|
}
|
|
930
947
|
activate(context) {
|
|
@@ -936,38 +953,93 @@ var DielineTool = class {
|
|
|
936
953
|
}
|
|
937
954
|
const configService = context.services.get("ConfigurationService");
|
|
938
955
|
if (configService) {
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
);
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
);
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
);
|
|
959
|
-
|
|
960
|
-
this.pathData = configService.get("dieline.pathData", this.pathData);
|
|
956
|
+
const s = this.state;
|
|
957
|
+
s.unit = configService.get("dieline.unit", s.unit);
|
|
958
|
+
s.shape = configService.get("dieline.shape", s.shape);
|
|
959
|
+
s.width = configService.get("dieline.width", s.width);
|
|
960
|
+
s.height = configService.get("dieline.height", s.height);
|
|
961
|
+
s.radius = configService.get("dieline.radius", s.radius);
|
|
962
|
+
s.padding = configService.get("dieline.padding", s.padding);
|
|
963
|
+
s.offset = configService.get("dieline.offset", s.offset);
|
|
964
|
+
s.mainLine.width = configService.get("dieline.strokeWidth", s.mainLine.width);
|
|
965
|
+
s.mainLine.color = configService.get("dieline.strokeColor", s.mainLine.color);
|
|
966
|
+
s.mainLine.dashLength = configService.get("dieline.dashLength", s.mainLine.dashLength);
|
|
967
|
+
s.mainLine.style = configService.get("dieline.style", s.mainLine.style);
|
|
968
|
+
s.offsetLine.width = configService.get("dieline.offsetStrokeWidth", s.offsetLine.width);
|
|
969
|
+
s.offsetLine.color = configService.get("dieline.offsetStrokeColor", s.offsetLine.color);
|
|
970
|
+
s.offsetLine.dashLength = configService.get("dieline.offsetDashLength", s.offsetLine.dashLength);
|
|
971
|
+
s.offsetLine.style = configService.get("dieline.offsetStyle", s.offsetLine.style);
|
|
972
|
+
s.insideColor = configService.get("dieline.insideColor", s.insideColor);
|
|
973
|
+
s.outsideColor = configService.get("dieline.outsideColor", s.outsideColor);
|
|
974
|
+
s.showBleedLines = configService.get("dieline.showBleedLines", s.showBleedLines);
|
|
975
|
+
s.features = configService.get("dieline.features", s.features);
|
|
976
|
+
s.pathData = configService.get("dieline.pathData", s.pathData);
|
|
961
977
|
configService.onAnyChange((e) => {
|
|
962
978
|
if (e.key.startsWith("dieline.")) {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
979
|
+
console.log(`[DielineTool] Config change detected: ${e.key} -> ${e.value}`);
|
|
980
|
+
switch (e.key) {
|
|
981
|
+
case "dieline.unit":
|
|
982
|
+
s.unit = e.value;
|
|
983
|
+
break;
|
|
984
|
+
case "dieline.shape":
|
|
985
|
+
s.shape = e.value;
|
|
986
|
+
break;
|
|
987
|
+
case "dieline.width":
|
|
988
|
+
s.width = e.value;
|
|
989
|
+
break;
|
|
990
|
+
case "dieline.height":
|
|
991
|
+
s.height = e.value;
|
|
992
|
+
break;
|
|
993
|
+
case "dieline.radius":
|
|
994
|
+
s.radius = e.value;
|
|
995
|
+
break;
|
|
996
|
+
case "dieline.padding":
|
|
997
|
+
s.padding = e.value;
|
|
998
|
+
break;
|
|
999
|
+
case "dieline.offset":
|
|
1000
|
+
s.offset = e.value;
|
|
1001
|
+
break;
|
|
1002
|
+
case "dieline.strokeWidth":
|
|
1003
|
+
s.mainLine.width = e.value;
|
|
1004
|
+
break;
|
|
1005
|
+
case "dieline.strokeColor":
|
|
1006
|
+
s.mainLine.color = e.value;
|
|
1007
|
+
break;
|
|
1008
|
+
case "dieline.dashLength":
|
|
1009
|
+
s.mainLine.dashLength = e.value;
|
|
1010
|
+
break;
|
|
1011
|
+
case "dieline.style":
|
|
1012
|
+
s.mainLine.style = e.value;
|
|
1013
|
+
break;
|
|
1014
|
+
case "dieline.offsetStrokeWidth":
|
|
1015
|
+
s.offsetLine.width = e.value;
|
|
1016
|
+
break;
|
|
1017
|
+
case "dieline.offsetStrokeColor":
|
|
1018
|
+
s.offsetLine.color = e.value;
|
|
1019
|
+
break;
|
|
1020
|
+
case "dieline.offsetDashLength":
|
|
1021
|
+
s.offsetLine.dashLength = e.value;
|
|
1022
|
+
break;
|
|
1023
|
+
case "dieline.offsetStyle":
|
|
1024
|
+
s.offsetLine.style = e.value;
|
|
1025
|
+
break;
|
|
1026
|
+
case "dieline.insideColor":
|
|
1027
|
+
s.insideColor = e.value;
|
|
1028
|
+
break;
|
|
1029
|
+
case "dieline.outsideColor":
|
|
1030
|
+
s.outsideColor = e.value;
|
|
1031
|
+
break;
|
|
1032
|
+
case "dieline.showBleedLines":
|
|
1033
|
+
s.showBleedLines = e.value;
|
|
1034
|
+
break;
|
|
1035
|
+
case "dieline.features":
|
|
1036
|
+
s.features = e.value;
|
|
1037
|
+
break;
|
|
1038
|
+
case "dieline.pathData":
|
|
1039
|
+
s.pathData = e.value;
|
|
1040
|
+
break;
|
|
970
1041
|
}
|
|
1042
|
+
this.updateDieline();
|
|
971
1043
|
}
|
|
972
1044
|
});
|
|
973
1045
|
}
|
|
@@ -980,6 +1052,7 @@ var DielineTool = class {
|
|
|
980
1052
|
this.context = void 0;
|
|
981
1053
|
}
|
|
982
1054
|
contribute() {
|
|
1055
|
+
const s = this.state;
|
|
983
1056
|
return {
|
|
984
1057
|
[ContributionPointIds2.CONFIGURATIONS]: [
|
|
985
1058
|
{
|
|
@@ -987,14 +1060,14 @@ var DielineTool = class {
|
|
|
987
1060
|
type: "select",
|
|
988
1061
|
label: "Unit",
|
|
989
1062
|
options: ["px", "mm", "cm", "in"],
|
|
990
|
-
default:
|
|
1063
|
+
default: s.unit
|
|
991
1064
|
},
|
|
992
1065
|
{
|
|
993
1066
|
id: "dieline.shape",
|
|
994
1067
|
type: "select",
|
|
995
1068
|
label: "Shape",
|
|
996
1069
|
options: ["rect", "circle", "ellipse", "custom"],
|
|
997
|
-
default:
|
|
1070
|
+
default: s.shape
|
|
998
1071
|
},
|
|
999
1072
|
{
|
|
1000
1073
|
id: "dieline.width",
|
|
@@ -1002,7 +1075,7 @@ var DielineTool = class {
|
|
|
1002
1075
|
label: "Width",
|
|
1003
1076
|
min: 10,
|
|
1004
1077
|
max: 2e3,
|
|
1005
|
-
default:
|
|
1078
|
+
default: s.width
|
|
1006
1079
|
},
|
|
1007
1080
|
{
|
|
1008
1081
|
id: "dieline.height",
|
|
@@ -1010,7 +1083,7 @@ var DielineTool = class {
|
|
|
1010
1083
|
label: "Height",
|
|
1011
1084
|
min: 10,
|
|
1012
1085
|
max: 2e3,
|
|
1013
|
-
default:
|
|
1086
|
+
default: s.height
|
|
1014
1087
|
},
|
|
1015
1088
|
{
|
|
1016
1089
|
id: "dieline.radius",
|
|
@@ -1018,20 +1091,14 @@ var DielineTool = class {
|
|
|
1018
1091
|
label: "Corner Radius",
|
|
1019
1092
|
min: 0,
|
|
1020
1093
|
max: 500,
|
|
1021
|
-
default:
|
|
1022
|
-
},
|
|
1023
|
-
{
|
|
1024
|
-
id: "dieline.position",
|
|
1025
|
-
type: "json",
|
|
1026
|
-
label: "Position (Normalized)",
|
|
1027
|
-
default: this.radius
|
|
1094
|
+
default: s.radius
|
|
1028
1095
|
},
|
|
1029
1096
|
{
|
|
1030
1097
|
id: "dieline.padding",
|
|
1031
1098
|
type: "select",
|
|
1032
1099
|
label: "View Padding",
|
|
1033
1100
|
options: [0, 10, 20, 40, 60, 100, "2%", "5%", "10%", "15%", "20%"],
|
|
1034
|
-
default:
|
|
1101
|
+
default: s.padding
|
|
1035
1102
|
},
|
|
1036
1103
|
{
|
|
1037
1104
|
id: "dieline.offset",
|
|
@@ -1039,38 +1106,91 @@ var DielineTool = class {
|
|
|
1039
1106
|
label: "Bleed Offset",
|
|
1040
1107
|
min: -100,
|
|
1041
1108
|
max: 100,
|
|
1042
|
-
default:
|
|
1109
|
+
default: s.offset
|
|
1043
1110
|
},
|
|
1044
1111
|
{
|
|
1045
1112
|
id: "dieline.showBleedLines",
|
|
1046
1113
|
type: "boolean",
|
|
1047
1114
|
label: "Show Bleed Lines",
|
|
1048
|
-
default:
|
|
1115
|
+
default: s.showBleedLines
|
|
1116
|
+
},
|
|
1117
|
+
{
|
|
1118
|
+
id: "dieline.strokeWidth",
|
|
1119
|
+
type: "number",
|
|
1120
|
+
label: "Line Width",
|
|
1121
|
+
min: 0.1,
|
|
1122
|
+
max: 10,
|
|
1123
|
+
step: 0.1,
|
|
1124
|
+
default: s.mainLine.width
|
|
1125
|
+
},
|
|
1126
|
+
{
|
|
1127
|
+
id: "dieline.strokeColor",
|
|
1128
|
+
type: "color",
|
|
1129
|
+
label: "Line Color",
|
|
1130
|
+
default: s.mainLine.color
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
id: "dieline.dashLength",
|
|
1134
|
+
type: "number",
|
|
1135
|
+
label: "Dash Length",
|
|
1136
|
+
min: 1,
|
|
1137
|
+
max: 50,
|
|
1138
|
+
default: s.mainLine.dashLength
|
|
1049
1139
|
},
|
|
1050
1140
|
{
|
|
1051
1141
|
id: "dieline.style",
|
|
1052
1142
|
type: "select",
|
|
1053
1143
|
label: "Line Style",
|
|
1054
|
-
options: ["solid", "dashed"],
|
|
1055
|
-
default:
|
|
1144
|
+
options: ["solid", "dashed", "hidden"],
|
|
1145
|
+
default: s.mainLine.style
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
id: "dieline.offsetStrokeWidth",
|
|
1149
|
+
type: "number",
|
|
1150
|
+
label: "Offset Line Width",
|
|
1151
|
+
min: 0.1,
|
|
1152
|
+
max: 10,
|
|
1153
|
+
step: 0.1,
|
|
1154
|
+
default: s.offsetLine.width
|
|
1155
|
+
},
|
|
1156
|
+
{
|
|
1157
|
+
id: "dieline.offsetStrokeColor",
|
|
1158
|
+
type: "color",
|
|
1159
|
+
label: "Offset Line Color",
|
|
1160
|
+
default: s.offsetLine.color
|
|
1161
|
+
},
|
|
1162
|
+
{
|
|
1163
|
+
id: "dieline.offsetDashLength",
|
|
1164
|
+
type: "number",
|
|
1165
|
+
label: "Offset Dash Length",
|
|
1166
|
+
min: 1,
|
|
1167
|
+
max: 50,
|
|
1168
|
+
default: s.offsetLine.dashLength
|
|
1169
|
+
},
|
|
1170
|
+
{
|
|
1171
|
+
id: "dieline.offsetStyle",
|
|
1172
|
+
type: "select",
|
|
1173
|
+
label: "Offset Line Style",
|
|
1174
|
+
options: ["solid", "dashed", "hidden"],
|
|
1175
|
+
default: s.offsetLine.style
|
|
1056
1176
|
},
|
|
1057
1177
|
{
|
|
1058
1178
|
id: "dieline.insideColor",
|
|
1059
1179
|
type: "color",
|
|
1060
1180
|
label: "Inside Color",
|
|
1061
|
-
default:
|
|
1181
|
+
default: s.insideColor
|
|
1062
1182
|
},
|
|
1063
1183
|
{
|
|
1064
1184
|
id: "dieline.outsideColor",
|
|
1065
1185
|
type: "color",
|
|
1066
1186
|
label: "Outside Color",
|
|
1067
|
-
default:
|
|
1187
|
+
default: s.outsideColor
|
|
1068
1188
|
},
|
|
1069
1189
|
{
|
|
1070
|
-
id: "dieline.
|
|
1190
|
+
id: "dieline.features",
|
|
1071
1191
|
type: "json",
|
|
1072
|
-
label: "
|
|
1073
|
-
default:
|
|
1192
|
+
label: "Edge Features",
|
|
1193
|
+
default: s.features
|
|
1074
1194
|
}
|
|
1075
1195
|
],
|
|
1076
1196
|
[ContributionPointIds2.COMMANDS]: [
|
|
@@ -1092,24 +1212,18 @@ var DielineTool = class {
|
|
|
1092
1212
|
command: "detectEdge",
|
|
1093
1213
|
title: "Detect Edge from Image",
|
|
1094
1214
|
handler: async (imageUrl, options) => {
|
|
1095
|
-
var _a;
|
|
1096
1215
|
try {
|
|
1097
1216
|
const pathData = await ImageTracer.trace(imageUrl, options);
|
|
1098
1217
|
const bounds = getPathBounds(pathData);
|
|
1099
|
-
const currentMax = Math.max(
|
|
1218
|
+
const currentMax = Math.max(s.width, s.height);
|
|
1100
1219
|
const scale = currentMax / Math.max(bounds.width, bounds.height);
|
|
1101
1220
|
const newWidth = bounds.width * scale;
|
|
1102
1221
|
const newHeight = bounds.height * scale;
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
configService.update("dieline.height", newHeight);
|
|
1109
|
-
configService.update("dieline.shape", "custom");
|
|
1110
|
-
configService.update("dieline.pathData", pathData);
|
|
1111
|
-
}
|
|
1112
|
-
return pathData;
|
|
1222
|
+
return {
|
|
1223
|
+
pathData,
|
|
1224
|
+
width: newWidth,
|
|
1225
|
+
height: newHeight
|
|
1226
|
+
};
|
|
1113
1227
|
} catch (e) {
|
|
1114
1228
|
console.error("Edge detection failed", e);
|
|
1115
1229
|
throw e;
|
|
@@ -1168,15 +1282,15 @@ var DielineTool = class {
|
|
|
1168
1282
|
return new Pattern({ source: canvas, repetition: "repeat" });
|
|
1169
1283
|
}
|
|
1170
1284
|
resolvePadding(containerWidth, containerHeight) {
|
|
1171
|
-
if (typeof this.padding === "number") {
|
|
1172
|
-
return this.padding;
|
|
1285
|
+
if (typeof this.state.padding === "number") {
|
|
1286
|
+
return this.state.padding;
|
|
1173
1287
|
}
|
|
1174
|
-
if (typeof this.padding === "string") {
|
|
1175
|
-
if (this.padding.endsWith("%")) {
|
|
1176
|
-
const percent = parseFloat(this.padding) / 100;
|
|
1288
|
+
if (typeof this.state.padding === "string") {
|
|
1289
|
+
if (this.state.padding.endsWith("%")) {
|
|
1290
|
+
const percent = parseFloat(this.state.padding) / 100;
|
|
1177
1291
|
return Math.min(containerWidth, containerHeight) * percent;
|
|
1178
1292
|
}
|
|
1179
|
-
return parseFloat(this.padding) || 0;
|
|
1293
|
+
return parseFloat(this.state.padding) || 0;
|
|
1180
1294
|
}
|
|
1181
1295
|
return 0;
|
|
1182
1296
|
}
|
|
@@ -1189,14 +1303,14 @@ var DielineTool = class {
|
|
|
1189
1303
|
shape,
|
|
1190
1304
|
radius,
|
|
1191
1305
|
offset,
|
|
1192
|
-
|
|
1306
|
+
mainLine,
|
|
1307
|
+
offsetLine,
|
|
1193
1308
|
insideColor,
|
|
1194
1309
|
outsideColor,
|
|
1195
|
-
position,
|
|
1196
1310
|
showBleedLines,
|
|
1197
|
-
|
|
1198
|
-
} = this;
|
|
1199
|
-
let { width, height } = this;
|
|
1311
|
+
features
|
|
1312
|
+
} = this.state;
|
|
1313
|
+
let { width, height } = this.state;
|
|
1200
1314
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
1201
1315
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
1202
1316
|
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
@@ -1213,37 +1327,18 @@ var DielineTool = class {
|
|
|
1213
1327
|
const visualRadius = radius * scale;
|
|
1214
1328
|
const visualOffset = offset * scale;
|
|
1215
1329
|
layer.remove(...layer.getObjects());
|
|
1216
|
-
const
|
|
1217
|
-
|
|
1218
|
-
y: cy,
|
|
1219
|
-
width: visualWidth,
|
|
1220
|
-
height: visualHeight
|
|
1221
|
-
// Pass scale/unit context if needed by resolveHolePosition (though currently unused there)
|
|
1222
|
-
};
|
|
1223
|
-
const absoluteHoles = (holes || []).map((h) => {
|
|
1224
|
-
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1225
|
-
const offsetScale = unitScale * scale;
|
|
1226
|
-
const hWithPixelOffsets = {
|
|
1227
|
-
...h,
|
|
1228
|
-
offsetX: (h.offsetX || 0) * offsetScale,
|
|
1229
|
-
offsetY: (h.offsetY || 0) * offsetScale
|
|
1230
|
-
};
|
|
1231
|
-
const pos = resolveHolePosition(hWithPixelOffsets, geometryForHoles, {
|
|
1232
|
-
width: canvasW,
|
|
1233
|
-
height: canvasH
|
|
1234
|
-
});
|
|
1330
|
+
const absoluteFeatures = (features || []).map((f) => {
|
|
1331
|
+
const featureScale = scale;
|
|
1235
1332
|
return {
|
|
1236
|
-
...
|
|
1237
|
-
x:
|
|
1238
|
-
y:
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
// Store scaled offsets in the result for consistency, though pos is already resolved
|
|
1243
|
-
offsetX: hWithPixelOffsets.offsetX,
|
|
1244
|
-
offsetY: hWithPixelOffsets.offsetY
|
|
1333
|
+
...f,
|
|
1334
|
+
x: f.x,
|
|
1335
|
+
y: f.y,
|
|
1336
|
+
width: (f.width || 0) * featureScale,
|
|
1337
|
+
height: (f.height || 0) * featureScale,
|
|
1338
|
+
radius: (f.radius || 0) * featureScale
|
|
1245
1339
|
};
|
|
1246
1340
|
});
|
|
1341
|
+
const cutFeatures = absoluteFeatures.filter((f) => !f.skipCut);
|
|
1247
1342
|
const cutW = Math.max(0, visualWidth + visualOffset * 2);
|
|
1248
1343
|
const cutH = Math.max(0, visualHeight + visualOffset * 2);
|
|
1249
1344
|
const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
@@ -1256,8 +1351,8 @@ var DielineTool = class {
|
|
|
1256
1351
|
radius: cutR,
|
|
1257
1352
|
x: cx,
|
|
1258
1353
|
y: cy,
|
|
1259
|
-
|
|
1260
|
-
pathData: this.pathData
|
|
1354
|
+
features: cutFeatures,
|
|
1355
|
+
pathData: this.state.pathData
|
|
1261
1356
|
});
|
|
1262
1357
|
const mask = new Path(maskPathData, {
|
|
1263
1358
|
fill: outsideColor,
|
|
@@ -1278,8 +1373,9 @@ var DielineTool = class {
|
|
|
1278
1373
|
radius: cutR,
|
|
1279
1374
|
x: cx,
|
|
1280
1375
|
y: cy,
|
|
1281
|
-
|
|
1282
|
-
|
|
1376
|
+
features: cutFeatures,
|
|
1377
|
+
// Use same features as mask for consistency
|
|
1378
|
+
pathData: this.state.pathData,
|
|
1283
1379
|
canvasWidth: canvasW,
|
|
1284
1380
|
canvasHeight: canvasH
|
|
1285
1381
|
});
|
|
@@ -1303,15 +1399,27 @@ var DielineTool = class {
|
|
|
1303
1399
|
radius: visualRadius,
|
|
1304
1400
|
x: cx,
|
|
1305
1401
|
y: cy,
|
|
1306
|
-
|
|
1307
|
-
pathData: this.pathData,
|
|
1402
|
+
features: cutFeatures,
|
|
1403
|
+
pathData: this.state.pathData,
|
|
1404
|
+
canvasWidth: canvasW,
|
|
1405
|
+
canvasHeight: canvasH
|
|
1406
|
+
},
|
|
1407
|
+
{
|
|
1408
|
+
shape,
|
|
1409
|
+
width: cutW,
|
|
1410
|
+
height: cutH,
|
|
1411
|
+
radius: cutR,
|
|
1412
|
+
x: cx,
|
|
1413
|
+
y: cy,
|
|
1414
|
+
features: cutFeatures,
|
|
1415
|
+
pathData: this.state.pathData,
|
|
1308
1416
|
canvasWidth: canvasW,
|
|
1309
1417
|
canvasHeight: canvasH
|
|
1310
1418
|
},
|
|
1311
1419
|
visualOffset
|
|
1312
1420
|
);
|
|
1313
1421
|
if (showBleedLines !== false) {
|
|
1314
|
-
const pattern = this.createHatchPattern(
|
|
1422
|
+
const pattern = this.createHatchPattern(mainLine.color);
|
|
1315
1423
|
if (pattern) {
|
|
1316
1424
|
const bleedObj = new Path(bleedPathData, {
|
|
1317
1425
|
fill: pattern,
|
|
@@ -1332,18 +1440,16 @@ var DielineTool = class {
|
|
|
1332
1440
|
radius: cutR,
|
|
1333
1441
|
x: cx,
|
|
1334
1442
|
y: cy,
|
|
1335
|
-
|
|
1336
|
-
pathData: this.pathData,
|
|
1443
|
+
features: cutFeatures,
|
|
1444
|
+
pathData: this.state.pathData,
|
|
1337
1445
|
canvasWidth: canvasW,
|
|
1338
1446
|
canvasHeight: canvasH
|
|
1339
1447
|
});
|
|
1340
1448
|
const offsetBorderObj = new Path(offsetPathData, {
|
|
1341
1449
|
fill: null,
|
|
1342
|
-
stroke: "
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
strokeDashArray: [4, 4],
|
|
1346
|
-
// Dashed
|
|
1450
|
+
stroke: offsetLine.style === "hidden" ? null : offsetLine.color,
|
|
1451
|
+
strokeWidth: offsetLine.width,
|
|
1452
|
+
strokeDashArray: offsetLine.style === "dashed" ? [offsetLine.dashLength, offsetLine.dashLength] : void 0,
|
|
1347
1453
|
selectable: false,
|
|
1348
1454
|
evented: false,
|
|
1349
1455
|
originX: "left",
|
|
@@ -1358,16 +1464,16 @@ var DielineTool = class {
|
|
|
1358
1464
|
radius: visualRadius,
|
|
1359
1465
|
x: cx,
|
|
1360
1466
|
y: cy,
|
|
1361
|
-
|
|
1362
|
-
pathData: this.pathData,
|
|
1467
|
+
features: absoluteFeatures,
|
|
1468
|
+
pathData: this.state.pathData,
|
|
1363
1469
|
canvasWidth: canvasW,
|
|
1364
1470
|
canvasHeight: canvasH
|
|
1365
1471
|
});
|
|
1366
1472
|
const borderObj = new Path(borderPathData, {
|
|
1367
1473
|
fill: "transparent",
|
|
1368
|
-
stroke: "
|
|
1369
|
-
strokeWidth:
|
|
1370
|
-
strokeDashArray: style === "dashed" ? [
|
|
1474
|
+
stroke: mainLine.style === "hidden" ? null : mainLine.color,
|
|
1475
|
+
strokeWidth: mainLine.width,
|
|
1476
|
+
strokeDashArray: mainLine.style === "dashed" ? [mainLine.dashLength, mainLine.dashLength] : void 0,
|
|
1371
1477
|
selectable: false,
|
|
1372
1478
|
evented: false,
|
|
1373
1479
|
originX: "left",
|
|
@@ -1399,7 +1505,7 @@ var DielineTool = class {
|
|
|
1399
1505
|
}
|
|
1400
1506
|
getGeometry() {
|
|
1401
1507
|
if (!this.canvasService) return null;
|
|
1402
|
-
const { unit, shape, width, height, radius,
|
|
1508
|
+
const { unit, shape, width, height, radius, offset, mainLine, pathData } = this.state;
|
|
1403
1509
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
1404
1510
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
1405
1511
|
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
@@ -1422,16 +1528,17 @@ var DielineTool = class {
|
|
|
1422
1528
|
height: visualHeight,
|
|
1423
1529
|
radius: radius * scale,
|
|
1424
1530
|
offset: offset * scale,
|
|
1425
|
-
// Pass scale to help other tools (like
|
|
1531
|
+
// Pass scale to help other tools (like FeatureTool) convert units
|
|
1426
1532
|
scale,
|
|
1427
|
-
|
|
1533
|
+
strokeWidth: mainLine.width,
|
|
1534
|
+
pathData
|
|
1428
1535
|
};
|
|
1429
1536
|
}
|
|
1430
1537
|
async exportCutImage() {
|
|
1431
1538
|
if (!this.canvasService) return null;
|
|
1432
1539
|
const userLayer = this.canvasService.getLayer("user");
|
|
1433
1540
|
if (!userLayer) return null;
|
|
1434
|
-
const { shape, width, height, radius,
|
|
1541
|
+
const { shape, width, height, radius, features, unit, pathData } = this.state;
|
|
1435
1542
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
1436
1543
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
1437
1544
|
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
@@ -1446,55 +1553,43 @@ var DielineTool = class {
|
|
|
1446
1553
|
const visualWidth = layout.width;
|
|
1447
1554
|
const visualHeight = layout.height;
|
|
1448
1555
|
const visualRadius = radius * scale;
|
|
1449
|
-
const
|
|
1450
|
-
const
|
|
1451
|
-
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1452
|
-
const pos = resolveHolePosition(
|
|
1453
|
-
{
|
|
1454
|
-
...h,
|
|
1455
|
-
offsetX: (h.offsetX || 0) * unitScale * scale,
|
|
1456
|
-
offsetY: (h.offsetY || 0) * unitScale * scale
|
|
1457
|
-
},
|
|
1458
|
-
{ x: cx, y: cy, width: visualWidth, height: visualHeight },
|
|
1459
|
-
{ width: canvasW, height: canvasH }
|
|
1460
|
-
);
|
|
1556
|
+
const absoluteFeatures = (features || []).map((f) => {
|
|
1557
|
+
const featureScale = scale;
|
|
1461
1558
|
return {
|
|
1462
|
-
...
|
|
1463
|
-
x:
|
|
1464
|
-
y:
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
offsetY: (h.offsetY || 0) * unitScale * scale
|
|
1559
|
+
...f,
|
|
1560
|
+
x: f.x,
|
|
1561
|
+
y: f.y,
|
|
1562
|
+
width: (f.width || 0) * featureScale,
|
|
1563
|
+
height: (f.height || 0) * featureScale,
|
|
1564
|
+
radius: (f.radius || 0) * featureScale
|
|
1469
1565
|
};
|
|
1470
1566
|
});
|
|
1471
|
-
const
|
|
1567
|
+
const cutFeatures = absoluteFeatures.filter((f) => !f.skipCut);
|
|
1568
|
+
const generatedPathData = generateDielinePath({
|
|
1472
1569
|
shape,
|
|
1473
1570
|
width: visualWidth,
|
|
1474
1571
|
height: visualHeight,
|
|
1475
1572
|
radius: visualRadius,
|
|
1476
1573
|
x: cx,
|
|
1477
1574
|
y: cy,
|
|
1478
|
-
|
|
1479
|
-
pathData
|
|
1575
|
+
features: cutFeatures,
|
|
1576
|
+
pathData,
|
|
1480
1577
|
canvasWidth: canvasW,
|
|
1481
1578
|
canvasHeight: canvasH
|
|
1482
1579
|
});
|
|
1483
1580
|
const clonedLayer = await userLayer.clone();
|
|
1484
|
-
const clipPath = new Path(
|
|
1581
|
+
const clipPath = new Path(generatedPathData, {
|
|
1485
1582
|
originX: "left",
|
|
1486
1583
|
originY: "top",
|
|
1487
1584
|
left: 0,
|
|
1488
1585
|
top: 0,
|
|
1489
1586
|
absolutePositioned: true
|
|
1490
|
-
// Important for groups
|
|
1491
1587
|
});
|
|
1492
1588
|
clonedLayer.clipPath = clipPath;
|
|
1493
1589
|
const bounds = clipPath.getBoundingRect();
|
|
1494
1590
|
const dataUrl = clonedLayer.toDataURL({
|
|
1495
1591
|
format: "png",
|
|
1496
1592
|
multiplier: 2,
|
|
1497
|
-
// Better quality
|
|
1498
1593
|
left: bounds.left,
|
|
1499
1594
|
top: bounds.top,
|
|
1500
1595
|
width: bounds.width,
|
|
@@ -1665,25 +1760,28 @@ var FilmTool = class {
|
|
|
1665
1760
|
}
|
|
1666
1761
|
};
|
|
1667
1762
|
|
|
1668
|
-
// src/
|
|
1763
|
+
// src/feature.ts
|
|
1669
1764
|
import {
|
|
1670
1765
|
ContributionPointIds as ContributionPointIds4
|
|
1671
1766
|
} from "@pooder/core";
|
|
1672
1767
|
import { Circle, Group, Point, Rect as Rect2 } from "fabric";
|
|
1673
|
-
var
|
|
1768
|
+
var FeatureTool = class {
|
|
1674
1769
|
constructor(options) {
|
|
1675
|
-
this.id = "pooder.kit.
|
|
1770
|
+
this.id = "pooder.kit.feature";
|
|
1676
1771
|
this.metadata = {
|
|
1677
|
-
name: "
|
|
1772
|
+
name: "FeatureTool"
|
|
1678
1773
|
};
|
|
1679
|
-
this.
|
|
1680
|
-
this.constraintTarget = "bleed";
|
|
1774
|
+
this.features = [];
|
|
1681
1775
|
this.isUpdatingConfig = false;
|
|
1776
|
+
this.isToolActive = false;
|
|
1682
1777
|
this.handleMoving = null;
|
|
1683
1778
|
this.handleModified = null;
|
|
1684
1779
|
this.handleDielineChange = null;
|
|
1685
|
-
// Cache geometry to enforce constraints during drag
|
|
1686
1780
|
this.currentGeometry = null;
|
|
1781
|
+
this.onToolActivated = (event) => {
|
|
1782
|
+
this.isToolActive = event.id === this.id;
|
|
1783
|
+
this.updateVisibility();
|
|
1784
|
+
};
|
|
1687
1785
|
if (options) {
|
|
1688
1786
|
Object.assign(this, options);
|
|
1689
1787
|
}
|
|
@@ -1692,135 +1790,82 @@ var HoleTool = class {
|
|
|
1692
1790
|
this.context = context;
|
|
1693
1791
|
this.canvasService = context.services.get("CanvasService");
|
|
1694
1792
|
if (!this.canvasService) {
|
|
1695
|
-
console.warn("CanvasService not found for
|
|
1793
|
+
console.warn("CanvasService not found for FeatureTool");
|
|
1696
1794
|
return;
|
|
1697
1795
|
}
|
|
1698
1796
|
const configService = context.services.get(
|
|
1699
1797
|
"ConfigurationService"
|
|
1700
1798
|
);
|
|
1701
1799
|
if (configService) {
|
|
1702
|
-
this.
|
|
1703
|
-
"hole.constraintTarget",
|
|
1704
|
-
this.constraintTarget
|
|
1705
|
-
);
|
|
1706
|
-
this.holes = configService.get("dieline.holes", []);
|
|
1800
|
+
this.features = configService.get("dieline.features", []);
|
|
1707
1801
|
configService.onAnyChange((e) => {
|
|
1708
1802
|
if (this.isUpdatingConfig) return;
|
|
1709
|
-
if (e.key === "
|
|
1710
|
-
this.
|
|
1711
|
-
this.enforceConstraints();
|
|
1712
|
-
}
|
|
1713
|
-
if (e.key === "dieline.holes") {
|
|
1714
|
-
this.holes = e.value || [];
|
|
1803
|
+
if (e.key === "dieline.features") {
|
|
1804
|
+
this.features = e.value || [];
|
|
1715
1805
|
this.redraw();
|
|
1716
1806
|
}
|
|
1717
1807
|
});
|
|
1718
1808
|
}
|
|
1809
|
+
context.eventBus.on("tool:activated", this.onToolActivated);
|
|
1719
1810
|
this.setup();
|
|
1720
1811
|
}
|
|
1721
1812
|
deactivate(context) {
|
|
1813
|
+
context.eventBus.off("tool:activated", this.onToolActivated);
|
|
1722
1814
|
this.teardown();
|
|
1723
1815
|
this.canvasService = void 0;
|
|
1724
1816
|
this.context = void 0;
|
|
1725
1817
|
}
|
|
1818
|
+
updateVisibility() {
|
|
1819
|
+
if (!this.canvasService) return;
|
|
1820
|
+
const canvas = this.canvasService.canvas;
|
|
1821
|
+
const markers = canvas.getObjects().filter((obj) => {
|
|
1822
|
+
var _a;
|
|
1823
|
+
return ((_a = obj.data) == null ? void 0 : _a.type) === "feature-marker";
|
|
1824
|
+
});
|
|
1825
|
+
markers.forEach((marker) => {
|
|
1826
|
+
marker.set({
|
|
1827
|
+
visible: this.isToolActive,
|
|
1828
|
+
// Or just selectable: false if we want them visible but locked
|
|
1829
|
+
selectable: this.isToolActive,
|
|
1830
|
+
evented: this.isToolActive
|
|
1831
|
+
});
|
|
1832
|
+
});
|
|
1833
|
+
canvas.requestRenderAll();
|
|
1834
|
+
}
|
|
1726
1835
|
contribute() {
|
|
1727
1836
|
return {
|
|
1728
|
-
[ContributionPointIds4.CONFIGURATIONS]: [
|
|
1729
|
-
{
|
|
1730
|
-
id: "hole.constraintTarget",
|
|
1731
|
-
type: "select",
|
|
1732
|
-
label: "Constraint Target",
|
|
1733
|
-
options: ["original", "bleed"],
|
|
1734
|
-
default: "bleed"
|
|
1735
|
-
}
|
|
1736
|
-
],
|
|
1737
1837
|
[ContributionPointIds4.COMMANDS]: [
|
|
1738
1838
|
{
|
|
1739
|
-
command: "
|
|
1740
|
-
title: "
|
|
1741
|
-
handler: () => {
|
|
1742
|
-
|
|
1743
|
-
if (!this.canvasService) return false;
|
|
1744
|
-
let defaultPos = { x: this.canvasService.canvas.width / 2, y: 50 };
|
|
1745
|
-
if (this.currentGeometry) {
|
|
1746
|
-
const g = this.currentGeometry;
|
|
1747
|
-
const topCenter = { x: g.x, y: g.y - g.height / 2 };
|
|
1748
|
-
defaultPos = getNearestPointOnDieline(topCenter, {
|
|
1749
|
-
...g,
|
|
1750
|
-
holes: []
|
|
1751
|
-
});
|
|
1752
|
-
}
|
|
1753
|
-
const { width, height } = this.canvasService.canvas;
|
|
1754
|
-
const normalizedHole = Coordinate.normalizePoint(defaultPos, {
|
|
1755
|
-
width: width || 800,
|
|
1756
|
-
height: height || 600
|
|
1757
|
-
});
|
|
1758
|
-
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1759
|
-
"ConfigurationService"
|
|
1760
|
-
);
|
|
1761
|
-
if (configService) {
|
|
1762
|
-
configService.update("dieline.holes", [
|
|
1763
|
-
{
|
|
1764
|
-
x: normalizedHole.x,
|
|
1765
|
-
y: normalizedHole.y,
|
|
1766
|
-
innerRadius: 15,
|
|
1767
|
-
outerRadius: 25
|
|
1768
|
-
}
|
|
1769
|
-
]);
|
|
1770
|
-
}
|
|
1771
|
-
return true;
|
|
1839
|
+
command: "addFeature",
|
|
1840
|
+
title: "Add Edge Feature",
|
|
1841
|
+
handler: (type = "subtract") => {
|
|
1842
|
+
return this.addFeature(type);
|
|
1772
1843
|
}
|
|
1773
1844
|
},
|
|
1774
1845
|
{
|
|
1775
1846
|
command: "addHole",
|
|
1776
1847
|
title: "Add Hole",
|
|
1777
|
-
handler: (
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
normalizedX = gw > 0 ? (x - left) / gw : 0.5;
|
|
1787
|
-
normalizedY = gh > 0 ? (y - top) / gh : 0.5;
|
|
1788
|
-
} else {
|
|
1789
|
-
const { width, height } = this.canvasService.canvas;
|
|
1790
|
-
normalizedX = Coordinate.toNormalized(x, width || 800);
|
|
1791
|
-
normalizedY = Coordinate.toNormalized(y, height || 600);
|
|
1792
|
-
}
|
|
1793
|
-
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1794
|
-
"ConfigurationService"
|
|
1795
|
-
);
|
|
1796
|
-
if (configService) {
|
|
1797
|
-
const currentHoles = configService.get("dieline.holes", []);
|
|
1798
|
-
const lastHole = currentHoles[currentHoles.length - 1];
|
|
1799
|
-
const innerRadius = (_b = lastHole == null ? void 0 : lastHole.innerRadius) != null ? _b : 15;
|
|
1800
|
-
const outerRadius = (_c = lastHole == null ? void 0 : lastHole.outerRadius) != null ? _c : 25;
|
|
1801
|
-
const shape = (_d = lastHole == null ? void 0 : lastHole.shape) != null ? _d : "circle";
|
|
1802
|
-
const newHole = {
|
|
1803
|
-
x: normalizedX,
|
|
1804
|
-
y: normalizedY,
|
|
1805
|
-
shape,
|
|
1806
|
-
innerRadius,
|
|
1807
|
-
outerRadius
|
|
1808
|
-
};
|
|
1809
|
-
configService.update("dieline.holes", [...currentHoles, newHole]);
|
|
1810
|
-
}
|
|
1811
|
-
return true;
|
|
1848
|
+
handler: () => {
|
|
1849
|
+
return this.addFeature("subtract");
|
|
1850
|
+
}
|
|
1851
|
+
},
|
|
1852
|
+
{
|
|
1853
|
+
command: "addDoubleLayerHole",
|
|
1854
|
+
title: "Add Double Layer Hole",
|
|
1855
|
+
handler: () => {
|
|
1856
|
+
return this.addDoubleLayerHole();
|
|
1812
1857
|
}
|
|
1813
1858
|
},
|
|
1814
1859
|
{
|
|
1815
|
-
command: "
|
|
1816
|
-
title: "Clear
|
|
1860
|
+
command: "clearFeatures",
|
|
1861
|
+
title: "Clear Features",
|
|
1817
1862
|
handler: () => {
|
|
1818
1863
|
var _a;
|
|
1819
1864
|
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1820
1865
|
"ConfigurationService"
|
|
1821
1866
|
);
|
|
1822
1867
|
if (configService) {
|
|
1823
|
-
configService.update("dieline.
|
|
1868
|
+
configService.update("dieline.features", []);
|
|
1824
1869
|
}
|
|
1825
1870
|
return true;
|
|
1826
1871
|
}
|
|
@@ -1828,6 +1873,82 @@ var HoleTool = class {
|
|
|
1828
1873
|
]
|
|
1829
1874
|
};
|
|
1830
1875
|
}
|
|
1876
|
+
addFeature(type) {
|
|
1877
|
+
var _a;
|
|
1878
|
+
if (!this.canvasService) return false;
|
|
1879
|
+
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1880
|
+
"ConfigurationService"
|
|
1881
|
+
);
|
|
1882
|
+
const unit = (configService == null ? void 0 : configService.get("dieline.unit", "mm")) || "mm";
|
|
1883
|
+
const defaultSize = Coordinate.convertUnit(10, "mm", unit);
|
|
1884
|
+
const newFeature = {
|
|
1885
|
+
id: Date.now().toString(),
|
|
1886
|
+
operation: type,
|
|
1887
|
+
placement: "edge",
|
|
1888
|
+
shape: "rect",
|
|
1889
|
+
x: 0.5,
|
|
1890
|
+
y: 0,
|
|
1891
|
+
// Top edge
|
|
1892
|
+
width: defaultSize,
|
|
1893
|
+
height: defaultSize,
|
|
1894
|
+
rotation: 0
|
|
1895
|
+
};
|
|
1896
|
+
if (configService) {
|
|
1897
|
+
const current = configService.get(
|
|
1898
|
+
"dieline.features",
|
|
1899
|
+
[]
|
|
1900
|
+
);
|
|
1901
|
+
configService.update("dieline.features", [...current, newFeature]);
|
|
1902
|
+
}
|
|
1903
|
+
return true;
|
|
1904
|
+
}
|
|
1905
|
+
addDoubleLayerHole() {
|
|
1906
|
+
var _a;
|
|
1907
|
+
if (!this.canvasService) return false;
|
|
1908
|
+
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
1909
|
+
"ConfigurationService"
|
|
1910
|
+
);
|
|
1911
|
+
const unit = (configService == null ? void 0 : configService.get("dieline.unit", "mm")) || "mm";
|
|
1912
|
+
const lugRadius = Coordinate.convertUnit(20, "mm", unit);
|
|
1913
|
+
const holeRadius = Coordinate.convertUnit(15, "mm", unit);
|
|
1914
|
+
const groupId = Date.now().toString();
|
|
1915
|
+
const timestamp = Date.now();
|
|
1916
|
+
const lug = {
|
|
1917
|
+
id: `${timestamp}-lug`,
|
|
1918
|
+
groupId,
|
|
1919
|
+
operation: "add",
|
|
1920
|
+
shape: "circle",
|
|
1921
|
+
placement: "edge",
|
|
1922
|
+
x: 0.5,
|
|
1923
|
+
y: 0,
|
|
1924
|
+
radius: lugRadius,
|
|
1925
|
+
// 20mm
|
|
1926
|
+
rotation: 0
|
|
1927
|
+
};
|
|
1928
|
+
const hole = {
|
|
1929
|
+
id: `${timestamp}-hole`,
|
|
1930
|
+
groupId,
|
|
1931
|
+
operation: "subtract",
|
|
1932
|
+
shape: "circle",
|
|
1933
|
+
placement: "edge",
|
|
1934
|
+
x: 0.5,
|
|
1935
|
+
y: 0,
|
|
1936
|
+
radius: holeRadius,
|
|
1937
|
+
// 15mm
|
|
1938
|
+
rotation: 0
|
|
1939
|
+
};
|
|
1940
|
+
if (configService) {
|
|
1941
|
+
const current = configService.get(
|
|
1942
|
+
"dieline.features",
|
|
1943
|
+
[]
|
|
1944
|
+
);
|
|
1945
|
+
configService.update("dieline.features", [...current, lug, hole]);
|
|
1946
|
+
}
|
|
1947
|
+
return true;
|
|
1948
|
+
}
|
|
1949
|
+
getGeometryForFeature(geometry, feature) {
|
|
1950
|
+
return geometry;
|
|
1951
|
+
}
|
|
1831
1952
|
setup() {
|
|
1832
1953
|
if (!this.canvasService || !this.context) return;
|
|
1833
1954
|
const canvas = this.canvasService.canvas;
|
|
@@ -1835,10 +1956,7 @@ var HoleTool = class {
|
|
|
1835
1956
|
this.handleDielineChange = (geometry) => {
|
|
1836
1957
|
this.currentGeometry = geometry;
|
|
1837
1958
|
this.redraw();
|
|
1838
|
-
|
|
1839
|
-
if (changed) {
|
|
1840
|
-
this.syncHolesToDieline();
|
|
1841
|
-
}
|
|
1959
|
+
this.enforceConstraints();
|
|
1842
1960
|
};
|
|
1843
1961
|
this.context.eventBus.on(
|
|
1844
1962
|
"dieline:geometry:change",
|
|
@@ -1848,69 +1966,101 @@ var HoleTool = class {
|
|
|
1848
1966
|
const commandService = this.context.services.get("CommandService");
|
|
1849
1967
|
if (commandService) {
|
|
1850
1968
|
try {
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
Promise.resolve(geometry).then((g) => {
|
|
1969
|
+
Promise.resolve(commandService.executeCommand("getGeometry")).then(
|
|
1970
|
+
(g) => {
|
|
1854
1971
|
if (g) {
|
|
1855
1972
|
this.currentGeometry = g;
|
|
1856
|
-
this.
|
|
1857
|
-
this.initializeHoles();
|
|
1973
|
+
this.redraw();
|
|
1858
1974
|
}
|
|
1859
|
-
}
|
|
1860
|
-
|
|
1975
|
+
}
|
|
1976
|
+
);
|
|
1861
1977
|
} catch (e) {
|
|
1862
1978
|
}
|
|
1863
1979
|
}
|
|
1864
1980
|
if (!this.handleMoving) {
|
|
1865
1981
|
this.handleMoving = (e) => {
|
|
1866
|
-
var _a, _b, _c, _d
|
|
1982
|
+
var _a, _b, _c, _d;
|
|
1867
1983
|
const target = e.target;
|
|
1868
|
-
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "
|
|
1984
|
+
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "feature-marker") return;
|
|
1869
1985
|
if (!this.currentGeometry) return;
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
}
|
|
1882
|
-
const
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
constraintGeometry,
|
|
1886
|
-
(_d = holeData == null ? void 0 : holeData.innerRadius) != null ? _d : 15,
|
|
1887
|
-
(_e = holeData == null ? void 0 : holeData.outerRadius) != null ? _e : 25
|
|
1986
|
+
let feature;
|
|
1987
|
+
if ((_b = target.data) == null ? void 0 : _b.isGroup) {
|
|
1988
|
+
const indices = (_c = target.data) == null ? void 0 : _c.indices;
|
|
1989
|
+
if (indices && indices.length > 0) {
|
|
1990
|
+
feature = this.features[indices[0]];
|
|
1991
|
+
}
|
|
1992
|
+
} else {
|
|
1993
|
+
const index = (_d = target.data) == null ? void 0 : _d.index;
|
|
1994
|
+
if (index !== void 0) {
|
|
1995
|
+
feature = this.features[index];
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
const geometry = this.getGeometryForFeature(
|
|
1999
|
+
this.currentGeometry,
|
|
2000
|
+
feature
|
|
1888
2001
|
);
|
|
2002
|
+
const p = new Point(target.left, target.top);
|
|
2003
|
+
const markerStrokeWidth = (target.strokeWidth || 2) * (target.scaleX || 1);
|
|
2004
|
+
const minDim = Math.min(target.getScaledWidth(), target.getScaledHeight());
|
|
2005
|
+
const limit = Math.max(0, minDim / 2 - markerStrokeWidth);
|
|
2006
|
+
const snapped = this.constrainPosition(p, geometry, limit, feature);
|
|
1889
2007
|
target.set({
|
|
1890
|
-
left:
|
|
1891
|
-
top:
|
|
2008
|
+
left: snapped.x,
|
|
2009
|
+
top: snapped.y
|
|
1892
2010
|
});
|
|
1893
2011
|
};
|
|
1894
2012
|
canvas.on("object:moving", this.handleMoving);
|
|
1895
2013
|
}
|
|
1896
2014
|
if (!this.handleModified) {
|
|
1897
2015
|
this.handleModified = (e) => {
|
|
1898
|
-
var _a;
|
|
2016
|
+
var _a, _b, _c, _d;
|
|
1899
2017
|
const target = e.target;
|
|
1900
|
-
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
2018
|
+
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "feature-marker") return;
|
|
2019
|
+
if ((_b = target.data) == null ? void 0 : _b.isGroup) {
|
|
2020
|
+
const groupObj = target;
|
|
2021
|
+
const indices = (_c = groupObj.data) == null ? void 0 : _c.indices;
|
|
2022
|
+
if (!indices) return;
|
|
2023
|
+
const groupCenter = new Point(groupObj.left, groupObj.top);
|
|
2024
|
+
const newFeatures = [...this.features];
|
|
2025
|
+
const { x, y } = this.currentGeometry;
|
|
2026
|
+
groupObj.getObjects().forEach((child, i) => {
|
|
2027
|
+
const originalIndex = indices[i];
|
|
2028
|
+
const feature = this.features[originalIndex];
|
|
2029
|
+
const geometry = this.getGeometryForFeature(
|
|
2030
|
+
this.currentGeometry,
|
|
2031
|
+
feature
|
|
2032
|
+
);
|
|
2033
|
+
const { width, height } = geometry;
|
|
2034
|
+
const layoutLeft = x - width / 2;
|
|
2035
|
+
const layoutTop = y - height / 2;
|
|
2036
|
+
const absX = groupCenter.x + (child.left || 0);
|
|
2037
|
+
const absY = groupCenter.y + (child.top || 0);
|
|
2038
|
+
const normalizedX = width > 0 ? (absX - layoutLeft) / width : 0.5;
|
|
2039
|
+
const normalizedY = height > 0 ? (absY - layoutTop) / height : 0.5;
|
|
2040
|
+
newFeatures[originalIndex] = {
|
|
2041
|
+
...newFeatures[originalIndex],
|
|
2042
|
+
x: normalizedX,
|
|
2043
|
+
y: normalizedY
|
|
2044
|
+
};
|
|
2045
|
+
});
|
|
2046
|
+
this.features = newFeatures;
|
|
2047
|
+
const configService = (_d = this.context) == null ? void 0 : _d.services.get(
|
|
2048
|
+
"ConfigurationService"
|
|
2049
|
+
);
|
|
2050
|
+
if (configService) {
|
|
2051
|
+
this.isUpdatingConfig = true;
|
|
2052
|
+
try {
|
|
2053
|
+
configService.update("dieline.features", this.features);
|
|
2054
|
+
} finally {
|
|
2055
|
+
this.isUpdatingConfig = false;
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
} else {
|
|
2059
|
+
this.syncFeatureFromCanvas(target);
|
|
1904
2060
|
}
|
|
1905
2061
|
};
|
|
1906
2062
|
canvas.on("object:modified", this.handleModified);
|
|
1907
2063
|
}
|
|
1908
|
-
this.initializeHoles();
|
|
1909
|
-
}
|
|
1910
|
-
initializeHoles() {
|
|
1911
|
-
if (!this.canvasService) return;
|
|
1912
|
-
this.redraw();
|
|
1913
|
-
this.syncHolesToDieline();
|
|
1914
2064
|
}
|
|
1915
2065
|
teardown() {
|
|
1916
2066
|
if (!this.canvasService) return;
|
|
@@ -1932,357 +2082,274 @@ var HoleTool = class {
|
|
|
1932
2082
|
}
|
|
1933
2083
|
const objects = canvas.getObjects().filter((obj) => {
|
|
1934
2084
|
var _a;
|
|
1935
|
-
return ((_a = obj.data) == null ? void 0 : _a.type) === "
|
|
2085
|
+
return ((_a = obj.data) == null ? void 0 : _a.type) === "feature-marker";
|
|
1936
2086
|
});
|
|
1937
2087
|
objects.forEach((obj) => canvas.remove(obj));
|
|
1938
|
-
if (this.context) {
|
|
1939
|
-
const commandService = this.context.services.get("CommandService");
|
|
1940
|
-
if (commandService) {
|
|
1941
|
-
try {
|
|
1942
|
-
commandService.executeCommand("setHoles", []);
|
|
1943
|
-
} catch (e) {
|
|
1944
|
-
}
|
|
1945
|
-
}
|
|
1946
|
-
}
|
|
1947
2088
|
this.canvasService.requestRenderAll();
|
|
1948
2089
|
}
|
|
1949
|
-
|
|
1950
|
-
if (
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
}
|
|
1956
|
-
);
|
|
1957
|
-
if (objects.length === 0 && this.holes.length > 0) {
|
|
1958
|
-
console.warn("HoleTool: No markers found on canvas to sync from");
|
|
1959
|
-
return;
|
|
1960
|
-
}
|
|
1961
|
-
objects.sort(
|
|
1962
|
-
(a, b) => {
|
|
1963
|
-
var _a, _b, _c, _d;
|
|
1964
|
-
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);
|
|
1965
|
-
}
|
|
1966
|
-
);
|
|
1967
|
-
const newHoles = objects.map((obj, i) => {
|
|
1968
|
-
var _a, _b, _c, _d;
|
|
1969
|
-
const original = this.holes[i];
|
|
1970
|
-
const newAbsX = obj.left;
|
|
1971
|
-
const newAbsY = obj.top;
|
|
1972
|
-
if (isNaN(newAbsX) || isNaN(newAbsY)) {
|
|
1973
|
-
console.error("HoleTool: Invalid marker coordinates", {
|
|
1974
|
-
newAbsX,
|
|
1975
|
-
newAbsY
|
|
1976
|
-
});
|
|
1977
|
-
return original;
|
|
1978
|
-
}
|
|
1979
|
-
const scale = ((_a = this.currentGeometry) == null ? void 0 : _a.scale) || 1;
|
|
1980
|
-
const unit = ((_b = this.currentGeometry) == null ? void 0 : _b.unit) || "mm";
|
|
1981
|
-
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
1982
|
-
if (original && original.anchor && this.currentGeometry) {
|
|
1983
|
-
const { x, y, width, height } = this.currentGeometry;
|
|
1984
|
-
let bx = x;
|
|
1985
|
-
let by = y;
|
|
1986
|
-
const left = x - width / 2;
|
|
1987
|
-
const right = x + width / 2;
|
|
1988
|
-
const top = y - height / 2;
|
|
1989
|
-
const bottom = y + height / 2;
|
|
1990
|
-
switch (original.anchor) {
|
|
1991
|
-
case "top-left":
|
|
1992
|
-
bx = left;
|
|
1993
|
-
by = top;
|
|
1994
|
-
break;
|
|
1995
|
-
case "top-center":
|
|
1996
|
-
bx = x;
|
|
1997
|
-
by = top;
|
|
1998
|
-
break;
|
|
1999
|
-
case "top-right":
|
|
2000
|
-
bx = right;
|
|
2001
|
-
by = top;
|
|
2002
|
-
break;
|
|
2003
|
-
case "center-left":
|
|
2004
|
-
bx = left;
|
|
2005
|
-
by = y;
|
|
2006
|
-
break;
|
|
2007
|
-
case "center":
|
|
2008
|
-
bx = x;
|
|
2009
|
-
by = y;
|
|
2010
|
-
break;
|
|
2011
|
-
case "center-right":
|
|
2012
|
-
bx = right;
|
|
2013
|
-
by = y;
|
|
2014
|
-
break;
|
|
2015
|
-
case "bottom-left":
|
|
2016
|
-
bx = left;
|
|
2017
|
-
by = bottom;
|
|
2018
|
-
break;
|
|
2019
|
-
case "bottom-center":
|
|
2020
|
-
bx = x;
|
|
2021
|
-
by = bottom;
|
|
2022
|
-
break;
|
|
2023
|
-
case "bottom-right":
|
|
2024
|
-
bx = right;
|
|
2025
|
-
by = bottom;
|
|
2026
|
-
break;
|
|
2027
|
-
}
|
|
2028
|
-
return {
|
|
2029
|
-
...original,
|
|
2030
|
-
// Denormalize offset back to physical units (mm)
|
|
2031
|
-
offsetX: (newAbsX - bx) / scale / unitScale,
|
|
2032
|
-
offsetY: (newAbsY - by) / scale / unitScale,
|
|
2033
|
-
// Clear direct coordinates if we use anchor
|
|
2034
|
-
x: void 0,
|
|
2035
|
-
y: void 0,
|
|
2036
|
-
// Ensure other properties are preserved
|
|
2037
|
-
innerRadius: original.innerRadius,
|
|
2038
|
-
outerRadius: original.outerRadius,
|
|
2039
|
-
shape: original.shape || "circle"
|
|
2040
|
-
};
|
|
2041
|
-
}
|
|
2042
|
-
let normalizedX = 0.5;
|
|
2043
|
-
let normalizedY = 0.5;
|
|
2044
|
-
if (this.currentGeometry) {
|
|
2045
|
-
const { x, y, width, height } = this.currentGeometry;
|
|
2046
|
-
const left = x - width / 2;
|
|
2047
|
-
const top = y - height / 2;
|
|
2048
|
-
normalizedX = width > 0 ? (newAbsX - left) / width : 0.5;
|
|
2049
|
-
normalizedY = height > 0 ? (newAbsY - top) / height : 0.5;
|
|
2050
|
-
} else {
|
|
2051
|
-
const { width, height } = this.canvasService.canvas;
|
|
2052
|
-
normalizedX = Coordinate.toNormalized(newAbsX, width || 800);
|
|
2053
|
-
normalizedY = Coordinate.toNormalized(newAbsY, height || 600);
|
|
2054
|
-
}
|
|
2090
|
+
constrainPosition(p, geometry, limit, feature) {
|
|
2091
|
+
if (feature && feature.placement === "internal") {
|
|
2092
|
+
const minX = geometry.x - geometry.width / 2;
|
|
2093
|
+
const maxX = geometry.x + geometry.width / 2;
|
|
2094
|
+
const minY = geometry.y - geometry.height / 2;
|
|
2095
|
+
const maxY = geometry.y + geometry.height / 2;
|
|
2055
2096
|
return {
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
y: normalizedY,
|
|
2059
|
-
// Clear offsets if we are using direct normalized coordinates
|
|
2060
|
-
offsetX: void 0,
|
|
2061
|
-
offsetY: void 0,
|
|
2062
|
-
// Ensure other properties are preserved
|
|
2063
|
-
innerRadius: (_c = original == null ? void 0 : original.innerRadius) != null ? _c : 15,
|
|
2064
|
-
outerRadius: (_d = original == null ? void 0 : original.outerRadius) != null ? _d : 25,
|
|
2065
|
-
shape: (original == null ? void 0 : original.shape) || "circle"
|
|
2097
|
+
x: Math.max(minX, Math.min(maxX, p.x)),
|
|
2098
|
+
y: Math.max(minY, Math.min(maxY, p.y))
|
|
2066
2099
|
};
|
|
2100
|
+
}
|
|
2101
|
+
const nearest = getNearestPointOnDieline({ x: p.x, y: p.y }, {
|
|
2102
|
+
...geometry,
|
|
2103
|
+
features: []
|
|
2067
2104
|
});
|
|
2068
|
-
|
|
2069
|
-
|
|
2105
|
+
const dx = p.x - nearest.x;
|
|
2106
|
+
const dy = p.y - nearest.y;
|
|
2107
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
2108
|
+
if (dist <= limit) {
|
|
2109
|
+
return { x: p.x, y: p.y };
|
|
2110
|
+
}
|
|
2111
|
+
const scale = limit / dist;
|
|
2112
|
+
return {
|
|
2113
|
+
x: nearest.x + dx * scale,
|
|
2114
|
+
y: nearest.y + dy * scale
|
|
2115
|
+
};
|
|
2070
2116
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2117
|
+
syncFeatureFromCanvas(target) {
|
|
2118
|
+
var _a;
|
|
2119
|
+
if (!this.currentGeometry || !this.context) return;
|
|
2120
|
+
const index = (_a = target.data) == null ? void 0 : _a.index;
|
|
2121
|
+
if (index === void 0 || index < 0 || index >= this.features.length)
|
|
2122
|
+
return;
|
|
2123
|
+
const feature = this.features[index];
|
|
2124
|
+
const geometry = this.getGeometryForFeature(this.currentGeometry, feature);
|
|
2125
|
+
const { width, height, x, y } = geometry;
|
|
2126
|
+
const left = x - width / 2;
|
|
2127
|
+
const top = y - height / 2;
|
|
2128
|
+
const normalizedX = width > 0 ? (target.left - left) / width : 0.5;
|
|
2129
|
+
const normalizedY = height > 0 ? (target.top - top) / height : 0.5;
|
|
2130
|
+
const updatedFeature = {
|
|
2131
|
+
...feature,
|
|
2132
|
+
x: normalizedX,
|
|
2133
|
+
y: normalizedY
|
|
2134
|
+
// Could also update rotation if we allowed rotating markers
|
|
2135
|
+
};
|
|
2136
|
+
const newFeatures = [...this.features];
|
|
2137
|
+
newFeatures[index] = updatedFeature;
|
|
2138
|
+
this.features = newFeatures;
|
|
2073
2139
|
const configService = this.context.services.get(
|
|
2074
2140
|
"ConfigurationService"
|
|
2075
2141
|
);
|
|
2076
2142
|
if (configService) {
|
|
2077
2143
|
this.isUpdatingConfig = true;
|
|
2078
2144
|
try {
|
|
2079
|
-
configService.update("dieline.
|
|
2145
|
+
configService.update("dieline.features", this.features);
|
|
2080
2146
|
} finally {
|
|
2081
2147
|
this.isUpdatingConfig = false;
|
|
2082
2148
|
}
|
|
2083
2149
|
}
|
|
2084
2150
|
}
|
|
2085
2151
|
redraw() {
|
|
2086
|
-
if (!this.canvasService) return;
|
|
2152
|
+
if (!this.canvasService || !this.currentGeometry) return;
|
|
2087
2153
|
const canvas = this.canvasService.canvas;
|
|
2088
|
-
const
|
|
2154
|
+
const geometry = this.currentGeometry;
|
|
2089
2155
|
const existing = canvas.getObjects().filter((obj) => {
|
|
2090
2156
|
var _a;
|
|
2091
|
-
return ((_a = obj.data) == null ? void 0 : _a.type) === "
|
|
2157
|
+
return ((_a = obj.data) == null ? void 0 : _a.type) === "feature-marker";
|
|
2092
2158
|
});
|
|
2093
2159
|
existing.forEach((obj) => canvas.remove(obj));
|
|
2094
|
-
|
|
2095
|
-
if (!holes || holes.length === 0) {
|
|
2160
|
+
if (!this.features || this.features.length === 0) {
|
|
2096
2161
|
this.canvasService.requestRenderAll();
|
|
2097
2162
|
return;
|
|
2098
2163
|
}
|
|
2099
|
-
const
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2164
|
+
const scale = geometry.scale || 1;
|
|
2165
|
+
const finalScale = scale;
|
|
2166
|
+
const groups = {};
|
|
2167
|
+
const singles = [];
|
|
2168
|
+
this.features.forEach((f, i) => {
|
|
2169
|
+
if (f.groupId) {
|
|
2170
|
+
if (!groups[f.groupId]) groups[f.groupId] = [];
|
|
2171
|
+
groups[f.groupId].push({ feature: f, index: i });
|
|
2172
|
+
} else {
|
|
2173
|
+
singles.push({ feature: f, index: i });
|
|
2174
|
+
}
|
|
2175
|
+
});
|
|
2176
|
+
const createMarkerShape = (feature, pos) => {
|
|
2177
|
+
const featureScale = scale;
|
|
2178
|
+
const visualWidth = (feature.width || 10) * featureScale;
|
|
2179
|
+
const visualHeight = (feature.height || 10) * featureScale;
|
|
2180
|
+
const visualRadius = (feature.radius || 0) * featureScale;
|
|
2181
|
+
const color = feature.color || (feature.operation === "add" ? "#00FF00" : "#FF0000");
|
|
2182
|
+
const strokeDash = feature.strokeDash || (feature.operation === "subtract" ? [4, 4] : void 0);
|
|
2183
|
+
let shape;
|
|
2184
|
+
if (feature.shape === "rect") {
|
|
2185
|
+
shape = new Rect2({
|
|
2186
|
+
width: visualWidth,
|
|
2187
|
+
height: visualHeight,
|
|
2188
|
+
rx: visualRadius,
|
|
2189
|
+
ry: visualRadius,
|
|
2190
|
+
fill: "transparent",
|
|
2191
|
+
stroke: color,
|
|
2192
|
+
strokeWidth: 2,
|
|
2193
|
+
strokeDashArray: strokeDash,
|
|
2194
|
+
originX: "center",
|
|
2195
|
+
originY: "center",
|
|
2196
|
+
left: pos.x,
|
|
2197
|
+
top: pos.y
|
|
2198
|
+
});
|
|
2199
|
+
} else {
|
|
2200
|
+
shape = new Circle({
|
|
2201
|
+
radius: visualRadius || 5 * finalScale,
|
|
2202
|
+
fill: "transparent",
|
|
2203
|
+
stroke: color,
|
|
2204
|
+
strokeWidth: 2,
|
|
2205
|
+
strokeDashArray: strokeDash,
|
|
2206
|
+
originX: "center",
|
|
2207
|
+
originY: "center",
|
|
2208
|
+
left: pos.x,
|
|
2209
|
+
top: pos.y
|
|
2210
|
+
});
|
|
2211
|
+
}
|
|
2212
|
+
if (feature.rotation) {
|
|
2213
|
+
shape.rotate(feature.rotation);
|
|
2214
|
+
}
|
|
2215
|
+
return shape;
|
|
2106
2216
|
};
|
|
2107
|
-
|
|
2108
|
-
const
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
const visualInnerRadius = hole.innerRadius * unitScale * scale;
|
|
2112
|
-
const visualOuterRadius = hole.outerRadius * unitScale * scale;
|
|
2113
|
-
const pos = resolveHolePosition(
|
|
2114
|
-
{
|
|
2115
|
-
...hole,
|
|
2116
|
-
offsetX: (hole.offsetX || 0) * unitScale * scale,
|
|
2117
|
-
offsetY: (hole.offsetY || 0) * unitScale * scale
|
|
2118
|
-
},
|
|
2119
|
-
geometry,
|
|
2120
|
-
{ width: geometry.width, height: geometry.height }
|
|
2121
|
-
// Use geometry dims instead of canvas
|
|
2217
|
+
singles.forEach(({ feature, index }) => {
|
|
2218
|
+
const geometry2 = this.getGeometryForFeature(
|
|
2219
|
+
this.currentGeometry,
|
|
2220
|
+
feature
|
|
2122
2221
|
);
|
|
2123
|
-
const
|
|
2124
|
-
const
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
strokeWidth: 2,
|
|
2137
|
-
originX: "center",
|
|
2138
|
-
originY: "center"
|
|
2222
|
+
const pos = resolveFeaturePosition(feature, geometry2);
|
|
2223
|
+
const marker = createMarkerShape(feature, pos);
|
|
2224
|
+
marker.set({
|
|
2225
|
+
visible: this.isToolActive,
|
|
2226
|
+
selectable: this.isToolActive,
|
|
2227
|
+
evented: this.isToolActive,
|
|
2228
|
+
hasControls: false,
|
|
2229
|
+
hasBorders: false,
|
|
2230
|
+
hoverCursor: "move",
|
|
2231
|
+
lockRotation: true,
|
|
2232
|
+
lockScalingX: true,
|
|
2233
|
+
lockScalingY: true,
|
|
2234
|
+
data: { type: "feature-marker", index, isGroup: false }
|
|
2139
2235
|
});
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
stroke: "#666",
|
|
2145
|
-
strokeWidth: 1,
|
|
2146
|
-
strokeDashArray: [5, 5],
|
|
2147
|
-
originX: "center",
|
|
2148
|
-
originY: "center"
|
|
2149
|
-
}) : new Circle({
|
|
2150
|
-
radius: visualOuterRadius,
|
|
2151
|
-
fill: "transparent",
|
|
2152
|
-
stroke: "#666",
|
|
2153
|
-
strokeWidth: 1,
|
|
2154
|
-
strokeDashArray: [5, 5],
|
|
2155
|
-
originX: "center",
|
|
2156
|
-
originY: "center"
|
|
2236
|
+
marker.set("opacity", 0);
|
|
2237
|
+
marker.on("mouseover", () => {
|
|
2238
|
+
marker.set("opacity", 1);
|
|
2239
|
+
canvas.requestRenderAll();
|
|
2157
2240
|
});
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2241
|
+
marker.on("mouseout", () => {
|
|
2242
|
+
if (canvas.getActiveObject() !== marker) {
|
|
2243
|
+
marker.set("opacity", 0);
|
|
2244
|
+
canvas.requestRenderAll();
|
|
2245
|
+
}
|
|
2246
|
+
});
|
|
2247
|
+
marker.on("selected", () => {
|
|
2248
|
+
marker.set("opacity", 1);
|
|
2249
|
+
canvas.requestRenderAll();
|
|
2250
|
+
});
|
|
2251
|
+
marker.on("deselected", () => {
|
|
2252
|
+
marker.set("opacity", 0);
|
|
2253
|
+
canvas.requestRenderAll();
|
|
2254
|
+
});
|
|
2255
|
+
canvas.add(marker);
|
|
2256
|
+
canvas.bringObjectToFront(marker);
|
|
2257
|
+
});
|
|
2258
|
+
Object.keys(groups).forEach((groupId) => {
|
|
2259
|
+
const members = groups[groupId];
|
|
2260
|
+
if (members.length === 0) return;
|
|
2261
|
+
const shapes = members.map(({ feature }) => {
|
|
2262
|
+
const geometry2 = this.getGeometryForFeature(
|
|
2263
|
+
this.currentGeometry,
|
|
2264
|
+
feature
|
|
2265
|
+
);
|
|
2266
|
+
const pos = resolveFeaturePosition(feature, geometry2);
|
|
2267
|
+
return createMarkerShape(feature, pos);
|
|
2268
|
+
});
|
|
2269
|
+
const groupObj = new Group(shapes, {
|
|
2270
|
+
visible: this.isToolActive,
|
|
2271
|
+
selectable: this.isToolActive,
|
|
2272
|
+
evented: this.isToolActive,
|
|
2164
2273
|
hasControls: false,
|
|
2165
|
-
// Don't allow resizing/rotating
|
|
2166
2274
|
hasBorders: false,
|
|
2167
|
-
subTargetCheck: false,
|
|
2168
|
-
opacity: 0,
|
|
2169
|
-
// Default hidden
|
|
2170
2275
|
hoverCursor: "move",
|
|
2171
|
-
|
|
2276
|
+
lockRotation: true,
|
|
2277
|
+
lockScalingX: true,
|
|
2278
|
+
lockScalingY: true,
|
|
2279
|
+
subTargetCheck: true,
|
|
2280
|
+
// Allow events to pass through if needed, but we treat as one
|
|
2281
|
+
interactive: false,
|
|
2282
|
+
// Children not interactive
|
|
2283
|
+
// @ts-ignore
|
|
2284
|
+
data: {
|
|
2285
|
+
type: "feature-marker",
|
|
2286
|
+
isGroup: true,
|
|
2287
|
+
groupId,
|
|
2288
|
+
indices: members.map((m) => m.index)
|
|
2289
|
+
}
|
|
2172
2290
|
});
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2291
|
+
groupObj.set("opacity", 0);
|
|
2292
|
+
groupObj.on("mouseover", () => {
|
|
2293
|
+
groupObj.set("opacity", 1);
|
|
2176
2294
|
canvas.requestRenderAll();
|
|
2177
2295
|
});
|
|
2178
|
-
|
|
2179
|
-
if (canvas.getActiveObject() !==
|
|
2180
|
-
|
|
2296
|
+
groupObj.on("mouseout", () => {
|
|
2297
|
+
if (canvas.getActiveObject() !== groupObj) {
|
|
2298
|
+
groupObj.set("opacity", 0);
|
|
2181
2299
|
canvas.requestRenderAll();
|
|
2182
2300
|
}
|
|
2183
2301
|
});
|
|
2184
|
-
|
|
2185
|
-
|
|
2302
|
+
groupObj.on("selected", () => {
|
|
2303
|
+
groupObj.set("opacity", 1);
|
|
2186
2304
|
canvas.requestRenderAll();
|
|
2187
2305
|
});
|
|
2188
|
-
|
|
2189
|
-
|
|
2306
|
+
groupObj.on("deselected", () => {
|
|
2307
|
+
groupObj.set("opacity", 0);
|
|
2190
2308
|
canvas.requestRenderAll();
|
|
2191
2309
|
});
|
|
2192
|
-
canvas.add(
|
|
2193
|
-
canvas.bringObjectToFront(
|
|
2194
|
-
});
|
|
2195
|
-
const markers = canvas.getObjects().filter((o) => {
|
|
2196
|
-
var _a;
|
|
2197
|
-
return ((_a = o.data) == null ? void 0 : _a.type) === "hole-marker";
|
|
2310
|
+
canvas.add(groupObj);
|
|
2311
|
+
canvas.bringObjectToFront(groupObj);
|
|
2198
2312
|
});
|
|
2199
|
-
markers.forEach((m) => canvas.bringObjectToFront(m));
|
|
2200
2313
|
this.canvasService.requestRenderAll();
|
|
2201
2314
|
}
|
|
2202
2315
|
enforceConstraints() {
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
}
|
|
2207
|
-
const effectiveOffset = this.constraintTarget === "original" ? 0 : geometry.offset;
|
|
2208
|
-
const constraintGeometry = {
|
|
2209
|
-
...geometry,
|
|
2210
|
-
width: Math.max(0, geometry.width + effectiveOffset * 2),
|
|
2211
|
-
height: Math.max(0, geometry.height + effectiveOffset * 2),
|
|
2212
|
-
radius: Math.max(0, geometry.radius + effectiveOffset)
|
|
2213
|
-
};
|
|
2214
|
-
const objects = this.canvasService.canvas.getObjects().filter((obj) => {
|
|
2316
|
+
if (!this.canvasService || !this.currentGeometry) return;
|
|
2317
|
+
const canvas = this.canvasService.canvas;
|
|
2318
|
+
const markers = canvas.getObjects().filter((obj) => {
|
|
2215
2319
|
var _a;
|
|
2216
|
-
return ((_a = obj.data) == null ? void 0 : _a.type) === "
|
|
2320
|
+
return ((_a = obj.data) == null ? void 0 : _a.type) === "feature-marker";
|
|
2217
2321
|
});
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2322
|
+
markers.forEach((marker) => {
|
|
2323
|
+
var _a, _b, _c;
|
|
2324
|
+
let feature;
|
|
2325
|
+
if ((_a = marker.data) == null ? void 0 : _a.isGroup) {
|
|
2326
|
+
const indices = (_b = marker.data) == null ? void 0 : _b.indices;
|
|
2327
|
+
if (indices && indices.length > 0) {
|
|
2328
|
+
feature = this.features[indices[0]];
|
|
2329
|
+
}
|
|
2330
|
+
} else {
|
|
2331
|
+
const index = (_c = marker.data) == null ? void 0 : _c.index;
|
|
2332
|
+
if (index !== void 0) {
|
|
2333
|
+
feature = this.features[index];
|
|
2334
|
+
}
|
|
2223
2335
|
}
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
var _a, _b;
|
|
2228
|
-
const currentPos = new Point(obj.left, obj.top);
|
|
2229
|
-
const holeData = this.holes[i];
|
|
2230
|
-
const scale = geometry.scale || 1;
|
|
2231
|
-
const unit = geometry.unit || "mm";
|
|
2232
|
-
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
2233
|
-
const innerR = ((_a = holeData == null ? void 0 : holeData.innerRadius) != null ? _a : 15) * unitScale * scale;
|
|
2234
|
-
const outerR = ((_b = holeData == null ? void 0 : holeData.outerRadius) != null ? _b : 25) * unitScale * scale;
|
|
2235
|
-
const newPos = this.calculateConstrainedPosition(
|
|
2236
|
-
currentPos,
|
|
2237
|
-
constraintGeometry,
|
|
2238
|
-
innerR,
|
|
2239
|
-
outerR
|
|
2336
|
+
const geometry = this.getGeometryForFeature(
|
|
2337
|
+
this.currentGeometry,
|
|
2338
|
+
feature
|
|
2240
2339
|
);
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2340
|
+
const markerStrokeWidth = (marker.strokeWidth || 2) * (marker.scaleX || 1);
|
|
2341
|
+
const minDim = Math.min(marker.getScaledWidth(), marker.getScaledHeight());
|
|
2342
|
+
const limit = Math.max(0, minDim / 2 - markerStrokeWidth);
|
|
2343
|
+
const snapped = this.constrainPosition(
|
|
2344
|
+
new Point(marker.left, marker.top),
|
|
2345
|
+
geometry,
|
|
2346
|
+
limit,
|
|
2347
|
+
feature
|
|
2348
|
+
);
|
|
2349
|
+
marker.set({ left: snapped.x, top: snapped.y });
|
|
2350
|
+
marker.setCoords();
|
|
2249
2351
|
});
|
|
2250
|
-
|
|
2251
|
-
this.syncHolesFromCanvas();
|
|
2252
|
-
return true;
|
|
2253
|
-
}
|
|
2254
|
-
return false;
|
|
2255
|
-
}
|
|
2256
|
-
calculateConstrainedPosition(p, g, innerRadius, outerRadius) {
|
|
2257
|
-
const options = {
|
|
2258
|
-
...g,
|
|
2259
|
-
holes: []
|
|
2260
|
-
// We don't need holes for boundary calculation
|
|
2261
|
-
};
|
|
2262
|
-
const nearest = getNearestPointOnDieline(
|
|
2263
|
-
{ x: p.x, y: p.y },
|
|
2264
|
-
options
|
|
2265
|
-
);
|
|
2266
|
-
const nearestP = new Point(nearest.x, nearest.y);
|
|
2267
|
-
const dist = p.distanceFrom(nearestP);
|
|
2268
|
-
const v = p.subtract(nearestP);
|
|
2269
|
-
const center = new Point(g.x, g.y);
|
|
2270
|
-
const distToCenter = p.distanceFrom(center);
|
|
2271
|
-
const nearestDistToCenter = nearestP.distanceFrom(center);
|
|
2272
|
-
let signedDist = dist;
|
|
2273
|
-
if (distToCenter < nearestDistToCenter) {
|
|
2274
|
-
signedDist = -dist;
|
|
2275
|
-
}
|
|
2276
|
-
let clampedDist = signedDist;
|
|
2277
|
-
if (signedDist > 0) {
|
|
2278
|
-
clampedDist = Math.min(signedDist, innerRadius);
|
|
2279
|
-
} else {
|
|
2280
|
-
clampedDist = Math.max(signedDist, -outerRadius);
|
|
2281
|
-
}
|
|
2282
|
-
if (dist < 1e-3) return nearestP;
|
|
2283
|
-
const scale = Math.abs(clampedDist) / (dist || 1);
|
|
2284
|
-
const offset = v.scalarMultiply(scale);
|
|
2285
|
-
return nearestP.add(offset);
|
|
2352
|
+
canvas.requestRenderAll();
|
|
2286
2353
|
}
|
|
2287
2354
|
};
|
|
2288
2355
|
|
|
@@ -2301,6 +2368,11 @@ var ImageTool = class {
|
|
|
2301
2368
|
this.objectMap = /* @__PURE__ */ new Map();
|
|
2302
2369
|
this.loadResolvers = /* @__PURE__ */ new Map();
|
|
2303
2370
|
this.isUpdatingConfig = false;
|
|
2371
|
+
this.isToolActive = false;
|
|
2372
|
+
this.onToolActivated = (event) => {
|
|
2373
|
+
this.isToolActive = event.id === this.id;
|
|
2374
|
+
this.updateInteractivity();
|
|
2375
|
+
};
|
|
2304
2376
|
}
|
|
2305
2377
|
activate(context) {
|
|
2306
2378
|
this.context = context;
|
|
@@ -2309,6 +2381,7 @@ var ImageTool = class {
|
|
|
2309
2381
|
console.warn("CanvasService not found for ImageTool");
|
|
2310
2382
|
return;
|
|
2311
2383
|
}
|
|
2384
|
+
context.eventBus.on("tool:activated", this.onToolActivated);
|
|
2312
2385
|
const configService = context.services.get(
|
|
2313
2386
|
"ConfigurationService"
|
|
2314
2387
|
);
|
|
@@ -2326,6 +2399,7 @@ var ImageTool = class {
|
|
|
2326
2399
|
this.updateImages();
|
|
2327
2400
|
}
|
|
2328
2401
|
deactivate(context) {
|
|
2402
|
+
context.eventBus.off("tool:activated", this.onToolActivated);
|
|
2329
2403
|
if (this.canvasService) {
|
|
2330
2404
|
const layer = this.canvasService.getLayer("user");
|
|
2331
2405
|
if (layer) {
|
|
@@ -2339,6 +2413,18 @@ var ImageTool = class {
|
|
|
2339
2413
|
this.context = void 0;
|
|
2340
2414
|
}
|
|
2341
2415
|
}
|
|
2416
|
+
updateInteractivity() {
|
|
2417
|
+
var _a;
|
|
2418
|
+
this.objectMap.forEach((obj) => {
|
|
2419
|
+
obj.set({
|
|
2420
|
+
selectable: this.isToolActive,
|
|
2421
|
+
evented: this.isToolActive,
|
|
2422
|
+
hasControls: this.isToolActive,
|
|
2423
|
+
hasBorders: this.isToolActive
|
|
2424
|
+
});
|
|
2425
|
+
});
|
|
2426
|
+
(_a = this.canvasService) == null ? void 0 : _a.requestRenderAll();
|
|
2427
|
+
}
|
|
2342
2428
|
contribute() {
|
|
2343
2429
|
return {
|
|
2344
2430
|
[ContributionPointIds5.CONFIGURATIONS]: [
|
|
@@ -2526,6 +2612,14 @@ var ImageTool = class {
|
|
|
2526
2612
|
const layout = this.getLayoutInfo();
|
|
2527
2613
|
this.items.forEach((item, index) => {
|
|
2528
2614
|
let obj = this.objectMap.get(item.id);
|
|
2615
|
+
if (obj && obj.getSrc) {
|
|
2616
|
+
const currentSrc = obj.getSrc();
|
|
2617
|
+
if (currentSrc !== item.url) {
|
|
2618
|
+
layer.remove(obj);
|
|
2619
|
+
this.objectMap.delete(item.id);
|
|
2620
|
+
obj = void 0;
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2529
2623
|
if (!obj) {
|
|
2530
2624
|
this.loadImage(item, layer, layout);
|
|
2531
2625
|
} else {
|
|
@@ -2582,7 +2676,11 @@ var ImageTool = class {
|
|
|
2582
2676
|
originY: "center",
|
|
2583
2677
|
data: { id: item.id },
|
|
2584
2678
|
uniformScaling: true,
|
|
2585
|
-
lockScalingFlip: true
|
|
2679
|
+
lockScalingFlip: true,
|
|
2680
|
+
selectable: this.isToolActive,
|
|
2681
|
+
evented: this.isToolActive,
|
|
2682
|
+
hasControls: this.isToolActive,
|
|
2683
|
+
hasBorders: this.isToolActive
|
|
2586
2684
|
});
|
|
2587
2685
|
image.setControlsVisibility({
|
|
2588
2686
|
mt: false,
|
|
@@ -3439,6 +3537,24 @@ var CanvasService = class {
|
|
|
3439
3537
|
...options
|
|
3440
3538
|
});
|
|
3441
3539
|
}
|
|
3540
|
+
if (options == null ? void 0 : options.eventBus) {
|
|
3541
|
+
this.setEventBus(options.eventBus);
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
setEventBus(eventBus) {
|
|
3545
|
+
this.eventBus = eventBus;
|
|
3546
|
+
this.setupEvents();
|
|
3547
|
+
}
|
|
3548
|
+
setupEvents() {
|
|
3549
|
+
if (!this.eventBus) return;
|
|
3550
|
+
const bus = this.eventBus;
|
|
3551
|
+
const forward = (name) => (e) => bus.emit(name, e);
|
|
3552
|
+
this.canvas.on("selection:created", forward("selection:created"));
|
|
3553
|
+
this.canvas.on("selection:updated", forward("selection:updated"));
|
|
3554
|
+
this.canvas.on("selection:cleared", forward("selection:cleared"));
|
|
3555
|
+
this.canvas.on("object:modified", forward("object:modified"));
|
|
3556
|
+
this.canvas.on("object:added", forward("object:added"));
|
|
3557
|
+
this.canvas.on("object:removed", forward("object:removed"));
|
|
3442
3558
|
}
|
|
3443
3559
|
dispose() {
|
|
3444
3560
|
this.canvas.dispose();
|
|
@@ -3495,8 +3611,8 @@ export {
|
|
|
3495
3611
|
BackgroundTool,
|
|
3496
3612
|
CanvasService,
|
|
3497
3613
|
DielineTool,
|
|
3614
|
+
FeatureTool,
|
|
3498
3615
|
FilmTool,
|
|
3499
|
-
HoleTool,
|
|
3500
3616
|
ImageTool,
|
|
3501
3617
|
MirrorTool,
|
|
3502
3618
|
RulerTool,
|