@pooder/kit 6.0.0 → 6.1.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/.test-dist/src/extensions/background/BackgroundTool.js +524 -0
- package/.test-dist/src/extensions/background/index.js +17 -0
- package/.test-dist/src/extensions/background.js +1 -1
- package/.test-dist/src/extensions/dieline/DielineTool.js +748 -0
- package/.test-dist/src/extensions/dieline/commands.js +127 -0
- package/.test-dist/src/extensions/dieline/config.js +107 -0
- package/.test-dist/src/extensions/dieline/index.js +21 -0
- package/.test-dist/src/extensions/dieline/model.js +2 -0
- package/.test-dist/src/extensions/dieline/renderer.js +2 -0
- package/.test-dist/src/extensions/dieline.js +4 -0
- package/.test-dist/src/extensions/feature/FeatureTool.js +914 -0
- package/.test-dist/src/extensions/feature/index.js +17 -0
- package/.test-dist/src/extensions/film/FilmTool.js +207 -0
- package/.test-dist/src/extensions/film/index.js +17 -0
- package/.test-dist/src/extensions/image/ImageTool.js +1499 -0
- package/.test-dist/src/extensions/image/commands.js +162 -0
- package/.test-dist/src/extensions/image/config.js +129 -0
- package/.test-dist/src/extensions/image/index.js +21 -0
- package/.test-dist/src/extensions/image/model.js +2 -0
- package/.test-dist/src/extensions/image/renderer.js +5 -0
- package/.test-dist/src/extensions/image.js +182 -7
- package/.test-dist/src/extensions/mirror/MirrorTool.js +104 -0
- package/.test-dist/src/extensions/mirror/index.js +17 -0
- package/.test-dist/src/extensions/ruler/RulerTool.js +442 -0
- package/.test-dist/src/extensions/ruler/index.js +17 -0
- package/.test-dist/src/extensions/sceneLayout.js +2 -93
- package/.test-dist/src/extensions/sceneLayoutModel.js +15 -200
- package/.test-dist/src/extensions/size/SizeTool.js +332 -0
- package/.test-dist/src/extensions/size/index.js +17 -0
- package/.test-dist/src/extensions/white-ink/WhiteInkTool.js +1003 -0
- package/.test-dist/src/extensions/white-ink/commands.js +148 -0
- package/.test-dist/src/extensions/white-ink/config.js +31 -0
- package/.test-dist/src/extensions/white-ink/index.js +21 -0
- package/.test-dist/src/extensions/white-ink/model.js +2 -0
- package/.test-dist/src/extensions/white-ink/renderer.js +5 -0
- package/.test-dist/src/services/CanvasService.js +34 -13
- package/.test-dist/src/services/SceneLayoutService.js +96 -0
- package/.test-dist/src/services/index.js +1 -0
- package/.test-dist/src/services/visibility.js +3 -0
- package/.test-dist/src/shared/constants/layers.js +25 -0
- package/.test-dist/src/shared/imaging/sourceSizeCache.js +82 -0
- package/.test-dist/src/shared/index.js +22 -0
- package/.test-dist/src/shared/runtime/sessionState.js +74 -0
- package/.test-dist/src/shared/runtime/subscriptions.js +30 -0
- package/.test-dist/src/shared/scene/frame.js +34 -0
- package/.test-dist/src/shared/scene/sceneLayoutModel.js +202 -0
- package/.test-dist/tests/run.js +118 -0
- package/CHANGELOG.md +12 -0
- package/dist/index.d.mts +403 -366
- package/dist/index.d.ts +403 -366
- package/dist/index.js +5172 -4752
- package/dist/index.mjs +1410 -2027
- package/dist/tracer-PO7CRBYY.mjs +1016 -0
- package/package.json +1 -1
- package/src/extensions/{background.ts → background/BackgroundTool.ts} +33 -50
- package/src/extensions/background/index.ts +1 -0
- package/src/extensions/{dieline.ts → dieline/DielineTool.ts} +18 -218
- package/src/extensions/dieline/commands.ts +109 -0
- package/src/extensions/dieline/config.ts +106 -0
- package/src/extensions/dieline/index.ts +5 -0
- package/src/extensions/dieline/model.ts +1 -0
- package/src/extensions/dieline/renderer.ts +1 -0
- package/src/extensions/{feature.ts → feature/FeatureTool.ts} +27 -21
- package/src/extensions/feature/index.ts +1 -0
- package/src/extensions/{film.ts → film/FilmTool.ts} +36 -48
- package/src/extensions/film/index.ts +1 -0
- package/src/extensions/{image.ts → image/ImageTool.ts} +289 -335
- package/src/extensions/image/commands.ts +176 -0
- package/src/extensions/image/config.ts +128 -0
- package/src/extensions/image/index.ts +5 -0
- package/src/extensions/image/model.ts +1 -0
- package/src/extensions/image/renderer.ts +1 -0
- package/src/extensions/{mirror.ts → mirror/MirrorTool.ts} +1 -1
- package/src/extensions/mirror/index.ts +1 -0
- package/src/extensions/{ruler.ts → ruler/RulerTool.ts} +4 -5
- package/src/extensions/ruler/index.ts +1 -0
- package/src/extensions/sceneLayout.ts +1 -140
- package/src/extensions/sceneLayoutModel.ts +1 -364
- package/src/extensions/{size.ts → size/SizeTool.ts} +7 -6
- package/src/extensions/size/index.ts +1 -0
- package/src/extensions/{white-ink.ts → white-ink/WhiteInkTool.ts} +130 -317
- package/src/extensions/white-ink/commands.ts +157 -0
- package/src/extensions/white-ink/config.ts +30 -0
- package/src/extensions/white-ink/index.ts +5 -0
- package/src/extensions/white-ink/model.ts +1 -0
- package/src/extensions/white-ink/renderer.ts +1 -0
- package/src/services/CanvasService.ts +43 -12
- package/src/services/SceneLayoutService.ts +139 -0
- package/src/services/index.ts +1 -0
- package/src/services/renderSpec.ts +2 -0
- package/src/services/visibility.ts +5 -0
- package/src/shared/constants/layers.ts +23 -0
- package/src/shared/imaging/sourceSizeCache.ts +103 -0
- package/src/shared/index.ts +6 -0
- package/src/shared/runtime/sessionState.ts +105 -0
- package/src/shared/runtime/subscriptions.ts +45 -0
- package/src/shared/scene/frame.ts +46 -0
- package/src/shared/scene/sceneLayoutModel.ts +367 -0
- package/tests/run.ts +151 -0
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/extensions/background.ts
|
|
1
|
+
// src/extensions/background/BackgroundTool.ts
|
|
2
2
|
import {
|
|
3
3
|
ContributionPointIds
|
|
4
4
|
} from "@pooder/core";
|
|
@@ -157,7 +157,7 @@ function getHeartShapeParams(style) {
|
|
|
157
157
|
};
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
// src/
|
|
160
|
+
// src/shared/scene/sceneLayoutModel.ts
|
|
161
161
|
var DEFAULT_SIZE_STATE = {
|
|
162
162
|
unit: "mm",
|
|
163
163
|
actualWidthMm: 500,
|
|
@@ -392,8 +392,117 @@ function buildSceneGeometry(configService, layout) {
|
|
|
392
392
|
};
|
|
393
393
|
}
|
|
394
394
|
|
|
395
|
-
// src/
|
|
395
|
+
// src/shared/constants/layers.ts
|
|
396
396
|
var BACKGROUND_LAYER_ID = "background";
|
|
397
|
+
var IMAGE_OBJECT_LAYER_ID = "image.user";
|
|
398
|
+
var IMAGE_OVERLAY_LAYER_ID = "image-overlay";
|
|
399
|
+
var WHITE_INK_OBJECT_LAYER_ID = "white-ink.user";
|
|
400
|
+
var WHITE_INK_COVER_LAYER_ID = "white-ink.cover";
|
|
401
|
+
var WHITE_INK_OVERLAY_LAYER_ID = "white-ink.overlay";
|
|
402
|
+
var DIELINE_LAYER_ID = "dieline-overlay";
|
|
403
|
+
var FEATURE_OVERLAY_LAYER_ID = "feature-overlay";
|
|
404
|
+
var RULER_LAYER_ID = "ruler-overlay";
|
|
405
|
+
var FILM_LAYER_ID = "overlay";
|
|
406
|
+
|
|
407
|
+
// src/shared/imaging/sourceSizeCache.ts
|
|
408
|
+
function normalizeSourceSize(size) {
|
|
409
|
+
const width = Number(size.width || 0);
|
|
410
|
+
const height = Number(size.height || 0);
|
|
411
|
+
if (!Number.isFinite(width) || !Number.isFinite(height)) return null;
|
|
412
|
+
if (width <= 0 || height <= 0) return null;
|
|
413
|
+
return { width, height };
|
|
414
|
+
}
|
|
415
|
+
function getCoverScale(frame, source) {
|
|
416
|
+
const frameWidth = Math.max(1, Number(frame.width || 0));
|
|
417
|
+
const frameHeight = Math.max(1, Number(frame.height || 0));
|
|
418
|
+
const sourceWidth = Math.max(1, Number(source.width || 0));
|
|
419
|
+
const sourceHeight = Math.max(1, Number(source.height || 0));
|
|
420
|
+
return Math.max(frameWidth / sourceWidth, frameHeight / sourceHeight);
|
|
421
|
+
}
|
|
422
|
+
function createSourceSizeCache(loadSize) {
|
|
423
|
+
const sizesBySrc = /* @__PURE__ */ new Map();
|
|
424
|
+
const pendingBySrc = /* @__PURE__ */ new Map();
|
|
425
|
+
const rememberSourceSize = (src, size) => {
|
|
426
|
+
const normalized = normalizeSourceSize(size);
|
|
427
|
+
if (!src || !normalized) return null;
|
|
428
|
+
sizesBySrc.set(src, normalized);
|
|
429
|
+
return normalized;
|
|
430
|
+
};
|
|
431
|
+
const getSourceSize = (src) => {
|
|
432
|
+
if (!src) return null;
|
|
433
|
+
const cached = sizesBySrc.get(src);
|
|
434
|
+
if (!cached) return null;
|
|
435
|
+
return { width: cached.width, height: cached.height };
|
|
436
|
+
};
|
|
437
|
+
const ensureImageSize = async (src) => {
|
|
438
|
+
if (!src) return null;
|
|
439
|
+
const cached = sizesBySrc.get(src);
|
|
440
|
+
if (cached) return { width: cached.width, height: cached.height };
|
|
441
|
+
const pending = pendingBySrc.get(src);
|
|
442
|
+
if (pending) {
|
|
443
|
+
return pending;
|
|
444
|
+
}
|
|
445
|
+
const task = loadSize(src);
|
|
446
|
+
pendingBySrc.set(src, task);
|
|
447
|
+
try {
|
|
448
|
+
const size = await task;
|
|
449
|
+
if (size) {
|
|
450
|
+
rememberSourceSize(src, size);
|
|
451
|
+
}
|
|
452
|
+
return size;
|
|
453
|
+
} finally {
|
|
454
|
+
if (pendingBySrc.get(src) === task) {
|
|
455
|
+
pendingBySrc.delete(src);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
const deleteSourceSize = (src) => {
|
|
460
|
+
if (!src) return;
|
|
461
|
+
sizesBySrc.delete(src);
|
|
462
|
+
pendingBySrc.delete(src);
|
|
463
|
+
};
|
|
464
|
+
const clear = () => {
|
|
465
|
+
sizesBySrc.clear();
|
|
466
|
+
pendingBySrc.clear();
|
|
467
|
+
};
|
|
468
|
+
return {
|
|
469
|
+
ensureImageSize,
|
|
470
|
+
rememberSourceSize,
|
|
471
|
+
getSourceSize,
|
|
472
|
+
deleteSourceSize,
|
|
473
|
+
clear
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// src/shared/runtime/subscriptions.ts
|
|
478
|
+
var SubscriptionBag = class {
|
|
479
|
+
constructor() {
|
|
480
|
+
this.disposables = [];
|
|
481
|
+
}
|
|
482
|
+
add(disposable) {
|
|
483
|
+
if (disposable) {
|
|
484
|
+
this.disposables.push(disposable);
|
|
485
|
+
}
|
|
486
|
+
return disposable;
|
|
487
|
+
}
|
|
488
|
+
on(eventBus, event, handler) {
|
|
489
|
+
eventBus.on(event, handler);
|
|
490
|
+
this.disposables.push({
|
|
491
|
+
dispose: () => eventBus.off(event, handler)
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
onConfigChange(configService, handler) {
|
|
495
|
+
this.add(configService.onAnyChange(handler));
|
|
496
|
+
}
|
|
497
|
+
disposeAll() {
|
|
498
|
+
while (this.disposables.length > 0) {
|
|
499
|
+
const disposable = this.disposables.pop();
|
|
500
|
+
disposable == null ? void 0 : disposable.dispose();
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
// src/extensions/background/BackgroundTool.ts
|
|
397
506
|
var BACKGROUND_CONFIG_KEY = "background.config";
|
|
398
507
|
var DEFAULT_WIDTH = 800;
|
|
399
508
|
var DEFAULT_HEIGHT = 600;
|
|
@@ -409,7 +518,7 @@ var DEFAULT_BACKGROUND_CONFIG = {
|
|
|
409
518
|
order: 0,
|
|
410
519
|
enabled: true,
|
|
411
520
|
exportable: false,
|
|
412
|
-
color: "#
|
|
521
|
+
color: "#eee"
|
|
413
522
|
}
|
|
414
523
|
]
|
|
415
524
|
};
|
|
@@ -520,10 +629,12 @@ var BackgroundTool = class {
|
|
|
520
629
|
};
|
|
521
630
|
this.config = cloneConfig(DEFAULT_BACKGROUND_CONFIG);
|
|
522
631
|
this.specs = [];
|
|
632
|
+
this.subscriptions = new SubscriptionBag();
|
|
523
633
|
this.renderSeq = 0;
|
|
524
634
|
this.latestSceneLayout = null;
|
|
525
|
-
this.
|
|
526
|
-
|
|
635
|
+
this.sourceSizeCache = createSourceSizeCache(
|
|
636
|
+
(src) => this.loadImageSize(src)
|
|
637
|
+
);
|
|
527
638
|
this.onCanvasResized = () => {
|
|
528
639
|
this.latestSceneLayout = null;
|
|
529
640
|
this.updateBackground();
|
|
@@ -537,7 +648,8 @@ var BackgroundTool = class {
|
|
|
537
648
|
}
|
|
538
649
|
}
|
|
539
650
|
activate(context) {
|
|
540
|
-
var _a
|
|
651
|
+
var _a;
|
|
652
|
+
this.subscriptions.disposeAll();
|
|
541
653
|
this.canvasService = context.services.get("CanvasService");
|
|
542
654
|
if (!this.canvasService) {
|
|
543
655
|
console.warn("CanvasService not found for BackgroundTool");
|
|
@@ -553,8 +665,8 @@ var BackgroundTool = class {
|
|
|
553
665
|
DEFAULT_BACKGROUND_CONFIG
|
|
554
666
|
)
|
|
555
667
|
);
|
|
556
|
-
|
|
557
|
-
|
|
668
|
+
this.subscriptions.onConfigChange(
|
|
669
|
+
this.configService,
|
|
558
670
|
(e) => {
|
|
559
671
|
if (e.key === BACKGROUND_CONFIG_KEY) {
|
|
560
672
|
this.config = normalizeConfig(e.value);
|
|
@@ -568,7 +680,7 @@ var BackgroundTool = class {
|
|
|
568
680
|
}
|
|
569
681
|
);
|
|
570
682
|
}
|
|
571
|
-
(
|
|
683
|
+
(_a = this.renderProducerDisposable) == null ? void 0 : _a.dispose();
|
|
572
684
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
573
685
|
this.id,
|
|
574
686
|
() => ({
|
|
@@ -583,20 +695,26 @@ var BackgroundTool = class {
|
|
|
583
695
|
}),
|
|
584
696
|
{ priority: 0 }
|
|
585
697
|
);
|
|
586
|
-
|
|
587
|
-
|
|
698
|
+
this.subscriptions.on(
|
|
699
|
+
context.eventBus,
|
|
700
|
+
"canvas:resized",
|
|
701
|
+
this.onCanvasResized
|
|
702
|
+
);
|
|
703
|
+
this.subscriptions.on(
|
|
704
|
+
context.eventBus,
|
|
705
|
+
"scene:layout:change",
|
|
706
|
+
this.onSceneLayoutChanged
|
|
707
|
+
);
|
|
588
708
|
this.updateBackground();
|
|
589
709
|
}
|
|
590
710
|
deactivate(context) {
|
|
591
|
-
var _a
|
|
592
|
-
|
|
593
|
-
context.eventBus.off("scene:layout:change", this.onSceneLayoutChanged);
|
|
711
|
+
var _a;
|
|
712
|
+
this.subscriptions.disposeAll();
|
|
594
713
|
this.renderSeq += 1;
|
|
595
714
|
this.specs = [];
|
|
596
715
|
this.latestSceneLayout = null;
|
|
597
|
-
|
|
598
|
-
this.
|
|
599
|
-
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
716
|
+
this.sourceSizeCache.clear();
|
|
717
|
+
(_a = this.renderProducerDisposable) == null ? void 0 : _a.dispose();
|
|
600
718
|
this.renderProducerDisposable = void 0;
|
|
601
719
|
if (!this.canvasService) return;
|
|
602
720
|
void this.canvasService.flushRenderFromProducers();
|
|
@@ -804,7 +922,7 @@ var BackgroundTool = class {
|
|
|
804
922
|
buildImageLayerSpec(layer) {
|
|
805
923
|
const src = String(layer.src || "").trim();
|
|
806
924
|
if (!src) return [];
|
|
807
|
-
const sourceSize = this.
|
|
925
|
+
const sourceSize = this.sourceSizeCache.getSourceSize(src);
|
|
808
926
|
if (!sourceSize) return [];
|
|
809
927
|
const rect = this.resolveAnchorRect(layer.anchor);
|
|
810
928
|
const placement = this.resolveImagePlacement(rect, sourceSize, layer.fit);
|
|
@@ -863,24 +981,6 @@ var BackgroundTool = class {
|
|
|
863
981
|
});
|
|
864
982
|
return Array.from(urls);
|
|
865
983
|
}
|
|
866
|
-
async ensureImageSize(src) {
|
|
867
|
-
if (!src) return null;
|
|
868
|
-
const cached = this.sourceSizeBySrc.get(src);
|
|
869
|
-
if (cached) return cached;
|
|
870
|
-
const pending = this.pendingSizeBySrc.get(src);
|
|
871
|
-
if (pending) {
|
|
872
|
-
return pending;
|
|
873
|
-
}
|
|
874
|
-
const task = this.loadImageSize(src);
|
|
875
|
-
this.pendingSizeBySrc.set(src, task);
|
|
876
|
-
try {
|
|
877
|
-
return await task;
|
|
878
|
-
} finally {
|
|
879
|
-
if (this.pendingSizeBySrc.get(src) === task) {
|
|
880
|
-
this.pendingSizeBySrc.delete(src);
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
984
|
async loadImageSize(src) {
|
|
885
985
|
try {
|
|
886
986
|
const image = await FabricImage.fromURL(src, {
|
|
@@ -889,9 +989,7 @@ var BackgroundTool = class {
|
|
|
889
989
|
const width = Number((image == null ? void 0 : image.width) || 0);
|
|
890
990
|
const height = Number((image == null ? void 0 : image.height) || 0);
|
|
891
991
|
if (width > 0 && height > 0) {
|
|
892
|
-
|
|
893
|
-
this.sourceSizeBySrc.set(src, size);
|
|
894
|
-
return size;
|
|
992
|
+
return { width, height };
|
|
895
993
|
}
|
|
896
994
|
} catch (error) {
|
|
897
995
|
console.error("[BackgroundTool] Failed to load image", src, error);
|
|
@@ -907,7 +1005,9 @@ var BackgroundTool = class {
|
|
|
907
1005
|
const currentConfig = cloneConfig(this.config);
|
|
908
1006
|
const activeUrls = this.collectActiveImageUrls(currentConfig);
|
|
909
1007
|
if (activeUrls.length > 0) {
|
|
910
|
-
await Promise.all(
|
|
1008
|
+
await Promise.all(
|
|
1009
|
+
activeUrls.map((url) => this.sourceSizeCache.ensureImageSize(url))
|
|
1010
|
+
);
|
|
911
1011
|
if (seq !== this.renderSeq) return;
|
|
912
1012
|
}
|
|
913
1013
|
this.specs = this.buildBackgroundSpecs(currentConfig);
|
|
@@ -917,15 +1017,17 @@ var BackgroundTool = class {
|
|
|
917
1017
|
}
|
|
918
1018
|
};
|
|
919
1019
|
|
|
920
|
-
// src/extensions/image.ts
|
|
1020
|
+
// src/extensions/image/ImageTool.ts
|
|
921
1021
|
import {
|
|
922
1022
|
ContributionPointIds as ContributionPointIds2
|
|
923
1023
|
} from "@pooder/core";
|
|
924
1024
|
import {
|
|
925
1025
|
Canvas as FabricCanvas,
|
|
1026
|
+
Control,
|
|
926
1027
|
Image as FabricImage2,
|
|
927
1028
|
Pattern,
|
|
928
|
-
Point
|
|
1029
|
+
Point,
|
|
1030
|
+
controlsUtils
|
|
929
1031
|
} from "fabric";
|
|
930
1032
|
|
|
931
1033
|
// src/extensions/geometry.ts
|
|
@@ -1547,9 +1649,379 @@ function getPathBounds(pathData) {
|
|
|
1547
1649
|
};
|
|
1548
1650
|
}
|
|
1549
1651
|
|
|
1550
|
-
// src/
|
|
1551
|
-
|
|
1552
|
-
|
|
1652
|
+
// src/shared/scene/frame.ts
|
|
1653
|
+
function emptyFrameRect() {
|
|
1654
|
+
return { left: 0, top: 0, width: 0, height: 0 };
|
|
1655
|
+
}
|
|
1656
|
+
function resolveCutFrameRect(canvasService, configService) {
|
|
1657
|
+
if (!canvasService || !configService) {
|
|
1658
|
+
return emptyFrameRect();
|
|
1659
|
+
}
|
|
1660
|
+
const sizeState = readSizeState(configService);
|
|
1661
|
+
const layout = computeSceneLayout(canvasService, sizeState);
|
|
1662
|
+
if (!layout) {
|
|
1663
|
+
return emptyFrameRect();
|
|
1664
|
+
}
|
|
1665
|
+
return canvasService.toSceneRect({
|
|
1666
|
+
left: layout.cutRect.left,
|
|
1667
|
+
top: layout.cutRect.top,
|
|
1668
|
+
width: layout.cutRect.width,
|
|
1669
|
+
height: layout.cutRect.height
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
function toLayoutSceneRect(rect) {
|
|
1673
|
+
return {
|
|
1674
|
+
left: rect.left,
|
|
1675
|
+
top: rect.top,
|
|
1676
|
+
width: rect.width,
|
|
1677
|
+
height: rect.height,
|
|
1678
|
+
space: "scene"
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
// src/shared/runtime/sessionState.ts
|
|
1683
|
+
function cloneWithJson(value) {
|
|
1684
|
+
return JSON.parse(JSON.stringify(value));
|
|
1685
|
+
}
|
|
1686
|
+
function applyCommittedSnapshot(session, nextCommitted, options) {
|
|
1687
|
+
const clone = options.clone;
|
|
1688
|
+
session.committed = clone(nextCommitted);
|
|
1689
|
+
const shouldPreserveDirtyWorking = options.toolActive && options.preserveDirtyWorking !== false && session.hasWorkingChanges;
|
|
1690
|
+
if (!shouldPreserveDirtyWorking) {
|
|
1691
|
+
session.working = clone(session.committed);
|
|
1692
|
+
session.hasWorkingChanges = false;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
function runDeferredConfigUpdate(state, action, cooldownMs = 0) {
|
|
1696
|
+
state.isUpdatingConfig = true;
|
|
1697
|
+
action();
|
|
1698
|
+
if (cooldownMs <= 0) {
|
|
1699
|
+
state.isUpdatingConfig = false;
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
setTimeout(() => {
|
|
1703
|
+
state.isUpdatingConfig = false;
|
|
1704
|
+
}, cooldownMs);
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
// src/extensions/image/commands.ts
|
|
1708
|
+
function createImageCommands(tool) {
|
|
1709
|
+
return [
|
|
1710
|
+
{
|
|
1711
|
+
command: "addImage",
|
|
1712
|
+
id: "addImage",
|
|
1713
|
+
title: "Add Image",
|
|
1714
|
+
handler: async (url, options) => {
|
|
1715
|
+
const result = await tool.upsertImageEntry(url, {
|
|
1716
|
+
mode: "add",
|
|
1717
|
+
addOptions: options
|
|
1718
|
+
});
|
|
1719
|
+
return result.id;
|
|
1720
|
+
}
|
|
1721
|
+
},
|
|
1722
|
+
{
|
|
1723
|
+
command: "upsertImage",
|
|
1724
|
+
id: "upsertImage",
|
|
1725
|
+
title: "Upsert Image",
|
|
1726
|
+
handler: async (url, options = {}) => {
|
|
1727
|
+
return await tool.upsertImageEntry(url, options);
|
|
1728
|
+
}
|
|
1729
|
+
},
|
|
1730
|
+
{
|
|
1731
|
+
command: "getWorkingImages",
|
|
1732
|
+
id: "getWorkingImages",
|
|
1733
|
+
title: "Get Working Images",
|
|
1734
|
+
handler: () => {
|
|
1735
|
+
return tool.cloneItems(tool.workingItems);
|
|
1736
|
+
}
|
|
1737
|
+
},
|
|
1738
|
+
{
|
|
1739
|
+
command: "setWorkingImage",
|
|
1740
|
+
id: "setWorkingImage",
|
|
1741
|
+
title: "Set Working Image",
|
|
1742
|
+
handler: (id, updates) => {
|
|
1743
|
+
tool.updateImageInWorking(id, updates);
|
|
1744
|
+
}
|
|
1745
|
+
},
|
|
1746
|
+
{
|
|
1747
|
+
command: "resetWorkingImages",
|
|
1748
|
+
id: "resetWorkingImages",
|
|
1749
|
+
title: "Reset Working Images",
|
|
1750
|
+
handler: () => {
|
|
1751
|
+
tool.workingItems = tool.cloneItems(tool.items);
|
|
1752
|
+
tool.hasWorkingChanges = false;
|
|
1753
|
+
tool.updateImages();
|
|
1754
|
+
tool.emitWorkingChange();
|
|
1755
|
+
}
|
|
1756
|
+
},
|
|
1757
|
+
{
|
|
1758
|
+
command: "completeImages",
|
|
1759
|
+
id: "completeImages",
|
|
1760
|
+
title: "Complete Images",
|
|
1761
|
+
handler: async () => {
|
|
1762
|
+
return await tool.commitWorkingImagesAsCropped();
|
|
1763
|
+
}
|
|
1764
|
+
},
|
|
1765
|
+
{
|
|
1766
|
+
command: "exportUserCroppedImage",
|
|
1767
|
+
id: "exportUserCroppedImage",
|
|
1768
|
+
title: "Export User Cropped Image",
|
|
1769
|
+
handler: async (options = {}) => {
|
|
1770
|
+
return await tool.exportUserCroppedImage(options);
|
|
1771
|
+
}
|
|
1772
|
+
},
|
|
1773
|
+
{
|
|
1774
|
+
command: "fitImageToArea",
|
|
1775
|
+
id: "fitImageToArea",
|
|
1776
|
+
title: "Fit Image to Area",
|
|
1777
|
+
handler: async (id, area) => {
|
|
1778
|
+
await tool.fitImageToArea(id, area);
|
|
1779
|
+
}
|
|
1780
|
+
},
|
|
1781
|
+
{
|
|
1782
|
+
command: "fitImageToDefaultArea",
|
|
1783
|
+
id: "fitImageToDefaultArea",
|
|
1784
|
+
title: "Fit Image to Default Area",
|
|
1785
|
+
handler: async (id) => {
|
|
1786
|
+
await tool.fitImageToDefaultArea(id);
|
|
1787
|
+
}
|
|
1788
|
+
},
|
|
1789
|
+
{
|
|
1790
|
+
command: "focusImage",
|
|
1791
|
+
id: "focusImage",
|
|
1792
|
+
title: "Focus Image",
|
|
1793
|
+
handler: (id, options = {}) => {
|
|
1794
|
+
return tool.setImageFocus(id, options);
|
|
1795
|
+
}
|
|
1796
|
+
},
|
|
1797
|
+
{
|
|
1798
|
+
command: "removeImage",
|
|
1799
|
+
id: "removeImage",
|
|
1800
|
+
title: "Remove Image",
|
|
1801
|
+
handler: (id) => {
|
|
1802
|
+
const removed = tool.items.find((item) => item.id === id);
|
|
1803
|
+
const next = tool.items.filter((item) => item.id !== id);
|
|
1804
|
+
if (next.length !== tool.items.length) {
|
|
1805
|
+
tool.purgeSourceSizeCacheForItem(removed);
|
|
1806
|
+
if (tool.focusedImageId === id) {
|
|
1807
|
+
tool.setImageFocus(null, {
|
|
1808
|
+
syncCanvasSelection: true,
|
|
1809
|
+
skipRender: true
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
tool.updateConfig(next);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
},
|
|
1816
|
+
{
|
|
1817
|
+
command: "updateImage",
|
|
1818
|
+
id: "updateImage",
|
|
1819
|
+
title: "Update Image",
|
|
1820
|
+
handler: async (id, updates, options = {}) => {
|
|
1821
|
+
await tool.updateImage(id, updates, options);
|
|
1822
|
+
}
|
|
1823
|
+
},
|
|
1824
|
+
{
|
|
1825
|
+
command: "clearImages",
|
|
1826
|
+
id: "clearImages",
|
|
1827
|
+
title: "Clear Images",
|
|
1828
|
+
handler: () => {
|
|
1829
|
+
tool.sourceSizeCache.clear();
|
|
1830
|
+
tool.setImageFocus(null, {
|
|
1831
|
+
syncCanvasSelection: true,
|
|
1832
|
+
skipRender: true
|
|
1833
|
+
});
|
|
1834
|
+
tool.updateConfig([]);
|
|
1835
|
+
}
|
|
1836
|
+
},
|
|
1837
|
+
{
|
|
1838
|
+
command: "bringToFront",
|
|
1839
|
+
id: "bringToFront",
|
|
1840
|
+
title: "Bring Image to Front",
|
|
1841
|
+
handler: (id) => {
|
|
1842
|
+
const index = tool.items.findIndex((item) => item.id === id);
|
|
1843
|
+
if (index !== -1 && index < tool.items.length - 1) {
|
|
1844
|
+
const next = [...tool.items];
|
|
1845
|
+
const [item] = next.splice(index, 1);
|
|
1846
|
+
next.push(item);
|
|
1847
|
+
tool.updateConfig(next);
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
},
|
|
1851
|
+
{
|
|
1852
|
+
command: "sendToBack",
|
|
1853
|
+
id: "sendToBack",
|
|
1854
|
+
title: "Send Image to Back",
|
|
1855
|
+
handler: (id) => {
|
|
1856
|
+
const index = tool.items.findIndex((item) => item.id === id);
|
|
1857
|
+
if (index > 0) {
|
|
1858
|
+
const next = [...tool.items];
|
|
1859
|
+
const [item] = next.splice(index, 1);
|
|
1860
|
+
next.unshift(item);
|
|
1861
|
+
tool.updateConfig(next);
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
];
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
// src/extensions/image/config.ts
|
|
1869
|
+
function createImageConfigurations() {
|
|
1870
|
+
return [
|
|
1871
|
+
{
|
|
1872
|
+
id: "image.items",
|
|
1873
|
+
type: "array",
|
|
1874
|
+
label: "Images",
|
|
1875
|
+
default: []
|
|
1876
|
+
},
|
|
1877
|
+
{
|
|
1878
|
+
id: "image.debug",
|
|
1879
|
+
type: "boolean",
|
|
1880
|
+
label: "Image Debug Log",
|
|
1881
|
+
default: false
|
|
1882
|
+
},
|
|
1883
|
+
{
|
|
1884
|
+
id: "image.control.cornerSize",
|
|
1885
|
+
type: "number",
|
|
1886
|
+
label: "Image Control Corner Size",
|
|
1887
|
+
min: 4,
|
|
1888
|
+
max: 64,
|
|
1889
|
+
step: 1,
|
|
1890
|
+
default: 14
|
|
1891
|
+
},
|
|
1892
|
+
{
|
|
1893
|
+
id: "image.control.touchCornerSize",
|
|
1894
|
+
type: "number",
|
|
1895
|
+
label: "Image Control Touch Corner Size",
|
|
1896
|
+
min: 8,
|
|
1897
|
+
max: 96,
|
|
1898
|
+
step: 1,
|
|
1899
|
+
default: 24
|
|
1900
|
+
},
|
|
1901
|
+
{
|
|
1902
|
+
id: "image.control.cornerStyle",
|
|
1903
|
+
type: "select",
|
|
1904
|
+
label: "Image Control Corner Style",
|
|
1905
|
+
options: ["circle", "rect"],
|
|
1906
|
+
default: "circle"
|
|
1907
|
+
},
|
|
1908
|
+
{
|
|
1909
|
+
id: "image.control.cornerColor",
|
|
1910
|
+
type: "color",
|
|
1911
|
+
label: "Image Control Corner Color",
|
|
1912
|
+
default: "#ffffff"
|
|
1913
|
+
},
|
|
1914
|
+
{
|
|
1915
|
+
id: "image.control.cornerStrokeColor",
|
|
1916
|
+
type: "color",
|
|
1917
|
+
label: "Image Control Corner Stroke Color",
|
|
1918
|
+
default: "#1677ff"
|
|
1919
|
+
},
|
|
1920
|
+
{
|
|
1921
|
+
id: "image.control.transparentCorners",
|
|
1922
|
+
type: "boolean",
|
|
1923
|
+
label: "Image Control Transparent Corners",
|
|
1924
|
+
default: false
|
|
1925
|
+
},
|
|
1926
|
+
{
|
|
1927
|
+
id: "image.control.borderColor",
|
|
1928
|
+
type: "color",
|
|
1929
|
+
label: "Image Control Border Color",
|
|
1930
|
+
default: "#1677ff"
|
|
1931
|
+
},
|
|
1932
|
+
{
|
|
1933
|
+
id: "image.control.borderScaleFactor",
|
|
1934
|
+
type: "number",
|
|
1935
|
+
label: "Image Control Border Width",
|
|
1936
|
+
min: 0.5,
|
|
1937
|
+
max: 8,
|
|
1938
|
+
step: 0.1,
|
|
1939
|
+
default: 1.5
|
|
1940
|
+
},
|
|
1941
|
+
{
|
|
1942
|
+
id: "image.control.padding",
|
|
1943
|
+
type: "number",
|
|
1944
|
+
label: "Image Control Padding",
|
|
1945
|
+
min: 0,
|
|
1946
|
+
max: 64,
|
|
1947
|
+
step: 1,
|
|
1948
|
+
default: 0
|
|
1949
|
+
},
|
|
1950
|
+
{
|
|
1951
|
+
id: "image.frame.strokeColor",
|
|
1952
|
+
type: "color",
|
|
1953
|
+
label: "Image Frame Stroke Color",
|
|
1954
|
+
default: "#808080"
|
|
1955
|
+
},
|
|
1956
|
+
{
|
|
1957
|
+
id: "image.frame.strokeWidth",
|
|
1958
|
+
type: "number",
|
|
1959
|
+
label: "Image Frame Stroke Width",
|
|
1960
|
+
min: 0,
|
|
1961
|
+
max: 20,
|
|
1962
|
+
step: 0.5,
|
|
1963
|
+
default: 2
|
|
1964
|
+
},
|
|
1965
|
+
{
|
|
1966
|
+
id: "image.frame.strokeStyle",
|
|
1967
|
+
type: "select",
|
|
1968
|
+
label: "Image Frame Stroke Style",
|
|
1969
|
+
options: ["solid", "dashed", "hidden"],
|
|
1970
|
+
default: "dashed"
|
|
1971
|
+
},
|
|
1972
|
+
{
|
|
1973
|
+
id: "image.frame.dashLength",
|
|
1974
|
+
type: "number",
|
|
1975
|
+
label: "Image Frame Dash Length",
|
|
1976
|
+
min: 1,
|
|
1977
|
+
max: 40,
|
|
1978
|
+
step: 1,
|
|
1979
|
+
default: 8
|
|
1980
|
+
},
|
|
1981
|
+
{
|
|
1982
|
+
id: "image.frame.innerBackground",
|
|
1983
|
+
type: "color",
|
|
1984
|
+
label: "Image Frame Inner Background",
|
|
1985
|
+
default: "rgba(0,0,0,0)"
|
|
1986
|
+
},
|
|
1987
|
+
{
|
|
1988
|
+
id: "image.frame.outerBackground",
|
|
1989
|
+
type: "color",
|
|
1990
|
+
label: "Image Frame Outer Background",
|
|
1991
|
+
default: "#f5f5f5"
|
|
1992
|
+
}
|
|
1993
|
+
];
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
// src/extensions/image/ImageTool.ts
|
|
1997
|
+
var IMAGE_DEFAULT_CONTROL_CAPABILITIES = [
|
|
1998
|
+
"rotate",
|
|
1999
|
+
"scale"
|
|
2000
|
+
];
|
|
2001
|
+
var IMAGE_CONTROL_DESCRIPTORS = [
|
|
2002
|
+
{
|
|
2003
|
+
key: "tl",
|
|
2004
|
+
capability: "rotate",
|
|
2005
|
+
create: () => new Control({
|
|
2006
|
+
x: -0.5,
|
|
2007
|
+
y: -0.5,
|
|
2008
|
+
actionName: "rotate",
|
|
2009
|
+
actionHandler: controlsUtils.rotationWithSnapping,
|
|
2010
|
+
cursorStyleHandler: controlsUtils.rotationStyleHandler
|
|
2011
|
+
})
|
|
2012
|
+
},
|
|
2013
|
+
{
|
|
2014
|
+
key: "br",
|
|
2015
|
+
capability: "scale",
|
|
2016
|
+
create: () => new Control({
|
|
2017
|
+
x: 0.5,
|
|
2018
|
+
y: 0.5,
|
|
2019
|
+
actionName: "scale",
|
|
2020
|
+
actionHandler: controlsUtils.scalingEqually,
|
|
2021
|
+
cursorStyleHandler: controlsUtils.scaleCursorStyleHandler
|
|
2022
|
+
})
|
|
2023
|
+
}
|
|
2024
|
+
];
|
|
1553
2025
|
var ImageTool = class {
|
|
1554
2026
|
constructor() {
|
|
1555
2027
|
this.id = "pooder.kit.image";
|
|
@@ -1560,7 +2032,9 @@ var ImageTool = class {
|
|
|
1560
2032
|
this.workingItems = [];
|
|
1561
2033
|
this.hasWorkingChanges = false;
|
|
1562
2034
|
this.loadResolvers = /* @__PURE__ */ new Map();
|
|
1563
|
-
this.
|
|
2035
|
+
this.sourceSizeCache = createSourceSizeCache(
|
|
2036
|
+
(src) => this.loadImageSize(src)
|
|
2037
|
+
);
|
|
1564
2038
|
this.isUpdatingConfig = false;
|
|
1565
2039
|
this.isToolActive = false;
|
|
1566
2040
|
this.isImageSelectionActive = false;
|
|
@@ -1568,6 +2042,8 @@ var ImageTool = class {
|
|
|
1568
2042
|
this.renderSeq = 0;
|
|
1569
2043
|
this.imageSpecs = [];
|
|
1570
2044
|
this.overlaySpecs = [];
|
|
2045
|
+
this.subscriptions = new SubscriptionBag();
|
|
2046
|
+
this.imageControlsByCapabilityKey = /* @__PURE__ */ new Map();
|
|
1571
2047
|
this.onToolActivated = (event) => {
|
|
1572
2048
|
const before = this.isToolActive;
|
|
1573
2049
|
this.syncToolActiveFromWorkbench(event.id);
|
|
@@ -1664,6 +2140,7 @@ var ImageTool = class {
|
|
|
1664
2140
|
}
|
|
1665
2141
|
activate(context) {
|
|
1666
2142
|
var _a;
|
|
2143
|
+
this.subscriptions.disposeAll();
|
|
1667
2144
|
this.context = context;
|
|
1668
2145
|
this.canvasService = context.services.get("CanvasService");
|
|
1669
2146
|
if (!this.canvasService) {
|
|
@@ -1705,37 +2182,55 @@ var ImageTool = class {
|
|
|
1705
2182
|
}),
|
|
1706
2183
|
{ priority: 300 }
|
|
1707
2184
|
);
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
2185
|
+
this.subscriptions.on(context.eventBus, "tool:activated", this.onToolActivated);
|
|
2186
|
+
this.subscriptions.on(context.eventBus, "object:modified", this.onObjectModified);
|
|
2187
|
+
this.subscriptions.on(
|
|
2188
|
+
context.eventBus,
|
|
2189
|
+
"selection:created",
|
|
2190
|
+
this.onSelectionChanged
|
|
2191
|
+
);
|
|
2192
|
+
this.subscriptions.on(
|
|
2193
|
+
context.eventBus,
|
|
2194
|
+
"selection:updated",
|
|
2195
|
+
this.onSelectionChanged
|
|
2196
|
+
);
|
|
2197
|
+
this.subscriptions.on(
|
|
2198
|
+
context.eventBus,
|
|
2199
|
+
"selection:cleared",
|
|
2200
|
+
this.onSelectionCleared
|
|
2201
|
+
);
|
|
2202
|
+
this.subscriptions.on(
|
|
2203
|
+
context.eventBus,
|
|
2204
|
+
"scene:layout:change",
|
|
2205
|
+
this.onSceneLayoutChanged
|
|
2206
|
+
);
|
|
2207
|
+
this.subscriptions.on(
|
|
2208
|
+
context.eventBus,
|
|
2209
|
+
"scene:geometry:change",
|
|
2210
|
+
this.onSceneGeometryChanged
|
|
2211
|
+
);
|
|
1715
2212
|
const configService = context.services.get(
|
|
1716
2213
|
"ConfigurationService"
|
|
1717
2214
|
);
|
|
1718
2215
|
if (configService) {
|
|
1719
|
-
this.items
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
2216
|
+
this.applyCommittedItems(configService.get("image.items", []) || []);
|
|
2217
|
+
this.subscriptions.onConfigChange(
|
|
2218
|
+
configService,
|
|
2219
|
+
(e) => {
|
|
2220
|
+
if (this.isUpdatingConfig) return;
|
|
2221
|
+
if (e.key === "image.items") {
|
|
2222
|
+
this.applyCommittedItems(e.value || []);
|
|
2223
|
+
this.updateImages();
|
|
2224
|
+
return;
|
|
2225
|
+
}
|
|
2226
|
+
if (e.key.startsWith("size.") || e.key.startsWith("image.frame.") || e.key.startsWith("image.control.")) {
|
|
2227
|
+
if (e.key.startsWith("image.control.")) {
|
|
2228
|
+
this.imageControlsByCapabilityKey.clear();
|
|
2229
|
+
}
|
|
2230
|
+
this.updateImages();
|
|
1731
2231
|
}
|
|
1732
|
-
this.updateImages();
|
|
1733
|
-
return;
|
|
1734
|
-
}
|
|
1735
|
-
if (e.key.startsWith("size.") || e.key.startsWith("image.frame.")) {
|
|
1736
|
-
this.updateImages();
|
|
1737
2232
|
}
|
|
1738
|
-
|
|
2233
|
+
);
|
|
1739
2234
|
}
|
|
1740
2235
|
const toolSessionService = context.services.get("ToolSessionService");
|
|
1741
2236
|
this.dirtyTrackerDisposable = toolSessionService == null ? void 0 : toolSessionService.registerDirtyTracker(
|
|
@@ -1746,20 +2241,16 @@ var ImageTool = class {
|
|
|
1746
2241
|
}
|
|
1747
2242
|
deactivate(context) {
|
|
1748
2243
|
var _a, _b;
|
|
1749
|
-
|
|
1750
|
-
context.eventBus.off("object:modified", this.onObjectModified);
|
|
1751
|
-
context.eventBus.off("selection:created", this.onSelectionChanged);
|
|
1752
|
-
context.eventBus.off("selection:updated", this.onSelectionChanged);
|
|
1753
|
-
context.eventBus.off("selection:cleared", this.onSelectionCleared);
|
|
1754
|
-
context.eventBus.off("scene:layout:change", this.onSceneLayoutChanged);
|
|
1755
|
-
context.eventBus.off("scene:geometry:change", this.onSceneGeometryChanged);
|
|
2244
|
+
this.subscriptions.disposeAll();
|
|
1756
2245
|
(_a = this.dirtyTrackerDisposable) == null ? void 0 : _a.dispose();
|
|
1757
2246
|
this.dirtyTrackerDisposable = void 0;
|
|
1758
2247
|
this.cropShapeHatchPattern = void 0;
|
|
1759
2248
|
this.cropShapeHatchPatternColor = void 0;
|
|
1760
2249
|
this.cropShapeHatchPatternKey = void 0;
|
|
2250
|
+
this.sourceSizeCache.clear();
|
|
1761
2251
|
this.imageSpecs = [];
|
|
1762
2252
|
this.overlaySpecs = [];
|
|
2253
|
+
this.imageControlsByCapabilityKey.clear();
|
|
1763
2254
|
this.clearRenderedImages();
|
|
1764
2255
|
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
1765
2256
|
this.renderProducerDisposable = void 0;
|
|
@@ -1782,6 +2273,90 @@ var ImageTool = class {
|
|
|
1782
2273
|
isImageEditingVisible() {
|
|
1783
2274
|
return this.isToolActive || this.isImageSelectionActive || !!this.focusedImageId;
|
|
1784
2275
|
}
|
|
2276
|
+
getEnabledImageControlCapabilities() {
|
|
2277
|
+
return IMAGE_DEFAULT_CONTROL_CAPABILITIES;
|
|
2278
|
+
}
|
|
2279
|
+
getImageControls(capabilities) {
|
|
2280
|
+
const normalized = [...new Set(capabilities)].sort();
|
|
2281
|
+
const cacheKey = normalized.join("|");
|
|
2282
|
+
const cached = this.imageControlsByCapabilityKey.get(cacheKey);
|
|
2283
|
+
if (cached) {
|
|
2284
|
+
return cached;
|
|
2285
|
+
}
|
|
2286
|
+
const enabled = new Set(normalized);
|
|
2287
|
+
const controls = {};
|
|
2288
|
+
IMAGE_CONTROL_DESCRIPTORS.forEach((descriptor) => {
|
|
2289
|
+
if (!enabled.has(descriptor.capability)) return;
|
|
2290
|
+
controls[descriptor.key] = descriptor.create();
|
|
2291
|
+
});
|
|
2292
|
+
this.imageControlsByCapabilityKey.set(cacheKey, controls);
|
|
2293
|
+
return controls;
|
|
2294
|
+
}
|
|
2295
|
+
getImageControlVisualConfig() {
|
|
2296
|
+
var _a, _b, _c, _d;
|
|
2297
|
+
const cornerSizeRaw = Number(
|
|
2298
|
+
(_a = this.getConfig("image.control.cornerSize", 14)) != null ? _a : 14
|
|
2299
|
+
);
|
|
2300
|
+
const touchCornerSizeRaw = Number(
|
|
2301
|
+
(_b = this.getConfig("image.control.touchCornerSize", 24)) != null ? _b : 24
|
|
2302
|
+
);
|
|
2303
|
+
const borderScaleFactorRaw = Number(
|
|
2304
|
+
(_c = this.getConfig("image.control.borderScaleFactor", 1.5)) != null ? _c : 1.5
|
|
2305
|
+
);
|
|
2306
|
+
const paddingRaw = Number(
|
|
2307
|
+
(_d = this.getConfig("image.control.padding", 0)) != null ? _d : 0
|
|
2308
|
+
);
|
|
2309
|
+
const cornerStyleRaw = this.getConfig(
|
|
2310
|
+
"image.control.cornerStyle",
|
|
2311
|
+
"circle"
|
|
2312
|
+
) || "circle";
|
|
2313
|
+
const cornerStyle = cornerStyleRaw === "rect" ? "rect" : "circle";
|
|
2314
|
+
return {
|
|
2315
|
+
cornerSize: Number.isFinite(cornerSizeRaw) ? Math.max(4, Math.min(64, cornerSizeRaw)) : 14,
|
|
2316
|
+
touchCornerSize: Number.isFinite(touchCornerSizeRaw) ? Math.max(8, Math.min(96, touchCornerSizeRaw)) : 24,
|
|
2317
|
+
cornerStyle,
|
|
2318
|
+
cornerColor: this.getConfig("image.control.cornerColor", "#ffffff") || "#ffffff",
|
|
2319
|
+
cornerStrokeColor: this.getConfig("image.control.cornerStrokeColor", "#1677ff") || "#1677ff",
|
|
2320
|
+
transparentCorners: !!this.getConfig(
|
|
2321
|
+
"image.control.transparentCorners",
|
|
2322
|
+
false
|
|
2323
|
+
),
|
|
2324
|
+
borderColor: this.getConfig("image.control.borderColor", "#1677ff") || "#1677ff",
|
|
2325
|
+
borderScaleFactor: Number.isFinite(borderScaleFactorRaw) ? Math.max(0.5, Math.min(8, borderScaleFactorRaw)) : 1.5,
|
|
2326
|
+
padding: Number.isFinite(paddingRaw) ? Math.max(0, Math.min(64, paddingRaw)) : 0
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
applyImageObjectInteractionState(obj) {
|
|
2330
|
+
var _a;
|
|
2331
|
+
if (!obj) return;
|
|
2332
|
+
const visible = this.isImageEditingVisible();
|
|
2333
|
+
const visual = this.getImageControlVisualConfig();
|
|
2334
|
+
obj.set({
|
|
2335
|
+
selectable: visible,
|
|
2336
|
+
evented: visible,
|
|
2337
|
+
hasControls: visible,
|
|
2338
|
+
hasBorders: visible,
|
|
2339
|
+
lockScalingFlip: true,
|
|
2340
|
+
cornerSize: visual.cornerSize,
|
|
2341
|
+
touchCornerSize: visual.touchCornerSize,
|
|
2342
|
+
cornerStyle: visual.cornerStyle,
|
|
2343
|
+
cornerColor: visual.cornerColor,
|
|
2344
|
+
cornerStrokeColor: visual.cornerStrokeColor,
|
|
2345
|
+
transparentCorners: visual.transparentCorners,
|
|
2346
|
+
borderColor: visual.borderColor,
|
|
2347
|
+
borderScaleFactor: visual.borderScaleFactor,
|
|
2348
|
+
padding: visual.padding
|
|
2349
|
+
});
|
|
2350
|
+
obj.controls = this.getImageControls(
|
|
2351
|
+
this.getEnabledImageControlCapabilities()
|
|
2352
|
+
);
|
|
2353
|
+
(_a = obj.setCoords) == null ? void 0 : _a.call(obj);
|
|
2354
|
+
}
|
|
2355
|
+
refreshImageObjectInteractionState() {
|
|
2356
|
+
this.getImageObjects().forEach(
|
|
2357
|
+
(obj) => this.applyImageObjectInteractionState(obj)
|
|
2358
|
+
);
|
|
2359
|
+
}
|
|
1785
2360
|
isDebugEnabled() {
|
|
1786
2361
|
return !!this.getConfig("image.debug", false);
|
|
1787
2362
|
}
|
|
@@ -1811,205 +2386,8 @@ var ImageTool = class {
|
|
|
1811
2386
|
}
|
|
1812
2387
|
}
|
|
1813
2388
|
],
|
|
1814
|
-
[ContributionPointIds2.CONFIGURATIONS]:
|
|
1815
|
-
|
|
1816
|
-
id: "image.items",
|
|
1817
|
-
type: "array",
|
|
1818
|
-
label: "Images",
|
|
1819
|
-
default: []
|
|
1820
|
-
},
|
|
1821
|
-
{
|
|
1822
|
-
id: "image.debug",
|
|
1823
|
-
type: "boolean",
|
|
1824
|
-
label: "Image Debug Log",
|
|
1825
|
-
default: false
|
|
1826
|
-
},
|
|
1827
|
-
{
|
|
1828
|
-
id: "image.frame.strokeColor",
|
|
1829
|
-
type: "color",
|
|
1830
|
-
label: "Image Frame Stroke Color",
|
|
1831
|
-
default: "#808080"
|
|
1832
|
-
},
|
|
1833
|
-
{
|
|
1834
|
-
id: "image.frame.strokeWidth",
|
|
1835
|
-
type: "number",
|
|
1836
|
-
label: "Image Frame Stroke Width",
|
|
1837
|
-
min: 0,
|
|
1838
|
-
max: 20,
|
|
1839
|
-
step: 0.5,
|
|
1840
|
-
default: 2
|
|
1841
|
-
},
|
|
1842
|
-
{
|
|
1843
|
-
id: "image.frame.strokeStyle",
|
|
1844
|
-
type: "select",
|
|
1845
|
-
label: "Image Frame Stroke Style",
|
|
1846
|
-
options: ["solid", "dashed", "hidden"],
|
|
1847
|
-
default: "dashed"
|
|
1848
|
-
},
|
|
1849
|
-
{
|
|
1850
|
-
id: "image.frame.dashLength",
|
|
1851
|
-
type: "number",
|
|
1852
|
-
label: "Image Frame Dash Length",
|
|
1853
|
-
min: 1,
|
|
1854
|
-
max: 40,
|
|
1855
|
-
step: 1,
|
|
1856
|
-
default: 8
|
|
1857
|
-
},
|
|
1858
|
-
{
|
|
1859
|
-
id: "image.frame.innerBackground",
|
|
1860
|
-
type: "color",
|
|
1861
|
-
label: "Image Frame Inner Background",
|
|
1862
|
-
default: "rgba(0,0,0,0)"
|
|
1863
|
-
},
|
|
1864
|
-
{
|
|
1865
|
-
id: "image.frame.outerBackground",
|
|
1866
|
-
type: "color",
|
|
1867
|
-
label: "Image Frame Outer Background",
|
|
1868
|
-
default: "#f5f5f5"
|
|
1869
|
-
}
|
|
1870
|
-
],
|
|
1871
|
-
[ContributionPointIds2.COMMANDS]: [
|
|
1872
|
-
{
|
|
1873
|
-
command: "addImage",
|
|
1874
|
-
title: "Add Image",
|
|
1875
|
-
handler: async (url, options) => {
|
|
1876
|
-
const result = await this.upsertImageEntry(url, {
|
|
1877
|
-
mode: "add",
|
|
1878
|
-
addOptions: options
|
|
1879
|
-
});
|
|
1880
|
-
return result.id;
|
|
1881
|
-
}
|
|
1882
|
-
},
|
|
1883
|
-
{
|
|
1884
|
-
command: "upsertImage",
|
|
1885
|
-
title: "Upsert Image",
|
|
1886
|
-
handler: async (url, options = {}) => {
|
|
1887
|
-
return await this.upsertImageEntry(url, options);
|
|
1888
|
-
}
|
|
1889
|
-
},
|
|
1890
|
-
{
|
|
1891
|
-
command: "getWorkingImages",
|
|
1892
|
-
title: "Get Working Images",
|
|
1893
|
-
handler: () => {
|
|
1894
|
-
return this.cloneItems(this.workingItems);
|
|
1895
|
-
}
|
|
1896
|
-
},
|
|
1897
|
-
{
|
|
1898
|
-
command: "setWorkingImage",
|
|
1899
|
-
title: "Set Working Image",
|
|
1900
|
-
handler: (id, updates) => {
|
|
1901
|
-
this.updateImageInWorking(id, updates);
|
|
1902
|
-
}
|
|
1903
|
-
},
|
|
1904
|
-
{
|
|
1905
|
-
command: "resetWorkingImages",
|
|
1906
|
-
title: "Reset Working Images",
|
|
1907
|
-
handler: () => {
|
|
1908
|
-
this.workingItems = this.cloneItems(this.items);
|
|
1909
|
-
this.hasWorkingChanges = false;
|
|
1910
|
-
this.updateImages();
|
|
1911
|
-
this.emitWorkingChange();
|
|
1912
|
-
}
|
|
1913
|
-
},
|
|
1914
|
-
{
|
|
1915
|
-
command: "completeImages",
|
|
1916
|
-
title: "Complete Images",
|
|
1917
|
-
handler: async () => {
|
|
1918
|
-
return await this.commitWorkingImagesAsCropped();
|
|
1919
|
-
}
|
|
1920
|
-
},
|
|
1921
|
-
{
|
|
1922
|
-
command: "exportUserCroppedImage",
|
|
1923
|
-
title: "Export User Cropped Image",
|
|
1924
|
-
handler: async (options = {}) => {
|
|
1925
|
-
return await this.exportUserCroppedImage(options);
|
|
1926
|
-
}
|
|
1927
|
-
},
|
|
1928
|
-
{
|
|
1929
|
-
command: "fitImageToArea",
|
|
1930
|
-
title: "Fit Image to Area",
|
|
1931
|
-
handler: async (id, area) => {
|
|
1932
|
-
await this.fitImageToArea(id, area);
|
|
1933
|
-
}
|
|
1934
|
-
},
|
|
1935
|
-
{
|
|
1936
|
-
command: "fitImageToDefaultArea",
|
|
1937
|
-
title: "Fit Image to Default Area",
|
|
1938
|
-
handler: async (id) => {
|
|
1939
|
-
await this.fitImageToDefaultArea(id);
|
|
1940
|
-
}
|
|
1941
|
-
},
|
|
1942
|
-
{
|
|
1943
|
-
command: "focusImage",
|
|
1944
|
-
title: "Focus Image",
|
|
1945
|
-
handler: (id, options = {}) => {
|
|
1946
|
-
return this.setImageFocus(id, options);
|
|
1947
|
-
}
|
|
1948
|
-
},
|
|
1949
|
-
{
|
|
1950
|
-
command: "removeImage",
|
|
1951
|
-
title: "Remove Image",
|
|
1952
|
-
handler: (id) => {
|
|
1953
|
-
const removed = this.items.find((item) => item.id === id);
|
|
1954
|
-
const next = this.items.filter((item) => item.id !== id);
|
|
1955
|
-
if (next.length !== this.items.length) {
|
|
1956
|
-
this.purgeSourceSizeCacheForItem(removed);
|
|
1957
|
-
if (this.focusedImageId === id) {
|
|
1958
|
-
this.setImageFocus(null, {
|
|
1959
|
-
syncCanvasSelection: true,
|
|
1960
|
-
skipRender: true
|
|
1961
|
-
});
|
|
1962
|
-
}
|
|
1963
|
-
this.updateConfig(next);
|
|
1964
|
-
}
|
|
1965
|
-
}
|
|
1966
|
-
},
|
|
1967
|
-
{
|
|
1968
|
-
command: "updateImage",
|
|
1969
|
-
title: "Update Image",
|
|
1970
|
-
handler: async (id, updates, options = {}) => {
|
|
1971
|
-
await this.updateImage(id, updates, options);
|
|
1972
|
-
}
|
|
1973
|
-
},
|
|
1974
|
-
{
|
|
1975
|
-
command: "clearImages",
|
|
1976
|
-
title: "Clear Images",
|
|
1977
|
-
handler: () => {
|
|
1978
|
-
this.sourceSizeBySrc.clear();
|
|
1979
|
-
this.setImageFocus(null, {
|
|
1980
|
-
syncCanvasSelection: true,
|
|
1981
|
-
skipRender: true
|
|
1982
|
-
});
|
|
1983
|
-
this.updateConfig([]);
|
|
1984
|
-
}
|
|
1985
|
-
},
|
|
1986
|
-
{
|
|
1987
|
-
command: "bringToFront",
|
|
1988
|
-
title: "Bring Image to Front",
|
|
1989
|
-
handler: (id) => {
|
|
1990
|
-
const index = this.items.findIndex((item) => item.id === id);
|
|
1991
|
-
if (index !== -1 && index < this.items.length - 1) {
|
|
1992
|
-
const next = [...this.items];
|
|
1993
|
-
const [item] = next.splice(index, 1);
|
|
1994
|
-
next.push(item);
|
|
1995
|
-
this.updateConfig(next);
|
|
1996
|
-
}
|
|
1997
|
-
}
|
|
1998
|
-
},
|
|
1999
|
-
{
|
|
2000
|
-
command: "sendToBack",
|
|
2001
|
-
title: "Send Image to Back",
|
|
2002
|
-
handler: (id) => {
|
|
2003
|
-
const index = this.items.findIndex((item) => item.id === id);
|
|
2004
|
-
if (index > 0) {
|
|
2005
|
-
const next = [...this.items];
|
|
2006
|
-
const [item] = next.splice(index, 1);
|
|
2007
|
-
next.unshift(item);
|
|
2008
|
-
this.updateConfig(next);
|
|
2009
|
-
}
|
|
2010
|
-
}
|
|
2011
|
-
}
|
|
2012
|
-
]
|
|
2389
|
+
[ContributionPointIds2.CONFIGURATIONS]: createImageConfigurations(),
|
|
2390
|
+
[ContributionPointIds2.COMMANDS]: createImageCommands(this)
|
|
2013
2391
|
};
|
|
2014
2392
|
}
|
|
2015
2393
|
normalizeItem(item) {
|
|
@@ -2061,12 +2439,7 @@ var ImageTool = class {
|
|
|
2061
2439
|
} else {
|
|
2062
2440
|
const obj = this.getImageObject(id);
|
|
2063
2441
|
if (obj) {
|
|
2064
|
-
|
|
2065
|
-
selectable: true,
|
|
2066
|
-
evented: true,
|
|
2067
|
-
hasControls: true,
|
|
2068
|
-
hasBorders: true
|
|
2069
|
-
});
|
|
2442
|
+
this.applyImageObjectInteractionState(obj);
|
|
2070
2443
|
canvas.setActiveObject(obj);
|
|
2071
2444
|
}
|
|
2072
2445
|
}
|
|
@@ -2140,47 +2513,45 @@ var ImageTool = class {
|
|
|
2140
2513
|
if (!configService) return fallback;
|
|
2141
2514
|
return (_a = configService.get(key, fallback)) != null ? _a : fallback;
|
|
2142
2515
|
}
|
|
2516
|
+
applyCommittedItems(nextItems) {
|
|
2517
|
+
const session = {
|
|
2518
|
+
committed: this.items,
|
|
2519
|
+
working: this.workingItems,
|
|
2520
|
+
hasWorkingChanges: this.hasWorkingChanges
|
|
2521
|
+
};
|
|
2522
|
+
applyCommittedSnapshot(session, this.normalizeItems(nextItems), {
|
|
2523
|
+
clone: (items) => this.cloneItems(items),
|
|
2524
|
+
toolActive: this.isToolActive,
|
|
2525
|
+
preserveDirtyWorking: true
|
|
2526
|
+
});
|
|
2527
|
+
this.items = session.committed;
|
|
2528
|
+
this.workingItems = session.working;
|
|
2529
|
+
this.hasWorkingChanges = session.hasWorkingChanges;
|
|
2530
|
+
}
|
|
2143
2531
|
updateConfig(newItems, skipCanvasUpdate = false) {
|
|
2144
2532
|
if (!this.context) return;
|
|
2145
|
-
this.
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2533
|
+
this.applyCommittedItems(newItems);
|
|
2534
|
+
runDeferredConfigUpdate(
|
|
2535
|
+
this,
|
|
2536
|
+
() => {
|
|
2537
|
+
var _a;
|
|
2538
|
+
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
2539
|
+
"ConfigurationService"
|
|
2540
|
+
);
|
|
2541
|
+
configService == null ? void 0 : configService.update("image.items", this.items);
|
|
2542
|
+
if (!skipCanvasUpdate) {
|
|
2543
|
+
this.updateImages();
|
|
2544
|
+
}
|
|
2545
|
+
},
|
|
2546
|
+
50
|
|
2153
2547
|
);
|
|
2154
|
-
configService == null ? void 0 : configService.update("image.items", this.items);
|
|
2155
|
-
if (!skipCanvasUpdate) {
|
|
2156
|
-
this.updateImages();
|
|
2157
|
-
}
|
|
2158
|
-
setTimeout(() => {
|
|
2159
|
-
this.isUpdatingConfig = false;
|
|
2160
|
-
}, 50);
|
|
2161
2548
|
}
|
|
2162
2549
|
getFrameRect() {
|
|
2163
2550
|
var _a;
|
|
2164
|
-
if (!this.canvasService) {
|
|
2165
|
-
return { left: 0, top: 0, width: 0, height: 0 };
|
|
2166
|
-
}
|
|
2167
2551
|
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
2168
2552
|
"ConfigurationService"
|
|
2169
2553
|
);
|
|
2170
|
-
|
|
2171
|
-
return { left: 0, top: 0, width: 0, height: 0 };
|
|
2172
|
-
}
|
|
2173
|
-
const sizeState = readSizeState(configService);
|
|
2174
|
-
const layout = computeSceneLayout(this.canvasService, sizeState);
|
|
2175
|
-
if (!layout) {
|
|
2176
|
-
return { left: 0, top: 0, width: 0, height: 0 };
|
|
2177
|
-
}
|
|
2178
|
-
return this.canvasService.toSceneRect({
|
|
2179
|
-
left: layout.cutRect.left,
|
|
2180
|
-
top: layout.cutRect.top,
|
|
2181
|
-
width: layout.cutRect.width,
|
|
2182
|
-
height: layout.cutRect.height
|
|
2183
|
-
});
|
|
2554
|
+
return resolveCutFrameRect(this.canvasService, configService);
|
|
2184
2555
|
}
|
|
2185
2556
|
getFrameRectScreen(frame) {
|
|
2186
2557
|
if (!this.canvasService) {
|
|
@@ -2189,13 +2560,7 @@ var ImageTool = class {
|
|
|
2189
2560
|
return this.canvasService.toScreenRect(frame || this.getFrameRect());
|
|
2190
2561
|
}
|
|
2191
2562
|
toLayoutSceneRect(rect) {
|
|
2192
|
-
return
|
|
2193
|
-
left: rect.left,
|
|
2194
|
-
top: rect.top,
|
|
2195
|
-
width: rect.width,
|
|
2196
|
-
height: rect.height,
|
|
2197
|
-
space: "scene"
|
|
2198
|
-
};
|
|
2563
|
+
return toLayoutSceneRect(rect);
|
|
2199
2564
|
}
|
|
2200
2565
|
async resolveDefaultFitArea() {
|
|
2201
2566
|
if (!this.canvasService) return null;
|
|
@@ -2253,31 +2618,31 @@ var ImageTool = class {
|
|
|
2253
2618
|
const sources = [item.url, item.sourceUrl, item.committedUrl].filter(
|
|
2254
2619
|
(value) => typeof value === "string" && value.length > 0
|
|
2255
2620
|
);
|
|
2256
|
-
sources.forEach((src) => this.
|
|
2621
|
+
sources.forEach((src) => this.sourceSizeCache.deleteSourceSize(src));
|
|
2257
2622
|
}
|
|
2258
2623
|
rememberSourceSize(src, obj) {
|
|
2259
2624
|
const width = Number((obj == null ? void 0 : obj.width) || 0);
|
|
2260
2625
|
const height = Number((obj == null ? void 0 : obj.height) || 0);
|
|
2261
2626
|
if (src && width > 0 && height > 0) {
|
|
2262
|
-
this.
|
|
2627
|
+
this.sourceSizeCache.rememberSourceSize(src, { width, height });
|
|
2263
2628
|
}
|
|
2264
2629
|
}
|
|
2265
2630
|
getSourceSize(src, obj) {
|
|
2266
|
-
const cached = src ? this.
|
|
2631
|
+
const cached = src ? this.sourceSizeCache.getSourceSize(src) : void 0;
|
|
2267
2632
|
if (cached) return cached;
|
|
2268
2633
|
const width = Number((obj == null ? void 0 : obj.width) || 0);
|
|
2269
2634
|
const height = Number((obj == null ? void 0 : obj.height) || 0);
|
|
2270
2635
|
if (src && width > 0 && height > 0) {
|
|
2271
2636
|
const size = { width, height };
|
|
2272
|
-
this.
|
|
2637
|
+
this.sourceSizeCache.rememberSourceSize(src, size);
|
|
2273
2638
|
return size;
|
|
2274
2639
|
}
|
|
2275
2640
|
return { width: 1, height: 1 };
|
|
2276
2641
|
}
|
|
2277
2642
|
async ensureSourceSize(src) {
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2643
|
+
return this.sourceSizeCache.ensureImageSize(src);
|
|
2644
|
+
}
|
|
2645
|
+
async loadImageSize(src) {
|
|
2281
2646
|
try {
|
|
2282
2647
|
const image = await FabricImage2.fromURL(src, {
|
|
2283
2648
|
crossOrigin: "anonymous"
|
|
@@ -2285,9 +2650,7 @@ var ImageTool = class {
|
|
|
2285
2650
|
const width = Number((image == null ? void 0 : image.width) || 0);
|
|
2286
2651
|
const height = Number((image == null ? void 0 : image.height) || 0);
|
|
2287
2652
|
if (width > 0 && height > 0) {
|
|
2288
|
-
|
|
2289
|
-
this.sourceSizeBySrc.set(src, size);
|
|
2290
|
-
return size;
|
|
2653
|
+
return { width, height };
|
|
2291
2654
|
}
|
|
2292
2655
|
} catch (error) {
|
|
2293
2656
|
this.debug("image:size:load-failed", {
|
|
@@ -2298,11 +2661,7 @@ var ImageTool = class {
|
|
|
2298
2661
|
return null;
|
|
2299
2662
|
}
|
|
2300
2663
|
getCoverScale(frame, size) {
|
|
2301
|
-
|
|
2302
|
-
const sh = Math.max(1, size.height);
|
|
2303
|
-
const fw = Math.max(1, frame.width);
|
|
2304
|
-
const fh = Math.max(1, frame.height);
|
|
2305
|
-
return Math.max(fw / sw, fh / sh);
|
|
2664
|
+
return getCoverScale(frame, size);
|
|
2306
2665
|
}
|
|
2307
2666
|
getFrameVisualConfig() {
|
|
2308
2667
|
var _a, _b;
|
|
@@ -2863,6 +3222,7 @@ var ImageTool = class {
|
|
|
2863
3222
|
this.overlaySpecs = this.buildOverlaySpecs(frame, sceneGeometry);
|
|
2864
3223
|
await this.canvasService.flushRenderFromProducers();
|
|
2865
3224
|
if (seq !== this.renderSeq) return;
|
|
3225
|
+
this.refreshImageObjectInteractionState();
|
|
2866
3226
|
renderItems.forEach((item) => {
|
|
2867
3227
|
if (!this.getImageObject(item.id)) return;
|
|
2868
3228
|
const resolver = this.loadResolvers.get(item.id);
|
|
@@ -3053,7 +3413,7 @@ var ImageTool = class {
|
|
|
3053
3413
|
})
|
|
3054
3414
|
);
|
|
3055
3415
|
if (previousCommitted && previousCommitted !== url) {
|
|
3056
|
-
this.
|
|
3416
|
+
this.sourceSizeCache.deleteSourceSize(previousCommitted);
|
|
3057
3417
|
}
|
|
3058
3418
|
}
|
|
3059
3419
|
this.hasWorkingChanges = false;
|
|
@@ -3149,7 +3509,7 @@ var ImageTool = class {
|
|
|
3149
3509
|
}
|
|
3150
3510
|
};
|
|
3151
3511
|
|
|
3152
|
-
// src/extensions/size.ts
|
|
3512
|
+
// src/extensions/size/SizeTool.ts
|
|
3153
3513
|
import {
|
|
3154
3514
|
ContributionPointIds as ContributionPointIds3
|
|
3155
3515
|
} from "@pooder/core";
|
|
@@ -3369,1140 +3729,324 @@ var SizeTool = class {
|
|
|
3369
3729
|
const anchor = changed === "height" ? nextHeightMm : changed === "width" ? nextWidthMm : (_b = providedWidthMm != null ? providedWidthMm : providedHeightMm) != null ? _b : nextWidthMm;
|
|
3370
3730
|
nextWidthMm = anchor;
|
|
3371
3731
|
nextHeightMm = anchor;
|
|
3372
|
-
} else if (state.constraintMode === "lockAspect") {
|
|
3373
|
-
const ratio = Math.max(1e-4, state.aspectRatio);
|
|
3374
|
-
if (changed === "height") {
|
|
3375
|
-
nextWidthMm = nextHeightMm * ratio;
|
|
3376
|
-
} else {
|
|
3377
|
-
nextHeightMm = nextWidthMm / ratio;
|
|
3378
|
-
}
|
|
3379
|
-
}
|
|
3380
|
-
nextWidthMm = sanitizeMmValue(nextWidthMm, limits);
|
|
3381
|
-
nextHeightMm = sanitizeMmValue(nextHeightMm, limits);
|
|
3382
|
-
if (state.constraintMode === "equal") {
|
|
3383
|
-
const value = Math.max(nextWidthMm, nextHeightMm);
|
|
3384
|
-
nextWidthMm = value;
|
|
3385
|
-
nextHeightMm = value;
|
|
3386
|
-
} else if (state.constraintMode === "lockAspect") {
|
|
3387
|
-
const ratio = Math.max(1e-4, state.aspectRatio);
|
|
3388
|
-
if (changed === "height") {
|
|
3389
|
-
nextWidthMm = sanitizeMmValue(nextHeightMm * ratio, limits);
|
|
3390
|
-
} else {
|
|
3391
|
-
nextHeightMm = sanitizeMmValue(nextWidthMm / ratio, limits);
|
|
3392
|
-
}
|
|
3393
|
-
}
|
|
3394
|
-
configService.update("size.actualWidthMm", nextWidthMm);
|
|
3395
|
-
configService.update("size.actualHeightMm", nextHeightMm);
|
|
3396
|
-
configService.update("size.unit", inputUnit);
|
|
3397
|
-
this.emitStateChanged();
|
|
3398
|
-
return this.getStateForUI();
|
|
3399
|
-
}
|
|
3400
|
-
setConstraintMode(modeRaw) {
|
|
3401
|
-
const configService = this.getConfigService();
|
|
3402
|
-
if (!configService) return null;
|
|
3403
|
-
const state = readSizeState(configService);
|
|
3404
|
-
const mode = normalizeConstraintMode(modeRaw);
|
|
3405
|
-
configService.update("size.constraintMode", mode);
|
|
3406
|
-
if (mode === "lockAspect") {
|
|
3407
|
-
const ratio = state.actualWidthMm / Math.max(1e-3, state.actualHeightMm);
|
|
3408
|
-
configService.update("size.aspectRatio", ratio);
|
|
3409
|
-
}
|
|
3410
|
-
if (mode === "equal") {
|
|
3411
|
-
const value = sanitizeMmValue(
|
|
3412
|
-
Math.max(state.actualWidthMm, state.actualHeightMm),
|
|
3413
|
-
{
|
|
3414
|
-
minMm: state.minMm,
|
|
3415
|
-
maxMm: state.maxMm,
|
|
3416
|
-
stepMm: state.stepMm
|
|
3417
|
-
}
|
|
3418
|
-
);
|
|
3419
|
-
configService.update("size.actualWidthMm", value);
|
|
3420
|
-
configService.update("size.actualHeightMm", value);
|
|
3421
|
-
configService.update("size.aspectRatio", 1);
|
|
3422
|
-
}
|
|
3423
|
-
this.emitStateChanged();
|
|
3424
|
-
return this.getStateForUI();
|
|
3425
|
-
}
|
|
3426
|
-
setUnit(unitRaw) {
|
|
3427
|
-
const configService = this.getConfigService();
|
|
3428
|
-
if (!configService) return null;
|
|
3429
|
-
const unit = normalizeUnit(unitRaw);
|
|
3430
|
-
configService.update("size.unit", unit);
|
|
3431
|
-
this.emitStateChanged();
|
|
3432
|
-
return this.getStateForUI();
|
|
3433
|
-
}
|
|
3434
|
-
setCut(cutModeRaw, cutMarginMm = 0) {
|
|
3435
|
-
const configService = this.getConfigService();
|
|
3436
|
-
if (!configService) return null;
|
|
3437
|
-
const cutMode = normalizeCutMode(cutModeRaw);
|
|
3438
|
-
const margin = Math.max(0, Number(cutMarginMm) || 0);
|
|
3439
|
-
configService.update("size.cutMode", cutMode);
|
|
3440
|
-
configService.update("size.cutMarginMm", margin);
|
|
3441
|
-
this.emitStateChanged();
|
|
3442
|
-
return this.getStateForUI();
|
|
3443
|
-
}
|
|
3444
|
-
getSelectedImageSize(id) {
|
|
3445
|
-
var _a, _b, _c;
|
|
3446
|
-
const configService = this.getConfigService();
|
|
3447
|
-
if (!configService || !this.canvasService) return null;
|
|
3448
|
-
const sizeState = readSizeState(configService);
|
|
3449
|
-
const layout = computeSceneLayout(this.canvasService, sizeState);
|
|
3450
|
-
if (!layout || layout.scale <= 0) return null;
|
|
3451
|
-
const all = this.canvasService.canvas.getObjects();
|
|
3452
|
-
const active = this.canvasService.canvas.getActiveObject();
|
|
3453
|
-
const activeId = ((_a = active == null ? void 0 : active.data) == null ? void 0 : _a.layerId) === "image.user" ? (_b = active == null ? void 0 : active.data) == null ? void 0 : _b.id : null;
|
|
3454
|
-
const targetId = id || activeId;
|
|
3455
|
-
const target = all.find(
|
|
3456
|
-
(obj) => {
|
|
3457
|
-
var _a2, _b2;
|
|
3458
|
-
return ((_a2 = obj == null ? void 0 : obj.data) == null ? void 0 : _a2.layerId) === "image.user" && ((_b2 = obj == null ? void 0 : obj.data) == null ? void 0 : _b2.id) === targetId;
|
|
3459
|
-
}
|
|
3460
|
-
) || all.find((obj) => {
|
|
3461
|
-
var _a2;
|
|
3462
|
-
return ((_a2 = obj == null ? void 0 : obj.data) == null ? void 0 : _a2.layerId) === "image.user";
|
|
3463
|
-
});
|
|
3464
|
-
if (!target) return null;
|
|
3465
|
-
const objectWidthPx = Math.abs((target.width || 0) * (target.scaleX || 1));
|
|
3466
|
-
const objectHeightPx = Math.abs(
|
|
3467
|
-
(target.height || 0) * (target.scaleY || 1)
|
|
3468
|
-
);
|
|
3469
|
-
if (objectWidthPx <= 0 || objectHeightPx <= 0) return null;
|
|
3470
|
-
const widthMm = objectWidthPx / layout.scale;
|
|
3471
|
-
const heightMm = objectHeightPx / layout.scale;
|
|
3472
|
-
return {
|
|
3473
|
-
id: ((_c = target == null ? void 0 : target.data) == null ? void 0 : _c.id) || null,
|
|
3474
|
-
widthMm,
|
|
3475
|
-
heightMm,
|
|
3476
|
-
width: fromMm(widthMm, sizeState.unit),
|
|
3477
|
-
height: fromMm(heightMm, sizeState.unit),
|
|
3478
|
-
unit: sizeState.unit
|
|
3479
|
-
};
|
|
3480
|
-
}
|
|
3481
|
-
};
|
|
3482
|
-
|
|
3483
|
-
// src/extensions/dieline.ts
|
|
3484
|
-
import {
|
|
3485
|
-
ContributionPointIds as ContributionPointIds4
|
|
3486
|
-
} from "@pooder/core";
|
|
3487
|
-
import { Canvas as FabricCanvas2, Path, Pattern as Pattern2 } from "fabric";
|
|
3488
|
-
|
|
3489
|
-
// src/extensions/tracer.ts
|
|
3490
|
-
import paper2 from "paper";
|
|
3491
|
-
|
|
3492
|
-
// src/extensions/maskOps.ts
|
|
3493
|
-
function createMask(imageData, options) {
|
|
3494
|
-
const { width, height, data } = imageData;
|
|
3495
|
-
const {
|
|
3496
|
-
threshold,
|
|
3497
|
-
padding,
|
|
3498
|
-
paddedWidth,
|
|
3499
|
-
paddedHeight,
|
|
3500
|
-
maskMode = "auto",
|
|
3501
|
-
whiteThreshold = 240,
|
|
3502
|
-
alphaOpaqueCutoff = 250
|
|
3503
|
-
} = options;
|
|
3504
|
-
const resolvedMode = maskMode === "auto" ? inferMaskMode(imageData, alphaOpaqueCutoff) : maskMode;
|
|
3505
|
-
const mask = new Uint8Array(paddedWidth * paddedHeight);
|
|
3506
|
-
for (let y = 0; y < height; y++) {
|
|
3507
|
-
for (let x = 0; x < width; x++) {
|
|
3508
|
-
const srcIdx = (y * width + x) * 4;
|
|
3509
|
-
const r = data[srcIdx];
|
|
3510
|
-
const g = data[srcIdx + 1];
|
|
3511
|
-
const b = data[srcIdx + 2];
|
|
3512
|
-
const a = data[srcIdx + 3];
|
|
3513
|
-
const destIdx = (y + padding) * paddedWidth + (x + padding);
|
|
3514
|
-
if (resolvedMode === "alpha") {
|
|
3515
|
-
if (a > threshold) mask[destIdx] = 1;
|
|
3516
|
-
} else {
|
|
3517
|
-
if (a > threshold && !(r > whiteThreshold && g > whiteThreshold && b > whiteThreshold)) {
|
|
3518
|
-
mask[destIdx] = 1;
|
|
3519
|
-
}
|
|
3520
|
-
}
|
|
3521
|
-
}
|
|
3522
|
-
}
|
|
3523
|
-
return mask;
|
|
3524
|
-
}
|
|
3525
|
-
function inferMaskMode(imageData, alphaOpaqueCutoff) {
|
|
3526
|
-
const analysis = analyzeAlpha(imageData, alphaOpaqueCutoff);
|
|
3527
|
-
if (analysis.minAlpha === 255) return "whitebg";
|
|
3528
|
-
if (analysis.veryTransparentRatio >= 5e-4) return "alpha";
|
|
3529
|
-
if (analysis.belowOpaqueRatio >= 0.01) return "alpha";
|
|
3530
|
-
return "whitebg";
|
|
3531
|
-
}
|
|
3532
|
-
function analyzeAlpha(imageData, alphaOpaqueCutoff) {
|
|
3533
|
-
const { data } = imageData;
|
|
3534
|
-
const total = data.length / 4;
|
|
3535
|
-
let belowOpaque = 0;
|
|
3536
|
-
let veryTransparent = 0;
|
|
3537
|
-
let minAlpha = 255;
|
|
3538
|
-
for (let i = 3; i < data.length; i += 4) {
|
|
3539
|
-
const a = data[i];
|
|
3540
|
-
if (a < minAlpha) minAlpha = a;
|
|
3541
|
-
if (a < alphaOpaqueCutoff) belowOpaque++;
|
|
3542
|
-
if (a < 32) veryTransparent++;
|
|
3543
|
-
}
|
|
3544
|
-
return {
|
|
3545
|
-
total,
|
|
3546
|
-
minAlpha,
|
|
3547
|
-
belowOpaqueRatio: belowOpaque / total,
|
|
3548
|
-
veryTransparentRatio: veryTransparent / total
|
|
3549
|
-
};
|
|
3550
|
-
}
|
|
3551
|
-
function circularMorphology(mask, width, height, radius, op) {
|
|
3552
|
-
const r = Math.max(0, Math.floor(radius));
|
|
3553
|
-
if (r <= 0) {
|
|
3554
|
-
return mask.slice();
|
|
3555
|
-
}
|
|
3556
|
-
const dilateDisk = (m, radiusPx) => {
|
|
3557
|
-
const horizontalDist = new Int32Array(width * height);
|
|
3558
|
-
for (let y = 0; y < height; y++) {
|
|
3559
|
-
let lastSolid = -radiusPx * 2;
|
|
3560
|
-
for (let x = 0; x < width; x++) {
|
|
3561
|
-
if (m[y * width + x]) lastSolid = x;
|
|
3562
|
-
horizontalDist[y * width + x] = x - lastSolid;
|
|
3563
|
-
}
|
|
3564
|
-
lastSolid = width + radiusPx * 2;
|
|
3565
|
-
for (let x = width - 1; x >= 0; x--) {
|
|
3566
|
-
if (m[y * width + x]) lastSolid = x;
|
|
3567
|
-
horizontalDist[y * width + x] = Math.min(
|
|
3568
|
-
horizontalDist[y * width + x],
|
|
3569
|
-
lastSolid - x
|
|
3570
|
-
);
|
|
3571
|
-
}
|
|
3572
|
-
}
|
|
3573
|
-
const result = new Uint8Array(width * height);
|
|
3574
|
-
const r2 = radiusPx * radiusPx;
|
|
3575
|
-
for (let x = 0; x < width; x++) {
|
|
3576
|
-
for (let y = 0; y < height; y++) {
|
|
3577
|
-
let found = false;
|
|
3578
|
-
const minY = Math.max(0, y - radiusPx);
|
|
3579
|
-
const maxY = Math.min(height - 1, y + radiusPx);
|
|
3580
|
-
for (let dy = minY; dy <= maxY; dy++) {
|
|
3581
|
-
const dY = dy - y;
|
|
3582
|
-
const hDist = horizontalDist[dy * width + x];
|
|
3583
|
-
if (hDist * hDist + dY * dY <= r2) {
|
|
3584
|
-
found = true;
|
|
3585
|
-
break;
|
|
3586
|
-
}
|
|
3587
|
-
}
|
|
3588
|
-
if (found) result[y * width + x] = 1;
|
|
3589
|
-
}
|
|
3590
|
-
}
|
|
3591
|
-
return result;
|
|
3592
|
-
};
|
|
3593
|
-
const erodeDiamond = (m, radiusPx) => {
|
|
3594
|
-
if (radiusPx <= 0) return m.slice();
|
|
3595
|
-
let current = m;
|
|
3596
|
-
for (let step = 0; step < radiusPx; step++) {
|
|
3597
|
-
const next = new Uint8Array(width * height);
|
|
3598
|
-
for (let y = 1; y < height - 1; y++) {
|
|
3599
|
-
const row = y * width;
|
|
3600
|
-
for (let x = 1; x < width - 1; x++) {
|
|
3601
|
-
const idx = row + x;
|
|
3602
|
-
if (current[idx] && current[idx - 1] && current[idx + 1] && current[idx - width] && current[idx + width]) {
|
|
3603
|
-
next[idx] = 1;
|
|
3604
|
-
}
|
|
3605
|
-
}
|
|
3606
|
-
}
|
|
3607
|
-
current = next;
|
|
3608
|
-
}
|
|
3609
|
-
return current;
|
|
3610
|
-
};
|
|
3611
|
-
const restoreBridgePixels = (source, eroded) => {
|
|
3612
|
-
const restored = eroded.slice();
|
|
3613
|
-
for (let y = 1; y < height - 1; y++) {
|
|
3614
|
-
const row = y * width;
|
|
3615
|
-
for (let x = 1; x < width - 1; x++) {
|
|
3616
|
-
const idx = row + x;
|
|
3617
|
-
if (!source[idx] || restored[idx]) continue;
|
|
3618
|
-
const up = source[idx - width] === 1;
|
|
3619
|
-
const down = source[idx + width] === 1;
|
|
3620
|
-
const left = source[idx - 1] === 1;
|
|
3621
|
-
const right = source[idx + 1] === 1;
|
|
3622
|
-
const upLeft = source[idx - width - 1] === 1;
|
|
3623
|
-
const upRight = source[idx - width + 1] === 1;
|
|
3624
|
-
const downLeft = source[idx + width - 1] === 1;
|
|
3625
|
-
const downRight = source[idx + width + 1] === 1;
|
|
3626
|
-
const keepsBridge = left && right || up && down || upLeft && downRight || upRight && downLeft;
|
|
3627
|
-
if (keepsBridge) {
|
|
3628
|
-
restored[idx] = 1;
|
|
3629
|
-
}
|
|
3630
|
-
}
|
|
3631
|
-
}
|
|
3632
|
-
return restored;
|
|
3633
|
-
};
|
|
3634
|
-
const erodePreservingBridges = (m, radiusPx) => {
|
|
3635
|
-
const eroded = erodeDiamond(m, radiusPx);
|
|
3636
|
-
return restoreBridgePixels(m, eroded);
|
|
3637
|
-
};
|
|
3638
|
-
switch (op) {
|
|
3639
|
-
case "dilate":
|
|
3640
|
-
return dilateDisk(mask, r);
|
|
3641
|
-
case "erode":
|
|
3642
|
-
return erodePreservingBridges(mask, r);
|
|
3643
|
-
case "closing": {
|
|
3644
|
-
const erodeRadius = Math.max(1, Math.floor(r * 0.65));
|
|
3645
|
-
return erodePreservingBridges(dilateDisk(mask, r), erodeRadius);
|
|
3646
|
-
}
|
|
3647
|
-
case "opening":
|
|
3648
|
-
return dilateDisk(erodePreservingBridges(mask, r), r);
|
|
3649
|
-
default:
|
|
3650
|
-
return mask;
|
|
3651
|
-
}
|
|
3652
|
-
}
|
|
3653
|
-
function fillHoles(mask, width, height) {
|
|
3654
|
-
const background = new Uint8Array(width * height);
|
|
3655
|
-
const queue = [];
|
|
3656
|
-
for (let x = 0; x < width; x++) {
|
|
3657
|
-
if (mask[x] === 0) {
|
|
3658
|
-
background[x] = 1;
|
|
3659
|
-
queue.push(x);
|
|
3660
|
-
}
|
|
3661
|
-
const lastRowIdx = (height - 1) * width + x;
|
|
3662
|
-
if (mask[lastRowIdx] === 0) {
|
|
3663
|
-
background[lastRowIdx] = 1;
|
|
3664
|
-
queue.push(lastRowIdx);
|
|
3665
|
-
}
|
|
3666
|
-
}
|
|
3667
|
-
for (let y = 1; y < height - 1; y++) {
|
|
3668
|
-
const leftIdx = y * width;
|
|
3669
|
-
const rightIdx = y * width + (width - 1);
|
|
3670
|
-
if (mask[leftIdx] === 0) {
|
|
3671
|
-
background[leftIdx] = 1;
|
|
3672
|
-
queue.push(leftIdx);
|
|
3673
|
-
}
|
|
3674
|
-
if (mask[rightIdx] === 0) {
|
|
3675
|
-
background[rightIdx] = 1;
|
|
3676
|
-
queue.push(rightIdx);
|
|
3677
|
-
}
|
|
3678
|
-
}
|
|
3679
|
-
let head = 0;
|
|
3680
|
-
while (head < queue.length) {
|
|
3681
|
-
const idx = queue[head++];
|
|
3682
|
-
const x = idx % width;
|
|
3683
|
-
const y = (idx - x) / width;
|
|
3684
|
-
const up = y > 0 ? idx - width : -1;
|
|
3685
|
-
const down = y < height - 1 ? idx + width : -1;
|
|
3686
|
-
const left = x > 0 ? idx - 1 : -1;
|
|
3687
|
-
const right = x < width - 1 ? idx + 1 : -1;
|
|
3688
|
-
if (up >= 0 && mask[up] === 0 && background[up] === 0) {
|
|
3689
|
-
background[up] = 1;
|
|
3690
|
-
queue.push(up);
|
|
3691
|
-
}
|
|
3692
|
-
if (down >= 0 && mask[down] === 0 && background[down] === 0) {
|
|
3693
|
-
background[down] = 1;
|
|
3694
|
-
queue.push(down);
|
|
3695
|
-
}
|
|
3696
|
-
if (left >= 0 && mask[left] === 0 && background[left] === 0) {
|
|
3697
|
-
background[left] = 1;
|
|
3698
|
-
queue.push(left);
|
|
3699
|
-
}
|
|
3700
|
-
if (right >= 0 && mask[right] === 0 && background[right] === 0) {
|
|
3701
|
-
background[right] = 1;
|
|
3702
|
-
queue.push(right);
|
|
3703
|
-
}
|
|
3704
|
-
}
|
|
3705
|
-
const filledMask = new Uint8Array(width * height);
|
|
3706
|
-
for (let i = 0; i < width * height; i++) {
|
|
3707
|
-
filledMask[i] = background[i] === 0 ? 1 : 0;
|
|
3708
|
-
}
|
|
3709
|
-
return filledMask;
|
|
3710
|
-
}
|
|
3711
|
-
function polygonSignedArea(points) {
|
|
3712
|
-
if (points.length < 3) return 0;
|
|
3713
|
-
let sum = 0;
|
|
3714
|
-
for (let i = 0; i < points.length; i++) {
|
|
3715
|
-
const a = points[i];
|
|
3716
|
-
const b = points[(i + 1) % points.length];
|
|
3717
|
-
sum += a.x * b.y - b.x * a.y;
|
|
3718
|
-
}
|
|
3719
|
-
return sum / 2;
|
|
3720
|
-
}
|
|
3721
|
-
|
|
3722
|
-
// src/extensions/tracer.ts
|
|
3723
|
-
var ImageTracer = class {
|
|
3724
|
-
/**
|
|
3725
|
-
* Main entry point: Traces an image URL to an SVG path string.
|
|
3726
|
-
* @param imageUrl The URL or Base64 string of the image.
|
|
3727
|
-
* @param options Configuration options.
|
|
3728
|
-
*/
|
|
3729
|
-
static async trace(imageUrl, options = {}) {
|
|
3730
|
-
const { pathData } = await this.traceWithBounds(imageUrl, options);
|
|
3731
|
-
return pathData;
|
|
3732
|
-
}
|
|
3733
|
-
static async traceWithBounds(imageUrl, options = {}) {
|
|
3734
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
3735
|
-
const img = await this.loadImage(imageUrl);
|
|
3736
|
-
const width = img.width;
|
|
3737
|
-
const height = img.height;
|
|
3738
|
-
if (width <= 0 || height <= 0) {
|
|
3739
|
-
const w = (_a = options.scaleToWidth) != null ? _a : 0;
|
|
3740
|
-
const h = (_b = options.scaleToHeight) != null ? _b : 0;
|
|
3741
|
-
return {
|
|
3742
|
-
pathData: `M 0 0 L ${w} 0 L ${w} ${h} L 0 ${h} Z`,
|
|
3743
|
-
baseBounds: { x: 0, y: 0, width: w, height: h },
|
|
3744
|
-
bounds: { x: 0, y: 0, width: w, height: h }
|
|
3745
|
-
};
|
|
3746
|
-
}
|
|
3747
|
-
const debug = options.debug === true;
|
|
3748
|
-
const debugLog = (message, payload) => {
|
|
3749
|
-
if (!debug) return;
|
|
3750
|
-
if (payload) {
|
|
3751
|
-
console.info(`[ImageTracer] ${message}`, payload);
|
|
3752
|
-
return;
|
|
3753
|
-
}
|
|
3754
|
-
console.info(`[ImageTracer] ${message}`);
|
|
3755
|
-
};
|
|
3756
|
-
const canvas = document.createElement("canvas");
|
|
3757
|
-
canvas.width = width;
|
|
3758
|
-
canvas.height = height;
|
|
3759
|
-
const ctx = canvas.getContext("2d");
|
|
3760
|
-
if (!ctx) throw new Error("Could not get 2D context");
|
|
3761
|
-
ctx.drawImage(img, 0, 0);
|
|
3762
|
-
const imageData = ctx.getImageData(0, 0, width, height);
|
|
3763
|
-
const threshold = (_c = options.threshold) != null ? _c : 10;
|
|
3764
|
-
const expand = Math.max(0, Math.floor((_d = options.expand) != null ? _d : 0));
|
|
3765
|
-
const simplifyTolerance = (_e = options.simplifyTolerance) != null ? _e : 2.5;
|
|
3766
|
-
const useSmoothing = options.smoothing !== false;
|
|
3767
|
-
const componentMode = "all";
|
|
3768
|
-
const minComponentArea = 0;
|
|
3769
|
-
const maxDim = Math.max(width, height);
|
|
3770
|
-
const maskMode = "auto";
|
|
3771
|
-
const whiteThreshold = 240;
|
|
3772
|
-
const alphaOpaqueCutoff = 250;
|
|
3773
|
-
const preprocessDilateRadius = Math.max(
|
|
3774
|
-
2,
|
|
3775
|
-
Math.floor(Math.max(maxDim * 0.012, expand * 0.35))
|
|
3776
|
-
);
|
|
3777
|
-
const preprocessErodeRadius = Math.max(
|
|
3778
|
-
1,
|
|
3779
|
-
Math.floor(preprocessDilateRadius * 0.65)
|
|
3780
|
-
);
|
|
3781
|
-
const smoothDilateRadius = Math.max(
|
|
3782
|
-
1,
|
|
3783
|
-
Math.floor(preprocessDilateRadius * 0.25)
|
|
3784
|
-
);
|
|
3785
|
-
const smoothErodeRadius = Math.max(1, Math.floor(smoothDilateRadius * 0.8));
|
|
3786
|
-
const connectStartDilateRadius = Math.max(
|
|
3787
|
-
1,
|
|
3788
|
-
Math.floor(Math.max(maxDim * 6e-3, expand * 0.2))
|
|
3789
|
-
);
|
|
3790
|
-
const connectMaxDilateRadius = Math.max(
|
|
3791
|
-
connectStartDilateRadius,
|
|
3792
|
-
Math.floor(Math.max(maxDim * 0.2, expand * 2.5))
|
|
3793
|
-
);
|
|
3794
|
-
const connectErodeRatio = 0.65;
|
|
3795
|
-
debugLog("traceWithBounds:start", {
|
|
3796
|
-
width,
|
|
3797
|
-
height,
|
|
3798
|
-
threshold,
|
|
3799
|
-
expand,
|
|
3800
|
-
simplifyTolerance,
|
|
3801
|
-
smoothing: useSmoothing,
|
|
3802
|
-
strategy: {
|
|
3803
|
-
maskMode,
|
|
3804
|
-
whiteThreshold,
|
|
3805
|
-
alphaOpaqueCutoff,
|
|
3806
|
-
fillHoles: true,
|
|
3807
|
-
preprocessDilateRadius,
|
|
3808
|
-
preprocessErodeRadius,
|
|
3809
|
-
smoothDilateRadius,
|
|
3810
|
-
smoothErodeRadius,
|
|
3811
|
-
connectEnabled: true,
|
|
3812
|
-
connectStartDilateRadius,
|
|
3813
|
-
connectMaxDilateRadius,
|
|
3814
|
-
connectErodeRatio
|
|
3815
|
-
}
|
|
3816
|
-
});
|
|
3817
|
-
const padding = Math.max(
|
|
3818
|
-
preprocessDilateRadius,
|
|
3819
|
-
smoothDilateRadius,
|
|
3820
|
-
connectMaxDilateRadius,
|
|
3821
|
-
expand
|
|
3822
|
-
) + 2;
|
|
3823
|
-
const paddedWidth = width + padding * 2;
|
|
3824
|
-
const paddedHeight = height + padding * 2;
|
|
3825
|
-
const summarizeMaskContours = (m) => {
|
|
3826
|
-
const summary = this.summarizeAllContours(
|
|
3827
|
-
m,
|
|
3828
|
-
paddedWidth,
|
|
3829
|
-
paddedHeight,
|
|
3830
|
-
minComponentArea
|
|
3831
|
-
);
|
|
3832
|
-
return {
|
|
3833
|
-
rawContourCount: summary.rawCount,
|
|
3834
|
-
selectedContourCount: summary.selectedCount
|
|
3835
|
-
};
|
|
3836
|
-
};
|
|
3837
|
-
let mask = createMask(imageData, {
|
|
3838
|
-
threshold,
|
|
3839
|
-
padding,
|
|
3840
|
-
paddedWidth,
|
|
3841
|
-
paddedHeight,
|
|
3842
|
-
maskMode,
|
|
3843
|
-
whiteThreshold,
|
|
3844
|
-
alphaOpaqueCutoff
|
|
3845
|
-
});
|
|
3846
|
-
if (debug) {
|
|
3847
|
-
debugLog(
|
|
3848
|
-
"traceWithBounds:mask:after-create",
|
|
3849
|
-
summarizeMaskContours(mask)
|
|
3850
|
-
);
|
|
3851
|
-
}
|
|
3852
|
-
mask = circularMorphology(
|
|
3853
|
-
mask,
|
|
3854
|
-
paddedWidth,
|
|
3855
|
-
paddedHeight,
|
|
3856
|
-
preprocessDilateRadius,
|
|
3857
|
-
"dilate"
|
|
3858
|
-
);
|
|
3859
|
-
mask = fillHoles(mask, paddedWidth, paddedHeight);
|
|
3860
|
-
mask = circularMorphology(
|
|
3861
|
-
mask,
|
|
3862
|
-
paddedWidth,
|
|
3863
|
-
paddedHeight,
|
|
3864
|
-
preprocessErodeRadius,
|
|
3865
|
-
"erode"
|
|
3866
|
-
);
|
|
3867
|
-
mask = fillHoles(mask, paddedWidth, paddedHeight);
|
|
3868
|
-
if (debug) {
|
|
3869
|
-
debugLog("traceWithBounds:mask:after-preprocess", {
|
|
3870
|
-
dilateRadius: preprocessDilateRadius,
|
|
3871
|
-
erodeRadius: preprocessErodeRadius,
|
|
3872
|
-
...summarizeMaskContours(mask)
|
|
3873
|
-
});
|
|
3874
|
-
}
|
|
3875
|
-
mask = circularMorphology(
|
|
3876
|
-
mask,
|
|
3877
|
-
paddedWidth,
|
|
3878
|
-
paddedHeight,
|
|
3879
|
-
smoothDilateRadius,
|
|
3880
|
-
"dilate"
|
|
3881
|
-
);
|
|
3882
|
-
mask = fillHoles(mask, paddedWidth, paddedHeight);
|
|
3883
|
-
mask = circularMorphology(
|
|
3884
|
-
mask,
|
|
3885
|
-
paddedWidth,
|
|
3886
|
-
paddedHeight,
|
|
3887
|
-
smoothErodeRadius,
|
|
3888
|
-
"erode"
|
|
3889
|
-
);
|
|
3890
|
-
mask = fillHoles(mask, paddedWidth, paddedHeight);
|
|
3891
|
-
if (debug) {
|
|
3892
|
-
debugLog("traceWithBounds:mask:after-smooth", {
|
|
3893
|
-
dilateRadius: smoothDilateRadius,
|
|
3894
|
-
erodeRadius: smoothErodeRadius,
|
|
3895
|
-
...summarizeMaskContours(mask)
|
|
3896
|
-
});
|
|
3897
|
-
}
|
|
3898
|
-
const beforeConnectSummary = summarizeMaskContours(mask);
|
|
3899
|
-
if (beforeConnectSummary.selectedContourCount <= 1) {
|
|
3900
|
-
debugLog("traceWithBounds:mask:connect-skipped", {
|
|
3901
|
-
reason: "already-single-component",
|
|
3902
|
-
before: beforeConnectSummary
|
|
3903
|
-
});
|
|
3904
|
-
} else {
|
|
3905
|
-
const connectResult = this.findForceConnectResult(
|
|
3906
|
-
mask,
|
|
3907
|
-
paddedWidth,
|
|
3908
|
-
paddedHeight,
|
|
3909
|
-
minComponentArea,
|
|
3910
|
-
connectStartDilateRadius,
|
|
3911
|
-
connectMaxDilateRadius,
|
|
3912
|
-
connectErodeRatio
|
|
3913
|
-
);
|
|
3914
|
-
if (debug) {
|
|
3915
|
-
debugLog("traceWithBounds:mask:after-connect", {
|
|
3916
|
-
before: beforeConnectSummary,
|
|
3917
|
-
appliedDilateRadius: connectResult.appliedDilateRadius,
|
|
3918
|
-
appliedErodeRadius: connectResult.appliedErodeRadius,
|
|
3919
|
-
reachedSingleComponent: connectResult.reachedSingleComponent,
|
|
3920
|
-
after: {
|
|
3921
|
-
rawContourCount: connectResult.rawContourCount,
|
|
3922
|
-
selectedContourCount: connectResult.selectedContourCount
|
|
3923
|
-
}
|
|
3924
|
-
});
|
|
3925
|
-
}
|
|
3926
|
-
mask = connectResult.mask;
|
|
3927
|
-
}
|
|
3928
|
-
if (debug) {
|
|
3929
|
-
const afterConnectSummary = summarizeMaskContours(mask);
|
|
3930
|
-
if (afterConnectSummary.selectedContourCount > 1) {
|
|
3931
|
-
debugLog("traceWithBounds:mask:connect-warning", {
|
|
3932
|
-
reason: "still-multi-component-after-connect-search",
|
|
3933
|
-
summary: afterConnectSummary
|
|
3934
|
-
});
|
|
3935
|
-
}
|
|
3936
|
-
}
|
|
3937
|
-
const baseMask = mask;
|
|
3938
|
-
const baseContoursRaw = this.traceAllContours(
|
|
3939
|
-
baseMask,
|
|
3940
|
-
paddedWidth,
|
|
3941
|
-
paddedHeight
|
|
3942
|
-
);
|
|
3943
|
-
const baseContours = this.selectContours(
|
|
3944
|
-
baseContoursRaw,
|
|
3945
|
-
componentMode,
|
|
3946
|
-
minComponentArea
|
|
3947
|
-
);
|
|
3948
|
-
if (!baseContours.length) {
|
|
3949
|
-
const w = (_f = options.scaleToWidth) != null ? _f : width;
|
|
3950
|
-
const h = (_g = options.scaleToHeight) != null ? _g : height;
|
|
3951
|
-
debugLog("fallback:no-base-contour", { width: w, height: h });
|
|
3952
|
-
return {
|
|
3953
|
-
pathData: `M 0 0 L ${w} 0 L ${w} ${h} L 0 ${h} Z`,
|
|
3954
|
-
baseBounds: { x: 0, y: 0, width: w, height: h },
|
|
3955
|
-
bounds: { x: 0, y: 0, width: w, height: h }
|
|
3956
|
-
};
|
|
3957
|
-
}
|
|
3958
|
-
const baseUnpaddedContours = baseContours.map(
|
|
3959
|
-
(contour) => contour.map((p) => ({
|
|
3960
|
-
x: p.x - padding,
|
|
3961
|
-
y: p.y - padding
|
|
3962
|
-
}))
|
|
3963
|
-
).filter((contour) => contour.length > 2);
|
|
3964
|
-
if (!baseUnpaddedContours.length) {
|
|
3965
|
-
const w = (_h = options.scaleToWidth) != null ? _h : width;
|
|
3966
|
-
const h = (_i = options.scaleToHeight) != null ? _i : height;
|
|
3967
|
-
debugLog("fallback:empty-base-contours", { width: w, height: h });
|
|
3968
|
-
return {
|
|
3969
|
-
pathData: `M 0 0 L ${w} 0 L ${w} ${h} L 0 ${h} Z`,
|
|
3970
|
-
baseBounds: { x: 0, y: 0, width: w, height: h },
|
|
3971
|
-
bounds: { x: 0, y: 0, width: w, height: h }
|
|
3972
|
-
};
|
|
3973
|
-
}
|
|
3974
|
-
let baseBounds = this.boundsFromPoints(
|
|
3975
|
-
this.flattenContours(baseUnpaddedContours)
|
|
3976
|
-
);
|
|
3977
|
-
let maskExpanded = baseMask;
|
|
3978
|
-
if (expand > 0) {
|
|
3979
|
-
maskExpanded = circularMorphology(
|
|
3980
|
-
baseMask,
|
|
3981
|
-
paddedWidth,
|
|
3982
|
-
paddedHeight,
|
|
3983
|
-
expand,
|
|
3984
|
-
"dilate"
|
|
3985
|
-
);
|
|
3986
|
-
}
|
|
3987
|
-
const expandedContoursRaw = this.traceAllContours(
|
|
3988
|
-
maskExpanded,
|
|
3989
|
-
paddedWidth,
|
|
3990
|
-
paddedHeight
|
|
3991
|
-
);
|
|
3992
|
-
const expandedContours = this.selectContours(
|
|
3993
|
-
expandedContoursRaw,
|
|
3994
|
-
componentMode,
|
|
3995
|
-
minComponentArea
|
|
3996
|
-
);
|
|
3997
|
-
if (!expandedContours.length) {
|
|
3998
|
-
debugLog("fallback:no-expanded-contour", {
|
|
3999
|
-
baseBounds,
|
|
4000
|
-
width,
|
|
4001
|
-
height,
|
|
4002
|
-
expand
|
|
4003
|
-
});
|
|
4004
|
-
return {
|
|
4005
|
-
pathData: `M 0 0 L ${width} 0 L ${width} ${height} L 0 ${height} Z`,
|
|
4006
|
-
baseBounds,
|
|
4007
|
-
bounds: baseBounds
|
|
4008
|
-
};
|
|
4009
|
-
}
|
|
4010
|
-
const expandedUnpaddedContours = expandedContours.map(
|
|
4011
|
-
(contour) => contour.map((p) => ({
|
|
4012
|
-
x: p.x - padding,
|
|
4013
|
-
y: p.y - padding
|
|
4014
|
-
}))
|
|
4015
|
-
).filter((contour) => contour.length > 2);
|
|
4016
|
-
if (!expandedUnpaddedContours.length) {
|
|
4017
|
-
debugLog("fallback:empty-expanded-contours", {
|
|
4018
|
-
baseBounds,
|
|
4019
|
-
width,
|
|
4020
|
-
height,
|
|
4021
|
-
expand
|
|
4022
|
-
});
|
|
4023
|
-
return {
|
|
4024
|
-
pathData: `M 0 0 L ${width} 0 L ${width} ${height} L 0 ${height} Z`,
|
|
4025
|
-
baseBounds,
|
|
4026
|
-
bounds: baseBounds
|
|
4027
|
-
};
|
|
4028
|
-
}
|
|
4029
|
-
let globalBounds = this.boundsFromPoints(
|
|
4030
|
-
this.flattenContours(expandedUnpaddedContours)
|
|
4031
|
-
);
|
|
4032
|
-
let finalContours = expandedUnpaddedContours;
|
|
4033
|
-
if (options.scaleToWidth && options.scaleToHeight) {
|
|
4034
|
-
finalContours = this.scaleContours(
|
|
4035
|
-
expandedUnpaddedContours,
|
|
4036
|
-
options.scaleToWidth,
|
|
4037
|
-
options.scaleToHeight,
|
|
4038
|
-
globalBounds
|
|
4039
|
-
);
|
|
4040
|
-
globalBounds = this.boundsFromPoints(this.flattenContours(finalContours));
|
|
4041
|
-
const baseScaledContours = this.scaleContours(
|
|
4042
|
-
baseUnpaddedContours,
|
|
4043
|
-
options.scaleToWidth,
|
|
4044
|
-
options.scaleToHeight,
|
|
4045
|
-
baseBounds
|
|
4046
|
-
);
|
|
4047
|
-
baseBounds = this.boundsFromPoints(
|
|
4048
|
-
this.flattenContours(baseScaledContours)
|
|
4049
|
-
);
|
|
4050
|
-
}
|
|
4051
|
-
if (expand > 0) {
|
|
4052
|
-
const expectedExpandedBounds = {
|
|
4053
|
-
x: baseBounds.x - expand,
|
|
4054
|
-
y: baseBounds.y - expand,
|
|
4055
|
-
width: baseBounds.width + expand * 2,
|
|
4056
|
-
height: baseBounds.height + expand * 2
|
|
4057
|
-
};
|
|
4058
|
-
if (expectedExpandedBounds.width > 0 && expectedExpandedBounds.height > 0 && globalBounds.width > 0 && globalBounds.height > 0) {
|
|
4059
|
-
const shouldNormalizeExpandBounds = Math.abs(globalBounds.x - expectedExpandedBounds.x) > 1 || Math.abs(globalBounds.y - expectedExpandedBounds.y) > 1 || Math.abs(globalBounds.width - expectedExpandedBounds.width) > 1 || Math.abs(globalBounds.height - expectedExpandedBounds.height) > 1;
|
|
4060
|
-
if (shouldNormalizeExpandBounds) {
|
|
4061
|
-
const beforeNormalize = globalBounds;
|
|
4062
|
-
finalContours = this.translateContours(
|
|
4063
|
-
this.scaleContours(
|
|
4064
|
-
finalContours,
|
|
4065
|
-
expectedExpandedBounds.width,
|
|
4066
|
-
expectedExpandedBounds.height,
|
|
4067
|
-
globalBounds
|
|
4068
|
-
),
|
|
4069
|
-
expectedExpandedBounds.x,
|
|
4070
|
-
expectedExpandedBounds.y
|
|
4071
|
-
);
|
|
4072
|
-
globalBounds = this.boundsFromPoints(
|
|
4073
|
-
this.flattenContours(finalContours)
|
|
4074
|
-
);
|
|
4075
|
-
debugLog("traceWithBounds:expand-normalized", {
|
|
4076
|
-
expand,
|
|
4077
|
-
expectedExpandedBounds,
|
|
4078
|
-
beforeNormalize,
|
|
4079
|
-
afterNormalize: globalBounds
|
|
4080
|
-
});
|
|
4081
|
-
}
|
|
4082
|
-
}
|
|
4083
|
-
}
|
|
4084
|
-
debugLog("traceWithBounds:contours", {
|
|
4085
|
-
baseContourCount: baseContoursRaw.length,
|
|
4086
|
-
baseSelectedCount: baseContours.length,
|
|
4087
|
-
expandedContourCount: expandedContoursRaw.length,
|
|
4088
|
-
expandedSelectedCount: expandedContours.length,
|
|
4089
|
-
baseBounds,
|
|
4090
|
-
expandedBounds: globalBounds,
|
|
4091
|
-
expandedDeltaX: globalBounds.width - baseBounds.width,
|
|
4092
|
-
expandedDeltaY: globalBounds.height - baseBounds.height,
|
|
4093
|
-
expandedMayOverflowImageBounds: expand > 0,
|
|
4094
|
-
useSmoothing,
|
|
4095
|
-
componentMode
|
|
4096
|
-
});
|
|
4097
|
-
if (useSmoothing) {
|
|
4098
|
-
return {
|
|
4099
|
-
pathData: this.contoursToSVGPaper(finalContours, simplifyTolerance),
|
|
4100
|
-
baseBounds,
|
|
4101
|
-
bounds: globalBounds
|
|
4102
|
-
};
|
|
4103
|
-
} else {
|
|
4104
|
-
const simplifiedContours = finalContours.map((points) => this.douglasPeucker(points, simplifyTolerance)).filter((points) => points.length > 2);
|
|
4105
|
-
const pathData = this.contoursToSVG(simplifiedContours) || this.contoursToSVG(finalContours);
|
|
4106
|
-
return {
|
|
4107
|
-
pathData,
|
|
4108
|
-
baseBounds,
|
|
4109
|
-
bounds: globalBounds
|
|
4110
|
-
};
|
|
4111
|
-
}
|
|
4112
|
-
}
|
|
4113
|
-
static pickPrimaryContour(contours) {
|
|
4114
|
-
if (contours.length === 0) return null;
|
|
4115
|
-
return contours.reduce((best, cur) => {
|
|
4116
|
-
if (!best) return cur;
|
|
4117
|
-
const bestArea = Math.abs(polygonSignedArea(best));
|
|
4118
|
-
const curArea = Math.abs(polygonSignedArea(cur));
|
|
4119
|
-
if (curArea !== bestArea) return curArea > bestArea ? cur : best;
|
|
4120
|
-
return cur.length > best.length ? cur : best;
|
|
4121
|
-
}, contours[0]);
|
|
4122
|
-
}
|
|
4123
|
-
static flattenContours(contours) {
|
|
4124
|
-
return contours.flatMap((contour) => contour);
|
|
4125
|
-
}
|
|
4126
|
-
static contourCentroid(points) {
|
|
4127
|
-
if (!points.length) return { x: 0, y: 0 };
|
|
4128
|
-
const sum = points.reduce(
|
|
4129
|
-
(acc, p) => ({ x: acc.x + p.x, y: acc.y + p.y }),
|
|
4130
|
-
{ x: 0, y: 0 }
|
|
4131
|
-
);
|
|
4132
|
-
return {
|
|
4133
|
-
x: sum.x / points.length,
|
|
4134
|
-
y: sum.y / points.length
|
|
4135
|
-
};
|
|
4136
|
-
}
|
|
4137
|
-
static pointInPolygon(point, polygon) {
|
|
4138
|
-
let inside = false;
|
|
4139
|
-
const { x, y } = point;
|
|
4140
|
-
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
4141
|
-
const xi = polygon[i].x;
|
|
4142
|
-
const yi = polygon[i].y;
|
|
4143
|
-
const xj = polygon[j].x;
|
|
4144
|
-
const yj = polygon[j].y;
|
|
4145
|
-
const intersects = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi || Number.EPSILON) + xi;
|
|
4146
|
-
if (intersects) inside = !inside;
|
|
4147
|
-
}
|
|
4148
|
-
return inside;
|
|
4149
|
-
}
|
|
4150
|
-
static keepOutermostContours(contours) {
|
|
4151
|
-
if (contours.length <= 1) return contours;
|
|
4152
|
-
const sorted = [...contours].sort(
|
|
4153
|
-
(a, b) => Math.abs(polygonSignedArea(b)) - Math.abs(polygonSignedArea(a))
|
|
4154
|
-
);
|
|
4155
|
-
const selected = [];
|
|
4156
|
-
for (const contour of sorted) {
|
|
4157
|
-
const centroid = this.contourCentroid(contour);
|
|
4158
|
-
const isNested = selected.some(
|
|
4159
|
-
(outer) => this.pointInPolygon(centroid, outer)
|
|
4160
|
-
);
|
|
4161
|
-
if (!isNested) {
|
|
4162
|
-
selected.push(contour);
|
|
4163
|
-
}
|
|
4164
|
-
}
|
|
4165
|
-
return selected;
|
|
4166
|
-
}
|
|
4167
|
-
static summarizeAllContours(mask, width, height, minComponentArea) {
|
|
4168
|
-
const raw = this.traceAllContours(mask, width, height);
|
|
4169
|
-
const selected = this.selectContours(raw, "all", minComponentArea);
|
|
4170
|
-
return {
|
|
4171
|
-
rawCount: raw.length,
|
|
4172
|
-
selectedCount: selected.length
|
|
4173
|
-
};
|
|
4174
|
-
}
|
|
4175
|
-
static findForceConnectResult(sourceMask, width, height, minComponentArea, startDilateRadius, maxDilateRadius, erodeRatio) {
|
|
4176
|
-
const initial = this.summarizeAllContours(
|
|
4177
|
-
sourceMask,
|
|
4178
|
-
width,
|
|
4179
|
-
height,
|
|
4180
|
-
minComponentArea
|
|
4181
|
-
);
|
|
4182
|
-
if (initial.selectedCount <= 1) {
|
|
4183
|
-
return {
|
|
4184
|
-
mask: sourceMask,
|
|
4185
|
-
appliedDilateRadius: 0,
|
|
4186
|
-
appliedErodeRadius: 0,
|
|
4187
|
-
reachedSingleComponent: true,
|
|
4188
|
-
rawContourCount: initial.rawCount,
|
|
4189
|
-
selectedContourCount: initial.selectedCount
|
|
4190
|
-
};
|
|
4191
|
-
}
|
|
4192
|
-
const normalizedStart = Math.max(1, Math.floor(startDilateRadius));
|
|
4193
|
-
const normalizedMax = Math.max(
|
|
4194
|
-
normalizedStart,
|
|
4195
|
-
Math.floor(maxDilateRadius)
|
|
4196
|
-
);
|
|
4197
|
-
const normalizedErodeRatio = Math.max(0, erodeRatio);
|
|
4198
|
-
const evaluate = (dilateRadius) => {
|
|
4199
|
-
const erodeRadius = Math.max(
|
|
4200
|
-
1,
|
|
4201
|
-
Math.floor(dilateRadius * normalizedErodeRatio)
|
|
4202
|
-
);
|
|
4203
|
-
let mask = sourceMask;
|
|
4204
|
-
mask = circularMorphology(mask, width, height, dilateRadius, "dilate");
|
|
4205
|
-
mask = fillHoles(mask, width, height);
|
|
4206
|
-
mask = circularMorphology(mask, width, height, erodeRadius, "erode");
|
|
4207
|
-
mask = fillHoles(mask, width, height);
|
|
4208
|
-
const summary = this.summarizeAllContours(
|
|
4209
|
-
mask,
|
|
4210
|
-
width,
|
|
4211
|
-
height,
|
|
4212
|
-
minComponentArea
|
|
4213
|
-
);
|
|
4214
|
-
return {
|
|
4215
|
-
dilateRadius,
|
|
4216
|
-
erodeRadius,
|
|
4217
|
-
mask,
|
|
4218
|
-
rawCount: summary.rawCount,
|
|
4219
|
-
selectedCount: summary.selectedCount
|
|
4220
|
-
};
|
|
4221
|
-
};
|
|
4222
|
-
let low = normalizedStart - 1;
|
|
4223
|
-
let high = normalizedStart;
|
|
4224
|
-
let highResult = evaluate(high);
|
|
4225
|
-
while (high < normalizedMax && highResult.selectedCount > 1) {
|
|
4226
|
-
low = high;
|
|
4227
|
-
high = Math.min(
|
|
4228
|
-
normalizedMax,
|
|
4229
|
-
Math.max(high + 1, Math.floor(high * 1.6))
|
|
4230
|
-
);
|
|
4231
|
-
highResult = evaluate(high);
|
|
4232
|
-
}
|
|
4233
|
-
if (highResult.selectedCount > 1) {
|
|
4234
|
-
return {
|
|
4235
|
-
mask: highResult.mask,
|
|
4236
|
-
appliedDilateRadius: highResult.dilateRadius,
|
|
4237
|
-
appliedErodeRadius: highResult.erodeRadius,
|
|
4238
|
-
reachedSingleComponent: false,
|
|
4239
|
-
rawContourCount: highResult.rawCount,
|
|
4240
|
-
selectedContourCount: highResult.selectedCount
|
|
4241
|
-
};
|
|
3732
|
+
} else if (state.constraintMode === "lockAspect") {
|
|
3733
|
+
const ratio = Math.max(1e-4, state.aspectRatio);
|
|
3734
|
+
if (changed === "height") {
|
|
3735
|
+
nextWidthMm = nextHeightMm * ratio;
|
|
3736
|
+
} else {
|
|
3737
|
+
nextHeightMm = nextWidthMm / ratio;
|
|
3738
|
+
}
|
|
4242
3739
|
}
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
const
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
3740
|
+
nextWidthMm = sanitizeMmValue(nextWidthMm, limits);
|
|
3741
|
+
nextHeightMm = sanitizeMmValue(nextHeightMm, limits);
|
|
3742
|
+
if (state.constraintMode === "equal") {
|
|
3743
|
+
const value = Math.max(nextWidthMm, nextHeightMm);
|
|
3744
|
+
nextWidthMm = value;
|
|
3745
|
+
nextHeightMm = value;
|
|
3746
|
+
} else if (state.constraintMode === "lockAspect") {
|
|
3747
|
+
const ratio = Math.max(1e-4, state.aspectRatio);
|
|
3748
|
+
if (changed === "height") {
|
|
3749
|
+
nextWidthMm = sanitizeMmValue(nextHeightMm * ratio, limits);
|
|
4250
3750
|
} else {
|
|
4251
|
-
|
|
3751
|
+
nextHeightMm = sanitizeMmValue(nextWidthMm / ratio, limits);
|
|
4252
3752
|
}
|
|
4253
3753
|
}
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
rawContourCount: best.rawCount,
|
|
4260
|
-
selectedContourCount: best.selectedCount
|
|
4261
|
-
};
|
|
3754
|
+
configService.update("size.actualWidthMm", nextWidthMm);
|
|
3755
|
+
configService.update("size.actualHeightMm", nextHeightMm);
|
|
3756
|
+
configService.update("size.unit", inputUnit);
|
|
3757
|
+
this.emitStateChanged();
|
|
3758
|
+
return this.getStateForUI();
|
|
4262
3759
|
}
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
if (
|
|
4266
|
-
|
|
4267
|
-
|
|
3760
|
+
setConstraintMode(modeRaw) {
|
|
3761
|
+
const configService = this.getConfigService();
|
|
3762
|
+
if (!configService) return null;
|
|
3763
|
+
const state = readSizeState(configService);
|
|
3764
|
+
const mode = normalizeConstraintMode(modeRaw);
|
|
3765
|
+
configService.update("size.constraintMode", mode);
|
|
3766
|
+
if (mode === "lockAspect") {
|
|
3767
|
+
const ratio = state.actualWidthMm / Math.max(1e-3, state.actualHeightMm);
|
|
3768
|
+
configService.update("size.aspectRatio", ratio);
|
|
4268
3769
|
}
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
3770
|
+
if (mode === "equal") {
|
|
3771
|
+
const value = sanitizeMmValue(
|
|
3772
|
+
Math.max(state.actualWidthMm, state.actualHeightMm),
|
|
3773
|
+
{
|
|
3774
|
+
minMm: state.minMm,
|
|
3775
|
+
maxMm: state.maxMm,
|
|
3776
|
+
stepMm: state.stepMm
|
|
3777
|
+
}
|
|
3778
|
+
);
|
|
3779
|
+
configService.update("size.actualWidthMm", value);
|
|
3780
|
+
configService.update("size.actualHeightMm", value);
|
|
3781
|
+
configService.update("size.aspectRatio", 1);
|
|
4272
3782
|
}
|
|
4273
|
-
|
|
4274
|
-
|
|
3783
|
+
this.emitStateChanged();
|
|
3784
|
+
return this.getStateForUI();
|
|
3785
|
+
}
|
|
3786
|
+
setUnit(unitRaw) {
|
|
3787
|
+
const configService = this.getConfigService();
|
|
3788
|
+
if (!configService) return null;
|
|
3789
|
+
const unit = normalizeUnit(unitRaw);
|
|
3790
|
+
configService.update("size.unit", unit);
|
|
3791
|
+
this.emitStateChanged();
|
|
3792
|
+
return this.getStateForUI();
|
|
3793
|
+
}
|
|
3794
|
+
setCut(cutModeRaw, cutMarginMm = 0) {
|
|
3795
|
+
const configService = this.getConfigService();
|
|
3796
|
+
if (!configService) return null;
|
|
3797
|
+
const cutMode = normalizeCutMode(cutModeRaw);
|
|
3798
|
+
const margin = Math.max(0, Number(cutMarginMm) || 0);
|
|
3799
|
+
configService.update("size.cutMode", cutMode);
|
|
3800
|
+
configService.update("size.cutMarginMm", margin);
|
|
3801
|
+
this.emitStateChanged();
|
|
3802
|
+
return this.getStateForUI();
|
|
3803
|
+
}
|
|
3804
|
+
getSelectedImageSize(id) {
|
|
3805
|
+
var _a, _b, _c;
|
|
3806
|
+
const configService = this.getConfigService();
|
|
3807
|
+
if (!configService || !this.canvasService) return null;
|
|
3808
|
+
const sizeState = readSizeState(configService);
|
|
3809
|
+
const layout = computeSceneLayout(this.canvasService, sizeState);
|
|
3810
|
+
if (!layout || layout.scale <= 0) return null;
|
|
3811
|
+
const all = this.canvasService.canvas.getObjects();
|
|
3812
|
+
const active = this.canvasService.canvas.getActiveObject();
|
|
3813
|
+
const activeId = ((_a = active == null ? void 0 : active.data) == null ? void 0 : _a.layerId) === IMAGE_OBJECT_LAYER_ID ? (_b = active == null ? void 0 : active.data) == null ? void 0 : _b.id : null;
|
|
3814
|
+
const targetId = id || activeId;
|
|
3815
|
+
const target = all.find(
|
|
3816
|
+
(obj) => {
|
|
3817
|
+
var _a2, _b2;
|
|
3818
|
+
return ((_a2 = obj == null ? void 0 : obj.data) == null ? void 0 : _a2.layerId) === IMAGE_OBJECT_LAYER_ID && ((_b2 = obj == null ? void 0 : obj.data) == null ? void 0 : _b2.id) === targetId;
|
|
3819
|
+
}
|
|
3820
|
+
) || all.find((obj) => {
|
|
3821
|
+
var _a2;
|
|
3822
|
+
return ((_a2 = obj == null ? void 0 : obj.data) == null ? void 0 : _a2.layerId) === IMAGE_OBJECT_LAYER_ID;
|
|
3823
|
+
});
|
|
3824
|
+
if (!target) return null;
|
|
3825
|
+
const objectWidthPx = Math.abs((target.width || 0) * (target.scaleX || 1));
|
|
3826
|
+
const objectHeightPx = Math.abs(
|
|
3827
|
+
(target.height || 0) * (target.scaleY || 1)
|
|
4275
3828
|
);
|
|
4276
|
-
if (
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
const primary = this.pickPrimaryContour(contours);
|
|
4280
|
-
return primary ? [primary] : [];
|
|
4281
|
-
}
|
|
4282
|
-
static boundsFromPoints(points) {
|
|
4283
|
-
let minX = Infinity;
|
|
4284
|
-
let minY = Infinity;
|
|
4285
|
-
let maxX = -Infinity;
|
|
4286
|
-
let maxY = -Infinity;
|
|
4287
|
-
for (const p of points) {
|
|
4288
|
-
if (p.x < minX) minX = p.x;
|
|
4289
|
-
if (p.y < minY) minY = p.y;
|
|
4290
|
-
if (p.x > maxX) maxX = p.x;
|
|
4291
|
-
if (p.y > maxY) maxY = p.y;
|
|
4292
|
-
}
|
|
4293
|
-
if (!Number.isFinite(minX) || !Number.isFinite(minY)) {
|
|
4294
|
-
return { x: 0, y: 0, width: 0, height: 0 };
|
|
4295
|
-
}
|
|
3829
|
+
if (objectWidthPx <= 0 || objectHeightPx <= 0) return null;
|
|
3830
|
+
const widthMm = objectWidthPx / layout.scale;
|
|
3831
|
+
const heightMm = objectHeightPx / layout.scale;
|
|
4296
3832
|
return {
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
3833
|
+
id: ((_c = target == null ? void 0 : target.data) == null ? void 0 : _c.id) || null,
|
|
3834
|
+
widthMm,
|
|
3835
|
+
heightMm,
|
|
3836
|
+
width: fromMm(widthMm, sizeState.unit),
|
|
3837
|
+
height: fromMm(heightMm, sizeState.unit),
|
|
3838
|
+
unit: sizeState.unit
|
|
4301
3839
|
};
|
|
4302
3840
|
}
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
3841
|
+
};
|
|
3842
|
+
|
|
3843
|
+
// src/extensions/dieline/DielineTool.ts
|
|
3844
|
+
import {
|
|
3845
|
+
ContributionPointIds as ContributionPointIds4
|
|
3846
|
+
} from "@pooder/core";
|
|
3847
|
+
import { Canvas as FabricCanvas2, Path, Pattern as Pattern2 } from "fabric";
|
|
3848
|
+
|
|
3849
|
+
// src/extensions/dieline/commands.ts
|
|
3850
|
+
function createDielineCommands(tool, state) {
|
|
3851
|
+
return [
|
|
3852
|
+
{
|
|
3853
|
+
command: "updateFeaturePosition",
|
|
3854
|
+
id: "updateFeaturePosition",
|
|
3855
|
+
title: "Update Feature Position",
|
|
3856
|
+
handler: (groupId, x, y) => {
|
|
3857
|
+
var _a;
|
|
3858
|
+
const configService = (_a = tool.context) == null ? void 0 : _a.services.get("ConfigurationService");
|
|
3859
|
+
if (!configService) return;
|
|
3860
|
+
const features = configService.get("dieline.features") || [];
|
|
3861
|
+
let changed = false;
|
|
3862
|
+
const newFeatures = features.map((f) => {
|
|
3863
|
+
if (f.groupId === groupId) {
|
|
3864
|
+
if (f.x !== x || f.y !== y) {
|
|
3865
|
+
changed = true;
|
|
3866
|
+
return { ...f, x, y };
|
|
4325
3867
|
}
|
|
4326
3868
|
}
|
|
3869
|
+
return f;
|
|
3870
|
+
});
|
|
3871
|
+
if (changed) {
|
|
3872
|
+
configService.update("dieline.features", newFeatures);
|
|
4327
3873
|
}
|
|
4328
3874
|
}
|
|
4329
|
-
}
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
img.onload = () => resolve(img);
|
|
4337
|
-
img.onerror = (e) => reject(e);
|
|
4338
|
-
img.src = url;
|
|
4339
|
-
});
|
|
4340
|
-
}
|
|
4341
|
-
/**
|
|
4342
|
-
* Moore-Neighbor Tracing Algorithm
|
|
4343
|
-
* More robust for irregular shapes than simple Marching Squares walker.
|
|
4344
|
-
*/
|
|
4345
|
-
static marchingSquares(mask, visited, startX, startY, width, height) {
|
|
4346
|
-
const isSolid = (x, y) => {
|
|
4347
|
-
if (x < 0 || x >= width || y < 0 || y >= height) return false;
|
|
4348
|
-
return mask[y * width + x] === 1;
|
|
4349
|
-
};
|
|
4350
|
-
const points = [];
|
|
4351
|
-
let cx = startX;
|
|
4352
|
-
let cy = startY;
|
|
4353
|
-
const neighbors = [
|
|
4354
|
-
{ x: 0, y: -1 },
|
|
4355
|
-
{ x: 1, y: -1 },
|
|
4356
|
-
{ x: 1, y: 0 },
|
|
4357
|
-
{ x: 1, y: 1 },
|
|
4358
|
-
{ x: 0, y: 1 },
|
|
4359
|
-
{ x: -1, y: 1 },
|
|
4360
|
-
{ x: -1, y: 0 },
|
|
4361
|
-
{ x: -1, y: -1 }
|
|
4362
|
-
];
|
|
4363
|
-
let backtrack = 6;
|
|
4364
|
-
const maxSteps = width * height * 3;
|
|
4365
|
-
let steps = 0;
|
|
4366
|
-
do {
|
|
4367
|
-
points.push({ x: cx, y: cy });
|
|
4368
|
-
visited[cy * width + cx] = 1;
|
|
4369
|
-
let found = false;
|
|
4370
|
-
for (let i = 0; i < 8; i++) {
|
|
4371
|
-
const idx = (backtrack + 1 + i) % 8;
|
|
4372
|
-
const nx = cx + neighbors[idx].x;
|
|
4373
|
-
const ny = cy + neighbors[idx].y;
|
|
4374
|
-
if (isSolid(nx, ny)) {
|
|
4375
|
-
cx = nx;
|
|
4376
|
-
cy = ny;
|
|
4377
|
-
backtrack = (idx + 4 + 1) % 8;
|
|
4378
|
-
found = true;
|
|
4379
|
-
break;
|
|
4380
|
-
}
|
|
4381
|
-
}
|
|
4382
|
-
if (!found) break;
|
|
4383
|
-
steps++;
|
|
4384
|
-
} while ((cx !== startX || cy !== startY) && steps < maxSteps);
|
|
4385
|
-
return points;
|
|
4386
|
-
}
|
|
4387
|
-
/**
|
|
4388
|
-
* Douglas-Peucker Line Simplification
|
|
4389
|
-
*/
|
|
4390
|
-
static douglasPeucker(points, tolerance) {
|
|
4391
|
-
if (points.length <= 2) return points;
|
|
4392
|
-
const sqTolerance = tolerance * tolerance;
|
|
4393
|
-
let maxSqDist = 0;
|
|
4394
|
-
let index = 0;
|
|
4395
|
-
const first = points[0];
|
|
4396
|
-
const last = points[points.length - 1];
|
|
4397
|
-
for (let i = 1; i < points.length - 1; i++) {
|
|
4398
|
-
const sqDist = this.getSqSegDist(points[i], first, last);
|
|
4399
|
-
if (sqDist > maxSqDist) {
|
|
4400
|
-
index = i;
|
|
4401
|
-
maxSqDist = sqDist;
|
|
3875
|
+
},
|
|
3876
|
+
{
|
|
3877
|
+
command: "exportCutImage",
|
|
3878
|
+
id: "exportCutImage",
|
|
3879
|
+
title: "Export Cut Image",
|
|
3880
|
+
handler: (options) => {
|
|
3881
|
+
return tool.exportCutImage(options);
|
|
4402
3882
|
}
|
|
4403
|
-
}
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
3883
|
+
},
|
|
3884
|
+
{
|
|
3885
|
+
command: "detectEdge",
|
|
3886
|
+
id: "detectEdge",
|
|
3887
|
+
title: "Detect Edge from Image",
|
|
3888
|
+
handler: async (imageUrl, options) => {
|
|
3889
|
+
var _a, _b, _c;
|
|
3890
|
+
try {
|
|
3891
|
+
const detectOptions = options || {};
|
|
3892
|
+
const debug = detectOptions.debug === true;
|
|
3893
|
+
const tracerOptions = {
|
|
3894
|
+
expand: (_a = detectOptions.expand) != null ? _a : 0,
|
|
3895
|
+
smoothing: (_b = detectOptions.smoothing) != null ? _b : true,
|
|
3896
|
+
simplifyTolerance: (_c = detectOptions.simplifyTolerance) != null ? _c : 2,
|
|
3897
|
+
threshold: detectOptions.threshold,
|
|
3898
|
+
debug
|
|
3899
|
+
};
|
|
3900
|
+
const loadImage = (url) => {
|
|
3901
|
+
return new Promise((resolve, reject) => {
|
|
3902
|
+
const img2 = new Image();
|
|
3903
|
+
img2.crossOrigin = "Anonymous";
|
|
3904
|
+
img2.onload = () => resolve(img2);
|
|
3905
|
+
img2.onerror = (e) => reject(e);
|
|
3906
|
+
img2.src = url;
|
|
3907
|
+
});
|
|
3908
|
+
};
|
|
3909
|
+
const [img, traced] = await Promise.all([
|
|
3910
|
+
loadImage(imageUrl),
|
|
3911
|
+
import("./tracer-PO7CRBYY.mjs").then(
|
|
3912
|
+
({ ImageTracer }) => ImageTracer.traceWithBounds(imageUrl, tracerOptions)
|
|
3913
|
+
)
|
|
3914
|
+
]);
|
|
3915
|
+
const { pathData, baseBounds, bounds } = traced;
|
|
3916
|
+
if (debug) {
|
|
3917
|
+
console.info("[DielineTool] detectEdge", {
|
|
3918
|
+
imageWidth: img.width,
|
|
3919
|
+
imageHeight: img.height,
|
|
3920
|
+
baseBounds,
|
|
3921
|
+
expandedBounds: bounds,
|
|
3922
|
+
currentDielineWidth: state.width,
|
|
3923
|
+
currentDielineHeight: state.height,
|
|
3924
|
+
options: tracerOptions,
|
|
3925
|
+
strategy: "single-connected-silhouette"
|
|
3926
|
+
});
|
|
3927
|
+
}
|
|
3928
|
+
return {
|
|
3929
|
+
pathData,
|
|
3930
|
+
rawBounds: bounds,
|
|
3931
|
+
baseBounds,
|
|
3932
|
+
imageWidth: img.width,
|
|
3933
|
+
imageHeight: img.height
|
|
3934
|
+
};
|
|
3935
|
+
} catch (e) {
|
|
3936
|
+
console.error("Edge detection failed", e);
|
|
3937
|
+
throw e;
|
|
3938
|
+
}
|
|
4425
3939
|
}
|
|
4426
3940
|
}
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
}
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
}
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
3941
|
+
];
|
|
3942
|
+
}
|
|
3943
|
+
|
|
3944
|
+
// src/extensions/dieline/config.ts
|
|
3945
|
+
function createDielineConfigurations(state) {
|
|
3946
|
+
return [
|
|
3947
|
+
{
|
|
3948
|
+
id: "dieline.shape",
|
|
3949
|
+
type: "select",
|
|
3950
|
+
label: "Shape",
|
|
3951
|
+
options: Array.from(DIELINE_SHAPES),
|
|
3952
|
+
default: state.shape
|
|
3953
|
+
},
|
|
3954
|
+
{
|
|
3955
|
+
id: "dieline.radius",
|
|
3956
|
+
type: "number",
|
|
3957
|
+
label: "Corner Radius (mm)",
|
|
3958
|
+
min: 0,
|
|
3959
|
+
max: 500,
|
|
3960
|
+
default: state.radius
|
|
3961
|
+
},
|
|
3962
|
+
{
|
|
3963
|
+
id: "dieline.shapeStyle",
|
|
3964
|
+
type: "json",
|
|
3965
|
+
label: "Shape Style",
|
|
3966
|
+
default: state.shapeStyle
|
|
3967
|
+
},
|
|
3968
|
+
{
|
|
3969
|
+
id: "dieline.showBleedLines",
|
|
3970
|
+
type: "boolean",
|
|
3971
|
+
label: "Show Bleed Lines",
|
|
3972
|
+
default: state.showBleedLines
|
|
3973
|
+
},
|
|
3974
|
+
{
|
|
3975
|
+
id: "dieline.strokeWidth",
|
|
3976
|
+
type: "number",
|
|
3977
|
+
label: "Line Width",
|
|
3978
|
+
min: 0.1,
|
|
3979
|
+
max: 10,
|
|
3980
|
+
step: 0.1,
|
|
3981
|
+
default: state.mainLine.width
|
|
3982
|
+
},
|
|
3983
|
+
{
|
|
3984
|
+
id: "dieline.strokeColor",
|
|
3985
|
+
type: "color",
|
|
3986
|
+
label: "Line Color",
|
|
3987
|
+
default: state.mainLine.color
|
|
3988
|
+
},
|
|
3989
|
+
{
|
|
3990
|
+
id: "dieline.dashLength",
|
|
3991
|
+
type: "number",
|
|
3992
|
+
label: "Dash Length",
|
|
3993
|
+
min: 1,
|
|
3994
|
+
max: 50,
|
|
3995
|
+
default: state.mainLine.dashLength
|
|
3996
|
+
},
|
|
3997
|
+
{
|
|
3998
|
+
id: "dieline.style",
|
|
3999
|
+
type: "select",
|
|
4000
|
+
label: "Line Style",
|
|
4001
|
+
options: ["solid", "dashed", "hidden"],
|
|
4002
|
+
default: state.mainLine.style
|
|
4003
|
+
},
|
|
4004
|
+
{
|
|
4005
|
+
id: "dieline.offsetStrokeWidth",
|
|
4006
|
+
type: "number",
|
|
4007
|
+
label: "Offset Line Width",
|
|
4008
|
+
min: 0.1,
|
|
4009
|
+
max: 10,
|
|
4010
|
+
step: 0.1,
|
|
4011
|
+
default: state.offsetLine.width
|
|
4012
|
+
},
|
|
4013
|
+
{
|
|
4014
|
+
id: "dieline.offsetStrokeColor",
|
|
4015
|
+
type: "color",
|
|
4016
|
+
label: "Offset Line Color",
|
|
4017
|
+
default: state.offsetLine.color
|
|
4018
|
+
},
|
|
4019
|
+
{
|
|
4020
|
+
id: "dieline.offsetDashLength",
|
|
4021
|
+
type: "number",
|
|
4022
|
+
label: "Offset Dash Length",
|
|
4023
|
+
min: 1,
|
|
4024
|
+
max: 50,
|
|
4025
|
+
default: state.offsetLine.dashLength
|
|
4026
|
+
},
|
|
4027
|
+
{
|
|
4028
|
+
id: "dieline.offsetStyle",
|
|
4029
|
+
type: "select",
|
|
4030
|
+
label: "Offset Line Style",
|
|
4031
|
+
options: ["solid", "dashed", "hidden"],
|
|
4032
|
+
default: state.offsetLine.style
|
|
4033
|
+
},
|
|
4034
|
+
{
|
|
4035
|
+
id: "dieline.insideColor",
|
|
4036
|
+
type: "color",
|
|
4037
|
+
label: "Inside Color",
|
|
4038
|
+
default: state.insideColor
|
|
4039
|
+
},
|
|
4040
|
+
{
|
|
4041
|
+
id: "dieline.features",
|
|
4042
|
+
type: "json",
|
|
4043
|
+
label: "Edge Features",
|
|
4044
|
+
default: state.features
|
|
4496
4045
|
}
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
return data;
|
|
4500
|
-
}
|
|
4501
|
-
};
|
|
4046
|
+
];
|
|
4047
|
+
}
|
|
4502
4048
|
|
|
4503
|
-
// src/extensions/dieline.ts
|
|
4504
|
-
var IMAGE_OBJECT_LAYER_ID2 = "image.user";
|
|
4505
|
-
var DIELINE_LAYER_ID = "dieline-overlay";
|
|
4049
|
+
// src/extensions/dieline/DielineTool.ts
|
|
4506
4050
|
var DielineTool = class {
|
|
4507
4051
|
constructor(options) {
|
|
4508
4052
|
this.id = "pooder.kit.dieline";
|
|
@@ -4743,7 +4287,6 @@ var DielineTool = class {
|
|
|
4743
4287
|
this.context = void 0;
|
|
4744
4288
|
}
|
|
4745
4289
|
contribute() {
|
|
4746
|
-
const s = this.state;
|
|
4747
4290
|
return {
|
|
4748
4291
|
[ContributionPointIds4.TOOLS]: [
|
|
4749
4292
|
{
|
|
@@ -4756,195 +4299,8 @@ var DielineTool = class {
|
|
|
4756
4299
|
}
|
|
4757
4300
|
}
|
|
4758
4301
|
],
|
|
4759
|
-
[ContributionPointIds4.CONFIGURATIONS]:
|
|
4760
|
-
|
|
4761
|
-
id: "dieline.shape",
|
|
4762
|
-
type: "select",
|
|
4763
|
-
label: "Shape",
|
|
4764
|
-
options: Array.from(DIELINE_SHAPES),
|
|
4765
|
-
default: s.shape
|
|
4766
|
-
},
|
|
4767
|
-
{
|
|
4768
|
-
id: "dieline.radius",
|
|
4769
|
-
type: "number",
|
|
4770
|
-
label: "Corner Radius (mm)",
|
|
4771
|
-
min: 0,
|
|
4772
|
-
max: 500,
|
|
4773
|
-
default: s.radius
|
|
4774
|
-
},
|
|
4775
|
-
{
|
|
4776
|
-
id: "dieline.shapeStyle",
|
|
4777
|
-
type: "json",
|
|
4778
|
-
label: "Shape Style",
|
|
4779
|
-
default: s.shapeStyle
|
|
4780
|
-
},
|
|
4781
|
-
{
|
|
4782
|
-
id: "dieline.showBleedLines",
|
|
4783
|
-
type: "boolean",
|
|
4784
|
-
label: "Show Bleed Lines",
|
|
4785
|
-
default: s.showBleedLines
|
|
4786
|
-
},
|
|
4787
|
-
{
|
|
4788
|
-
id: "dieline.strokeWidth",
|
|
4789
|
-
type: "number",
|
|
4790
|
-
label: "Line Width",
|
|
4791
|
-
min: 0.1,
|
|
4792
|
-
max: 10,
|
|
4793
|
-
step: 0.1,
|
|
4794
|
-
default: s.mainLine.width
|
|
4795
|
-
},
|
|
4796
|
-
{
|
|
4797
|
-
id: "dieline.strokeColor",
|
|
4798
|
-
type: "color",
|
|
4799
|
-
label: "Line Color",
|
|
4800
|
-
default: s.mainLine.color
|
|
4801
|
-
},
|
|
4802
|
-
{
|
|
4803
|
-
id: "dieline.dashLength",
|
|
4804
|
-
type: "number",
|
|
4805
|
-
label: "Dash Length",
|
|
4806
|
-
min: 1,
|
|
4807
|
-
max: 50,
|
|
4808
|
-
default: s.mainLine.dashLength
|
|
4809
|
-
},
|
|
4810
|
-
{
|
|
4811
|
-
id: "dieline.style",
|
|
4812
|
-
type: "select",
|
|
4813
|
-
label: "Line Style",
|
|
4814
|
-
options: ["solid", "dashed", "hidden"],
|
|
4815
|
-
default: s.mainLine.style
|
|
4816
|
-
},
|
|
4817
|
-
{
|
|
4818
|
-
id: "dieline.offsetStrokeWidth",
|
|
4819
|
-
type: "number",
|
|
4820
|
-
label: "Offset Line Width",
|
|
4821
|
-
min: 0.1,
|
|
4822
|
-
max: 10,
|
|
4823
|
-
step: 0.1,
|
|
4824
|
-
default: s.offsetLine.width
|
|
4825
|
-
},
|
|
4826
|
-
{
|
|
4827
|
-
id: "dieline.offsetStrokeColor",
|
|
4828
|
-
type: "color",
|
|
4829
|
-
label: "Offset Line Color",
|
|
4830
|
-
default: s.offsetLine.color
|
|
4831
|
-
},
|
|
4832
|
-
{
|
|
4833
|
-
id: "dieline.offsetDashLength",
|
|
4834
|
-
type: "number",
|
|
4835
|
-
label: "Offset Dash Length",
|
|
4836
|
-
min: 1,
|
|
4837
|
-
max: 50,
|
|
4838
|
-
default: s.offsetLine.dashLength
|
|
4839
|
-
},
|
|
4840
|
-
{
|
|
4841
|
-
id: "dieline.offsetStyle",
|
|
4842
|
-
type: "select",
|
|
4843
|
-
label: "Offset Line Style",
|
|
4844
|
-
options: ["solid", "dashed", "hidden"],
|
|
4845
|
-
default: s.offsetLine.style
|
|
4846
|
-
},
|
|
4847
|
-
{
|
|
4848
|
-
id: "dieline.insideColor",
|
|
4849
|
-
type: "color",
|
|
4850
|
-
label: "Inside Color",
|
|
4851
|
-
default: s.insideColor
|
|
4852
|
-
},
|
|
4853
|
-
{
|
|
4854
|
-
id: "dieline.features",
|
|
4855
|
-
type: "json",
|
|
4856
|
-
label: "Edge Features",
|
|
4857
|
-
default: s.features
|
|
4858
|
-
}
|
|
4859
|
-
],
|
|
4860
|
-
[ContributionPointIds4.COMMANDS]: [
|
|
4861
|
-
{
|
|
4862
|
-
command: "updateFeaturePosition",
|
|
4863
|
-
title: "Update Feature Position",
|
|
4864
|
-
handler: (groupId, x, y) => {
|
|
4865
|
-
var _a;
|
|
4866
|
-
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
4867
|
-
"ConfigurationService"
|
|
4868
|
-
);
|
|
4869
|
-
if (!configService) return;
|
|
4870
|
-
const features = configService.get("dieline.features") || [];
|
|
4871
|
-
let changed = false;
|
|
4872
|
-
const newFeatures = features.map((f) => {
|
|
4873
|
-
if (f.groupId === groupId) {
|
|
4874
|
-
if (f.x !== x || f.y !== y) {
|
|
4875
|
-
changed = true;
|
|
4876
|
-
return { ...f, x, y };
|
|
4877
|
-
}
|
|
4878
|
-
}
|
|
4879
|
-
return f;
|
|
4880
|
-
});
|
|
4881
|
-
if (changed) {
|
|
4882
|
-
configService.update("dieline.features", newFeatures);
|
|
4883
|
-
}
|
|
4884
|
-
}
|
|
4885
|
-
},
|
|
4886
|
-
{
|
|
4887
|
-
command: "exportCutImage",
|
|
4888
|
-
title: "Export Cut Image",
|
|
4889
|
-
handler: (options) => {
|
|
4890
|
-
return this.exportCutImage(options);
|
|
4891
|
-
}
|
|
4892
|
-
},
|
|
4893
|
-
{
|
|
4894
|
-
command: "detectEdge",
|
|
4895
|
-
title: "Detect Edge from Image",
|
|
4896
|
-
handler: async (imageUrl, options) => {
|
|
4897
|
-
var _a, _b, _c;
|
|
4898
|
-
try {
|
|
4899
|
-
const detectOptions = options || {};
|
|
4900
|
-
const debug = detectOptions.debug === true;
|
|
4901
|
-
const tracerOptions = {
|
|
4902
|
-
expand: (_a = detectOptions.expand) != null ? _a : 0,
|
|
4903
|
-
smoothing: (_b = detectOptions.smoothing) != null ? _b : true,
|
|
4904
|
-
simplifyTolerance: (_c = detectOptions.simplifyTolerance) != null ? _c : 2,
|
|
4905
|
-
threshold: detectOptions.threshold,
|
|
4906
|
-
debug
|
|
4907
|
-
};
|
|
4908
|
-
const loadImage = (url) => {
|
|
4909
|
-
return new Promise((resolve, reject) => {
|
|
4910
|
-
const img2 = new Image();
|
|
4911
|
-
img2.crossOrigin = "Anonymous";
|
|
4912
|
-
img2.onload = () => resolve(img2);
|
|
4913
|
-
img2.onerror = (e) => reject(e);
|
|
4914
|
-
img2.src = url;
|
|
4915
|
-
});
|
|
4916
|
-
};
|
|
4917
|
-
const [img, traced] = await Promise.all([
|
|
4918
|
-
loadImage(imageUrl),
|
|
4919
|
-
ImageTracer.traceWithBounds(imageUrl, tracerOptions)
|
|
4920
|
-
]);
|
|
4921
|
-
const { pathData, baseBounds, bounds } = traced;
|
|
4922
|
-
if (debug) {
|
|
4923
|
-
console.info("[DielineTool] detectEdge", {
|
|
4924
|
-
imageWidth: img.width,
|
|
4925
|
-
imageHeight: img.height,
|
|
4926
|
-
baseBounds,
|
|
4927
|
-
expandedBounds: bounds,
|
|
4928
|
-
currentDielineWidth: s.width,
|
|
4929
|
-
currentDielineHeight: s.height,
|
|
4930
|
-
options: tracerOptions,
|
|
4931
|
-
strategy: "single-connected-silhouette"
|
|
4932
|
-
});
|
|
4933
|
-
}
|
|
4934
|
-
return {
|
|
4935
|
-
pathData,
|
|
4936
|
-
rawBounds: bounds,
|
|
4937
|
-
baseBounds,
|
|
4938
|
-
imageWidth: img.width,
|
|
4939
|
-
imageHeight: img.height
|
|
4940
|
-
};
|
|
4941
|
-
} catch (e) {
|
|
4942
|
-
console.error("Edge detection failed", e);
|
|
4943
|
-
throw e;
|
|
4944
|
-
}
|
|
4945
|
-
}
|
|
4946
|
-
}
|
|
4947
|
-
]
|
|
4302
|
+
[ContributionPointIds4.CONFIGURATIONS]: createDielineConfigurations(this.state),
|
|
4303
|
+
[ContributionPointIds4.COMMANDS]: createDielineCommands(this, this.state)
|
|
4948
4304
|
};
|
|
4949
4305
|
}
|
|
4950
4306
|
createHatchPattern(color = "rgba(0, 0, 0, 0.3)") {
|
|
@@ -5218,7 +4574,11 @@ var DielineTool = class {
|
|
|
5218
4574
|
{
|
|
5219
4575
|
type: "clipPath",
|
|
5220
4576
|
id: "dieline.clip.image",
|
|
5221
|
-
|
|
4577
|
+
visibility: {
|
|
4578
|
+
op: "not",
|
|
4579
|
+
expr: { op: "anySessionActive" }
|
|
4580
|
+
},
|
|
4581
|
+
targetPassIds: [IMAGE_OBJECT_LAYER_ID],
|
|
5222
4582
|
source: {
|
|
5223
4583
|
id: "dieline.effect.clip-path",
|
|
5224
4584
|
type: "path",
|
|
@@ -5383,7 +4743,7 @@ var DielineTool = class {
|
|
|
5383
4743
|
const exportBounds = pathBounds;
|
|
5384
4744
|
const sourceImages = this.canvasService.canvas.getObjects().filter((obj) => {
|
|
5385
4745
|
var _a2;
|
|
5386
|
-
return ((_a2 = obj == null ? void 0 : obj.data) == null ? void 0 : _a2.layerId) ===
|
|
4746
|
+
return ((_a2 = obj == null ? void 0 : obj.data) == null ? void 0 : _a2.layerId) === IMAGE_OBJECT_LAYER_ID;
|
|
5387
4747
|
});
|
|
5388
4748
|
if (!sourceImages.length) {
|
|
5389
4749
|
console.warn(
|
|
@@ -5455,7 +4815,7 @@ var DielineTool = class {
|
|
|
5455
4815
|
}
|
|
5456
4816
|
};
|
|
5457
4817
|
|
|
5458
|
-
// src/extensions/feature.ts
|
|
4818
|
+
// src/extensions/feature/FeatureTool.ts
|
|
5459
4819
|
import {
|
|
5460
4820
|
ContributionPointIds as ContributionPointIds5
|
|
5461
4821
|
} from "@pooder/core";
|
|
@@ -5657,8 +5017,7 @@ function completeFeaturesStrict(features, context, update) {
|
|
|
5657
5017
|
return { ok: true };
|
|
5658
5018
|
}
|
|
5659
5019
|
|
|
5660
|
-
// src/extensions/feature.ts
|
|
5661
|
-
var FEATURE_OVERLAY_LAYER_ID = "feature-overlay";
|
|
5020
|
+
// src/extensions/feature/FeatureTool.ts
|
|
5662
5021
|
var FEATURE_STROKE_WIDTH = 2;
|
|
5663
5022
|
var DEFAULT_RECT_SIZE = 10;
|
|
5664
5023
|
var DEFAULT_CIRCLE_RADIUS = 5;
|
|
@@ -5676,6 +5035,7 @@ var FeatureTool = class {
|
|
|
5676
5035
|
this.hasWorkingChanges = false;
|
|
5677
5036
|
this.specs = [];
|
|
5678
5037
|
this.renderSeq = 0;
|
|
5038
|
+
this.subscriptions = new SubscriptionBag();
|
|
5679
5039
|
this.handleMoving = null;
|
|
5680
5040
|
this.handleModified = null;
|
|
5681
5041
|
this.handleSceneGeometryChange = null;
|
|
@@ -5693,6 +5053,7 @@ var FeatureTool = class {
|
|
|
5693
5053
|
}
|
|
5694
5054
|
activate(context) {
|
|
5695
5055
|
var _a;
|
|
5056
|
+
this.subscriptions.disposeAll();
|
|
5696
5057
|
this.context = context;
|
|
5697
5058
|
this.canvasService = context.services.get("CanvasService");
|
|
5698
5059
|
if (!this.canvasService) {
|
|
@@ -5721,29 +5082,32 @@ var FeatureTool = class {
|
|
|
5721
5082
|
const features = configService.get("dieline.features", []) || [];
|
|
5722
5083
|
this.workingFeatures = this.cloneFeatures(features);
|
|
5723
5084
|
this.hasWorkingChanges = false;
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
if (this.
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5085
|
+
this.subscriptions.onConfigChange(
|
|
5086
|
+
configService,
|
|
5087
|
+
(e) => {
|
|
5088
|
+
if (this.isUpdatingConfig) return;
|
|
5089
|
+
if (e.key === "dieline.features") {
|
|
5090
|
+
if (this.isFeatureSessionActive) return;
|
|
5091
|
+
const next = e.value || [];
|
|
5092
|
+
this.workingFeatures = this.cloneFeatures(next);
|
|
5093
|
+
this.hasWorkingChanges = false;
|
|
5094
|
+
this.redraw();
|
|
5095
|
+
this.emitWorkingChange();
|
|
5096
|
+
}
|
|
5733
5097
|
}
|
|
5734
|
-
|
|
5098
|
+
);
|
|
5735
5099
|
}
|
|
5736
5100
|
const toolSessionService = context.services.get("ToolSessionService");
|
|
5737
5101
|
this.dirtyTrackerDisposable = toolSessionService == null ? void 0 : toolSessionService.registerDirtyTracker(
|
|
5738
5102
|
this.id,
|
|
5739
5103
|
() => this.hasWorkingChanges
|
|
5740
5104
|
);
|
|
5741
|
-
|
|
5105
|
+
this.subscriptions.on(context.eventBus, "tool:activated", this.onToolActivated);
|
|
5742
5106
|
this.setup();
|
|
5743
5107
|
}
|
|
5744
5108
|
deactivate(context) {
|
|
5745
5109
|
var _a;
|
|
5746
|
-
|
|
5110
|
+
this.subscriptions.disposeAll();
|
|
5747
5111
|
this.restoreSessionFeaturesToConfig();
|
|
5748
5112
|
(_a = this.dirtyTrackerDisposable) == null ? void 0 : _a.dispose();
|
|
5749
5113
|
this.dirtyTrackerDisposable = void 0;
|
|
@@ -5886,7 +5250,7 @@ var FeatureTool = class {
|
|
|
5886
5250
|
};
|
|
5887
5251
|
}
|
|
5888
5252
|
cloneFeatures(features) {
|
|
5889
|
-
return
|
|
5253
|
+
return cloneWithJson(features || []);
|
|
5890
5254
|
}
|
|
5891
5255
|
getConfigService() {
|
|
5892
5256
|
var _a;
|
|
@@ -6570,12 +5934,11 @@ var FeatureTool = class {
|
|
|
6570
5934
|
}
|
|
6571
5935
|
};
|
|
6572
5936
|
|
|
6573
|
-
// src/extensions/film.ts
|
|
5937
|
+
// src/extensions/film/FilmTool.ts
|
|
6574
5938
|
import {
|
|
6575
5939
|
ContributionPointIds as ContributionPointIds6
|
|
6576
5940
|
} from "@pooder/core";
|
|
6577
5941
|
import { FabricImage as FabricImage3 } from "fabric";
|
|
6578
|
-
var FILM_LAYER_ID = "overlay";
|
|
6579
5942
|
var FILM_IMAGE_ID = "film-image";
|
|
6580
5943
|
var DEFAULT_WIDTH2 = 800;
|
|
6581
5944
|
var DEFAULT_HEIGHT2 = 600;
|
|
@@ -6590,8 +5953,10 @@ var FilmTool = class {
|
|
|
6590
5953
|
this.specs = [];
|
|
6591
5954
|
this.renderSeq = 0;
|
|
6592
5955
|
this.renderImageUrl = "";
|
|
6593
|
-
this.
|
|
6594
|
-
|
|
5956
|
+
this.sourceSizeCache = createSourceSizeCache(
|
|
5957
|
+
(src) => this.loadImageSize(src)
|
|
5958
|
+
);
|
|
5959
|
+
this.subscriptions = new SubscriptionBag();
|
|
6595
5960
|
this.onCanvasResized = () => {
|
|
6596
5961
|
this.updateFilm();
|
|
6597
5962
|
};
|
|
@@ -6601,6 +5966,7 @@ var FilmTool = class {
|
|
|
6601
5966
|
}
|
|
6602
5967
|
activate(context) {
|
|
6603
5968
|
var _a;
|
|
5969
|
+
this.subscriptions.disposeAll();
|
|
6604
5970
|
this.canvasService = context.services.get("CanvasService");
|
|
6605
5971
|
if (!this.canvasService) {
|
|
6606
5972
|
console.warn("CanvasService not found for FilmTool");
|
|
@@ -6627,28 +5993,32 @@ var FilmTool = class {
|
|
|
6627
5993
|
if (configService) {
|
|
6628
5994
|
this.url = configService.get("film.url", this.url);
|
|
6629
5995
|
this.opacity = configService.get("film.opacity", this.opacity);
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
this
|
|
5996
|
+
this.subscriptions.onConfigChange(
|
|
5997
|
+
configService,
|
|
5998
|
+
(e) => {
|
|
5999
|
+
if (e.key.startsWith("film.")) {
|
|
6000
|
+
const prop = e.key.split(".")[1];
|
|
6001
|
+
console.log(
|
|
6002
|
+
`[FilmTool] Config change detected: ${e.key} -> ${e.value}`
|
|
6003
|
+
);
|
|
6004
|
+
if (prop && prop in this) {
|
|
6005
|
+
this[prop] = e.value;
|
|
6006
|
+
this.updateFilm();
|
|
6007
|
+
}
|
|
6639
6008
|
}
|
|
6640
6009
|
}
|
|
6641
|
-
|
|
6010
|
+
);
|
|
6642
6011
|
}
|
|
6643
|
-
|
|
6012
|
+
this.subscriptions.on(context.eventBus, "canvas:resized", this.onCanvasResized);
|
|
6644
6013
|
this.updateFilm();
|
|
6645
6014
|
}
|
|
6646
6015
|
deactivate(context) {
|
|
6647
6016
|
var _a;
|
|
6648
|
-
|
|
6017
|
+
this.subscriptions.disposeAll();
|
|
6649
6018
|
this.renderSeq += 1;
|
|
6650
6019
|
this.specs = [];
|
|
6651
6020
|
this.renderImageUrl = "";
|
|
6021
|
+
this.sourceSizeCache.clear();
|
|
6652
6022
|
(_a = this.renderProducerDisposable) == null ? void 0 : _a.dispose();
|
|
6653
6023
|
this.renderProducerDisposable = void 0;
|
|
6654
6024
|
if (!this.canvasService) return;
|
|
@@ -6707,10 +6077,13 @@ var FilmTool = class {
|
|
|
6707
6077
|
return [];
|
|
6708
6078
|
}
|
|
6709
6079
|
const { width, height } = this.getViewportSize();
|
|
6710
|
-
const sourceSize = this.
|
|
6080
|
+
const sourceSize = this.sourceSizeCache.getSourceSize(imageUrl);
|
|
6711
6081
|
const sourceWidth = Math.max(1, Number((sourceSize == null ? void 0 : sourceSize.width) || width));
|
|
6712
6082
|
const sourceHeight = Math.max(1, Number((sourceSize == null ? void 0 : sourceSize.height) || height));
|
|
6713
|
-
const coverScale =
|
|
6083
|
+
const coverScale = getCoverScale(
|
|
6084
|
+
{ width, height },
|
|
6085
|
+
{ width: sourceWidth, height: sourceHeight }
|
|
6086
|
+
);
|
|
6714
6087
|
return [
|
|
6715
6088
|
{
|
|
6716
6089
|
id: FILM_IMAGE_ID,
|
|
@@ -6737,24 +6110,6 @@ var FilmTool = class {
|
|
|
6737
6110
|
}
|
|
6738
6111
|
];
|
|
6739
6112
|
}
|
|
6740
|
-
async ensureImageSize(src) {
|
|
6741
|
-
if (!src) return null;
|
|
6742
|
-
const cached = this.sourceSizeBySrc.get(src);
|
|
6743
|
-
if (cached) return cached;
|
|
6744
|
-
const pending = this.pendingSizeBySrc.get(src);
|
|
6745
|
-
if (pending) {
|
|
6746
|
-
return pending;
|
|
6747
|
-
}
|
|
6748
|
-
const task = this.loadImageSize(src);
|
|
6749
|
-
this.pendingSizeBySrc.set(src, task);
|
|
6750
|
-
try {
|
|
6751
|
-
return await task;
|
|
6752
|
-
} finally {
|
|
6753
|
-
if (this.pendingSizeBySrc.get(src) === task) {
|
|
6754
|
-
this.pendingSizeBySrc.delete(src);
|
|
6755
|
-
}
|
|
6756
|
-
}
|
|
6757
|
-
}
|
|
6758
6113
|
async loadImageSize(src) {
|
|
6759
6114
|
try {
|
|
6760
6115
|
const image = await FabricImage3.fromURL(src, {
|
|
@@ -6763,9 +6118,7 @@ var FilmTool = class {
|
|
|
6763
6118
|
const width = Number((image == null ? void 0 : image.width) || 0);
|
|
6764
6119
|
const height = Number((image == null ? void 0 : image.height) || 0);
|
|
6765
6120
|
if (width > 0 && height > 0) {
|
|
6766
|
-
|
|
6767
|
-
this.sourceSizeBySrc.set(src, size);
|
|
6768
|
-
return size;
|
|
6121
|
+
return { width, height };
|
|
6769
6122
|
}
|
|
6770
6123
|
} catch (error) {
|
|
6771
6124
|
console.error("[FilmTool] Failed to load film image", src, error);
|
|
@@ -6782,7 +6135,7 @@ var FilmTool = class {
|
|
|
6782
6135
|
if (!nextUrl) {
|
|
6783
6136
|
this.renderImageUrl = "";
|
|
6784
6137
|
} else if (nextUrl !== this.renderImageUrl) {
|
|
6785
|
-
const loaded = await this.ensureImageSize(nextUrl);
|
|
6138
|
+
const loaded = await this.sourceSizeCache.ensureImageSize(nextUrl);
|
|
6786
6139
|
if (seq !== this.renderSeq) return;
|
|
6787
6140
|
if (loaded) {
|
|
6788
6141
|
this.renderImageUrl = nextUrl;
|
|
@@ -6795,7 +6148,7 @@ var FilmTool = class {
|
|
|
6795
6148
|
}
|
|
6796
6149
|
};
|
|
6797
6150
|
|
|
6798
|
-
// src/extensions/mirror.ts
|
|
6151
|
+
// src/extensions/mirror/MirrorTool.ts
|
|
6799
6152
|
import {
|
|
6800
6153
|
ContributionPointIds as ContributionPointIds7
|
|
6801
6154
|
} from "@pooder/core";
|
|
@@ -6887,11 +6240,10 @@ var MirrorTool = class {
|
|
|
6887
6240
|
}
|
|
6888
6241
|
};
|
|
6889
6242
|
|
|
6890
|
-
// src/extensions/ruler.ts
|
|
6243
|
+
// src/extensions/ruler/RulerTool.ts
|
|
6891
6244
|
import {
|
|
6892
6245
|
ContributionPointIds as ContributionPointIds8
|
|
6893
6246
|
} from "@pooder/core";
|
|
6894
|
-
var RULER_LAYER_ID = "ruler-overlay";
|
|
6895
6247
|
var EXTENSION_LINE_LENGTH = 5;
|
|
6896
6248
|
var MIN_ARROW_SIZE = 4;
|
|
6897
6249
|
var THICKNESS_TO_STROKE_WIDTH_RATIO = 20;
|
|
@@ -7453,17 +6805,197 @@ var RulerTool = class {
|
|
|
7453
6805
|
}
|
|
7454
6806
|
};
|
|
7455
6807
|
|
|
7456
|
-
// src/extensions/white-ink.ts
|
|
6808
|
+
// src/extensions/white-ink/WhiteInkTool.ts
|
|
7457
6809
|
import {
|
|
7458
6810
|
ContributionPointIds as ContributionPointIds9
|
|
7459
6811
|
} from "@pooder/core";
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
var WHITE_INK_OVERLAY_LAYER_ID = "white-ink.overlay";
|
|
7463
|
-
var IMAGE_OBJECT_LAYER_ID3 = "image.user";
|
|
7464
|
-
var WHITE_INK_DEBUG_KEY = "whiteInk.debug";
|
|
6812
|
+
|
|
6813
|
+
// src/extensions/white-ink/commands.ts
|
|
7465
6814
|
var WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY = "whiteInk.previewImageVisible";
|
|
7466
6815
|
var WHITE_INK_DEFAULT_OPACITY = 0.85;
|
|
6816
|
+
function createWhiteInkCommands(tool) {
|
|
6817
|
+
return [
|
|
6818
|
+
{
|
|
6819
|
+
command: "addWhiteInk",
|
|
6820
|
+
id: "addWhiteInk",
|
|
6821
|
+
title: "Add White Ink",
|
|
6822
|
+
handler: async (url, options) => {
|
|
6823
|
+
return await tool.addWhiteInkEntry(url, options);
|
|
6824
|
+
}
|
|
6825
|
+
},
|
|
6826
|
+
{
|
|
6827
|
+
command: "upsertWhiteInk",
|
|
6828
|
+
id: "upsertWhiteInk",
|
|
6829
|
+
title: "Upsert White Ink",
|
|
6830
|
+
handler: async (url, options = {}) => {
|
|
6831
|
+
return await tool.upsertWhiteInkEntry(url, options);
|
|
6832
|
+
}
|
|
6833
|
+
},
|
|
6834
|
+
{
|
|
6835
|
+
command: "getWhiteInks",
|
|
6836
|
+
id: "getWhiteInks",
|
|
6837
|
+
title: "Get White Inks",
|
|
6838
|
+
handler: () => tool.cloneItems(tool.items)
|
|
6839
|
+
},
|
|
6840
|
+
{
|
|
6841
|
+
command: "getWhiteInkSettings",
|
|
6842
|
+
id: "getWhiteInkSettings",
|
|
6843
|
+
title: "Get White Ink Settings",
|
|
6844
|
+
handler: () => {
|
|
6845
|
+
const first = tool.getEffectiveWhiteInkItem(tool.items);
|
|
6846
|
+
const primarySource = tool.getPrimaryImageSource();
|
|
6847
|
+
const sourceUrl = tool.resolveSourceUrl(first) || primarySource;
|
|
6848
|
+
return {
|
|
6849
|
+
id: (first == null ? void 0 : first.id) || null,
|
|
6850
|
+
url: sourceUrl,
|
|
6851
|
+
sourceUrl,
|
|
6852
|
+
opacity: WHITE_INK_DEFAULT_OPACITY,
|
|
6853
|
+
printWithWhiteInk: tool.printWithWhiteInk,
|
|
6854
|
+
previewImageVisible: tool.previewImageVisible
|
|
6855
|
+
};
|
|
6856
|
+
}
|
|
6857
|
+
},
|
|
6858
|
+
{
|
|
6859
|
+
command: "setWhiteInkPrintEnabled",
|
|
6860
|
+
id: "setWhiteInkPrintEnabled",
|
|
6861
|
+
title: "Set White Ink Preview Enabled",
|
|
6862
|
+
handler: (enabled) => {
|
|
6863
|
+
var _a;
|
|
6864
|
+
tool.printWithWhiteInk = !!enabled;
|
|
6865
|
+
const configService = (_a = tool.context) == null ? void 0 : _a.services.get("ConfigurationService");
|
|
6866
|
+
configService == null ? void 0 : configService.update("whiteInk.printWithWhiteInk", tool.printWithWhiteInk);
|
|
6867
|
+
tool.updateWhiteInks();
|
|
6868
|
+
return { ok: true };
|
|
6869
|
+
}
|
|
6870
|
+
},
|
|
6871
|
+
{
|
|
6872
|
+
command: "setWhiteInkPreviewImageVisible",
|
|
6873
|
+
id: "setWhiteInkPreviewImageVisible",
|
|
6874
|
+
title: "Set White Ink Cover Visible",
|
|
6875
|
+
handler: (visible) => {
|
|
6876
|
+
var _a;
|
|
6877
|
+
tool.previewImageVisible = !!visible;
|
|
6878
|
+
const configService = (_a = tool.context) == null ? void 0 : _a.services.get("ConfigurationService");
|
|
6879
|
+
configService == null ? void 0 : configService.update(
|
|
6880
|
+
WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY,
|
|
6881
|
+
tool.previewImageVisible
|
|
6882
|
+
);
|
|
6883
|
+
tool.updateWhiteInks();
|
|
6884
|
+
return { ok: true };
|
|
6885
|
+
}
|
|
6886
|
+
},
|
|
6887
|
+
{
|
|
6888
|
+
command: "getWorkingWhiteInks",
|
|
6889
|
+
id: "getWorkingWhiteInks",
|
|
6890
|
+
title: "Get Working White Inks",
|
|
6891
|
+
handler: () => tool.cloneItems(tool.workingItems)
|
|
6892
|
+
},
|
|
6893
|
+
{
|
|
6894
|
+
command: "setWorkingWhiteInk",
|
|
6895
|
+
id: "setWorkingWhiteInk",
|
|
6896
|
+
title: "Set Working White Ink",
|
|
6897
|
+
handler: (id, updates) => {
|
|
6898
|
+
tool.updateWhiteInkInWorking(id, updates);
|
|
6899
|
+
}
|
|
6900
|
+
},
|
|
6901
|
+
{
|
|
6902
|
+
command: "updateWhiteInk",
|
|
6903
|
+
id: "updateWhiteInk",
|
|
6904
|
+
title: "Update White Ink",
|
|
6905
|
+
handler: async (id, updates, options = {}) => {
|
|
6906
|
+
await tool.updateWhiteInkItem(id, updates, options);
|
|
6907
|
+
}
|
|
6908
|
+
},
|
|
6909
|
+
{
|
|
6910
|
+
command: "removeWhiteInk",
|
|
6911
|
+
id: "removeWhiteInk",
|
|
6912
|
+
title: "Remove White Ink",
|
|
6913
|
+
handler: (id) => {
|
|
6914
|
+
tool.removeWhiteInk(id);
|
|
6915
|
+
}
|
|
6916
|
+
},
|
|
6917
|
+
{
|
|
6918
|
+
command: "clearWhiteInks",
|
|
6919
|
+
id: "clearWhiteInks",
|
|
6920
|
+
title: "Clear White Inks",
|
|
6921
|
+
handler: () => {
|
|
6922
|
+
tool.clearWhiteInks();
|
|
6923
|
+
}
|
|
6924
|
+
},
|
|
6925
|
+
{
|
|
6926
|
+
command: "resetWorkingWhiteInks",
|
|
6927
|
+
id: "resetWorkingWhiteInks",
|
|
6928
|
+
title: "Reset Working White Inks",
|
|
6929
|
+
handler: () => {
|
|
6930
|
+
tool.workingItems = tool.cloneItems(tool.items);
|
|
6931
|
+
tool.hasWorkingChanges = false;
|
|
6932
|
+
tool.updateWhiteInks();
|
|
6933
|
+
}
|
|
6934
|
+
},
|
|
6935
|
+
{
|
|
6936
|
+
command: "completeWhiteInks",
|
|
6937
|
+
id: "completeWhiteInks",
|
|
6938
|
+
title: "Complete White Inks",
|
|
6939
|
+
handler: async () => {
|
|
6940
|
+
return await tool.completeWhiteInks();
|
|
6941
|
+
}
|
|
6942
|
+
},
|
|
6943
|
+
{
|
|
6944
|
+
command: "setWhiteInkImage",
|
|
6945
|
+
id: "setWhiteInkImage",
|
|
6946
|
+
title: "Set White Ink Image",
|
|
6947
|
+
handler: async (url) => {
|
|
6948
|
+
if (!url) {
|
|
6949
|
+
tool.clearWhiteInks();
|
|
6950
|
+
return { ok: true };
|
|
6951
|
+
}
|
|
6952
|
+
const targetId = tool.resolveReplaceTargetId(null);
|
|
6953
|
+
const upsertResult = await tool.upsertWhiteInkEntry(url, {
|
|
6954
|
+
id: targetId || void 0,
|
|
6955
|
+
mode: targetId ? "replace" : "add",
|
|
6956
|
+
createIfMissing: true,
|
|
6957
|
+
addOptions: {}
|
|
6958
|
+
});
|
|
6959
|
+
return { ok: true, id: upsertResult.id };
|
|
6960
|
+
}
|
|
6961
|
+
}
|
|
6962
|
+
];
|
|
6963
|
+
}
|
|
6964
|
+
|
|
6965
|
+
// src/extensions/white-ink/config.ts
|
|
6966
|
+
function createWhiteInkConfigurations() {
|
|
6967
|
+
return [
|
|
6968
|
+
{
|
|
6969
|
+
id: "whiteInk.items",
|
|
6970
|
+
type: "array",
|
|
6971
|
+
label: "White Ink Images",
|
|
6972
|
+
default: []
|
|
6973
|
+
},
|
|
6974
|
+
{
|
|
6975
|
+
id: "whiteInk.printWithWhiteInk",
|
|
6976
|
+
type: "boolean",
|
|
6977
|
+
label: "Preview White Ink",
|
|
6978
|
+
default: true
|
|
6979
|
+
},
|
|
6980
|
+
{
|
|
6981
|
+
id: "whiteInk.previewImageVisible",
|
|
6982
|
+
type: "boolean",
|
|
6983
|
+
label: "Show Cover During White Ink Preview",
|
|
6984
|
+
default: true
|
|
6985
|
+
},
|
|
6986
|
+
{
|
|
6987
|
+
id: "whiteInk.debug",
|
|
6988
|
+
type: "boolean",
|
|
6989
|
+
label: "White Ink Debug Log",
|
|
6990
|
+
default: false
|
|
6991
|
+
}
|
|
6992
|
+
];
|
|
6993
|
+
}
|
|
6994
|
+
|
|
6995
|
+
// src/extensions/white-ink/WhiteInkTool.ts
|
|
6996
|
+
var WHITE_INK_DEBUG_KEY = "whiteInk.debug";
|
|
6997
|
+
var WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY2 = "whiteInk.previewImageVisible";
|
|
6998
|
+
var WHITE_INK_DEFAULT_OPACITY2 = 0.85;
|
|
7467
6999
|
var WHITE_INK_AUTO_ITEM_ID = "white-ink-auto";
|
|
7468
7000
|
var WHITE_INK_COVER_OPACITY_FACTOR = 0.45;
|
|
7469
7001
|
var WHITE_INK_COVER_OPACITY_MIN = 0.15;
|
|
@@ -7479,7 +7011,9 @@ var WhiteInkTool = class {
|
|
|
7479
7011
|
this.items = [];
|
|
7480
7012
|
this.workingItems = [];
|
|
7481
7013
|
this.hasWorkingChanges = false;
|
|
7482
|
-
this.
|
|
7014
|
+
this.sourceSizeCache = createSourceSizeCache(
|
|
7015
|
+
(src) => this.loadImageSize(src)
|
|
7016
|
+
);
|
|
7483
7017
|
this.previewMaskBySource = /* @__PURE__ */ new Map();
|
|
7484
7018
|
this.pendingPreviewMaskBySource = /* @__PURE__ */ new Map();
|
|
7485
7019
|
this.isUpdatingConfig = false;
|
|
@@ -7490,6 +7024,7 @@ var WhiteInkTool = class {
|
|
|
7490
7024
|
this.whiteSpecs = [];
|
|
7491
7025
|
this.coverSpecs = [];
|
|
7492
7026
|
this.overlaySpecs = [];
|
|
7027
|
+
this.subscriptions = new SubscriptionBag();
|
|
7493
7028
|
this.onToolActivated = (event) => {
|
|
7494
7029
|
const before = this.isToolActive;
|
|
7495
7030
|
this.syncToolActiveFromWorkbench(event.id);
|
|
@@ -7507,19 +7042,19 @@ var WhiteInkTool = class {
|
|
|
7507
7042
|
this.onObjectAdded = (e) => {
|
|
7508
7043
|
var _a, _b;
|
|
7509
7044
|
const layerId = (_b = (_a = e == null ? void 0 : e.target) == null ? void 0 : _a.data) == null ? void 0 : _b.layerId;
|
|
7510
|
-
if (layerId !==
|
|
7045
|
+
if (layerId !== IMAGE_OBJECT_LAYER_ID) return;
|
|
7511
7046
|
this.updateWhiteInks();
|
|
7512
7047
|
};
|
|
7513
7048
|
this.onObjectModified = (e) => {
|
|
7514
7049
|
var _a, _b;
|
|
7515
7050
|
const layerId = (_b = (_a = e == null ? void 0 : e.target) == null ? void 0 : _a.data) == null ? void 0 : _b.layerId;
|
|
7516
|
-
if (layerId !==
|
|
7051
|
+
if (layerId !== IMAGE_OBJECT_LAYER_ID) return;
|
|
7517
7052
|
this.updateWhiteInks();
|
|
7518
7053
|
};
|
|
7519
7054
|
this.onObjectRemoved = (e) => {
|
|
7520
7055
|
var _a, _b;
|
|
7521
7056
|
const layerId = (_b = (_a = e == null ? void 0 : e.target) == null ? void 0 : _a.data) == null ? void 0 : _b.layerId;
|
|
7522
|
-
if (layerId !==
|
|
7057
|
+
if (layerId !== IMAGE_OBJECT_LAYER_ID) return;
|
|
7523
7058
|
this.updateWhiteInks();
|
|
7524
7059
|
};
|
|
7525
7060
|
this.onImageWorkingChanged = () => {
|
|
@@ -7528,6 +7063,7 @@ var WhiteInkTool = class {
|
|
|
7528
7063
|
}
|
|
7529
7064
|
activate(context) {
|
|
7530
7065
|
var _a;
|
|
7066
|
+
this.subscriptions.disposeAll();
|
|
7531
7067
|
this.context = context;
|
|
7532
7068
|
this.canvasService = context.services.get("CanvasService");
|
|
7533
7069
|
if (!this.canvasService) {
|
|
@@ -7561,62 +7097,73 @@ var WhiteInkTool = class {
|
|
|
7561
7097
|
}),
|
|
7562
7098
|
{ priority: 260 }
|
|
7563
7099
|
);
|
|
7564
|
-
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7100
|
+
this.subscriptions.on(context.eventBus, "tool:activated", this.onToolActivated);
|
|
7101
|
+
this.subscriptions.on(
|
|
7102
|
+
context.eventBus,
|
|
7103
|
+
"scene:layout:change",
|
|
7104
|
+
this.onSceneLayoutChanged
|
|
7105
|
+
);
|
|
7106
|
+
this.subscriptions.on(context.eventBus, "object:added", this.onObjectAdded);
|
|
7107
|
+
this.subscriptions.on(
|
|
7108
|
+
context.eventBus,
|
|
7109
|
+
"object:modified",
|
|
7110
|
+
this.onObjectModified
|
|
7111
|
+
);
|
|
7112
|
+
this.subscriptions.on(
|
|
7113
|
+
context.eventBus,
|
|
7114
|
+
"object:removed",
|
|
7115
|
+
this.onObjectRemoved
|
|
7116
|
+
);
|
|
7117
|
+
this.subscriptions.on(
|
|
7118
|
+
context.eventBus,
|
|
7119
|
+
"image:working:change",
|
|
7120
|
+
this.onImageWorkingChanged
|
|
7121
|
+
);
|
|
7570
7122
|
const configService = context.services.get(
|
|
7571
7123
|
"ConfigurationService"
|
|
7572
7124
|
);
|
|
7573
7125
|
if (configService) {
|
|
7574
|
-
this.items
|
|
7575
|
-
configService.get("whiteInk.items", []) || []
|
|
7576
|
-
);
|
|
7577
|
-
this.workingItems = this.cloneItems(this.items);
|
|
7578
|
-
this.hasWorkingChanges = false;
|
|
7126
|
+
this.applyCommittedItems(configService.get("whiteInk.items", []) || []);
|
|
7579
7127
|
this.printWithWhiteInk = !!configService.get(
|
|
7580
7128
|
"whiteInk.printWithWhiteInk",
|
|
7581
7129
|
true
|
|
7582
7130
|
);
|
|
7583
7131
|
this.previewImageVisible = !!configService.get(
|
|
7584
|
-
|
|
7132
|
+
WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY2,
|
|
7585
7133
|
true
|
|
7586
7134
|
);
|
|
7587
7135
|
this.migrateLegacyConfigIfNeeded(configService);
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
if (
|
|
7593
|
-
this.
|
|
7594
|
-
this.
|
|
7136
|
+
this.subscriptions.onConfigChange(
|
|
7137
|
+
configService,
|
|
7138
|
+
(e) => {
|
|
7139
|
+
if (this.isUpdatingConfig) return;
|
|
7140
|
+
if (e.key === "whiteInk.items") {
|
|
7141
|
+
this.applyCommittedItems(e.value || []);
|
|
7142
|
+
this.updateWhiteInks();
|
|
7143
|
+
return;
|
|
7144
|
+
}
|
|
7145
|
+
if (e.key === "whiteInk.printWithWhiteInk") {
|
|
7146
|
+
this.printWithWhiteInk = !!e.value;
|
|
7147
|
+
this.updateWhiteInks();
|
|
7148
|
+
return;
|
|
7149
|
+
}
|
|
7150
|
+
if (e.key === WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY2) {
|
|
7151
|
+
this.previewImageVisible = !!e.value;
|
|
7152
|
+
this.updateWhiteInks();
|
|
7153
|
+
return;
|
|
7154
|
+
}
|
|
7155
|
+
if (e.key === "image.items") {
|
|
7156
|
+
this.updateWhiteInks();
|
|
7157
|
+
return;
|
|
7158
|
+
}
|
|
7159
|
+
if (e.key === WHITE_INK_DEBUG_KEY) {
|
|
7160
|
+
return;
|
|
7161
|
+
}
|
|
7162
|
+
if (e.key.startsWith("size.")) {
|
|
7163
|
+
this.updateWhiteInks();
|
|
7595
7164
|
}
|
|
7596
|
-
this.updateWhiteInks();
|
|
7597
|
-
return;
|
|
7598
|
-
}
|
|
7599
|
-
if (e.key === "whiteInk.printWithWhiteInk") {
|
|
7600
|
-
this.printWithWhiteInk = !!e.value;
|
|
7601
|
-
this.updateWhiteInks();
|
|
7602
|
-
return;
|
|
7603
|
-
}
|
|
7604
|
-
if (e.key === WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY) {
|
|
7605
|
-
this.previewImageVisible = !!e.value;
|
|
7606
|
-
this.updateWhiteInks();
|
|
7607
|
-
return;
|
|
7608
|
-
}
|
|
7609
|
-
if (e.key === "image.items") {
|
|
7610
|
-
this.updateWhiteInks();
|
|
7611
|
-
return;
|
|
7612
|
-
}
|
|
7613
|
-
if (e.key === WHITE_INK_DEBUG_KEY) {
|
|
7614
|
-
return;
|
|
7615
|
-
}
|
|
7616
|
-
if (e.key.startsWith("size.")) {
|
|
7617
|
-
this.updateWhiteInks();
|
|
7618
7165
|
}
|
|
7619
|
-
|
|
7166
|
+
);
|
|
7620
7167
|
}
|
|
7621
7168
|
const toolSessionService = context.services.get("ToolSessionService");
|
|
7622
7169
|
this.dirtyTrackerDisposable = toolSessionService == null ? void 0 : toolSessionService.registerDirtyTracker(
|
|
@@ -7627,14 +7174,10 @@ var WhiteInkTool = class {
|
|
|
7627
7174
|
}
|
|
7628
7175
|
deactivate(context) {
|
|
7629
7176
|
var _a, _b;
|
|
7630
|
-
|
|
7631
|
-
context.eventBus.off("scene:layout:change", this.onSceneLayoutChanged);
|
|
7632
|
-
context.eventBus.off("object:added", this.onObjectAdded);
|
|
7633
|
-
context.eventBus.off("object:modified", this.onObjectModified);
|
|
7634
|
-
context.eventBus.off("object:removed", this.onObjectRemoved);
|
|
7635
|
-
context.eventBus.off("image:working:change", this.onImageWorkingChanged);
|
|
7177
|
+
this.subscriptions.disposeAll();
|
|
7636
7178
|
(_a = this.dirtyTrackerDisposable) == null ? void 0 : _a.dispose();
|
|
7637
7179
|
this.dirtyTrackerDisposable = void 0;
|
|
7180
|
+
this.sourceSizeCache.clear();
|
|
7638
7181
|
this.clearRenderedWhiteInks();
|
|
7639
7182
|
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
7640
7183
|
this.renderProducerDisposable = void 0;
|
|
@@ -7662,171 +7205,8 @@ var WhiteInkTool = class {
|
|
|
7662
7205
|
}
|
|
7663
7206
|
}
|
|
7664
7207
|
],
|
|
7665
|
-
[ContributionPointIds9.CONFIGURATIONS]:
|
|
7666
|
-
|
|
7667
|
-
id: "whiteInk.items",
|
|
7668
|
-
type: "array",
|
|
7669
|
-
label: "White Ink Images",
|
|
7670
|
-
default: []
|
|
7671
|
-
},
|
|
7672
|
-
{
|
|
7673
|
-
id: "whiteInk.printWithWhiteInk",
|
|
7674
|
-
type: "boolean",
|
|
7675
|
-
label: "Preview White Ink",
|
|
7676
|
-
default: true
|
|
7677
|
-
},
|
|
7678
|
-
{
|
|
7679
|
-
id: WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY,
|
|
7680
|
-
type: "boolean",
|
|
7681
|
-
label: "Show Cover During White Ink Preview",
|
|
7682
|
-
default: true
|
|
7683
|
-
},
|
|
7684
|
-
{
|
|
7685
|
-
id: WHITE_INK_DEBUG_KEY,
|
|
7686
|
-
type: "boolean",
|
|
7687
|
-
label: "White Ink Debug Log",
|
|
7688
|
-
default: false
|
|
7689
|
-
}
|
|
7690
|
-
],
|
|
7691
|
-
[ContributionPointIds9.COMMANDS]: [
|
|
7692
|
-
{
|
|
7693
|
-
command: "addWhiteInk",
|
|
7694
|
-
title: "Add White Ink",
|
|
7695
|
-
handler: async (url, options) => {
|
|
7696
|
-
return await this.addWhiteInkEntry(url, options);
|
|
7697
|
-
}
|
|
7698
|
-
},
|
|
7699
|
-
{
|
|
7700
|
-
command: "upsertWhiteInk",
|
|
7701
|
-
title: "Upsert White Ink",
|
|
7702
|
-
handler: async (url, options = {}) => {
|
|
7703
|
-
return await this.upsertWhiteInkEntry(url, options);
|
|
7704
|
-
}
|
|
7705
|
-
},
|
|
7706
|
-
{
|
|
7707
|
-
command: "getWhiteInks",
|
|
7708
|
-
title: "Get White Inks",
|
|
7709
|
-
handler: () => this.cloneItems(this.items)
|
|
7710
|
-
},
|
|
7711
|
-
{
|
|
7712
|
-
command: "getWhiteInkSettings",
|
|
7713
|
-
title: "Get White Ink Settings",
|
|
7714
|
-
handler: () => {
|
|
7715
|
-
const first = this.getEffectiveWhiteInkItem(this.items);
|
|
7716
|
-
const primarySource = this.getPrimaryImageSource();
|
|
7717
|
-
const sourceUrl = this.resolveSourceUrl(first) || primarySource;
|
|
7718
|
-
return {
|
|
7719
|
-
id: (first == null ? void 0 : first.id) || null,
|
|
7720
|
-
url: sourceUrl,
|
|
7721
|
-
sourceUrl,
|
|
7722
|
-
opacity: WHITE_INK_DEFAULT_OPACITY,
|
|
7723
|
-
printWithWhiteInk: this.printWithWhiteInk,
|
|
7724
|
-
previewImageVisible: this.previewImageVisible
|
|
7725
|
-
};
|
|
7726
|
-
}
|
|
7727
|
-
},
|
|
7728
|
-
{
|
|
7729
|
-
command: "setWhiteInkPrintEnabled",
|
|
7730
|
-
title: "Set White Ink Preview Enabled",
|
|
7731
|
-
handler: (enabled) => {
|
|
7732
|
-
var _a;
|
|
7733
|
-
this.printWithWhiteInk = !!enabled;
|
|
7734
|
-
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
7735
|
-
"ConfigurationService"
|
|
7736
|
-
);
|
|
7737
|
-
configService == null ? void 0 : configService.update(
|
|
7738
|
-
"whiteInk.printWithWhiteInk",
|
|
7739
|
-
this.printWithWhiteInk
|
|
7740
|
-
);
|
|
7741
|
-
this.updateWhiteInks();
|
|
7742
|
-
return { ok: true };
|
|
7743
|
-
}
|
|
7744
|
-
},
|
|
7745
|
-
{
|
|
7746
|
-
command: "setWhiteInkPreviewImageVisible",
|
|
7747
|
-
title: "Set White Ink Cover Visible",
|
|
7748
|
-
handler: (visible) => {
|
|
7749
|
-
var _a;
|
|
7750
|
-
this.previewImageVisible = !!visible;
|
|
7751
|
-
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
7752
|
-
"ConfigurationService"
|
|
7753
|
-
);
|
|
7754
|
-
configService == null ? void 0 : configService.update(
|
|
7755
|
-
WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY,
|
|
7756
|
-
this.previewImageVisible
|
|
7757
|
-
);
|
|
7758
|
-
this.updateWhiteInks();
|
|
7759
|
-
return { ok: true };
|
|
7760
|
-
}
|
|
7761
|
-
},
|
|
7762
|
-
{
|
|
7763
|
-
command: "getWorkingWhiteInks",
|
|
7764
|
-
title: "Get Working White Inks",
|
|
7765
|
-
handler: () => this.cloneItems(this.workingItems)
|
|
7766
|
-
},
|
|
7767
|
-
{
|
|
7768
|
-
command: "setWorkingWhiteInk",
|
|
7769
|
-
title: "Set Working White Ink",
|
|
7770
|
-
handler: (id, updates) => {
|
|
7771
|
-
this.updateWhiteInkInWorking(id, updates);
|
|
7772
|
-
}
|
|
7773
|
-
},
|
|
7774
|
-
{
|
|
7775
|
-
command: "updateWhiteInk",
|
|
7776
|
-
title: "Update White Ink",
|
|
7777
|
-
handler: async (id, updates, options = {}) => {
|
|
7778
|
-
await this.updateWhiteInkItem(id, updates, options);
|
|
7779
|
-
}
|
|
7780
|
-
},
|
|
7781
|
-
{
|
|
7782
|
-
command: "removeWhiteInk",
|
|
7783
|
-
title: "Remove White Ink",
|
|
7784
|
-
handler: (id) => {
|
|
7785
|
-
this.removeWhiteInk(id);
|
|
7786
|
-
}
|
|
7787
|
-
},
|
|
7788
|
-
{
|
|
7789
|
-
command: "clearWhiteInks",
|
|
7790
|
-
title: "Clear White Inks",
|
|
7791
|
-
handler: () => {
|
|
7792
|
-
this.clearWhiteInks();
|
|
7793
|
-
}
|
|
7794
|
-
},
|
|
7795
|
-
{
|
|
7796
|
-
command: "resetWorkingWhiteInks",
|
|
7797
|
-
title: "Reset Working White Inks",
|
|
7798
|
-
handler: () => {
|
|
7799
|
-
this.workingItems = this.cloneItems(this.items);
|
|
7800
|
-
this.hasWorkingChanges = false;
|
|
7801
|
-
this.updateWhiteInks();
|
|
7802
|
-
}
|
|
7803
|
-
},
|
|
7804
|
-
{
|
|
7805
|
-
command: "completeWhiteInks",
|
|
7806
|
-
title: "Complete White Inks",
|
|
7807
|
-
handler: async () => {
|
|
7808
|
-
return await this.completeWhiteInks();
|
|
7809
|
-
}
|
|
7810
|
-
},
|
|
7811
|
-
{
|
|
7812
|
-
command: "setWhiteInkImage",
|
|
7813
|
-
title: "Set White Ink Image",
|
|
7814
|
-
handler: async (url) => {
|
|
7815
|
-
if (!url) {
|
|
7816
|
-
this.clearWhiteInks();
|
|
7817
|
-
return { ok: true };
|
|
7818
|
-
}
|
|
7819
|
-
const targetId = this.resolveReplaceTargetId(null);
|
|
7820
|
-
const upsertResult = await this.upsertWhiteInkEntry(url, {
|
|
7821
|
-
id: targetId || void 0,
|
|
7822
|
-
mode: targetId ? "replace" : "add",
|
|
7823
|
-
createIfMissing: true,
|
|
7824
|
-
addOptions: {}
|
|
7825
|
-
});
|
|
7826
|
-
return { ok: true, id: upsertResult.id };
|
|
7827
|
-
}
|
|
7828
|
-
}
|
|
7829
|
-
]
|
|
7208
|
+
[ContributionPointIds9.CONFIGURATIONS]: createWhiteInkConfigurations(),
|
|
7209
|
+
[ContributionPointIds9.COMMANDS]: createWhiteInkCommands(this)
|
|
7830
7210
|
};
|
|
7831
7211
|
}
|
|
7832
7212
|
migrateLegacyConfigIfNeeded(configService) {
|
|
@@ -7836,15 +7216,12 @@ var WhiteInkTool = class {
|
|
|
7836
7216
|
const item = this.normalizeItem({
|
|
7837
7217
|
id: this.generateId(),
|
|
7838
7218
|
sourceUrl: legacyMask,
|
|
7839
|
-
opacity:
|
|
7219
|
+
opacity: WHITE_INK_DEFAULT_OPACITY2
|
|
7220
|
+
});
|
|
7221
|
+
this.applyCommittedItems([item]);
|
|
7222
|
+
runDeferredConfigUpdate(this, () => {
|
|
7223
|
+
configService.update("whiteInk.items", this.items);
|
|
7840
7224
|
});
|
|
7841
|
-
this.items = [item];
|
|
7842
|
-
this.workingItems = this.cloneItems(this.items);
|
|
7843
|
-
this.isUpdatingConfig = true;
|
|
7844
|
-
configService.update("whiteInk.items", this.items);
|
|
7845
|
-
setTimeout(() => {
|
|
7846
|
-
this.isUpdatingConfig = false;
|
|
7847
|
-
}, 0);
|
|
7848
7225
|
}
|
|
7849
7226
|
syncToolActiveFromWorkbench(fallbackId) {
|
|
7850
7227
|
var _a;
|
|
@@ -7886,7 +7263,7 @@ var WhiteInkTool = class {
|
|
|
7886
7263
|
id: String(item.id || this.generateId()),
|
|
7887
7264
|
sourceUrl,
|
|
7888
7265
|
url: sourceUrl,
|
|
7889
|
-
opacity:
|
|
7266
|
+
opacity: WHITE_INK_DEFAULT_OPACITY2
|
|
7890
7267
|
};
|
|
7891
7268
|
}
|
|
7892
7269
|
normalizeItems(items) {
|
|
@@ -7905,7 +7282,7 @@ var WhiteInkTool = class {
|
|
|
7905
7282
|
}
|
|
7906
7283
|
return {
|
|
7907
7284
|
id: WHITE_INK_AUTO_ITEM_ID,
|
|
7908
|
-
opacity:
|
|
7285
|
+
opacity: WHITE_INK_DEFAULT_OPACITY2
|
|
7909
7286
|
};
|
|
7910
7287
|
}
|
|
7911
7288
|
generateId() {
|
|
@@ -7928,31 +7305,45 @@ var WhiteInkTool = class {
|
|
|
7928
7305
|
}
|
|
7929
7306
|
return null;
|
|
7930
7307
|
}
|
|
7308
|
+
applyCommittedItems(nextItems) {
|
|
7309
|
+
const session = {
|
|
7310
|
+
committed: this.items,
|
|
7311
|
+
working: this.workingItems,
|
|
7312
|
+
hasWorkingChanges: this.hasWorkingChanges
|
|
7313
|
+
};
|
|
7314
|
+
applyCommittedSnapshot(session, this.normalizeItems(nextItems), {
|
|
7315
|
+
clone: (items) => this.cloneItems(items),
|
|
7316
|
+
toolActive: this.isToolActive,
|
|
7317
|
+
preserveDirtyWorking: true
|
|
7318
|
+
});
|
|
7319
|
+
this.items = session.committed;
|
|
7320
|
+
this.workingItems = session.working;
|
|
7321
|
+
this.hasWorkingChanges = session.hasWorkingChanges;
|
|
7322
|
+
}
|
|
7931
7323
|
updateConfig(newItems, skipCanvasUpdate = false) {
|
|
7932
7324
|
if (!this.context) return;
|
|
7933
|
-
this.
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7325
|
+
this.applyCommittedItems(newItems);
|
|
7326
|
+
runDeferredConfigUpdate(
|
|
7327
|
+
this,
|
|
7328
|
+
() => {
|
|
7329
|
+
var _a;
|
|
7330
|
+
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
7331
|
+
"ConfigurationService"
|
|
7332
|
+
);
|
|
7333
|
+
configService == null ? void 0 : configService.update("whiteInk.items", this.items);
|
|
7334
|
+
if (!skipCanvasUpdate) {
|
|
7335
|
+
this.updateWhiteInks();
|
|
7336
|
+
}
|
|
7337
|
+
},
|
|
7338
|
+
50
|
|
7941
7339
|
);
|
|
7942
|
-
configService == null ? void 0 : configService.update("whiteInk.items", this.items);
|
|
7943
|
-
if (!skipCanvasUpdate) {
|
|
7944
|
-
this.updateWhiteInks();
|
|
7945
|
-
}
|
|
7946
|
-
setTimeout(() => {
|
|
7947
|
-
this.isUpdatingConfig = false;
|
|
7948
|
-
}, 50);
|
|
7949
7340
|
}
|
|
7950
7341
|
async addWhiteInkEntry(url, options) {
|
|
7951
7342
|
const id = this.generateId();
|
|
7952
7343
|
const item = this.normalizeItem({
|
|
7953
7344
|
id,
|
|
7954
7345
|
sourceUrl: url,
|
|
7955
|
-
opacity:
|
|
7346
|
+
opacity: WHITE_INK_DEFAULT_OPACITY2,
|
|
7956
7347
|
...options
|
|
7957
7348
|
});
|
|
7958
7349
|
const sessionDirtyBeforeAdd = this.isToolActive && this.hasWorkingChanges;
|
|
@@ -8037,7 +7428,7 @@ var WhiteInkTool = class {
|
|
|
8037
7428
|
this.updateConfig(next);
|
|
8038
7429
|
}
|
|
8039
7430
|
clearWhiteInks() {
|
|
8040
|
-
this.
|
|
7431
|
+
this.sourceSizeCache.clear();
|
|
8041
7432
|
this.previewMaskBySource.clear();
|
|
8042
7433
|
this.pendingPreviewMaskBySource.clear();
|
|
8043
7434
|
this.updateConfig([]);
|
|
@@ -8049,41 +7440,19 @@ var WhiteInkTool = class {
|
|
|
8049
7440
|
}
|
|
8050
7441
|
getFrameRect() {
|
|
8051
7442
|
var _a;
|
|
8052
|
-
if (!this.canvasService) {
|
|
8053
|
-
return { left: 0, top: 0, width: 0, height: 0 };
|
|
8054
|
-
}
|
|
8055
7443
|
const configService = (_a = this.context) == null ? void 0 : _a.services.get(
|
|
8056
7444
|
"ConfigurationService"
|
|
8057
7445
|
);
|
|
8058
|
-
|
|
8059
|
-
return { left: 0, top: 0, width: 0, height: 0 };
|
|
8060
|
-
}
|
|
8061
|
-
const sizeState = readSizeState(configService);
|
|
8062
|
-
const layout = computeSceneLayout(this.canvasService, sizeState);
|
|
8063
|
-
if (!layout) {
|
|
8064
|
-
return { left: 0, top: 0, width: 0, height: 0 };
|
|
8065
|
-
}
|
|
8066
|
-
return this.canvasService.toSceneRect({
|
|
8067
|
-
left: layout.cutRect.left,
|
|
8068
|
-
top: layout.cutRect.top,
|
|
8069
|
-
width: layout.cutRect.width,
|
|
8070
|
-
height: layout.cutRect.height
|
|
8071
|
-
});
|
|
7446
|
+
return resolveCutFrameRect(this.canvasService, configService);
|
|
8072
7447
|
}
|
|
8073
7448
|
toLayoutSceneRect(rect) {
|
|
8074
|
-
return
|
|
8075
|
-
left: rect.left,
|
|
8076
|
-
top: rect.top,
|
|
8077
|
-
width: rect.width,
|
|
8078
|
-
height: rect.height,
|
|
8079
|
-
space: "scene"
|
|
8080
|
-
};
|
|
7449
|
+
return toLayoutSceneRect(rect);
|
|
8081
7450
|
}
|
|
8082
7451
|
getImageObjects() {
|
|
8083
7452
|
if (!this.canvasService) return [];
|
|
8084
7453
|
return this.canvasService.canvas.getObjects().filter((obj) => {
|
|
8085
7454
|
var _a;
|
|
8086
|
-
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.layerId) ===
|
|
7455
|
+
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.layerId) === IMAGE_OBJECT_LAYER_ID;
|
|
8087
7456
|
});
|
|
8088
7457
|
}
|
|
8089
7458
|
getPrimaryImageObject() {
|
|
@@ -8155,21 +7524,16 @@ var WhiteInkTool = class {
|
|
|
8155
7524
|
return snapshot.src === placement.committedUrl;
|
|
8156
7525
|
}
|
|
8157
7526
|
getCoverScale(frame, source) {
|
|
8158
|
-
|
|
8159
|
-
const frameH = Math.max(1, frame.height);
|
|
8160
|
-
const sourceW = Math.max(1, source.width);
|
|
8161
|
-
const sourceH = Math.max(1, source.height);
|
|
8162
|
-
return Math.max(frameW / sourceW, frameH / sourceH);
|
|
7527
|
+
return getCoverScale(frame, source);
|
|
8163
7528
|
}
|
|
8164
7529
|
async ensureSourceSize(sourceUrl) {
|
|
8165
|
-
|
|
8166
|
-
|
|
8167
|
-
|
|
7530
|
+
return this.sourceSizeCache.ensureImageSize(sourceUrl);
|
|
7531
|
+
}
|
|
7532
|
+
async loadImageSize(sourceUrl) {
|
|
8168
7533
|
try {
|
|
8169
7534
|
const image = await this.loadImageElement(sourceUrl);
|
|
8170
7535
|
const size = this.getElementSize(image);
|
|
8171
7536
|
if (!size) return null;
|
|
8172
|
-
this.rememberSourceSize(sourceUrl, size);
|
|
8173
7537
|
return {
|
|
8174
7538
|
width: size.width,
|
|
8175
7539
|
height: size.height
|
|
@@ -8214,22 +7578,10 @@ var WhiteInkTool = class {
|
|
|
8214
7578
|
return (obj == null ? void 0 : obj._element) || (obj == null ? void 0 : obj._originalElement) || null;
|
|
8215
7579
|
}
|
|
8216
7580
|
rememberSourceSize(src, size) {
|
|
8217
|
-
|
|
8218
|
-
if (!Number.isFinite(size.width) || !Number.isFinite(size.height)) return;
|
|
8219
|
-
if (size.width <= 0 || size.height <= 0) return;
|
|
8220
|
-
this.sourceSizeBySrc.set(src, {
|
|
8221
|
-
width: size.width,
|
|
8222
|
-
height: size.height
|
|
8223
|
-
});
|
|
7581
|
+
this.sourceSizeCache.rememberSourceSize(src, size);
|
|
8224
7582
|
}
|
|
8225
7583
|
getSourceSize(src) {
|
|
8226
|
-
|
|
8227
|
-
const cached = this.sourceSizeBySrc.get(src);
|
|
8228
|
-
if (!cached) return null;
|
|
8229
|
-
return {
|
|
8230
|
-
width: cached.width,
|
|
8231
|
-
height: cached.height
|
|
8232
|
-
};
|
|
7584
|
+
return this.sourceSizeCache.getSourceSize(src);
|
|
8233
7585
|
}
|
|
8234
7586
|
computeWhiteScaleAdjust(baseSource, whiteSource) {
|
|
8235
7587
|
if (!baseSource || !whiteSource || baseSource === whiteSource) {
|
|
@@ -8249,7 +7601,7 @@ var WhiteInkTool = class {
|
|
|
8249
7601
|
};
|
|
8250
7602
|
}
|
|
8251
7603
|
computeCoverOpacity() {
|
|
8252
|
-
const raw =
|
|
7604
|
+
const raw = WHITE_INK_DEFAULT_OPACITY2 * WHITE_INK_COVER_OPACITY_FACTOR;
|
|
8253
7605
|
return Math.max(
|
|
8254
7606
|
WHITE_INK_COVER_OPACITY_MIN,
|
|
8255
7607
|
Math.min(WHITE_INK_COVER_OPACITY_MAX, raw)
|
|
@@ -8513,7 +7865,7 @@ var WhiteInkTool = class {
|
|
|
8513
7865
|
purgeSourceCaches(item) {
|
|
8514
7866
|
const sourceUrl = this.resolveSourceUrl(item);
|
|
8515
7867
|
if (!sourceUrl) return;
|
|
8516
|
-
this.
|
|
7868
|
+
this.sourceSizeCache.deleteSourceSize(sourceUrl);
|
|
8517
7869
|
const prefix = `${sourceUrl}::`;
|
|
8518
7870
|
Array.from(this.previewMaskBySource.keys()).forEach((cacheKey) => {
|
|
8519
7871
|
if (cacheKey.startsWith(prefix)) {
|
|
@@ -8552,7 +7904,7 @@ var WhiteInkTool = class {
|
|
|
8552
7904
|
"white-ink.main",
|
|
8553
7905
|
snapshot,
|
|
8554
7906
|
sources.whiteSrc,
|
|
8555
|
-
|
|
7907
|
+
WHITE_INK_DEFAULT_OPACITY2,
|
|
8556
7908
|
WHITE_INK_OBJECT_LAYER_ID,
|
|
8557
7909
|
"white-ink",
|
|
8558
7910
|
sources.whiteScaleAdjustX,
|
|
@@ -8660,7 +8012,7 @@ var WhiteInkTool = class {
|
|
|
8660
8012
|
}
|
|
8661
8013
|
};
|
|
8662
8014
|
|
|
8663
|
-
// src/
|
|
8015
|
+
// src/services/SceneLayoutService.ts
|
|
8664
8016
|
import {
|
|
8665
8017
|
COMMAND_SERVICE,
|
|
8666
8018
|
CONFIGURATION_SERVICE
|
|
@@ -8673,6 +8025,7 @@ var SceneLayoutService = class {
|
|
|
8673
8025
|
constructor() {
|
|
8674
8026
|
this.lastLayout = null;
|
|
8675
8027
|
this.lastGeometry = null;
|
|
8028
|
+
this.subscriptions = new SubscriptionBag();
|
|
8676
8029
|
this.commandDisposables = [];
|
|
8677
8030
|
this.onCanvasResized = () => {
|
|
8678
8031
|
this.refresh();
|
|
@@ -8708,16 +8061,13 @@ var SceneLayoutService = class {
|
|
|
8708
8061
|
() => this.getGeometry()
|
|
8709
8062
|
)
|
|
8710
8063
|
);
|
|
8711
|
-
this.
|
|
8712
|
-
|
|
8064
|
+
this.subscriptions.disposeAll();
|
|
8065
|
+
this.subscriptions.onConfigChange(configService, this.onConfigChanged);
|
|
8066
|
+
this.subscriptions.on(context.eventBus, "canvas:resized", this.onCanvasResized);
|
|
8713
8067
|
this.refresh();
|
|
8714
8068
|
}
|
|
8715
8069
|
dispose(context) {
|
|
8716
|
-
|
|
8717
|
-
const activeContext = (_a = this.context) != null ? _a : context;
|
|
8718
|
-
activeContext.eventBus.off("canvas:resized", this.onCanvasResized);
|
|
8719
|
-
(_b = this.onConfigChange) == null ? void 0 : _b.dispose();
|
|
8720
|
-
this.onConfigChange = void 0;
|
|
8070
|
+
this.subscriptions.disposeAll();
|
|
8721
8071
|
this.commandDisposables.forEach((item) => item.dispose());
|
|
8722
8072
|
this.commandDisposables = [];
|
|
8723
8073
|
this.context = void 0;
|
|
@@ -8871,6 +8221,9 @@ function evaluateVisibilityExpr(expr, context) {
|
|
|
8871
8221
|
if (!toolId) return false;
|
|
8872
8222
|
return context.isSessionActive ? context.isSessionActive(toolId) : false;
|
|
8873
8223
|
}
|
|
8224
|
+
if (expr.op === "anySessionActive") {
|
|
8225
|
+
return context.hasAnyActiveSession ? context.hasAnyActiveSession() : false;
|
|
8226
|
+
}
|
|
8874
8227
|
if (expr.op === "layerExists") {
|
|
8875
8228
|
return layerState(context, expr.layerId).exists === true;
|
|
8876
8229
|
}
|
|
@@ -8904,6 +8257,7 @@ var CanvasService = class {
|
|
|
8904
8257
|
this.visibilityRefreshScheduled = false;
|
|
8905
8258
|
this.managedProducerPassIds = /* @__PURE__ */ new Set();
|
|
8906
8259
|
this.managedPassMetas = /* @__PURE__ */ new Map();
|
|
8260
|
+
this.managedPassEffects = [];
|
|
8907
8261
|
this.canvasForwardersBound = false;
|
|
8908
8262
|
this.forwardSelectionCreated = (e) => {
|
|
8909
8263
|
var _a;
|
|
@@ -8931,9 +8285,11 @@ var CanvasService = class {
|
|
|
8931
8285
|
};
|
|
8932
8286
|
this.onToolActivated = () => {
|
|
8933
8287
|
this.applyManagedPassVisibility();
|
|
8288
|
+
void this.applyManagedPassEffects(void 0, { render: true });
|
|
8934
8289
|
};
|
|
8935
8290
|
this.onToolSessionChanged = () => {
|
|
8936
8291
|
this.applyManagedPassVisibility();
|
|
8292
|
+
void this.applyManagedPassEffects(void 0, { render: true });
|
|
8937
8293
|
};
|
|
8938
8294
|
this.onCanvasObjectChanged = () => {
|
|
8939
8295
|
if (this.producerApplyInProgress) return;
|
|
@@ -8998,6 +8354,7 @@ var CanvasService = class {
|
|
|
8998
8354
|
this.renderProducers.clear();
|
|
8999
8355
|
this.managedProducerPassIds.clear();
|
|
9000
8356
|
this.managedPassMetas.clear();
|
|
8357
|
+
this.managedPassEffects = [];
|
|
9001
8358
|
this.context = void 0;
|
|
9002
8359
|
this.workbenchService = void 0;
|
|
9003
8360
|
this.toolSessionService = void 0;
|
|
@@ -9104,6 +8461,7 @@ var CanvasService = class {
|
|
|
9104
8461
|
return {
|
|
9105
8462
|
type: "clipPath",
|
|
9106
8463
|
key,
|
|
8464
|
+
visibility: effect.visibility,
|
|
9107
8465
|
source: {
|
|
9108
8466
|
...source,
|
|
9109
8467
|
id: sourceId
|
|
@@ -9211,22 +8569,30 @@ var CanvasService = class {
|
|
|
9211
8569
|
});
|
|
9212
8570
|
return state;
|
|
9213
8571
|
}
|
|
9214
|
-
|
|
8572
|
+
isSessionActive(toolId) {
|
|
8573
|
+
if (!this.toolSessionService) return false;
|
|
8574
|
+
return this.toolSessionService.getState(toolId).status === "active";
|
|
8575
|
+
}
|
|
8576
|
+
hasAnyActiveSession() {
|
|
8577
|
+
var _a, _b;
|
|
8578
|
+
return (_b = (_a = this.toolSessionService) == null ? void 0 : _a.hasAnyActiveSession()) != null ? _b : false;
|
|
8579
|
+
}
|
|
8580
|
+
buildVisibilityEvalContext(layers) {
|
|
9215
8581
|
var _a, _b;
|
|
8582
|
+
return {
|
|
8583
|
+
activeToolId: (_b = (_a = this.workbenchService) == null ? void 0 : _a.activeToolId) != null ? _b : null,
|
|
8584
|
+
isSessionActive: (toolId) => this.isSessionActive(toolId),
|
|
8585
|
+
hasAnyActiveSession: () => this.hasAnyActiveSession(),
|
|
8586
|
+
layers
|
|
8587
|
+
};
|
|
8588
|
+
}
|
|
8589
|
+
applyManagedPassVisibility(options = {}) {
|
|
9216
8590
|
if (!this.managedPassMetas.size) return false;
|
|
9217
8591
|
const layers = this.getPassRuntimeState();
|
|
9218
|
-
const
|
|
9219
|
-
const isSessionActive = (toolId) => {
|
|
9220
|
-
if (!this.toolSessionService) return false;
|
|
9221
|
-
return this.toolSessionService.getState(toolId).status === "active";
|
|
9222
|
-
};
|
|
8592
|
+
const context = this.buildVisibilityEvalContext(layers);
|
|
9223
8593
|
let changed = false;
|
|
9224
8594
|
this.managedPassMetas.forEach((meta) => {
|
|
9225
|
-
const visible = evaluateVisibilityExpr(meta.visibility,
|
|
9226
|
-
activeToolId,
|
|
9227
|
-
isSessionActive,
|
|
9228
|
-
layers
|
|
9229
|
-
});
|
|
8595
|
+
const visible = evaluateVisibilityExpr(meta.visibility, context);
|
|
9230
8596
|
changed = this.setPassVisibility(meta.id, visible) || changed;
|
|
9231
8597
|
});
|
|
9232
8598
|
if (changed && options.render !== false) {
|
|
@@ -9294,18 +8660,24 @@ var CanvasService = class {
|
|
|
9294
8660
|
}
|
|
9295
8661
|
this.managedProducerPassIds = nextPassIds;
|
|
9296
8662
|
this.managedPassMetas = nextManagedPassMetas;
|
|
8663
|
+
this.managedPassEffects = nextEffects;
|
|
9297
8664
|
this.syncManagedPassStacking(Array.from(nextManagedPassMetas.values()));
|
|
9298
|
-
await this.applyManagedPassEffects(nextEffects);
|
|
8665
|
+
await this.applyManagedPassEffects(nextEffects, { render: false });
|
|
9299
8666
|
this.applyManagedPassVisibility({ render: false });
|
|
9300
8667
|
} finally {
|
|
9301
8668
|
this.producerApplyInProgress = false;
|
|
9302
8669
|
}
|
|
9303
8670
|
this.requestRenderAll();
|
|
9304
8671
|
}
|
|
9305
|
-
async applyManagedPassEffects(effects) {
|
|
8672
|
+
async applyManagedPassEffects(effects = this.managedPassEffects, options = {}) {
|
|
9306
8673
|
const effectTargetMap = /* @__PURE__ */ new Map();
|
|
8674
|
+
const layers = this.getPassRuntimeState();
|
|
8675
|
+
const visibilityContext = this.buildVisibilityEvalContext(layers);
|
|
9307
8676
|
for (const effect of effects) {
|
|
9308
8677
|
if (effect.type !== "clipPath") continue;
|
|
8678
|
+
if (!evaluateVisibilityExpr(effect.visibility, visibilityContext)) {
|
|
8679
|
+
continue;
|
|
8680
|
+
}
|
|
9309
8681
|
effect.targetPassIds.forEach((targetPassId) => {
|
|
9310
8682
|
this.getPassCanvasObjects(targetPassId).forEach((obj) => {
|
|
9311
8683
|
effectTargetMap.set(obj, effect);
|
|
@@ -9337,6 +8709,9 @@ var CanvasService = class {
|
|
|
9337
8709
|
targetEffect.key
|
|
9338
8710
|
);
|
|
9339
8711
|
}
|
|
8712
|
+
if (options.render !== false) {
|
|
8713
|
+
this.requestRenderAll();
|
|
8714
|
+
}
|
|
9340
8715
|
}
|
|
9341
8716
|
getObject(id, passId) {
|
|
9342
8717
|
const normalizedId = String(id || "").trim();
|
|
@@ -9873,5 +9248,13 @@ export {
|
|
|
9873
9248
|
SizeTool,
|
|
9874
9249
|
ViewportSystem,
|
|
9875
9250
|
WhiteInkTool,
|
|
9251
|
+
getCoverScale as computeImageCoverScale,
|
|
9252
|
+
getCoverScale as computeWhiteInkCoverScale,
|
|
9253
|
+
createDielineCommands,
|
|
9254
|
+
createDielineConfigurations,
|
|
9255
|
+
createImageCommands,
|
|
9256
|
+
createImageConfigurations,
|
|
9257
|
+
createWhiteInkCommands,
|
|
9258
|
+
createWhiteInkConfigurations,
|
|
9876
9259
|
evaluateVisibilityExpr
|
|
9877
9260
|
};
|