@pooder/kit 5.4.0 → 6.0.1
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/coordinate.js +74 -0
- package/.test-dist/src/extensions/background.js +547 -0
- package/.test-dist/src/extensions/bridgeSelection.js +20 -0
- package/.test-dist/src/extensions/constraints.js +237 -0
- package/.test-dist/src/extensions/dieline.js +935 -0
- package/.test-dist/src/extensions/dielineShape.js +66 -0
- package/.test-dist/src/extensions/edgeScale.js +12 -0
- package/.test-dist/src/extensions/feature.js +910 -0
- package/.test-dist/src/extensions/featureComplete.js +32 -0
- package/.test-dist/src/extensions/film.js +226 -0
- package/.test-dist/src/extensions/geometry.js +609 -0
- package/.test-dist/src/extensions/image.js +1788 -0
- package/.test-dist/src/extensions/index.js +28 -0
- package/.test-dist/src/extensions/maskOps.js +334 -0
- package/.test-dist/src/extensions/mirror.js +104 -0
- package/.test-dist/src/extensions/ruler.js +442 -0
- package/.test-dist/src/extensions/sceneLayout.js +96 -0
- package/.test-dist/src/extensions/sceneLayoutModel.js +202 -0
- package/.test-dist/src/extensions/sceneVisibility.js +55 -0
- package/.test-dist/src/extensions/size.js +331 -0
- package/.test-dist/src/extensions/tracer.js +709 -0
- package/.test-dist/src/extensions/white-ink.js +1200 -0
- package/.test-dist/src/extensions/wrappedOffsets.js +33 -0
- package/.test-dist/src/index.js +18 -0
- package/.test-dist/src/services/CanvasService.js +1032 -0
- package/.test-dist/src/services/ViewportSystem.js +76 -0
- package/.test-dist/src/services/index.js +25 -0
- package/.test-dist/src/services/renderSpec.js +2 -0
- package/.test-dist/src/services/visibility.js +57 -0
- package/.test-dist/src/units.js +30 -0
- package/.test-dist/tests/run.js +150 -0
- package/CHANGELOG.md +12 -0
- package/dist/index.d.mts +164 -62
- package/dist/index.d.ts +164 -62
- package/dist/index.js +2433 -1719
- package/dist/index.mjs +2442 -1723
- package/package.json +1 -1
- package/src/coordinate.ts +106 -106
- package/src/extensions/background.ts +716 -323
- package/src/extensions/bridgeSelection.ts +17 -17
- package/src/extensions/constraints.ts +322 -322
- package/src/extensions/dieline.ts +1173 -1149
- package/src/extensions/dielineShape.ts +109 -109
- package/src/extensions/edgeScale.ts +19 -19
- package/src/extensions/feature.ts +1140 -1137
- package/src/extensions/featureComplete.ts +46 -46
- package/src/extensions/film.ts +270 -266
- package/src/extensions/geometry.ts +851 -885
- package/src/extensions/image.ts +2240 -2054
- package/src/extensions/index.ts +10 -11
- package/src/extensions/maskOps.ts +283 -283
- package/src/extensions/mirror.ts +128 -128
- package/src/extensions/ruler.ts +664 -654
- package/src/extensions/sceneLayout.ts +140 -140
- package/src/extensions/sceneLayoutModel.ts +364 -364
- package/src/extensions/size.ts +389 -389
- package/src/extensions/tracer.ts +1019 -1019
- package/src/extensions/white-ink.ts +1508 -1575
- package/src/extensions/wrappedOffsets.ts +33 -33
- package/src/index.ts +2 -2
- package/src/services/CanvasService.ts +1317 -832
- package/src/services/ViewportSystem.ts +95 -95
- package/src/services/index.ts +4 -3
- package/src/services/renderSpec.ts +85 -53
- package/src/services/visibility.ts +83 -0
- package/src/units.ts +27 -27
- package/tests/run.ts +258 -118
- package/tsconfig.test.json +15 -15
- package/src/extensions/sceneVisibility.ts +0 -64
package/dist/index.mjs
CHANGED
|
@@ -3,291 +3,94 @@ import {
|
|
|
3
3
|
ContributionPointIds
|
|
4
4
|
} from "@pooder/core";
|
|
5
5
|
import { FabricImage } from "fabric";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
this.color = "";
|
|
18
|
-
this.url = "";
|
|
19
|
-
this.specs = [];
|
|
20
|
-
this.renderSeq = 0;
|
|
21
|
-
this.renderImageUrl = "";
|
|
22
|
-
this.sourceSizeBySrc = /* @__PURE__ */ new Map();
|
|
23
|
-
this.pendingSizeBySrc = /* @__PURE__ */ new Map();
|
|
24
|
-
this.onCanvasResized = () => {
|
|
25
|
-
this.updateBackground();
|
|
26
|
-
};
|
|
27
|
-
if (options) {
|
|
28
|
-
Object.assign(this, options);
|
|
6
|
+
|
|
7
|
+
// src/coordinate.ts
|
|
8
|
+
var Coordinate = class {
|
|
9
|
+
/**
|
|
10
|
+
* Calculate layout to fit content within container while preserving aspect ratio.
|
|
11
|
+
*/
|
|
12
|
+
static calculateLayout(container, content, padding = 0) {
|
|
13
|
+
const availableWidth = Math.max(0, container.width - padding * 2);
|
|
14
|
+
const availableHeight = Math.max(0, container.height - padding * 2);
|
|
15
|
+
if (content.width === 0 || content.height === 0) {
|
|
16
|
+
return { scale: 1, offsetX: 0, offsetY: 0, width: 0, height: 0 };
|
|
29
17
|
}
|
|
18
|
+
const scaleX = availableWidth / content.width;
|
|
19
|
+
const scaleY = availableHeight / content.height;
|
|
20
|
+
const scale = Math.min(scaleX, scaleY);
|
|
21
|
+
const width = content.width * scale;
|
|
22
|
+
const height = content.height * scale;
|
|
23
|
+
const offsetX = (container.width - width) / 2;
|
|
24
|
+
const offsetY = (container.height - height) / 2;
|
|
25
|
+
return { scale, offsetX, offsetY, width, height };
|
|
30
26
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
(_a = this.renderProducerDisposable) == null ? void 0 : _a.dispose();
|
|
39
|
-
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
40
|
-
this.id,
|
|
41
|
-
() => ({
|
|
42
|
-
layerSpecs: {
|
|
43
|
-
[BACKGROUND_LAYER_ID]: this.specs
|
|
44
|
-
}
|
|
45
|
-
}),
|
|
46
|
-
{ priority: 0 }
|
|
47
|
-
);
|
|
48
|
-
const configService = context.services.get(
|
|
49
|
-
"ConfigurationService"
|
|
50
|
-
);
|
|
51
|
-
if (configService) {
|
|
52
|
-
this.color = configService.get("background.color", this.color);
|
|
53
|
-
this.url = configService.get("background.url", this.url);
|
|
54
|
-
configService.onAnyChange((e) => {
|
|
55
|
-
if (e.key.startsWith("background.")) {
|
|
56
|
-
const prop = e.key.split(".")[1];
|
|
57
|
-
console.log(
|
|
58
|
-
`[BackgroundTool] Config change detected: ${e.key} -> ${e.value}, prop: ${prop}`
|
|
59
|
-
);
|
|
60
|
-
if (prop && prop in this) {
|
|
61
|
-
console.log(
|
|
62
|
-
`[BackgroundTool] Updating option ${prop} to ${e.value}`
|
|
63
|
-
);
|
|
64
|
-
this[prop] = e.value;
|
|
65
|
-
this.updateBackground();
|
|
66
|
-
} else {
|
|
67
|
-
console.warn(
|
|
68
|
-
`[BackgroundTool] Property ${prop} not found in options`
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
context.eventBus.on("canvas:resized", this.onCanvasResized);
|
|
75
|
-
this.updateBackground();
|
|
27
|
+
/**
|
|
28
|
+
* Convert an absolute value to a normalized value (0-1).
|
|
29
|
+
* @param value Absolute value (e.g., pixels)
|
|
30
|
+
* @param total Total dimension size (e.g., canvas width)
|
|
31
|
+
*/
|
|
32
|
+
static toNormalized(value, total) {
|
|
33
|
+
return total === 0 ? 0 : value / total;
|
|
76
34
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
this.renderProducerDisposable = void 0;
|
|
85
|
-
if (!this.canvasService) return;
|
|
86
|
-
const layer = this.canvasService.getLayer(BACKGROUND_LAYER_ID);
|
|
87
|
-
if (layer) {
|
|
88
|
-
this.canvasService.canvas.remove(layer);
|
|
89
|
-
}
|
|
90
|
-
void this.canvasService.flushRenderFromProducers();
|
|
91
|
-
this.canvasService.requestRenderAll();
|
|
92
|
-
this.canvasService = void 0;
|
|
35
|
+
/**
|
|
36
|
+
* Convert a normalized value (0-1) to an absolute value.
|
|
37
|
+
* @param normalized Normalized value (0-1)
|
|
38
|
+
* @param total Total dimension size (e.g., canvas width)
|
|
39
|
+
*/
|
|
40
|
+
static toAbsolute(normalized, total) {
|
|
41
|
+
return normalized * total;
|
|
93
42
|
}
|
|
94
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Normalize a point's coordinates.
|
|
45
|
+
*/
|
|
46
|
+
static normalizePoint(point, size) {
|
|
95
47
|
return {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
id: "background.color",
|
|
99
|
-
type: "color",
|
|
100
|
-
label: "Background Color",
|
|
101
|
-
default: ""
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
id: "background.url",
|
|
105
|
-
type: "string",
|
|
106
|
-
label: "Image URL",
|
|
107
|
-
default: ""
|
|
108
|
-
}
|
|
109
|
-
],
|
|
110
|
-
[ContributionPointIds.COMMANDS]: [
|
|
111
|
-
{
|
|
112
|
-
command: "reset",
|
|
113
|
-
title: "Reset Background",
|
|
114
|
-
handler: () => {
|
|
115
|
-
this.updateBackground();
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
command: "clear",
|
|
121
|
-
title: "Clear Background",
|
|
122
|
-
handler: () => {
|
|
123
|
-
this.color = "transparent";
|
|
124
|
-
this.url = "";
|
|
125
|
-
this.updateBackground();
|
|
126
|
-
return true;
|
|
127
|
-
}
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
command: "setBackgroundColor",
|
|
131
|
-
title: "Set Background Color",
|
|
132
|
-
handler: (color) => {
|
|
133
|
-
if (this.color === color) return true;
|
|
134
|
-
this.color = color;
|
|
135
|
-
this.updateBackground();
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
command: "setBackgroundImage",
|
|
141
|
-
title: "Set Background Image",
|
|
142
|
-
handler: (url) => {
|
|
143
|
-
if (this.url === url) return true;
|
|
144
|
-
this.url = url;
|
|
145
|
-
this.updateBackground();
|
|
146
|
-
return true;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
]
|
|
48
|
+
x: this.toNormalized(point.x, size.width),
|
|
49
|
+
y: this.toNormalized(point.y, size.height)
|
|
150
50
|
};
|
|
151
51
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Denormalize a point's coordinates to absolute pixels.
|
|
54
|
+
*/
|
|
55
|
+
static denormalizePoint(point, size) {
|
|
156
56
|
return {
|
|
157
|
-
|
|
158
|
-
|
|
57
|
+
x: this.toAbsolute(point.x, size.width),
|
|
58
|
+
y: this.toAbsolute(point.y, size.height)
|
|
159
59
|
};
|
|
160
60
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
props: {
|
|
174
|
-
left: 0,
|
|
175
|
-
top: 0,
|
|
176
|
-
width,
|
|
177
|
-
height,
|
|
178
|
-
originX: "left",
|
|
179
|
-
originY: "top",
|
|
180
|
-
fill: color,
|
|
181
|
-
selectable: false,
|
|
182
|
-
evented: false,
|
|
183
|
-
excludeFromExport: true
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
];
|
|
187
|
-
if (!imageUrl) {
|
|
188
|
-
return specs;
|
|
189
|
-
}
|
|
190
|
-
const sourceSize = this.sourceSizeBySrc.get(imageUrl);
|
|
191
|
-
const sourceWidth = Math.max(1, Number((sourceSize == null ? void 0 : sourceSize.width) || width));
|
|
192
|
-
const sourceHeight = Math.max(1, Number((sourceSize == null ? void 0 : sourceSize.height) || height));
|
|
193
|
-
const coverScale = Math.max(width / sourceWidth, height / sourceHeight);
|
|
194
|
-
specs.push({
|
|
195
|
-
id: BACKGROUND_IMAGE_ID,
|
|
196
|
-
type: "image",
|
|
197
|
-
src: imageUrl,
|
|
198
|
-
space: "screen",
|
|
199
|
-
data: {
|
|
200
|
-
id: BACKGROUND_IMAGE_ID,
|
|
201
|
-
layerId: BACKGROUND_LAYER_ID,
|
|
202
|
-
type: "background-image"
|
|
203
|
-
},
|
|
204
|
-
props: {
|
|
205
|
-
left: 0,
|
|
206
|
-
top: 0,
|
|
207
|
-
originX: "left",
|
|
208
|
-
originY: "top",
|
|
209
|
-
scaleX: coverScale,
|
|
210
|
-
scaleY: coverScale,
|
|
211
|
-
selectable: false,
|
|
212
|
-
evented: false,
|
|
213
|
-
excludeFromExport: true
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
return specs;
|
|
217
|
-
}
|
|
218
|
-
async ensureImageSize(src) {
|
|
219
|
-
if (!src) return null;
|
|
220
|
-
const cached = this.sourceSizeBySrc.get(src);
|
|
221
|
-
if (cached) return cached;
|
|
222
|
-
const pending = this.pendingSizeBySrc.get(src);
|
|
223
|
-
if (pending) {
|
|
224
|
-
return pending;
|
|
225
|
-
}
|
|
226
|
-
const task = this.loadImageSize(src);
|
|
227
|
-
this.pendingSizeBySrc.set(src, task);
|
|
228
|
-
try {
|
|
229
|
-
return await task;
|
|
230
|
-
} finally {
|
|
231
|
-
if (this.pendingSizeBySrc.get(src) === task) {
|
|
232
|
-
this.pendingSizeBySrc.delete(src);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
async loadImageSize(src) {
|
|
237
|
-
try {
|
|
238
|
-
const image = await FabricImage.fromURL(src, {
|
|
239
|
-
crossOrigin: "anonymous"
|
|
240
|
-
});
|
|
241
|
-
const width = Number((image == null ? void 0 : image.width) || 0);
|
|
242
|
-
const height = Number((image == null ? void 0 : image.height) || 0);
|
|
243
|
-
if (width > 0 && height > 0) {
|
|
244
|
-
const size = { width, height };
|
|
245
|
-
this.sourceSizeBySrc.set(src, size);
|
|
246
|
-
return size;
|
|
247
|
-
}
|
|
248
|
-
} catch (error) {
|
|
249
|
-
console.error("[BackgroundTool] Failed to load image", src, error);
|
|
61
|
+
static convertUnit(value, from, to) {
|
|
62
|
+
if (from === to) return value;
|
|
63
|
+
const toMM = {
|
|
64
|
+
px: 0.264583,
|
|
65
|
+
// 1px = 0.264583mm (96 DPI)
|
|
66
|
+
mm: 1,
|
|
67
|
+
cm: 10,
|
|
68
|
+
in: 25.4
|
|
69
|
+
};
|
|
70
|
+
const mmValue = value * (from === "px" ? toMM.px : toMM[from] || 1);
|
|
71
|
+
if (to === "px") {
|
|
72
|
+
return mmValue / toMM.px;
|
|
250
73
|
}
|
|
251
|
-
return
|
|
74
|
+
return mmValue / (toMM[to] || 1);
|
|
252
75
|
}
|
|
253
|
-
|
|
254
|
-
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/units.ts
|
|
79
|
+
function parseLengthToMm(input, defaultUnit) {
|
|
80
|
+
var _a, _b;
|
|
81
|
+
if (typeof input === "number") {
|
|
82
|
+
if (!Number.isFinite(input)) return 0;
|
|
83
|
+
return Coordinate.convertUnit(input, defaultUnit, "mm");
|
|
255
84
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (seq !== this.renderSeq) return;
|
|
266
|
-
if (loaded) {
|
|
267
|
-
this.renderImageUrl = nextUrl;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
this.specs = this.buildBackgroundSpecs(color, this.renderImageUrl);
|
|
271
|
-
await this.canvasService.flushRenderFromProducers();
|
|
272
|
-
if (seq !== this.renderSeq) return;
|
|
273
|
-
const layer = this.canvasService.getLayer(BACKGROUND_LAYER_ID);
|
|
274
|
-
if (layer) {
|
|
275
|
-
this.canvasService.canvas.sendObjectToBack(layer);
|
|
276
|
-
}
|
|
277
|
-
this.canvasService.requestRenderAll();
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
// src/extensions/image.ts
|
|
282
|
-
import {
|
|
283
|
-
ContributionPointIds as ContributionPointIds2
|
|
284
|
-
} from "@pooder/core";
|
|
285
|
-
import {
|
|
286
|
-
Canvas as FabricCanvas,
|
|
287
|
-
Image as FabricImage2,
|
|
288
|
-
Pattern,
|
|
289
|
-
Point
|
|
290
|
-
} from "fabric";
|
|
85
|
+
const raw = input.trim();
|
|
86
|
+
if (!raw) return 0;
|
|
87
|
+
const match = raw.match(/^([+-]?\d+(?:\.\d+)?)\s*(px|mm|cm|in)?$/i);
|
|
88
|
+
if (!match) return 0;
|
|
89
|
+
const value = Number(match[1]);
|
|
90
|
+
if (!Number.isFinite(value)) return 0;
|
|
91
|
+
const unit = (_b = (_a = match[2]) == null ? void 0 : _a.toLowerCase()) != null ? _b : defaultUnit;
|
|
92
|
+
return Coordinate.convertUnit(value, unit, "mm");
|
|
93
|
+
}
|
|
291
94
|
|
|
292
95
|
// src/extensions/dielineShape.ts
|
|
293
96
|
var BUILTIN_DIELINE_SHAPES = [
|
|
@@ -304,7 +107,7 @@ var DEFAULT_HEART_SHAPE_PARAMS = {
|
|
|
304
107
|
tipSharpness: 0
|
|
305
108
|
};
|
|
306
109
|
var DEFAULT_DIELINE_SHAPE_STYLE = {
|
|
307
|
-
fitMode: "
|
|
110
|
+
fitMode: "stretch",
|
|
308
111
|
...DEFAULT_HEART_SHAPE_PARAMS
|
|
309
112
|
};
|
|
310
113
|
function isDielineShape(value) {
|
|
@@ -354,968 +157,1429 @@ function getHeartShapeParams(style) {
|
|
|
354
157
|
};
|
|
355
158
|
}
|
|
356
159
|
|
|
357
|
-
// src/extensions/
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
160
|
+
// src/extensions/sceneLayoutModel.ts
|
|
161
|
+
var DEFAULT_SIZE_STATE = {
|
|
162
|
+
unit: "mm",
|
|
163
|
+
actualWidthMm: 500,
|
|
164
|
+
actualHeightMm: 500,
|
|
165
|
+
constraintMode: "free",
|
|
166
|
+
aspectRatio: 1,
|
|
167
|
+
cutMode: "trim",
|
|
168
|
+
cutMarginMm: 0,
|
|
169
|
+
viewPadding: 140,
|
|
170
|
+
minMm: 10,
|
|
171
|
+
maxMm: 2e3,
|
|
172
|
+
stepMm: 0.1
|
|
173
|
+
};
|
|
174
|
+
function clamp(value, min, max) {
|
|
175
|
+
return Math.max(min, Math.min(max, value));
|
|
367
176
|
}
|
|
368
|
-
function
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if (s.outsideAbove) score++;
|
|
372
|
-
}
|
|
373
|
-
return score;
|
|
177
|
+
function roundToStep(value, step) {
|
|
178
|
+
if (!Number.isFinite(step) || step <= 0) return value;
|
|
179
|
+
return Math.round(value / step) * step;
|
|
374
180
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
if (!Number.isFinite(start) || !Number.isFinite(end)) return 0;
|
|
380
|
-
const s = (start % total + total) % total;
|
|
381
|
-
const e = (end % total + total) % total;
|
|
382
|
-
return e >= s ? e - s : total - s + e;
|
|
181
|
+
function sanitizeMmValue(valueMm, limits) {
|
|
182
|
+
if (!Number.isFinite(valueMm)) return limits.minMm;
|
|
183
|
+
const rounded = roundToStep(valueMm, limits.stepMm);
|
|
184
|
+
return clamp(rounded, limits.minMm, limits.maxMm);
|
|
383
185
|
}
|
|
384
|
-
function
|
|
385
|
-
if (
|
|
386
|
-
|
|
387
|
-
const n = Math.max(0, Math.floor(count));
|
|
388
|
-
if (n <= 0) return [];
|
|
389
|
-
const dist = wrappedDistance(total, start, end);
|
|
390
|
-
if (n === 1) return [(start % total + total) % total];
|
|
391
|
-
const step = dist / (n - 1);
|
|
392
|
-
const offsets = [];
|
|
393
|
-
for (let i = 0; i < n; i++) {
|
|
394
|
-
const raw = start + step * i;
|
|
395
|
-
const wrapped = (raw % total + total) % total;
|
|
396
|
-
offsets.push(wrapped);
|
|
397
|
-
}
|
|
398
|
-
return offsets;
|
|
186
|
+
function normalizeUnit(value) {
|
|
187
|
+
if (value === "cm" || value === "in") return value;
|
|
188
|
+
return "mm";
|
|
399
189
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const { x, y, width, height } = geometry;
|
|
404
|
-
const left = x - width / 2;
|
|
405
|
-
const top = y - height / 2;
|
|
406
|
-
return {
|
|
407
|
-
x: left + feature.x * width,
|
|
408
|
-
y: top + feature.y * height
|
|
409
|
-
};
|
|
190
|
+
function normalizeConstraintMode(value) {
|
|
191
|
+
if (value === "lockAspect" || value === "equal") return value;
|
|
192
|
+
return "free";
|
|
410
193
|
}
|
|
411
|
-
function
|
|
412
|
-
if (
|
|
413
|
-
|
|
414
|
-
} else {
|
|
415
|
-
paper.view.viewSize = new paper.Size(width, height);
|
|
416
|
-
}
|
|
194
|
+
function normalizeCutMode(value) {
|
|
195
|
+
if (value === "outset" || value === "inset") return value;
|
|
196
|
+
return "trim";
|
|
417
197
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
let result = shape;
|
|
421
|
-
if (typeof result.resolveCrossings === "function") result = result.resolveCrossings();
|
|
422
|
-
if (typeof result.reduce === "function") result = result.reduce({});
|
|
423
|
-
if (typeof result.reorient === "function") result = result.reorient(true, true);
|
|
424
|
-
if (typeof result.reduce === "function") result = result.reduce({});
|
|
425
|
-
return result;
|
|
198
|
+
function toMm(value, fromUnit) {
|
|
199
|
+
return Coordinate.convertUnit(value, fromUnit, "mm");
|
|
426
200
|
}
|
|
427
|
-
function
|
|
428
|
-
return
|
|
201
|
+
function fromMm(valueMm, toUnit) {
|
|
202
|
+
return Coordinate.convertUnit(valueMm, "mm", toUnit);
|
|
429
203
|
}
|
|
430
|
-
function
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
if (validHits.length === 0) return null;
|
|
441
|
-
validHits.sort((a, b) => b.point.y - a.point.y);
|
|
442
|
-
const flags = validHits.map((h) => {
|
|
443
|
-
const above = h.point.add(new paper.Point(0, -delta));
|
|
444
|
-
const below = h.point.add(new paper.Point(0, delta));
|
|
445
|
-
return {
|
|
446
|
-
insideAbove: mainShape.contains(above),
|
|
447
|
-
insideBelow: mainShape.contains(below)
|
|
448
|
-
};
|
|
449
|
-
});
|
|
450
|
-
const idx = pickExitIndex(flags);
|
|
451
|
-
if (idx < 0) return null;
|
|
452
|
-
if (isBridgeDebugEnabled()) {
|
|
453
|
-
console.debug("Geometry: Bridge ray", {
|
|
454
|
-
x,
|
|
455
|
-
validHits: validHits.length,
|
|
456
|
-
idx,
|
|
457
|
-
delta,
|
|
458
|
-
overlap,
|
|
459
|
-
op
|
|
460
|
-
});
|
|
204
|
+
function resolvePaddingPx(raw, containerWidth, containerHeight) {
|
|
205
|
+
if (typeof raw === "number") return Math.max(0, raw);
|
|
206
|
+
if (typeof raw === "string") {
|
|
207
|
+
if (raw.endsWith("%")) {
|
|
208
|
+
const percent = parseFloat(raw) / 100;
|
|
209
|
+
if (!Number.isFinite(percent)) return 0;
|
|
210
|
+
return Math.max(0, Math.min(containerWidth, containerHeight) * percent);
|
|
211
|
+
}
|
|
212
|
+
const fixed = parseFloat(raw);
|
|
213
|
+
return Number.isFinite(fixed) ? Math.max(0, fixed) : 0;
|
|
461
214
|
}
|
|
462
|
-
|
|
463
|
-
return { point: hit.point, location: hit };
|
|
215
|
+
return 0;
|
|
464
216
|
}
|
|
465
|
-
function
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
pointsA.map((p) => ({
|
|
469
|
-
outsideAbove: !mainShape.contains(p.add(new paper.Point(0, -delta)))
|
|
470
|
-
}))
|
|
217
|
+
function readSizeState(configService) {
|
|
218
|
+
const unit = normalizeUnit(
|
|
219
|
+
configService.get("size.unit", DEFAULT_SIZE_STATE.unit)
|
|
471
220
|
);
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
}))
|
|
221
|
+
const minMm = Math.max(
|
|
222
|
+
0.1,
|
|
223
|
+
Number(configService.get("size.minMm", DEFAULT_SIZE_STATE.minMm))
|
|
476
224
|
);
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
console.debug("Geometry: Bridge chain", {
|
|
481
|
-
scoreA,
|
|
482
|
-
scoreB,
|
|
483
|
-
lenA: pointsA.length,
|
|
484
|
-
lenB: pointsB.length,
|
|
485
|
-
ratioA,
|
|
486
|
-
ratioB,
|
|
487
|
-
delta,
|
|
488
|
-
overlap,
|
|
489
|
-
op
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
const ratioEps = 1e-6;
|
|
493
|
-
if (Math.abs(ratioA - ratioB) > ratioEps) {
|
|
494
|
-
return ratioA > ratioB ? pointsA : pointsB;
|
|
495
|
-
}
|
|
496
|
-
if (scoreA !== scoreB) return scoreA > scoreB ? pointsA : pointsB;
|
|
497
|
-
return pointsA.length <= pointsB.length ? pointsA : pointsB;
|
|
498
|
-
}
|
|
499
|
-
function fitPathItemToRect(item, rect, fitMode) {
|
|
500
|
-
const { left, top, width, height } = rect;
|
|
501
|
-
const bounds = item.bounds;
|
|
502
|
-
if (width <= 0 || height <= 0 || !Number.isFinite(bounds.width) || !Number.isFinite(bounds.height) || bounds.width <= 0 || bounds.height <= 0) {
|
|
503
|
-
item.position = new paper.Point(left + width / 2, top + height / 2);
|
|
504
|
-
return item;
|
|
505
|
-
}
|
|
506
|
-
item.translate(new paper.Point(-bounds.left, -bounds.top));
|
|
507
|
-
if (fitMode === "stretch") {
|
|
508
|
-
item.scale(width / bounds.width, height / bounds.height, new paper.Point(0, 0));
|
|
509
|
-
item.translate(new paper.Point(left, top));
|
|
510
|
-
return item;
|
|
511
|
-
}
|
|
512
|
-
const uniformScale = Math.min(width / bounds.width, height / bounds.height);
|
|
513
|
-
item.scale(uniformScale, uniformScale, new paper.Point(0, 0));
|
|
514
|
-
const scaledWidth = bounds.width * uniformScale;
|
|
515
|
-
const scaledHeight = bounds.height * uniformScale;
|
|
516
|
-
item.translate(
|
|
517
|
-
new paper.Point(
|
|
518
|
-
left + (width - scaledWidth) / 2,
|
|
519
|
-
top + (height - scaledHeight) / 2
|
|
520
|
-
)
|
|
225
|
+
const maxMm = Math.max(
|
|
226
|
+
minMm,
|
|
227
|
+
Number(configService.get("size.maxMm", DEFAULT_SIZE_STATE.maxMm))
|
|
521
228
|
);
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
const { lobeSpread, notchDepth, tipSharpness } = params;
|
|
526
|
-
const halfSpread = 0.22 + lobeSpread * 0.18;
|
|
527
|
-
const notchY = 0.06 + notchDepth * 0.2;
|
|
528
|
-
const shoulderY = 0.24 + notchDepth * 0.2;
|
|
529
|
-
const topLift = 0.12 + (1 - notchDepth) * 0.06;
|
|
530
|
-
const topY = notchY - topLift;
|
|
531
|
-
const sideCtrlY = shoulderY - (0.18 - notchDepth * 0.08);
|
|
532
|
-
const lowerCtrlY = 0.58 + (1 - tipSharpness) * 0.16;
|
|
533
|
-
const tipCtrlX = 0.34 - tipSharpness * 0.2;
|
|
534
|
-
const notchCtrlX = 0.06 + lobeSpread * 0.06;
|
|
535
|
-
const lobeCtrlX = 0.1 + lobeSpread * 0.08;
|
|
536
|
-
const notchCtrlY = notchY - topLift * 0.45;
|
|
537
|
-
const xPeakL = 0.5 - halfSpread;
|
|
538
|
-
const xPeakR = 0.5 + halfSpread;
|
|
539
|
-
const heartPath = new paper.Path({ insert: false });
|
|
540
|
-
heartPath.moveTo(new paper.Point(0.5, notchY));
|
|
541
|
-
heartPath.cubicCurveTo(
|
|
542
|
-
new paper.Point(0.5 - notchCtrlX, notchCtrlY),
|
|
543
|
-
new paper.Point(xPeakL + lobeCtrlX, topY),
|
|
544
|
-
new paper.Point(xPeakL, topY)
|
|
229
|
+
const stepMm = Math.max(
|
|
230
|
+
1e-3,
|
|
231
|
+
Number(configService.get("size.stepMm", DEFAULT_SIZE_STATE.stepMm))
|
|
545
232
|
);
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
233
|
+
const actualWidthMm = sanitizeMmValue(
|
|
234
|
+
parseLengthToMm(
|
|
235
|
+
configService.get("size.actualWidthMm", DEFAULT_SIZE_STATE.actualWidthMm),
|
|
236
|
+
"mm"
|
|
237
|
+
),
|
|
238
|
+
{ minMm, maxMm, stepMm }
|
|
550
239
|
);
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
240
|
+
const actualHeightMm = sanitizeMmValue(
|
|
241
|
+
parseLengthToMm(
|
|
242
|
+
configService.get(
|
|
243
|
+
"size.actualHeightMm",
|
|
244
|
+
DEFAULT_SIZE_STATE.actualHeightMm
|
|
245
|
+
),
|
|
246
|
+
"mm"
|
|
247
|
+
),
|
|
248
|
+
{ minMm, maxMm, stepMm }
|
|
555
249
|
);
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
new paper.Point(1, lowerCtrlY),
|
|
559
|
-
new paper.Point(1, shoulderY)
|
|
250
|
+
const aspectRaw = Number(
|
|
251
|
+
configService.get("size.aspectRatio", DEFAULT_SIZE_STATE.aspectRatio)
|
|
560
252
|
);
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
253
|
+
const aspectRatio = Number.isFinite(aspectRaw) && aspectRaw > 0 ? aspectRaw : actualWidthMm / Math.max(1e-3, actualHeightMm);
|
|
254
|
+
const cutMarginMm = Math.max(
|
|
255
|
+
0,
|
|
256
|
+
parseLengthToMm(
|
|
257
|
+
configService.get("size.cutMarginMm", DEFAULT_SIZE_STATE.cutMarginMm),
|
|
258
|
+
"mm"
|
|
259
|
+
)
|
|
565
260
|
);
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
new paper.Point(0.5, notchY)
|
|
261
|
+
const viewPadding = configService.get(
|
|
262
|
+
"size.viewPadding",
|
|
263
|
+
DEFAULT_SIZE_STATE.viewPadding
|
|
570
264
|
);
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
265
|
+
return {
|
|
266
|
+
unit,
|
|
267
|
+
actualWidthMm,
|
|
268
|
+
actualHeightMm,
|
|
269
|
+
constraintMode: normalizeConstraintMode(
|
|
270
|
+
configService.get(
|
|
271
|
+
"size.constraintMode",
|
|
272
|
+
DEFAULT_SIZE_STATE.constraintMode
|
|
273
|
+
)
|
|
274
|
+
),
|
|
275
|
+
aspectRatio,
|
|
276
|
+
cutMode: normalizeCutMode(
|
|
277
|
+
configService.get("size.cutMode", DEFAULT_SIZE_STATE.cutMode)
|
|
278
|
+
),
|
|
279
|
+
cutMarginMm,
|
|
280
|
+
viewPadding,
|
|
281
|
+
minMm,
|
|
282
|
+
maxMm,
|
|
283
|
+
stepMm
|
|
284
|
+
};
|
|
584
285
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
point: [x - width / 2, y - height / 2],
|
|
590
|
-
size: [Math.max(0, width), Math.max(0, height)],
|
|
591
|
-
radius: Math.max(0, radius)
|
|
592
|
-
});
|
|
593
|
-
},
|
|
594
|
-
circle: (options) => {
|
|
595
|
-
const { x, y, width, height } = options;
|
|
596
|
-
const r = Math.min(width, height) / 2;
|
|
597
|
-
return new paper.Path.Circle({
|
|
598
|
-
center: new paper.Point(x, y),
|
|
599
|
-
radius: Math.max(0, r)
|
|
600
|
-
});
|
|
601
|
-
},
|
|
602
|
-
ellipse: (options) => {
|
|
603
|
-
const { x, y, width, height } = options;
|
|
604
|
-
return new paper.Path.Ellipse({
|
|
605
|
-
center: new paper.Point(x, y),
|
|
606
|
-
radius: [Math.max(0, width / 2), Math.max(0, height / 2)]
|
|
607
|
-
});
|
|
608
|
-
},
|
|
609
|
-
heart: createHeartBaseShape
|
|
610
|
-
};
|
|
611
|
-
function createCustomBaseShape(options) {
|
|
612
|
-
var _a;
|
|
613
|
-
const {
|
|
614
|
-
pathData,
|
|
615
|
-
customSourceWidthPx,
|
|
616
|
-
customSourceHeightPx,
|
|
617
|
-
x,
|
|
618
|
-
y,
|
|
286
|
+
function rectByCenter(centerX, centerY, width, height) {
|
|
287
|
+
return {
|
|
288
|
+
left: centerX - width / 2,
|
|
289
|
+
top: centerY - height / 2,
|
|
619
290
|
width,
|
|
620
|
-
height
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
}
|
|
625
|
-
const center = new paper.Point(x, y);
|
|
626
|
-
const hasMultipleSubPaths = ((_a = (pathData.match(/[Mm]/g) || []).length) != null ? _a : 0) > 1;
|
|
627
|
-
const path = hasMultipleSubPaths ? new paper.CompoundPath(pathData) : (() => {
|
|
628
|
-
const single = new paper.Path();
|
|
629
|
-
single.pathData = pathData;
|
|
630
|
-
return single;
|
|
631
|
-
})();
|
|
632
|
-
const sourceWidth = Number(customSourceWidthPx != null ? customSourceWidthPx : 0);
|
|
633
|
-
const sourceHeight = Number(customSourceHeightPx != null ? customSourceHeightPx : 0);
|
|
634
|
-
if (Number.isFinite(sourceWidth) && Number.isFinite(sourceHeight) && sourceWidth > 0 && sourceHeight > 0 && width > 0 && height > 0) {
|
|
635
|
-
const targetLeft = x - width / 2;
|
|
636
|
-
const targetTop = y - height / 2;
|
|
637
|
-
path.scale(width / sourceWidth, height / sourceHeight, new paper.Point(0, 0));
|
|
638
|
-
path.translate(new paper.Point(targetLeft, targetTop));
|
|
639
|
-
return path;
|
|
640
|
-
}
|
|
641
|
-
if (width > 0 && height > 0 && path.bounds.width > 0 && path.bounds.height > 0) {
|
|
642
|
-
path.position = center;
|
|
643
|
-
path.scale(width / path.bounds.width, height / path.bounds.height);
|
|
644
|
-
return path;
|
|
645
|
-
}
|
|
646
|
-
path.position = center;
|
|
647
|
-
return path;
|
|
648
|
-
}
|
|
649
|
-
function createBaseShape(options) {
|
|
650
|
-
const { shape } = options;
|
|
651
|
-
if (shape === "custom") {
|
|
652
|
-
const customShape = createCustomBaseShape(options);
|
|
653
|
-
if (customShape) return customShape;
|
|
654
|
-
return BUILTIN_SHAPE_BUILDERS[DEFAULT_DIELINE_SHAPE](options);
|
|
655
|
-
}
|
|
656
|
-
return BUILTIN_SHAPE_BUILDERS[shape](options);
|
|
291
|
+
height,
|
|
292
|
+
centerX,
|
|
293
|
+
centerY
|
|
294
|
+
};
|
|
657
295
|
}
|
|
658
|
-
function
|
|
659
|
-
if (
|
|
660
|
-
return
|
|
296
|
+
function getCutSizeMm(size) {
|
|
297
|
+
if (size.cutMode === "trim") {
|
|
298
|
+
return { widthMm: size.actualWidthMm, heightMm: size.actualHeightMm };
|
|
661
299
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
let bestDistance = Infinity;
|
|
669
|
-
for (const child of children) {
|
|
670
|
-
const location = child.getNearestLocation(anchor);
|
|
671
|
-
const point = location == null ? void 0 : location.point;
|
|
672
|
-
if (!point) continue;
|
|
673
|
-
const distance = point.getDistance(anchor);
|
|
674
|
-
if (distance < bestDistance) {
|
|
675
|
-
bestDistance = distance;
|
|
676
|
-
best = child;
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
return best;
|
|
300
|
+
const delta = size.cutMarginMm * 2;
|
|
301
|
+
if (size.cutMode === "outset") {
|
|
302
|
+
return {
|
|
303
|
+
widthMm: size.actualWidthMm + delta,
|
|
304
|
+
heightMm: size.actualHeightMm + delta
|
|
305
|
+
};
|
|
680
306
|
}
|
|
681
|
-
return
|
|
307
|
+
return {
|
|
308
|
+
widthMm: Math.max(size.minMm, size.actualWidthMm - delta),
|
|
309
|
+
heightMm: Math.max(size.minMm, size.actualHeightMm - delta)
|
|
310
|
+
};
|
|
682
311
|
}
|
|
683
|
-
function
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
radius: r
|
|
693
|
-
});
|
|
694
|
-
} else {
|
|
695
|
-
const r = feature.radius || 5;
|
|
696
|
-
item = new paper.Path.Circle({
|
|
697
|
-
center,
|
|
698
|
-
radius: r
|
|
699
|
-
});
|
|
312
|
+
function computeSceneLayout(canvasService, size) {
|
|
313
|
+
const canvasWidth = canvasService.canvas.width || 0;
|
|
314
|
+
const canvasHeight = canvasService.canvas.height || 0;
|
|
315
|
+
if (canvasWidth <= 0 || canvasHeight <= 0) return null;
|
|
316
|
+
const { widthMm: cutWidthMm, heightMm: cutHeightMm } = getCutSizeMm(size);
|
|
317
|
+
const viewWidthMm = Math.max(size.actualWidthMm, cutWidthMm);
|
|
318
|
+
const viewHeightMm = Math.max(size.actualHeightMm, cutHeightMm);
|
|
319
|
+
if (!Number.isFinite(viewWidthMm) || !Number.isFinite(viewHeightMm) || viewWidthMm <= 0 || viewHeightMm <= 0) {
|
|
320
|
+
return null;
|
|
700
321
|
}
|
|
701
|
-
|
|
702
|
-
|
|
322
|
+
const paddingPx = resolvePaddingPx(
|
|
323
|
+
size.viewPadding,
|
|
324
|
+
canvasWidth,
|
|
325
|
+
canvasHeight
|
|
326
|
+
);
|
|
327
|
+
canvasService.viewport.updateContainer(canvasWidth, canvasHeight);
|
|
328
|
+
canvasService.viewport.setPadding(paddingPx);
|
|
329
|
+
canvasService.viewport.updatePhysical(viewWidthMm, viewHeightMm);
|
|
330
|
+
const layout = canvasService.viewport.layout;
|
|
331
|
+
if (!Number.isFinite(layout.scale) || !Number.isFinite(layout.offsetX) || !Number.isFinite(layout.offsetY) || layout.scale <= 0) {
|
|
332
|
+
return null;
|
|
703
333
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
const xRight = itemBounds.right - inset;
|
|
733
|
-
const bridgeBasePath = resolveBridgeBasePath(mainShape, center);
|
|
734
|
-
const canBridge = !!bridgeBasePath && xRight - xLeft > eps;
|
|
735
|
-
if (canBridge && bridgeBasePath) {
|
|
736
|
-
const leftHit = getExitHit({
|
|
737
|
-
mainShape: bridgeBasePath,
|
|
738
|
-
x: xLeft,
|
|
739
|
-
bridgeBottom,
|
|
740
|
-
toY,
|
|
741
|
-
eps,
|
|
742
|
-
delta,
|
|
743
|
-
overlap,
|
|
744
|
-
op: f.operation
|
|
745
|
-
});
|
|
746
|
-
const rightHit = getExitHit({
|
|
747
|
-
mainShape: bridgeBasePath,
|
|
748
|
-
x: xRight,
|
|
749
|
-
bridgeBottom,
|
|
750
|
-
toY,
|
|
751
|
-
eps,
|
|
752
|
-
delta,
|
|
753
|
-
overlap,
|
|
754
|
-
op: f.operation
|
|
755
|
-
});
|
|
756
|
-
if (leftHit && rightHit) {
|
|
757
|
-
const pathLength = bridgeBasePath.length;
|
|
758
|
-
const leftOffset = leftHit.location.offset;
|
|
759
|
-
const rightOffset = rightHit.location.offset;
|
|
760
|
-
const distanceA = wrappedDistance(pathLength, leftOffset, rightOffset);
|
|
761
|
-
const distanceB = wrappedDistance(pathLength, rightOffset, leftOffset);
|
|
762
|
-
const countFor = (d) => Math.max(8, Math.min(80, Math.ceil(d / 6)));
|
|
763
|
-
const offsetsA = sampleWrappedOffsets(
|
|
764
|
-
pathLength,
|
|
765
|
-
leftOffset,
|
|
766
|
-
rightOffset,
|
|
767
|
-
countFor(distanceA)
|
|
768
|
-
);
|
|
769
|
-
const offsetsB = sampleWrappedOffsets(
|
|
770
|
-
pathLength,
|
|
771
|
-
rightOffset,
|
|
772
|
-
leftOffset,
|
|
773
|
-
countFor(distanceB)
|
|
774
|
-
);
|
|
775
|
-
const pointsA = offsetsA.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
776
|
-
const pointsB = offsetsB.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
777
|
-
if (pointsA.length >= 2 && pointsB.length >= 2) {
|
|
778
|
-
let topBase = selectOuterChain({
|
|
779
|
-
mainShape: bridgeBasePath,
|
|
780
|
-
pointsA,
|
|
781
|
-
pointsB,
|
|
782
|
-
delta,
|
|
783
|
-
overlap,
|
|
784
|
-
op: f.operation
|
|
785
|
-
});
|
|
786
|
-
const dist2 = (a, b) => {
|
|
787
|
-
const dx = a.x - b.x;
|
|
788
|
-
const dy = a.y - b.y;
|
|
789
|
-
return dx * dx + dy * dy;
|
|
790
|
-
};
|
|
791
|
-
if (dist2(topBase[0], leftHit.point) > dist2(topBase[0], rightHit.point)) {
|
|
792
|
-
topBase = topBase.slice().reverse();
|
|
793
|
-
}
|
|
794
|
-
topBase = topBase.slice();
|
|
795
|
-
topBase[0] = leftHit.point;
|
|
796
|
-
topBase[topBase.length - 1] = rightHit.point;
|
|
797
|
-
const capShiftY = f.operation === "subtract" ? -Math.max(overlap * 2, delta) : overlap;
|
|
798
|
-
const topPoints = topBase.map(
|
|
799
|
-
(p) => p.add(new paper.Point(0, capShiftY))
|
|
800
|
-
);
|
|
801
|
-
const bridgeBottomY = bridgeBottom + overlap * 2;
|
|
802
|
-
const bridgePoly = new paper.Path({ insert: false });
|
|
803
|
-
for (const p of topPoints) bridgePoly.add(p);
|
|
804
|
-
bridgePoly.add(new paper.Point(xRight, bridgeBottomY));
|
|
805
|
-
bridgePoly.add(new paper.Point(xLeft, bridgeBottomY));
|
|
806
|
-
bridgePoly.closed = true;
|
|
807
|
-
const unitedItem = item.unite(bridgePoly);
|
|
808
|
-
item.remove();
|
|
809
|
-
bridgePoly.remove();
|
|
810
|
-
if (f.operation === "add") {
|
|
811
|
-
adds.push(unitedItem);
|
|
812
|
-
} else {
|
|
813
|
-
subtracts.push(unitedItem);
|
|
814
|
-
}
|
|
815
|
-
return;
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
if (f.operation === "add") {
|
|
820
|
-
adds.push(item);
|
|
821
|
-
} else {
|
|
822
|
-
subtracts.push(item);
|
|
823
|
-
}
|
|
824
|
-
} else {
|
|
825
|
-
if (f.operation === "add") {
|
|
826
|
-
adds.push(item);
|
|
827
|
-
} else {
|
|
828
|
-
subtracts.push(item);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
} else {
|
|
832
|
-
if (f.operation === "add") {
|
|
833
|
-
adds.push(item);
|
|
834
|
-
} else {
|
|
835
|
-
subtracts.push(item);
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
});
|
|
839
|
-
if (adds.length > 0) {
|
|
840
|
-
for (const item of adds) {
|
|
841
|
-
try {
|
|
842
|
-
const temp = mainShape.unite(item);
|
|
843
|
-
mainShape.remove();
|
|
844
|
-
item.remove();
|
|
845
|
-
mainShape = normalizePathItem(temp);
|
|
846
|
-
} catch (e) {
|
|
847
|
-
console.error("Geometry: Failed to unite feature", e);
|
|
848
|
-
item.remove();
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
if (subtracts.length > 0) {
|
|
853
|
-
for (const item of subtracts) {
|
|
854
|
-
try {
|
|
855
|
-
const temp = mainShape.subtract(item);
|
|
856
|
-
mainShape.remove();
|
|
857
|
-
item.remove();
|
|
858
|
-
mainShape = normalizePathItem(temp);
|
|
859
|
-
} catch (e) {
|
|
860
|
-
console.error("Geometry: Failed to subtract feature", e);
|
|
861
|
-
item.remove();
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
return mainShape;
|
|
334
|
+
const centerX = layout.offsetX + layout.width / 2;
|
|
335
|
+
const centerY = layout.offsetY + layout.height / 2;
|
|
336
|
+
const trimWidthPx = size.actualWidthMm * layout.scale;
|
|
337
|
+
const trimHeightPx = size.actualHeightMm * layout.scale;
|
|
338
|
+
const cutWidthPx = cutWidthMm * layout.scale;
|
|
339
|
+
const cutHeightPx = cutHeightMm * layout.scale;
|
|
340
|
+
const trimRect = rectByCenter(centerX, centerY, trimWidthPx, trimHeightPx);
|
|
341
|
+
const cutRect = rectByCenter(centerX, centerY, cutWidthPx, cutHeightPx);
|
|
342
|
+
const bleedRect = rectByCenter(
|
|
343
|
+
centerX,
|
|
344
|
+
centerY,
|
|
345
|
+
Math.max(trimWidthPx, cutWidthPx),
|
|
346
|
+
Math.max(trimHeightPx, cutHeightPx)
|
|
347
|
+
);
|
|
348
|
+
return {
|
|
349
|
+
scale: layout.scale,
|
|
350
|
+
canvasWidth,
|
|
351
|
+
canvasHeight,
|
|
352
|
+
trimRect,
|
|
353
|
+
cutRect,
|
|
354
|
+
bleedRect,
|
|
355
|
+
trimWidthMm: size.actualWidthMm,
|
|
356
|
+
trimHeightMm: size.actualHeightMm,
|
|
357
|
+
cutWidthMm,
|
|
358
|
+
cutHeightMm,
|
|
359
|
+
cutMode: size.cutMode,
|
|
360
|
+
cutMarginMm: size.cutMarginMm
|
|
361
|
+
};
|
|
867
362
|
}
|
|
868
|
-
function
|
|
869
|
-
const
|
|
870
|
-
(
|
|
363
|
+
function buildSceneGeometry(configService, layout) {
|
|
364
|
+
const radiusMm = parseLengthToMm(
|
|
365
|
+
configService.get("dieline.radius", 0),
|
|
366
|
+
"mm"
|
|
871
367
|
);
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
368
|
+
const offset = (layout.cutRect.width - layout.trimRect.width) / 2;
|
|
369
|
+
const sourceWidth = Number(configService.get("dieline.customSourceWidthPx", 0));
|
|
370
|
+
const sourceHeight = Number(
|
|
371
|
+
configService.get("dieline.customSourceHeightPx", 0)
|
|
372
|
+
);
|
|
373
|
+
const shapeStyle = normalizeShapeStyle(
|
|
374
|
+
configService.get("dieline.shapeStyle", DEFAULT_DIELINE_SHAPE_STYLE)
|
|
375
|
+
);
|
|
376
|
+
return {
|
|
377
|
+
shape: normalizeDielineShape(
|
|
378
|
+
configService.get("dieline.shape", DEFAULT_DIELINE_SHAPE)
|
|
379
|
+
),
|
|
380
|
+
shapeStyle,
|
|
381
|
+
unit: "px",
|
|
382
|
+
x: layout.trimRect.centerX,
|
|
383
|
+
y: layout.trimRect.centerY,
|
|
384
|
+
width: layout.trimRect.width,
|
|
385
|
+
height: layout.trimRect.height,
|
|
386
|
+
radius: radiusMm * layout.scale,
|
|
387
|
+
offset,
|
|
388
|
+
scale: layout.scale,
|
|
389
|
+
pathData: configService.get("dieline.pathData"),
|
|
390
|
+
customSourceWidthPx: Number.isFinite(sourceWidth) && sourceWidth > 0 ? sourceWidth : void 0,
|
|
391
|
+
customSourceHeightPx: Number.isFinite(sourceHeight) && sourceHeight > 0 ? sourceHeight : void 0
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/extensions/background.ts
|
|
396
|
+
var BACKGROUND_LAYER_ID = "background";
|
|
397
|
+
var BACKGROUND_CONFIG_KEY = "background.config";
|
|
398
|
+
var DEFAULT_WIDTH = 800;
|
|
399
|
+
var DEFAULT_HEIGHT = 600;
|
|
400
|
+
var DEFAULT_BACKGROUND_CONFIG = {
|
|
401
|
+
version: 1,
|
|
402
|
+
layers: [
|
|
403
|
+
{
|
|
404
|
+
id: "base-color",
|
|
405
|
+
kind: "color",
|
|
406
|
+
anchor: "viewport",
|
|
407
|
+
fit: "cover",
|
|
408
|
+
opacity: 1,
|
|
409
|
+
order: 0,
|
|
410
|
+
enabled: true,
|
|
411
|
+
exportable: false,
|
|
412
|
+
color: "#fff"
|
|
893
413
|
}
|
|
414
|
+
]
|
|
415
|
+
};
|
|
416
|
+
function clampOpacity(value, fallback) {
|
|
417
|
+
const numeric = Number(value);
|
|
418
|
+
if (!Number.isFinite(numeric)) {
|
|
419
|
+
return Math.max(0, Math.min(1, fallback));
|
|
894
420
|
}
|
|
895
|
-
return
|
|
421
|
+
return Math.max(0, Math.min(1, numeric));
|
|
896
422
|
}
|
|
897
|
-
function
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
const perimeter = getPerimeterShape(options);
|
|
903
|
-
const finalShape = applySurfaceFeatures(perimeter, options.features, options);
|
|
904
|
-
const pathData = finalShape.pathData;
|
|
905
|
-
finalShape.remove();
|
|
906
|
-
return pathData;
|
|
423
|
+
function normalizeLayerKind(value, fallback) {
|
|
424
|
+
if (value === "color" || value === "image") {
|
|
425
|
+
return value;
|
|
426
|
+
}
|
|
427
|
+
return fallback;
|
|
907
428
|
}
|
|
908
|
-
function
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
const { canvasWidth, canvasHeight } = options;
|
|
912
|
-
const maskRect = new paper.Path.Rectangle({
|
|
913
|
-
point: [0, 0],
|
|
914
|
-
size: [canvasWidth, canvasHeight]
|
|
915
|
-
});
|
|
916
|
-
const perimeter = getPerimeterShape(options);
|
|
917
|
-
const mainShape = applySurfaceFeatures(perimeter, options.features, options);
|
|
918
|
-
const finalMask = maskRect.subtract(mainShape);
|
|
919
|
-
maskRect.remove();
|
|
920
|
-
mainShape.remove();
|
|
921
|
-
const pathData = finalMask.pathData;
|
|
922
|
-
finalMask.remove();
|
|
923
|
-
return pathData;
|
|
924
|
-
}
|
|
925
|
-
function generateBleedZonePath(originalOptions, offsetOptions, offset) {
|
|
926
|
-
const paperWidth = originalOptions.canvasWidth || originalOptions.width * 2 || 2e3;
|
|
927
|
-
const paperHeight = originalOptions.canvasHeight || originalOptions.height * 2 || 2e3;
|
|
928
|
-
ensurePaper(paperWidth, paperHeight);
|
|
929
|
-
paper.project.activeLayer.removeChildren();
|
|
930
|
-
const pOriginal = getPerimeterShape(originalOptions);
|
|
931
|
-
const shapeOriginal = applySurfaceFeatures(
|
|
932
|
-
pOriginal,
|
|
933
|
-
originalOptions.features,
|
|
934
|
-
originalOptions
|
|
935
|
-
);
|
|
936
|
-
const pOffset = getPerimeterShape(offsetOptions);
|
|
937
|
-
const shapeOffset = applySurfaceFeatures(
|
|
938
|
-
pOffset,
|
|
939
|
-
offsetOptions.features,
|
|
940
|
-
offsetOptions
|
|
941
|
-
);
|
|
942
|
-
let bleedZone;
|
|
943
|
-
if (offset > 0) {
|
|
944
|
-
bleedZone = shapeOffset.subtract(shapeOriginal);
|
|
945
|
-
} else {
|
|
946
|
-
bleedZone = shapeOriginal.subtract(shapeOffset);
|
|
429
|
+
function normalizeFitMode2(value, fallback) {
|
|
430
|
+
if (value === "contain" || value === "cover" || value === "stretch") {
|
|
431
|
+
return value;
|
|
947
432
|
}
|
|
948
|
-
|
|
949
|
-
shapeOriginal.remove();
|
|
950
|
-
shapeOffset.remove();
|
|
951
|
-
bleedZone.remove();
|
|
952
|
-
return pathData;
|
|
433
|
+
return fallback;
|
|
953
434
|
}
|
|
954
|
-
function
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
435
|
+
function normalizeAnchor(value, fallback) {
|
|
436
|
+
if (typeof value !== "string") return fallback;
|
|
437
|
+
const trimmed = value.trim();
|
|
438
|
+
return trimmed || fallback;
|
|
439
|
+
}
|
|
440
|
+
function normalizeOrder(value, fallback) {
|
|
441
|
+
const numeric = Number(value);
|
|
442
|
+
if (!Number.isFinite(numeric)) return fallback;
|
|
443
|
+
return numeric;
|
|
444
|
+
}
|
|
445
|
+
function normalizeLayer(raw, index, fallback) {
|
|
446
|
+
const fallbackLayer = fallback || {
|
|
447
|
+
id: `layer-${index + 1}`,
|
|
448
|
+
kind: "image",
|
|
449
|
+
anchor: "viewport",
|
|
450
|
+
fit: "contain",
|
|
451
|
+
opacity: 1,
|
|
452
|
+
order: index,
|
|
453
|
+
enabled: true,
|
|
454
|
+
exportable: false,
|
|
455
|
+
src: ""
|
|
456
|
+
};
|
|
457
|
+
if (!raw || typeof raw !== "object") {
|
|
458
|
+
return { ...fallbackLayer };
|
|
459
|
+
}
|
|
460
|
+
const input = raw;
|
|
461
|
+
const kind = normalizeLayerKind(input.kind, fallbackLayer.kind);
|
|
462
|
+
return {
|
|
463
|
+
id: typeof input.id === "string" && input.id.trim().length > 0 ? input.id.trim() : fallbackLayer.id,
|
|
464
|
+
kind,
|
|
465
|
+
anchor: normalizeAnchor(input.anchor, fallbackLayer.anchor),
|
|
466
|
+
fit: normalizeFitMode2(input.fit, fallbackLayer.fit),
|
|
467
|
+
opacity: clampOpacity(input.opacity, fallbackLayer.opacity),
|
|
468
|
+
order: normalizeOrder(input.order, fallbackLayer.order),
|
|
469
|
+
enabled: typeof input.enabled === "boolean" ? input.enabled : fallbackLayer.enabled,
|
|
470
|
+
exportable: typeof input.exportable === "boolean" ? input.exportable : fallbackLayer.exportable,
|
|
471
|
+
color: kind === "color" ? typeof input.color === "string" ? input.color : typeof fallbackLayer.color === "string" ? fallbackLayer.color : "#ffffff" : void 0,
|
|
472
|
+
src: kind === "image" ? typeof input.src === "string" ? input.src.trim() : typeof fallbackLayer.src === "string" ? fallbackLayer.src : "" : void 0
|
|
962
473
|
};
|
|
963
|
-
shape.remove();
|
|
964
|
-
return result;
|
|
965
474
|
}
|
|
966
|
-
function
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
const
|
|
971
|
-
const
|
|
972
|
-
const
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
475
|
+
function normalizeConfig(raw) {
|
|
476
|
+
if (!raw || typeof raw !== "object") {
|
|
477
|
+
return cloneConfig(DEFAULT_BACKGROUND_CONFIG);
|
|
478
|
+
}
|
|
479
|
+
const input = raw;
|
|
480
|
+
const version = Number.isFinite(Number(input.version)) ? Number(input.version) : DEFAULT_BACKGROUND_CONFIG.version;
|
|
481
|
+
const baseLayers = Array.isArray(input.layers) ? input.layers.map((layer, index) => normalizeLayer(layer, index)) : cloneConfig(DEFAULT_BACKGROUND_CONFIG).layers;
|
|
482
|
+
const uniqueLayers = [];
|
|
483
|
+
const seen = /* @__PURE__ */ new Set();
|
|
484
|
+
baseLayers.forEach((layer, index) => {
|
|
485
|
+
let nextId = layer.id || `layer-${index + 1}`;
|
|
486
|
+
let serial = 1;
|
|
487
|
+
while (seen.has(nextId)) {
|
|
488
|
+
serial += 1;
|
|
489
|
+
nextId = `${layer.id || `layer-${index + 1}`}-${serial}`;
|
|
490
|
+
}
|
|
491
|
+
seen.add(nextId);
|
|
492
|
+
uniqueLayers.push({ ...layer, id: nextId });
|
|
493
|
+
});
|
|
494
|
+
return {
|
|
495
|
+
version,
|
|
496
|
+
layers: uniqueLayers
|
|
976
497
|
};
|
|
977
|
-
shape.remove();
|
|
978
|
-
return result;
|
|
979
498
|
}
|
|
980
|
-
function
|
|
981
|
-
const path = new paper.Path();
|
|
982
|
-
path.pathData = pathData;
|
|
983
|
-
const bounds = path.bounds;
|
|
984
|
-
path.remove();
|
|
499
|
+
function cloneConfig(config) {
|
|
985
500
|
return {
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
width: bounds.width,
|
|
989
|
-
height: bounds.height
|
|
501
|
+
version: config.version,
|
|
502
|
+
layers: (config.layers || []).map((layer) => ({ ...layer }))
|
|
990
503
|
};
|
|
991
504
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
505
|
+
function mergeConfig(base, patch) {
|
|
506
|
+
const merged = {
|
|
507
|
+
version: patch.version === void 0 ? base.version : Number.isFinite(Number(patch.version)) ? Number(patch.version) : base.version,
|
|
508
|
+
layers: Array.isArray(patch.layers) ? patch.layers.map((layer, index) => normalizeLayer(layer, index)) : base.layers.map((layer) => ({ ...layer }))
|
|
509
|
+
};
|
|
510
|
+
return normalizeConfig(merged);
|
|
511
|
+
}
|
|
512
|
+
function configSignature(config) {
|
|
513
|
+
return JSON.stringify(config);
|
|
514
|
+
}
|
|
515
|
+
var BackgroundTool = class {
|
|
516
|
+
constructor(options) {
|
|
517
|
+
this.id = "pooder.kit.background";
|
|
518
|
+
this.metadata = {
|
|
519
|
+
name: "BackgroundTool"
|
|
520
|
+
};
|
|
521
|
+
this.config = cloneConfig(DEFAULT_BACKGROUND_CONFIG);
|
|
522
|
+
this.specs = [];
|
|
523
|
+
this.renderSeq = 0;
|
|
524
|
+
this.latestSceneLayout = null;
|
|
525
|
+
this.sourceSizeBySrc = /* @__PURE__ */ new Map();
|
|
526
|
+
this.pendingSizeBySrc = /* @__PURE__ */ new Map();
|
|
527
|
+
this.onCanvasResized = () => {
|
|
528
|
+
this.latestSceneLayout = null;
|
|
529
|
+
this.updateBackground();
|
|
530
|
+
};
|
|
531
|
+
this.onSceneLayoutChanged = (layout) => {
|
|
532
|
+
this.latestSceneLayout = layout;
|
|
533
|
+
this.updateBackground();
|
|
534
|
+
};
|
|
535
|
+
if (options && typeof options === "object") {
|
|
536
|
+
this.config = mergeConfig(this.config, options);
|
|
1003
537
|
}
|
|
1004
|
-
const scaleX = availableWidth / content.width;
|
|
1005
|
-
const scaleY = availableHeight / content.height;
|
|
1006
|
-
const scale = Math.min(scaleX, scaleY);
|
|
1007
|
-
const width = content.width * scale;
|
|
1008
|
-
const height = content.height * scale;
|
|
1009
|
-
const offsetX = (container.width - width) / 2;
|
|
1010
|
-
const offsetY = (container.height - height) / 2;
|
|
1011
|
-
return { scale, offsetX, offsetY, width, height };
|
|
1012
538
|
}
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
539
|
+
activate(context) {
|
|
540
|
+
var _a, _b;
|
|
541
|
+
this.canvasService = context.services.get("CanvasService");
|
|
542
|
+
if (!this.canvasService) {
|
|
543
|
+
console.warn("CanvasService not found for BackgroundTool");
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
this.configService = context.services.get(
|
|
547
|
+
"ConfigurationService"
|
|
548
|
+
);
|
|
549
|
+
if (this.configService) {
|
|
550
|
+
this.config = normalizeConfig(
|
|
551
|
+
this.configService.get(
|
|
552
|
+
BACKGROUND_CONFIG_KEY,
|
|
553
|
+
DEFAULT_BACKGROUND_CONFIG
|
|
554
|
+
)
|
|
555
|
+
);
|
|
556
|
+
(_a = this.configChangeDisposable) == null ? void 0 : _a.dispose();
|
|
557
|
+
this.configChangeDisposable = this.configService.onAnyChange(
|
|
558
|
+
(e) => {
|
|
559
|
+
if (e.key === BACKGROUND_CONFIG_KEY) {
|
|
560
|
+
this.config = normalizeConfig(e.value);
|
|
561
|
+
this.updateBackground();
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
if (e.key.startsWith("size.")) {
|
|
565
|
+
this.latestSceneLayout = null;
|
|
566
|
+
this.updateBackground();
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
572
|
+
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
573
|
+
this.id,
|
|
574
|
+
() => ({
|
|
575
|
+
passes: [
|
|
576
|
+
{
|
|
577
|
+
id: BACKGROUND_LAYER_ID,
|
|
578
|
+
stack: 0,
|
|
579
|
+
order: 0,
|
|
580
|
+
objects: this.specs
|
|
581
|
+
}
|
|
582
|
+
]
|
|
583
|
+
}),
|
|
584
|
+
{ priority: 0 }
|
|
585
|
+
);
|
|
586
|
+
context.eventBus.on("canvas:resized", this.onCanvasResized);
|
|
587
|
+
context.eventBus.on("scene:layout:change", this.onSceneLayoutChanged);
|
|
588
|
+
this.updateBackground();
|
|
1020
589
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
590
|
+
deactivate(context) {
|
|
591
|
+
var _a, _b;
|
|
592
|
+
context.eventBus.off("canvas:resized", this.onCanvasResized);
|
|
593
|
+
context.eventBus.off("scene:layout:change", this.onSceneLayoutChanged);
|
|
594
|
+
this.renderSeq += 1;
|
|
595
|
+
this.specs = [];
|
|
596
|
+
this.latestSceneLayout = null;
|
|
597
|
+
(_a = this.configChangeDisposable) == null ? void 0 : _a.dispose();
|
|
598
|
+
this.configChangeDisposable = void 0;
|
|
599
|
+
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
600
|
+
this.renderProducerDisposable = void 0;
|
|
601
|
+
if (!this.canvasService) return;
|
|
602
|
+
void this.canvasService.flushRenderFromProducers();
|
|
603
|
+
this.canvasService.requestRenderAll();
|
|
604
|
+
this.canvasService = void 0;
|
|
605
|
+
this.configService = void 0;
|
|
1028
606
|
}
|
|
1029
|
-
|
|
1030
|
-
* Normalize a point's coordinates.
|
|
1031
|
-
*/
|
|
1032
|
-
static normalizePoint(point, size) {
|
|
607
|
+
contribute() {
|
|
1033
608
|
return {
|
|
1034
|
-
|
|
1035
|
-
|
|
609
|
+
[ContributionPointIds.CONFIGURATIONS]: [
|
|
610
|
+
{
|
|
611
|
+
id: BACKGROUND_CONFIG_KEY,
|
|
612
|
+
type: "json",
|
|
613
|
+
label: "Background Config",
|
|
614
|
+
default: cloneConfig(DEFAULT_BACKGROUND_CONFIG)
|
|
615
|
+
}
|
|
616
|
+
],
|
|
617
|
+
[ContributionPointIds.COMMANDS]: [
|
|
618
|
+
{
|
|
619
|
+
command: "background.getConfig",
|
|
620
|
+
title: "Get Background Config",
|
|
621
|
+
handler: () => cloneConfig(this.config)
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
command: "background.resetConfig",
|
|
625
|
+
title: "Reset Background Config",
|
|
626
|
+
handler: () => {
|
|
627
|
+
this.commitConfig(cloneConfig(DEFAULT_BACKGROUND_CONFIG));
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
command: "background.replaceConfig",
|
|
633
|
+
title: "Replace Background Config",
|
|
634
|
+
handler: (config) => {
|
|
635
|
+
this.commitConfig(normalizeConfig(config));
|
|
636
|
+
return true;
|
|
637
|
+
}
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
command: "background.patchConfig",
|
|
641
|
+
title: "Patch Background Config",
|
|
642
|
+
handler: (patch) => {
|
|
643
|
+
this.commitConfig(mergeConfig(this.config, patch || {}));
|
|
644
|
+
return true;
|
|
645
|
+
}
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
command: "background.upsertLayer",
|
|
649
|
+
title: "Upsert Background Layer",
|
|
650
|
+
handler: (layer) => {
|
|
651
|
+
const normalized = normalizeLayer(layer, 0);
|
|
652
|
+
const existingIndex = this.config.layers.findIndex(
|
|
653
|
+
(item) => item.id === normalized.id
|
|
654
|
+
);
|
|
655
|
+
const nextLayers = [...this.config.layers];
|
|
656
|
+
if (existingIndex >= 0) {
|
|
657
|
+
nextLayers[existingIndex] = normalizeLayer(
|
|
658
|
+
{ ...nextLayers[existingIndex], ...layer },
|
|
659
|
+
existingIndex,
|
|
660
|
+
nextLayers[existingIndex]
|
|
661
|
+
);
|
|
662
|
+
} else {
|
|
663
|
+
nextLayers.push(
|
|
664
|
+
normalizeLayer(
|
|
665
|
+
{
|
|
666
|
+
...normalized,
|
|
667
|
+
order: Number.isFinite(Number(layer.order)) ? Number(layer.order) : nextLayers.length
|
|
668
|
+
},
|
|
669
|
+
nextLayers.length
|
|
670
|
+
)
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
this.commitConfig(
|
|
674
|
+
normalizeConfig({
|
|
675
|
+
...this.config,
|
|
676
|
+
layers: nextLayers
|
|
677
|
+
})
|
|
678
|
+
);
|
|
679
|
+
return true;
|
|
680
|
+
}
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
command: "background.removeLayer",
|
|
684
|
+
title: "Remove Background Layer",
|
|
685
|
+
handler: (id) => {
|
|
686
|
+
const nextLayers = this.config.layers.filter(
|
|
687
|
+
(layer) => layer.id !== id
|
|
688
|
+
);
|
|
689
|
+
this.commitConfig(
|
|
690
|
+
normalizeConfig({
|
|
691
|
+
...this.config,
|
|
692
|
+
layers: nextLayers
|
|
693
|
+
})
|
|
694
|
+
);
|
|
695
|
+
return true;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
]
|
|
1036
699
|
};
|
|
1037
700
|
}
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
701
|
+
commitConfig(next) {
|
|
702
|
+
const normalized = normalizeConfig(next);
|
|
703
|
+
if (configSignature(normalized) === configSignature(this.config)) {
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
if (this.configService) {
|
|
707
|
+
this.configService.update(BACKGROUND_CONFIG_KEY, cloneConfig(normalized));
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
this.config = normalized;
|
|
711
|
+
this.updateBackground();
|
|
712
|
+
}
|
|
713
|
+
getViewportRect() {
|
|
714
|
+
var _a, _b;
|
|
715
|
+
const width = Number(((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 0);
|
|
716
|
+
const height = Number(((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 0);
|
|
1042
717
|
return {
|
|
1043
|
-
|
|
1044
|
-
|
|
718
|
+
left: 0,
|
|
719
|
+
top: 0,
|
|
720
|
+
width: width > 0 ? width : DEFAULT_WIDTH,
|
|
721
|
+
height: height > 0 ? height : DEFAULT_HEIGHT
|
|
1045
722
|
};
|
|
1046
723
|
}
|
|
1047
|
-
|
|
1048
|
-
if (
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
724
|
+
resolveSceneLayout() {
|
|
725
|
+
if (this.latestSceneLayout) return this.latestSceneLayout;
|
|
726
|
+
if (!this.canvasService || !this.configService) return null;
|
|
727
|
+
const layout = computeSceneLayout(
|
|
728
|
+
this.canvasService,
|
|
729
|
+
readSizeState(this.configService)
|
|
730
|
+
);
|
|
731
|
+
this.latestSceneLayout = layout;
|
|
732
|
+
return layout;
|
|
733
|
+
}
|
|
734
|
+
resolveFocusRect() {
|
|
735
|
+
const layout = this.resolveSceneLayout();
|
|
736
|
+
if (!layout) return null;
|
|
737
|
+
return {
|
|
738
|
+
left: layout.trimRect.left,
|
|
739
|
+
top: layout.trimRect.top,
|
|
740
|
+
width: layout.trimRect.width,
|
|
741
|
+
height: layout.trimRect.height
|
|
1055
742
|
};
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
743
|
+
}
|
|
744
|
+
resolveAnchorRect(anchor) {
|
|
745
|
+
if (anchor === "focus") {
|
|
746
|
+
return this.resolveFocusRect() || this.getViewportRect();
|
|
1059
747
|
}
|
|
1060
|
-
|
|
748
|
+
if (anchor !== "viewport") {
|
|
749
|
+
return this.getViewportRect();
|
|
750
|
+
}
|
|
751
|
+
return this.getViewportRect();
|
|
1061
752
|
}
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
753
|
+
resolveImagePlacement(target, sourceSize, fit) {
|
|
754
|
+
const targetWidth = Math.max(1, Number(target.width || 0));
|
|
755
|
+
const targetHeight = Math.max(1, Number(target.height || 0));
|
|
756
|
+
const sourceWidth = Math.max(1, Number(sourceSize.width || 0));
|
|
757
|
+
const sourceHeight = Math.max(1, Number(sourceSize.height || 0));
|
|
758
|
+
if (fit === "stretch") {
|
|
759
|
+
return {
|
|
760
|
+
left: target.left,
|
|
761
|
+
top: target.top,
|
|
762
|
+
scaleX: targetWidth / sourceWidth,
|
|
763
|
+
scaleY: targetHeight / sourceHeight
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
const scale = fit === "contain" ? Math.min(targetWidth / sourceWidth, targetHeight / sourceHeight) : Math.max(targetWidth / sourceWidth, targetHeight / sourceHeight);
|
|
767
|
+
const renderWidth = sourceWidth * scale;
|
|
768
|
+
const renderHeight = sourceHeight * scale;
|
|
769
|
+
return {
|
|
770
|
+
left: target.left + (targetWidth - renderWidth) / 2,
|
|
771
|
+
top: target.top + (targetHeight - renderHeight) / 2,
|
|
772
|
+
scaleX: scale,
|
|
773
|
+
scaleY: scale
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
buildColorLayerSpec(layer) {
|
|
777
|
+
const rect = this.resolveAnchorRect(layer.anchor);
|
|
778
|
+
return {
|
|
779
|
+
id: `background.layer.${layer.id}.color`,
|
|
780
|
+
type: "rect",
|
|
781
|
+
space: "screen",
|
|
782
|
+
data: {
|
|
783
|
+
id: `background.layer.${layer.id}.color`,
|
|
784
|
+
layerId: BACKGROUND_LAYER_ID,
|
|
785
|
+
type: "background-layer",
|
|
786
|
+
layerRef: layer.id,
|
|
787
|
+
layerKind: layer.kind
|
|
788
|
+
},
|
|
789
|
+
props: {
|
|
790
|
+
left: rect.left,
|
|
791
|
+
top: rect.top,
|
|
792
|
+
width: rect.width,
|
|
793
|
+
height: rect.height,
|
|
794
|
+
originX: "left",
|
|
795
|
+
originY: "top",
|
|
796
|
+
fill: layer.color || "transparent",
|
|
797
|
+
opacity: layer.opacity,
|
|
798
|
+
selectable: false,
|
|
799
|
+
evented: false,
|
|
800
|
+
excludeFromExport: !layer.exportable
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
buildImageLayerSpec(layer) {
|
|
805
|
+
const src = String(layer.src || "").trim();
|
|
806
|
+
if (!src) return [];
|
|
807
|
+
const sourceSize = this.sourceSizeBySrc.get(src);
|
|
808
|
+
if (!sourceSize) return [];
|
|
809
|
+
const rect = this.resolveAnchorRect(layer.anchor);
|
|
810
|
+
const placement = this.resolveImagePlacement(rect, sourceSize, layer.fit);
|
|
811
|
+
return [
|
|
812
|
+
{
|
|
813
|
+
id: `background.layer.${layer.id}.image`,
|
|
814
|
+
type: "image",
|
|
815
|
+
src,
|
|
816
|
+
space: "screen",
|
|
817
|
+
data: {
|
|
818
|
+
id: `background.layer.${layer.id}.image`,
|
|
819
|
+
layerId: BACKGROUND_LAYER_ID,
|
|
820
|
+
type: "background-layer",
|
|
821
|
+
layerRef: layer.id,
|
|
822
|
+
layerKind: layer.kind
|
|
823
|
+
},
|
|
824
|
+
props: {
|
|
825
|
+
left: placement.left,
|
|
826
|
+
top: placement.top,
|
|
827
|
+
originX: "left",
|
|
828
|
+
originY: "top",
|
|
829
|
+
scaleX: placement.scaleX,
|
|
830
|
+
scaleY: placement.scaleY,
|
|
831
|
+
opacity: layer.opacity,
|
|
832
|
+
selectable: false,
|
|
833
|
+
evented: false,
|
|
834
|
+
excludeFromExport: !layer.exportable
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
];
|
|
838
|
+
}
|
|
839
|
+
buildBackgroundSpecs(config) {
|
|
840
|
+
const activeLayers = (config.layers || []).filter((layer) => layer.enabled).map((layer, index) => ({ layer, index })).sort((a, b) => {
|
|
841
|
+
if (a.layer.order !== b.layer.order) {
|
|
842
|
+
return a.layer.order - b.layer.order;
|
|
843
|
+
}
|
|
844
|
+
return a.index - b.index;
|
|
845
|
+
});
|
|
846
|
+
const specs = [];
|
|
847
|
+
activeLayers.forEach(({ layer }) => {
|
|
848
|
+
if (layer.kind === "color") {
|
|
849
|
+
specs.push(this.buildColorLayerSpec(layer));
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
specs.push(...this.buildImageLayerSpec(layer));
|
|
853
|
+
});
|
|
854
|
+
return specs;
|
|
855
|
+
}
|
|
856
|
+
collectActiveImageUrls(config) {
|
|
857
|
+
const urls = /* @__PURE__ */ new Set();
|
|
858
|
+
(config.layers || []).forEach((layer) => {
|
|
859
|
+
if (!layer.enabled || layer.kind !== "image") return;
|
|
860
|
+
const src = String(layer.src || "").trim();
|
|
861
|
+
if (!src) return;
|
|
862
|
+
urls.add(src);
|
|
863
|
+
});
|
|
864
|
+
return Array.from(urls);
|
|
865
|
+
}
|
|
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
|
+
async loadImageSize(src) {
|
|
885
|
+
try {
|
|
886
|
+
const image = await FabricImage.fromURL(src, {
|
|
887
|
+
crossOrigin: "anonymous"
|
|
888
|
+
});
|
|
889
|
+
const width = Number((image == null ? void 0 : image.width) || 0);
|
|
890
|
+
const height = Number((image == null ? void 0 : image.height) || 0);
|
|
891
|
+
if (width > 0 && height > 0) {
|
|
892
|
+
const size = { width, height };
|
|
893
|
+
this.sourceSizeBySrc.set(src, size);
|
|
894
|
+
return size;
|
|
895
|
+
}
|
|
896
|
+
} catch (error) {
|
|
897
|
+
console.error("[BackgroundTool] Failed to load image", src, error);
|
|
898
|
+
}
|
|
899
|
+
return null;
|
|
900
|
+
}
|
|
901
|
+
updateBackground() {
|
|
902
|
+
void this.updateBackgroundAsync();
|
|
903
|
+
}
|
|
904
|
+
async updateBackgroundAsync() {
|
|
905
|
+
if (!this.canvasService) return;
|
|
906
|
+
const seq = ++this.renderSeq;
|
|
907
|
+
const currentConfig = cloneConfig(this.config);
|
|
908
|
+
const activeUrls = this.collectActiveImageUrls(currentConfig);
|
|
909
|
+
if (activeUrls.length > 0) {
|
|
910
|
+
await Promise.all(activeUrls.map((url) => this.ensureImageSize(url)));
|
|
911
|
+
if (seq !== this.renderSeq) return;
|
|
912
|
+
}
|
|
913
|
+
this.specs = this.buildBackgroundSpecs(currentConfig);
|
|
914
|
+
await this.canvasService.flushRenderFromProducers();
|
|
915
|
+
if (seq !== this.renderSeq) return;
|
|
916
|
+
this.canvasService.requestRenderAll();
|
|
1070
917
|
}
|
|
1071
|
-
const raw = input.trim();
|
|
1072
|
-
if (!raw) return 0;
|
|
1073
|
-
const match = raw.match(/^([+-]?\d+(?:\.\d+)?)\s*(px|mm|cm|in)?$/i);
|
|
1074
|
-
if (!match) return 0;
|
|
1075
|
-
const value = Number(match[1]);
|
|
1076
|
-
if (!Number.isFinite(value)) return 0;
|
|
1077
|
-
const unit = (_b = (_a = match[2]) == null ? void 0 : _a.toLowerCase()) != null ? _b : defaultUnit;
|
|
1078
|
-
return Coordinate.convertUnit(value, unit, "mm");
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
// src/extensions/sceneLayoutModel.ts
|
|
1082
|
-
var DEFAULT_SIZE_STATE = {
|
|
1083
|
-
unit: "mm",
|
|
1084
|
-
actualWidthMm: 500,
|
|
1085
|
-
actualHeightMm: 500,
|
|
1086
|
-
constraintMode: "free",
|
|
1087
|
-
aspectRatio: 1,
|
|
1088
|
-
cutMode: "trim",
|
|
1089
|
-
cutMarginMm: 0,
|
|
1090
|
-
viewPadding: 140,
|
|
1091
|
-
minMm: 10,
|
|
1092
|
-
maxMm: 2e3,
|
|
1093
|
-
stepMm: 0.1
|
|
1094
918
|
};
|
|
1095
|
-
|
|
1096
|
-
|
|
919
|
+
|
|
920
|
+
// src/extensions/image.ts
|
|
921
|
+
import {
|
|
922
|
+
ContributionPointIds as ContributionPointIds2
|
|
923
|
+
} from "@pooder/core";
|
|
924
|
+
import {
|
|
925
|
+
Canvas as FabricCanvas,
|
|
926
|
+
Control,
|
|
927
|
+
Image as FabricImage2,
|
|
928
|
+
Pattern,
|
|
929
|
+
Point,
|
|
930
|
+
controlsUtils
|
|
931
|
+
} from "fabric";
|
|
932
|
+
|
|
933
|
+
// src/extensions/geometry.ts
|
|
934
|
+
import paper from "paper";
|
|
935
|
+
|
|
936
|
+
// src/extensions/bridgeSelection.ts
|
|
937
|
+
function pickExitIndex(hits) {
|
|
938
|
+
for (let i = 0; i < hits.length; i++) {
|
|
939
|
+
const h = hits[i];
|
|
940
|
+
if (h.insideBelow && !h.insideAbove) return i;
|
|
941
|
+
}
|
|
942
|
+
return -1;
|
|
1097
943
|
}
|
|
1098
|
-
function
|
|
1099
|
-
|
|
1100
|
-
|
|
944
|
+
function scoreOutsideAbove(samples) {
|
|
945
|
+
let score = 0;
|
|
946
|
+
for (const s of samples) {
|
|
947
|
+
if (s.outsideAbove) score++;
|
|
948
|
+
}
|
|
949
|
+
return score;
|
|
1101
950
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
951
|
+
|
|
952
|
+
// src/extensions/wrappedOffsets.ts
|
|
953
|
+
function wrappedDistance(total, start, end) {
|
|
954
|
+
if (!Number.isFinite(total) || total <= 0) return 0;
|
|
955
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return 0;
|
|
956
|
+
const s = (start % total + total) % total;
|
|
957
|
+
const e = (end % total + total) % total;
|
|
958
|
+
return e >= s ? e - s : total - s + e;
|
|
1106
959
|
}
|
|
1107
|
-
function
|
|
1108
|
-
if (
|
|
1109
|
-
return
|
|
960
|
+
function sampleWrappedOffsets(total, start, end, count) {
|
|
961
|
+
if (!Number.isFinite(total) || total <= 0) return [];
|
|
962
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return [];
|
|
963
|
+
const n = Math.max(0, Math.floor(count));
|
|
964
|
+
if (n <= 0) return [];
|
|
965
|
+
const dist = wrappedDistance(total, start, end);
|
|
966
|
+
if (n === 1) return [(start % total + total) % total];
|
|
967
|
+
const step = dist / (n - 1);
|
|
968
|
+
const offsets = [];
|
|
969
|
+
for (let i = 0; i < n; i++) {
|
|
970
|
+
const raw = start + step * i;
|
|
971
|
+
const wrapped = (raw % total + total) % total;
|
|
972
|
+
offsets.push(wrapped);
|
|
973
|
+
}
|
|
974
|
+
return offsets;
|
|
1110
975
|
}
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
976
|
+
|
|
977
|
+
// src/extensions/geometry.ts
|
|
978
|
+
function resolveFeaturePosition(feature, geometry) {
|
|
979
|
+
const { x, y, width, height } = geometry;
|
|
980
|
+
const left = x - width / 2;
|
|
981
|
+
const top = y - height / 2;
|
|
982
|
+
return {
|
|
983
|
+
x: left + feature.x * width,
|
|
984
|
+
y: top + feature.y * height
|
|
985
|
+
};
|
|
1114
986
|
}
|
|
1115
|
-
function
|
|
1116
|
-
if (
|
|
1117
|
-
|
|
987
|
+
function ensurePaper(width, height) {
|
|
988
|
+
if (!paper.project) {
|
|
989
|
+
paper.setup(new paper.Size(width, height));
|
|
990
|
+
} else {
|
|
991
|
+
paper.view.viewSize = new paper.Size(width, height);
|
|
992
|
+
}
|
|
1118
993
|
}
|
|
1119
|
-
|
|
1120
|
-
|
|
994
|
+
var isBridgeDebugEnabled = () => Boolean(globalThis.__POODER_BRIDGE_DEBUG__);
|
|
995
|
+
function normalizePathItem(shape) {
|
|
996
|
+
let result = shape;
|
|
997
|
+
if (typeof result.resolveCrossings === "function") result = result.resolveCrossings();
|
|
998
|
+
if (typeof result.reduce === "function") result = result.reduce({});
|
|
999
|
+
if (typeof result.reorient === "function") result = result.reorient(true, true);
|
|
1000
|
+
if (typeof result.reduce === "function") result = result.reduce({});
|
|
1001
|
+
return result;
|
|
1121
1002
|
}
|
|
1122
|
-
function
|
|
1123
|
-
return
|
|
1003
|
+
function getBridgeDelta(itemBounds, overlap) {
|
|
1004
|
+
return Math.max(overlap, Math.min(5, Math.max(1, itemBounds.height * 0.02)));
|
|
1124
1005
|
}
|
|
1125
|
-
function
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1006
|
+
function getExitHit(args) {
|
|
1007
|
+
const { mainShape, x, bridgeBottom, toY, eps, delta, overlap, op } = args;
|
|
1008
|
+
const ray = new paper.Path.Line({
|
|
1009
|
+
from: [x, bridgeBottom],
|
|
1010
|
+
to: [x, toY],
|
|
1011
|
+
insert: false
|
|
1012
|
+
});
|
|
1013
|
+
const intersections = mainShape.getIntersections(ray) || [];
|
|
1014
|
+
ray.remove();
|
|
1015
|
+
const validHits = intersections.filter((i) => i.point.y < bridgeBottom - eps);
|
|
1016
|
+
if (validHits.length === 0) return null;
|
|
1017
|
+
validHits.sort((a, b) => b.point.y - a.point.y);
|
|
1018
|
+
const flags = validHits.map((h) => {
|
|
1019
|
+
const above = h.point.add(new paper.Point(0, -delta));
|
|
1020
|
+
const below = h.point.add(new paper.Point(0, delta));
|
|
1021
|
+
return {
|
|
1022
|
+
insideAbove: mainShape.contains(above),
|
|
1023
|
+
insideBelow: mainShape.contains(below)
|
|
1024
|
+
};
|
|
1025
|
+
});
|
|
1026
|
+
const idx = pickExitIndex(flags);
|
|
1027
|
+
if (idx < 0) return null;
|
|
1028
|
+
if (isBridgeDebugEnabled()) {
|
|
1029
|
+
console.debug("Geometry: Bridge ray", {
|
|
1030
|
+
x,
|
|
1031
|
+
validHits: validHits.length,
|
|
1032
|
+
idx,
|
|
1033
|
+
delta,
|
|
1034
|
+
overlap,
|
|
1035
|
+
op
|
|
1036
|
+
});
|
|
1135
1037
|
}
|
|
1136
|
-
|
|
1038
|
+
const hit = validHits[idx];
|
|
1039
|
+
return { point: hit.point, location: hit };
|
|
1137
1040
|
}
|
|
1138
|
-
function
|
|
1139
|
-
const
|
|
1140
|
-
|
|
1041
|
+
function selectOuterChain(args) {
|
|
1042
|
+
const { mainShape, pointsA, pointsB, delta, overlap, op } = args;
|
|
1043
|
+
const scoreA = scoreOutsideAbove(
|
|
1044
|
+
pointsA.map((p) => ({
|
|
1045
|
+
outsideAbove: !mainShape.contains(p.add(new paper.Point(0, -delta)))
|
|
1046
|
+
}))
|
|
1141
1047
|
);
|
|
1142
|
-
const
|
|
1143
|
-
|
|
1144
|
-
|
|
1048
|
+
const scoreB = scoreOutsideAbove(
|
|
1049
|
+
pointsB.map((p) => ({
|
|
1050
|
+
outsideAbove: !mainShape.contains(p.add(new paper.Point(0, -delta)))
|
|
1051
|
+
}))
|
|
1145
1052
|
);
|
|
1146
|
-
const
|
|
1147
|
-
|
|
1148
|
-
|
|
1053
|
+
const ratioA = scoreA / pointsA.length;
|
|
1054
|
+
const ratioB = scoreB / pointsB.length;
|
|
1055
|
+
if (isBridgeDebugEnabled()) {
|
|
1056
|
+
console.debug("Geometry: Bridge chain", {
|
|
1057
|
+
scoreA,
|
|
1058
|
+
scoreB,
|
|
1059
|
+
lenA: pointsA.length,
|
|
1060
|
+
lenB: pointsB.length,
|
|
1061
|
+
ratioA,
|
|
1062
|
+
ratioB,
|
|
1063
|
+
delta,
|
|
1064
|
+
overlap,
|
|
1065
|
+
op
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
const ratioEps = 1e-6;
|
|
1069
|
+
if (Math.abs(ratioA - ratioB) > ratioEps) {
|
|
1070
|
+
return ratioA > ratioB ? pointsA : pointsB;
|
|
1071
|
+
}
|
|
1072
|
+
if (scoreA !== scoreB) return scoreA > scoreB ? pointsA : pointsB;
|
|
1073
|
+
return pointsA.length <= pointsB.length ? pointsA : pointsB;
|
|
1074
|
+
}
|
|
1075
|
+
function fitPathItemToRect(item, rect, fitMode) {
|
|
1076
|
+
const { left, top, width, height } = rect;
|
|
1077
|
+
const bounds = item.bounds;
|
|
1078
|
+
if (width <= 0 || height <= 0 || !Number.isFinite(bounds.width) || !Number.isFinite(bounds.height) || bounds.width <= 0 || bounds.height <= 0) {
|
|
1079
|
+
item.position = new paper.Point(left + width / 2, top + height / 2);
|
|
1080
|
+
return item;
|
|
1081
|
+
}
|
|
1082
|
+
item.translate(new paper.Point(-bounds.left, -bounds.top));
|
|
1083
|
+
if (fitMode === "stretch") {
|
|
1084
|
+
item.scale(width / bounds.width, height / bounds.height, new paper.Point(0, 0));
|
|
1085
|
+
item.translate(new paper.Point(left, top));
|
|
1086
|
+
return item;
|
|
1087
|
+
}
|
|
1088
|
+
const uniformScale = Math.min(width / bounds.width, height / bounds.height);
|
|
1089
|
+
item.scale(uniformScale, uniformScale, new paper.Point(0, 0));
|
|
1090
|
+
const scaledWidth = bounds.width * uniformScale;
|
|
1091
|
+
const scaledHeight = bounds.height * uniformScale;
|
|
1092
|
+
item.translate(
|
|
1093
|
+
new paper.Point(
|
|
1094
|
+
left + (width - scaledWidth) / 2,
|
|
1095
|
+
top + (height - scaledHeight) / 2
|
|
1096
|
+
)
|
|
1149
1097
|
);
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1098
|
+
return item;
|
|
1099
|
+
}
|
|
1100
|
+
function createNormalizedHeartPath(params) {
|
|
1101
|
+
const { lobeSpread, notchDepth, tipSharpness } = params;
|
|
1102
|
+
const halfSpread = 0.22 + lobeSpread * 0.18;
|
|
1103
|
+
const notchY = 0.06 + notchDepth * 0.2;
|
|
1104
|
+
const shoulderY = 0.24 + notchDepth * 0.2;
|
|
1105
|
+
const topLift = 0.12 + (1 - notchDepth) * 0.06;
|
|
1106
|
+
const topY = notchY - topLift;
|
|
1107
|
+
const sideCtrlY = shoulderY - (0.18 - notchDepth * 0.08);
|
|
1108
|
+
const lowerCtrlY = 0.58 + (1 - tipSharpness) * 0.16;
|
|
1109
|
+
const tipCtrlX = 0.34 - tipSharpness * 0.2;
|
|
1110
|
+
const notchCtrlX = 0.06 + lobeSpread * 0.06;
|
|
1111
|
+
const lobeCtrlX = 0.1 + lobeSpread * 0.08;
|
|
1112
|
+
const notchCtrlY = notchY - topLift * 0.45;
|
|
1113
|
+
const xPeakL = 0.5 - halfSpread;
|
|
1114
|
+
const xPeakR = 0.5 + halfSpread;
|
|
1115
|
+
const heartPath = new paper.Path({ insert: false });
|
|
1116
|
+
heartPath.moveTo(new paper.Point(0.5, notchY));
|
|
1117
|
+
heartPath.cubicCurveTo(
|
|
1118
|
+
new paper.Point(0.5 - notchCtrlX, notchCtrlY),
|
|
1119
|
+
new paper.Point(xPeakL + lobeCtrlX, topY),
|
|
1120
|
+
new paper.Point(xPeakL, topY)
|
|
1153
1121
|
);
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
),
|
|
1159
|
-
{ minMm, maxMm, stepMm }
|
|
1122
|
+
heartPath.cubicCurveTo(
|
|
1123
|
+
new paper.Point(xPeakL - lobeCtrlX, topY),
|
|
1124
|
+
new paper.Point(0, sideCtrlY),
|
|
1125
|
+
new paper.Point(0, shoulderY)
|
|
1160
1126
|
);
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
DEFAULT_SIZE_STATE.actualHeightMm
|
|
1166
|
-
),
|
|
1167
|
-
"mm"
|
|
1168
|
-
),
|
|
1169
|
-
{ minMm, maxMm, stepMm }
|
|
1127
|
+
heartPath.cubicCurveTo(
|
|
1128
|
+
new paper.Point(0, lowerCtrlY),
|
|
1129
|
+
new paper.Point(tipCtrlX, 1),
|
|
1130
|
+
new paper.Point(0.5, 1)
|
|
1170
1131
|
);
|
|
1171
|
-
|
|
1172
|
-
|
|
1132
|
+
heartPath.cubicCurveTo(
|
|
1133
|
+
new paper.Point(1 - tipCtrlX, 1),
|
|
1134
|
+
new paper.Point(1, lowerCtrlY),
|
|
1135
|
+
new paper.Point(1, shoulderY)
|
|
1173
1136
|
);
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
configService.get("size.cutMarginMm", DEFAULT_SIZE_STATE.cutMarginMm),
|
|
1179
|
-
"mm"
|
|
1180
|
-
)
|
|
1137
|
+
heartPath.cubicCurveTo(
|
|
1138
|
+
new paper.Point(1, sideCtrlY),
|
|
1139
|
+
new paper.Point(xPeakR + lobeCtrlX, topY),
|
|
1140
|
+
new paper.Point(xPeakR, topY)
|
|
1181
1141
|
);
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1142
|
+
heartPath.cubicCurveTo(
|
|
1143
|
+
new paper.Point(xPeakR - lobeCtrlX, topY),
|
|
1144
|
+
new paper.Point(0.5 + notchCtrlX, notchCtrlY),
|
|
1145
|
+
new paper.Point(0.5, notchY)
|
|
1185
1146
|
);
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
actualWidthMm,
|
|
1189
|
-
actualHeightMm,
|
|
1190
|
-
constraintMode: normalizeConstraintMode(
|
|
1191
|
-
configService.get(
|
|
1192
|
-
"size.constraintMode",
|
|
1193
|
-
DEFAULT_SIZE_STATE.constraintMode
|
|
1194
|
-
)
|
|
1195
|
-
),
|
|
1196
|
-
aspectRatio,
|
|
1197
|
-
cutMode: normalizeCutMode(
|
|
1198
|
-
configService.get("size.cutMode", DEFAULT_SIZE_STATE.cutMode)
|
|
1199
|
-
),
|
|
1200
|
-
cutMarginMm,
|
|
1201
|
-
viewPadding,
|
|
1202
|
-
minMm,
|
|
1203
|
-
maxMm,
|
|
1204
|
-
stepMm
|
|
1205
|
-
};
|
|
1147
|
+
heartPath.closed = true;
|
|
1148
|
+
return heartPath;
|
|
1206
1149
|
}
|
|
1207
|
-
function
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1150
|
+
function createHeartBaseShape(options) {
|
|
1151
|
+
const { x, y, width, height } = options;
|
|
1152
|
+
const w = Math.max(0, width);
|
|
1153
|
+
const h = Math.max(0, height);
|
|
1154
|
+
const left = x - w / 2;
|
|
1155
|
+
const top = y - h / 2;
|
|
1156
|
+
const fitMode = getShapeFitMode(options.shapeStyle);
|
|
1157
|
+
const heartParams = getHeartShapeParams(options.shapeStyle);
|
|
1158
|
+
const rawHeart = createNormalizedHeartPath(heartParams);
|
|
1159
|
+
return fitPathItemToRect(rawHeart, { left, top, width: w, height: h }, fitMode);
|
|
1160
|
+
}
|
|
1161
|
+
var BUILTIN_SHAPE_BUILDERS = {
|
|
1162
|
+
rect: (options) => {
|
|
1163
|
+
const { x, y, width, height, radius } = options;
|
|
1164
|
+
return new paper.Path.Rectangle({
|
|
1165
|
+
point: [x - width / 2, y - height / 2],
|
|
1166
|
+
size: [Math.max(0, width), Math.max(0, height)],
|
|
1167
|
+
radius: Math.max(0, radius)
|
|
1168
|
+
});
|
|
1169
|
+
},
|
|
1170
|
+
circle: (options) => {
|
|
1171
|
+
const { x, y, width, height } = options;
|
|
1172
|
+
const r = Math.min(width, height) / 2;
|
|
1173
|
+
return new paper.Path.Circle({
|
|
1174
|
+
center: new paper.Point(x, y),
|
|
1175
|
+
radius: Math.max(0, r)
|
|
1176
|
+
});
|
|
1177
|
+
},
|
|
1178
|
+
ellipse: (options) => {
|
|
1179
|
+
const { x, y, width, height } = options;
|
|
1180
|
+
return new paper.Path.Ellipse({
|
|
1181
|
+
center: new paper.Point(x, y),
|
|
1182
|
+
radius: [Math.max(0, width / 2), Math.max(0, height / 2)]
|
|
1183
|
+
});
|
|
1184
|
+
},
|
|
1185
|
+
heart: createHeartBaseShape
|
|
1186
|
+
};
|
|
1187
|
+
function createCustomBaseShape(options) {
|
|
1188
|
+
var _a;
|
|
1189
|
+
const {
|
|
1190
|
+
pathData,
|
|
1191
|
+
customSourceWidthPx,
|
|
1192
|
+
customSourceHeightPx,
|
|
1193
|
+
x,
|
|
1194
|
+
y,
|
|
1211
1195
|
width,
|
|
1212
|
-
height
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1196
|
+
height
|
|
1197
|
+
} = options;
|
|
1198
|
+
if (typeof pathData !== "string" || pathData.trim().length === 0) {
|
|
1199
|
+
return null;
|
|
1200
|
+
}
|
|
1201
|
+
const center = new paper.Point(x, y);
|
|
1202
|
+
const hasMultipleSubPaths = ((_a = (pathData.match(/[Mm]/g) || []).length) != null ? _a : 0) > 1;
|
|
1203
|
+
const path = hasMultipleSubPaths ? new paper.CompoundPath(pathData) : (() => {
|
|
1204
|
+
const single = new paper.Path();
|
|
1205
|
+
single.pathData = pathData;
|
|
1206
|
+
return single;
|
|
1207
|
+
})();
|
|
1208
|
+
const sourceWidth = Number(customSourceWidthPx != null ? customSourceWidthPx : 0);
|
|
1209
|
+
const sourceHeight = Number(customSourceHeightPx != null ? customSourceHeightPx : 0);
|
|
1210
|
+
if (Number.isFinite(sourceWidth) && Number.isFinite(sourceHeight) && sourceWidth > 0 && sourceHeight > 0 && width > 0 && height > 0) {
|
|
1211
|
+
const targetLeft = x - width / 2;
|
|
1212
|
+
const targetTop = y - height / 2;
|
|
1213
|
+
path.scale(width / sourceWidth, height / sourceHeight, new paper.Point(0, 0));
|
|
1214
|
+
path.translate(new paper.Point(targetLeft, targetTop));
|
|
1215
|
+
return path;
|
|
1216
|
+
}
|
|
1217
|
+
if (width > 0 && height > 0 && path.bounds.width > 0 && path.bounds.height > 0) {
|
|
1218
|
+
path.position = center;
|
|
1219
|
+
path.scale(width / path.bounds.width, height / path.bounds.height);
|
|
1220
|
+
return path;
|
|
1221
|
+
}
|
|
1222
|
+
path.position = center;
|
|
1223
|
+
return path;
|
|
1216
1224
|
}
|
|
1217
|
-
function
|
|
1218
|
-
|
|
1219
|
-
|
|
1225
|
+
function createBaseShape(options) {
|
|
1226
|
+
const { shape } = options;
|
|
1227
|
+
if (shape === "custom") {
|
|
1228
|
+
const customShape = createCustomBaseShape(options);
|
|
1229
|
+
if (customShape) return customShape;
|
|
1230
|
+
return BUILTIN_SHAPE_BUILDERS[DEFAULT_DIELINE_SHAPE](options);
|
|
1220
1231
|
}
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1232
|
+
return BUILTIN_SHAPE_BUILDERS[shape](options);
|
|
1233
|
+
}
|
|
1234
|
+
function resolveBridgeBasePath(shape, anchor) {
|
|
1235
|
+
if (shape instanceof paper.Path) {
|
|
1236
|
+
return shape;
|
|
1237
|
+
}
|
|
1238
|
+
if (shape instanceof paper.CompoundPath) {
|
|
1239
|
+
const children = (shape.children || []).filter(
|
|
1240
|
+
(child) => child instanceof paper.Path
|
|
1241
|
+
);
|
|
1242
|
+
if (!children.length) return null;
|
|
1243
|
+
let best = children[0];
|
|
1244
|
+
let bestDistance = Infinity;
|
|
1245
|
+
for (const child of children) {
|
|
1246
|
+
const location = child.getNearestLocation(anchor);
|
|
1247
|
+
const point = location == null ? void 0 : location.point;
|
|
1248
|
+
if (!point) continue;
|
|
1249
|
+
const distance = point.getDistance(anchor);
|
|
1250
|
+
if (distance < bestDistance) {
|
|
1251
|
+
bestDistance = distance;
|
|
1252
|
+
best = child;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
return best;
|
|
1256
|
+
}
|
|
1257
|
+
return null;
|
|
1258
|
+
}
|
|
1259
|
+
function createFeatureItem(feature, center) {
|
|
1260
|
+
let item;
|
|
1261
|
+
if (feature.shape === "rect") {
|
|
1262
|
+
const w = feature.width || 10;
|
|
1263
|
+
const h = feature.height || 10;
|
|
1264
|
+
const r = feature.radius || 0;
|
|
1265
|
+
item = new paper.Path.Rectangle({
|
|
1266
|
+
point: [center.x - w / 2, center.y - h / 2],
|
|
1267
|
+
size: [w, h],
|
|
1268
|
+
radius: r
|
|
1269
|
+
});
|
|
1270
|
+
} else {
|
|
1271
|
+
const r = feature.radius || 5;
|
|
1272
|
+
item = new paper.Path.Circle({
|
|
1273
|
+
center,
|
|
1274
|
+
radius: r
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
if (feature.rotation) {
|
|
1278
|
+
item.rotate(feature.rotation, center);
|
|
1279
|
+
}
|
|
1280
|
+
return item;
|
|
1281
|
+
}
|
|
1282
|
+
function getPerimeterShape(options) {
|
|
1283
|
+
let mainShape = createBaseShape(options);
|
|
1284
|
+
const { features } = options;
|
|
1285
|
+
if (features && features.length > 0) {
|
|
1286
|
+
const edgeFeatures = features.filter(
|
|
1287
|
+
(f) => !f.renderBehavior || f.renderBehavior === "edge"
|
|
1288
|
+
);
|
|
1289
|
+
const adds = [];
|
|
1290
|
+
const subtracts = [];
|
|
1291
|
+
edgeFeatures.forEach((f) => {
|
|
1292
|
+
const pos = resolveFeaturePosition(f, options);
|
|
1293
|
+
const center = new paper.Point(pos.x, pos.y);
|
|
1294
|
+
const item = createFeatureItem(f, center);
|
|
1295
|
+
if (f.bridge && f.bridge.type === "vertical") {
|
|
1296
|
+
const itemBounds = item.bounds;
|
|
1297
|
+
const mainBounds = mainShape.bounds;
|
|
1298
|
+
const bridgeTop = mainBounds.top;
|
|
1299
|
+
const bridgeBottom = itemBounds.top;
|
|
1300
|
+
if (bridgeBottom > bridgeTop) {
|
|
1301
|
+
const overlap = 2;
|
|
1302
|
+
const rayPadding = 10;
|
|
1303
|
+
const eps = 0.1;
|
|
1304
|
+
const delta = getBridgeDelta(itemBounds, overlap);
|
|
1305
|
+
const toY = bridgeTop - rayPadding;
|
|
1306
|
+
const inset = Math.min(1, Math.max(0, itemBounds.width * 0.01));
|
|
1307
|
+
const xLeft = itemBounds.left + inset;
|
|
1308
|
+
const xRight = itemBounds.right - inset;
|
|
1309
|
+
const bridgeBasePath = resolveBridgeBasePath(mainShape, center);
|
|
1310
|
+
const canBridge = !!bridgeBasePath && xRight - xLeft > eps;
|
|
1311
|
+
if (canBridge && bridgeBasePath) {
|
|
1312
|
+
const leftHit = getExitHit({
|
|
1313
|
+
mainShape: bridgeBasePath,
|
|
1314
|
+
x: xLeft,
|
|
1315
|
+
bridgeBottom,
|
|
1316
|
+
toY,
|
|
1317
|
+
eps,
|
|
1318
|
+
delta,
|
|
1319
|
+
overlap,
|
|
1320
|
+
op: f.operation
|
|
1321
|
+
});
|
|
1322
|
+
const rightHit = getExitHit({
|
|
1323
|
+
mainShape: bridgeBasePath,
|
|
1324
|
+
x: xRight,
|
|
1325
|
+
bridgeBottom,
|
|
1326
|
+
toY,
|
|
1327
|
+
eps,
|
|
1328
|
+
delta,
|
|
1329
|
+
overlap,
|
|
1330
|
+
op: f.operation
|
|
1331
|
+
});
|
|
1332
|
+
if (leftHit && rightHit) {
|
|
1333
|
+
const pathLength = bridgeBasePath.length;
|
|
1334
|
+
const leftOffset = leftHit.location.offset;
|
|
1335
|
+
const rightOffset = rightHit.location.offset;
|
|
1336
|
+
const distanceA = wrappedDistance(pathLength, leftOffset, rightOffset);
|
|
1337
|
+
const distanceB = wrappedDistance(pathLength, rightOffset, leftOffset);
|
|
1338
|
+
const countFor = (d) => Math.max(8, Math.min(80, Math.ceil(d / 6)));
|
|
1339
|
+
const offsetsA = sampleWrappedOffsets(
|
|
1340
|
+
pathLength,
|
|
1341
|
+
leftOffset,
|
|
1342
|
+
rightOffset,
|
|
1343
|
+
countFor(distanceA)
|
|
1344
|
+
);
|
|
1345
|
+
const offsetsB = sampleWrappedOffsets(
|
|
1346
|
+
pathLength,
|
|
1347
|
+
rightOffset,
|
|
1348
|
+
leftOffset,
|
|
1349
|
+
countFor(distanceB)
|
|
1350
|
+
);
|
|
1351
|
+
const pointsA = offsetsA.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
1352
|
+
const pointsB = offsetsB.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
1353
|
+
if (pointsA.length >= 2 && pointsB.length >= 2) {
|
|
1354
|
+
let topBase = selectOuterChain({
|
|
1355
|
+
mainShape: bridgeBasePath,
|
|
1356
|
+
pointsA,
|
|
1357
|
+
pointsB,
|
|
1358
|
+
delta,
|
|
1359
|
+
overlap,
|
|
1360
|
+
op: f.operation
|
|
1361
|
+
});
|
|
1362
|
+
const dist2 = (a, b) => {
|
|
1363
|
+
const dx = a.x - b.x;
|
|
1364
|
+
const dy = a.y - b.y;
|
|
1365
|
+
return dx * dx + dy * dy;
|
|
1366
|
+
};
|
|
1367
|
+
if (dist2(topBase[0], leftHit.point) > dist2(topBase[0], rightHit.point)) {
|
|
1368
|
+
topBase = topBase.slice().reverse();
|
|
1369
|
+
}
|
|
1370
|
+
topBase = topBase.slice();
|
|
1371
|
+
topBase[0] = leftHit.point;
|
|
1372
|
+
topBase[topBase.length - 1] = rightHit.point;
|
|
1373
|
+
const capShiftY = f.operation === "subtract" ? -Math.max(overlap * 2, delta) : overlap;
|
|
1374
|
+
const topPoints = topBase.map(
|
|
1375
|
+
(p) => p.add(new paper.Point(0, capShiftY))
|
|
1376
|
+
);
|
|
1377
|
+
const bridgeBottomY = bridgeBottom + overlap * 2;
|
|
1378
|
+
const bridgePoly = new paper.Path({ insert: false });
|
|
1379
|
+
for (const p of topPoints) bridgePoly.add(p);
|
|
1380
|
+
bridgePoly.add(new paper.Point(xRight, bridgeBottomY));
|
|
1381
|
+
bridgePoly.add(new paper.Point(xLeft, bridgeBottomY));
|
|
1382
|
+
bridgePoly.closed = true;
|
|
1383
|
+
const unitedItem = item.unite(bridgePoly);
|
|
1384
|
+
item.remove();
|
|
1385
|
+
bridgePoly.remove();
|
|
1386
|
+
if (f.operation === "add") {
|
|
1387
|
+
adds.push(unitedItem);
|
|
1388
|
+
} else {
|
|
1389
|
+
subtracts.push(unitedItem);
|
|
1390
|
+
}
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
if (f.operation === "add") {
|
|
1396
|
+
adds.push(item);
|
|
1397
|
+
} else {
|
|
1398
|
+
subtracts.push(item);
|
|
1399
|
+
}
|
|
1400
|
+
} else {
|
|
1401
|
+
if (f.operation === "add") {
|
|
1402
|
+
adds.push(item);
|
|
1403
|
+
} else {
|
|
1404
|
+
subtracts.push(item);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
} else {
|
|
1408
|
+
if (f.operation === "add") {
|
|
1409
|
+
adds.push(item);
|
|
1410
|
+
} else {
|
|
1411
|
+
subtracts.push(item);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
});
|
|
1415
|
+
if (adds.length > 0) {
|
|
1416
|
+
for (const item of adds) {
|
|
1417
|
+
try {
|
|
1418
|
+
const temp = mainShape.unite(item);
|
|
1419
|
+
mainShape.remove();
|
|
1420
|
+
item.remove();
|
|
1421
|
+
mainShape = normalizePathItem(temp);
|
|
1422
|
+
} catch (e) {
|
|
1423
|
+
console.error("Geometry: Failed to unite feature", e);
|
|
1424
|
+
item.remove();
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
if (subtracts.length > 0) {
|
|
1429
|
+
for (const item of subtracts) {
|
|
1430
|
+
try {
|
|
1431
|
+
const temp = mainShape.subtract(item);
|
|
1432
|
+
mainShape.remove();
|
|
1433
|
+
item.remove();
|
|
1434
|
+
mainShape = normalizePathItem(temp);
|
|
1435
|
+
} catch (e) {
|
|
1436
|
+
console.error("Geometry: Failed to subtract feature", e);
|
|
1437
|
+
item.remove();
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1227
1441
|
}
|
|
1228
|
-
return
|
|
1229
|
-
widthMm: Math.max(size.minMm, size.actualWidthMm - delta),
|
|
1230
|
-
heightMm: Math.max(size.minMm, size.actualHeightMm - delta)
|
|
1231
|
-
};
|
|
1442
|
+
return mainShape;
|
|
1232
1443
|
}
|
|
1233
|
-
function
|
|
1234
|
-
const
|
|
1235
|
-
|
|
1236
|
-
if (canvasWidth <= 0 || canvasHeight <= 0) return null;
|
|
1237
|
-
const { widthMm: cutWidthMm, heightMm: cutHeightMm } = getCutSizeMm(size);
|
|
1238
|
-
const viewWidthMm = Math.max(size.actualWidthMm, cutWidthMm);
|
|
1239
|
-
const viewHeightMm = Math.max(size.actualHeightMm, cutHeightMm);
|
|
1240
|
-
if (!Number.isFinite(viewWidthMm) || !Number.isFinite(viewHeightMm) || viewWidthMm <= 0 || viewHeightMm <= 0) {
|
|
1241
|
-
return null;
|
|
1242
|
-
}
|
|
1243
|
-
const paddingPx = resolvePaddingPx(
|
|
1244
|
-
size.viewPadding,
|
|
1245
|
-
canvasWidth,
|
|
1246
|
-
canvasHeight
|
|
1444
|
+
function applySurfaceFeatures(shape, features, options) {
|
|
1445
|
+
const surfaceFeatures = features.filter(
|
|
1446
|
+
(f) => f.renderBehavior === "surface"
|
|
1247
1447
|
);
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1448
|
+
if (surfaceFeatures.length === 0) return shape;
|
|
1449
|
+
let result = shape;
|
|
1450
|
+
for (const f of surfaceFeatures) {
|
|
1451
|
+
const pos = resolveFeaturePosition(f, options);
|
|
1452
|
+
const center = new paper.Point(pos.x, pos.y);
|
|
1453
|
+
const item = createFeatureItem(f, center);
|
|
1454
|
+
try {
|
|
1455
|
+
if (f.operation === "add") {
|
|
1456
|
+
const temp = result.unite(item);
|
|
1457
|
+
result.remove();
|
|
1458
|
+
item.remove();
|
|
1459
|
+
result = normalizePathItem(temp);
|
|
1460
|
+
} else {
|
|
1461
|
+
const temp = result.subtract(item);
|
|
1462
|
+
result.remove();
|
|
1463
|
+
item.remove();
|
|
1464
|
+
result = normalizePathItem(temp);
|
|
1465
|
+
}
|
|
1466
|
+
} catch (e) {
|
|
1467
|
+
console.error("Geometry: Failed to apply surface feature", e);
|
|
1468
|
+
item.remove();
|
|
1469
|
+
}
|
|
1254
1470
|
}
|
|
1255
|
-
|
|
1256
|
-
const centerY = layout.offsetY + layout.height / 2;
|
|
1257
|
-
const trimWidthPx = size.actualWidthMm * layout.scale;
|
|
1258
|
-
const trimHeightPx = size.actualHeightMm * layout.scale;
|
|
1259
|
-
const cutWidthPx = cutWidthMm * layout.scale;
|
|
1260
|
-
const cutHeightPx = cutHeightMm * layout.scale;
|
|
1261
|
-
const trimRect = rectByCenter(centerX, centerY, trimWidthPx, trimHeightPx);
|
|
1262
|
-
const cutRect = rectByCenter(centerX, centerY, cutWidthPx, cutHeightPx);
|
|
1263
|
-
const bleedRect = rectByCenter(
|
|
1264
|
-
centerX,
|
|
1265
|
-
centerY,
|
|
1266
|
-
Math.max(trimWidthPx, cutWidthPx),
|
|
1267
|
-
Math.max(trimHeightPx, cutHeightPx)
|
|
1268
|
-
);
|
|
1269
|
-
return {
|
|
1270
|
-
scale: layout.scale,
|
|
1271
|
-
canvasWidth,
|
|
1272
|
-
canvasHeight,
|
|
1273
|
-
trimRect,
|
|
1274
|
-
cutRect,
|
|
1275
|
-
bleedRect,
|
|
1276
|
-
trimWidthMm: size.actualWidthMm,
|
|
1277
|
-
trimHeightMm: size.actualHeightMm,
|
|
1278
|
-
cutWidthMm,
|
|
1279
|
-
cutHeightMm,
|
|
1280
|
-
cutMode: size.cutMode,
|
|
1281
|
-
cutMarginMm: size.cutMarginMm
|
|
1282
|
-
};
|
|
1471
|
+
return result;
|
|
1283
1472
|
}
|
|
1284
|
-
function
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
);
|
|
1289
|
-
const
|
|
1290
|
-
const
|
|
1291
|
-
const
|
|
1292
|
-
|
|
1473
|
+
function generateDielinePath(options) {
|
|
1474
|
+
const paperWidth = options.canvasWidth || options.width * 2 || 2e3;
|
|
1475
|
+
const paperHeight = options.canvasHeight || options.height * 2 || 2e3;
|
|
1476
|
+
ensurePaper(paperWidth, paperHeight);
|
|
1477
|
+
paper.project.activeLayer.removeChildren();
|
|
1478
|
+
const perimeter = getPerimeterShape(options);
|
|
1479
|
+
const finalShape = applySurfaceFeatures(perimeter, options.features, options);
|
|
1480
|
+
const pathData = finalShape.pathData;
|
|
1481
|
+
finalShape.remove();
|
|
1482
|
+
return pathData;
|
|
1483
|
+
}
|
|
1484
|
+
function generateBleedZonePath(originalOptions, offsetOptions, offset) {
|
|
1485
|
+
const paperWidth = originalOptions.canvasWidth || originalOptions.width * 2 || 2e3;
|
|
1486
|
+
const paperHeight = originalOptions.canvasHeight || originalOptions.height * 2 || 2e3;
|
|
1487
|
+
ensurePaper(paperWidth, paperHeight);
|
|
1488
|
+
paper.project.activeLayer.removeChildren();
|
|
1489
|
+
const pOriginal = getPerimeterShape(originalOptions);
|
|
1490
|
+
const shapeOriginal = applySurfaceFeatures(
|
|
1491
|
+
pOriginal,
|
|
1492
|
+
originalOptions.features,
|
|
1493
|
+
originalOptions
|
|
1293
1494
|
);
|
|
1294
|
-
const
|
|
1295
|
-
|
|
1495
|
+
const pOffset = getPerimeterShape(offsetOptions);
|
|
1496
|
+
const shapeOffset = applySurfaceFeatures(
|
|
1497
|
+
pOffset,
|
|
1498
|
+
offsetOptions.features,
|
|
1499
|
+
offsetOptions
|
|
1296
1500
|
);
|
|
1501
|
+
let bleedZone;
|
|
1502
|
+
if (offset > 0) {
|
|
1503
|
+
bleedZone = shapeOffset.subtract(shapeOriginal);
|
|
1504
|
+
} else {
|
|
1505
|
+
bleedZone = shapeOriginal.subtract(shapeOffset);
|
|
1506
|
+
}
|
|
1507
|
+
const pathData = bleedZone.pathData;
|
|
1508
|
+
shapeOriginal.remove();
|
|
1509
|
+
shapeOffset.remove();
|
|
1510
|
+
bleedZone.remove();
|
|
1511
|
+
return pathData;
|
|
1512
|
+
}
|
|
1513
|
+
function getLowestPointOnDieline(options) {
|
|
1514
|
+
ensurePaper(options.width * 2, options.height * 2);
|
|
1515
|
+
paper.project.activeLayer.removeChildren();
|
|
1516
|
+
const shape = createBaseShape(options);
|
|
1517
|
+
const bounds = shape.bounds;
|
|
1518
|
+
const result = {
|
|
1519
|
+
x: bounds.center.x,
|
|
1520
|
+
y: bounds.bottom
|
|
1521
|
+
};
|
|
1522
|
+
shape.remove();
|
|
1523
|
+
return result;
|
|
1524
|
+
}
|
|
1525
|
+
function getNearestPointOnDieline(point, options) {
|
|
1526
|
+
ensurePaper(options.width * 2, options.height * 2);
|
|
1527
|
+
paper.project.activeLayer.removeChildren();
|
|
1528
|
+
const shape = createBaseShape(options);
|
|
1529
|
+
const p = new paper.Point(point.x, point.y);
|
|
1530
|
+
const location = shape.getNearestLocation(p);
|
|
1531
|
+
const result = {
|
|
1532
|
+
x: location.point.x,
|
|
1533
|
+
y: location.point.y,
|
|
1534
|
+
normal: location.normal ? { x: location.normal.x, y: location.normal.y } : void 0
|
|
1535
|
+
};
|
|
1536
|
+
shape.remove();
|
|
1537
|
+
return result;
|
|
1538
|
+
}
|
|
1539
|
+
function getPathBounds(pathData) {
|
|
1540
|
+
const path = new paper.Path();
|
|
1541
|
+
path.pathData = pathData;
|
|
1542
|
+
const bounds = path.bounds;
|
|
1543
|
+
path.remove();
|
|
1297
1544
|
return {
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
unit: "px",
|
|
1303
|
-
x: layout.trimRect.centerX,
|
|
1304
|
-
y: layout.trimRect.centerY,
|
|
1305
|
-
width: layout.trimRect.width,
|
|
1306
|
-
height: layout.trimRect.height,
|
|
1307
|
-
radius: radiusMm * layout.scale,
|
|
1308
|
-
offset,
|
|
1309
|
-
scale: layout.scale,
|
|
1310
|
-
pathData: configService.get("dieline.pathData"),
|
|
1311
|
-
customSourceWidthPx: Number.isFinite(sourceWidth) && sourceWidth > 0 ? sourceWidth : void 0,
|
|
1312
|
-
customSourceHeightPx: Number.isFinite(sourceHeight) && sourceHeight > 0 ? sourceHeight : void 0
|
|
1545
|
+
x: bounds.x,
|
|
1546
|
+
y: bounds.y,
|
|
1547
|
+
width: bounds.width,
|
|
1548
|
+
height: bounds.height
|
|
1313
1549
|
};
|
|
1314
1550
|
}
|
|
1315
1551
|
|
|
1316
1552
|
// src/extensions/image.ts
|
|
1317
1553
|
var IMAGE_OBJECT_LAYER_ID = "image.user";
|
|
1318
1554
|
var IMAGE_OVERLAY_LAYER_ID = "image-overlay";
|
|
1555
|
+
var IMAGE_DEFAULT_CONTROL_CAPABILITIES = [
|
|
1556
|
+
"rotate",
|
|
1557
|
+
"scale"
|
|
1558
|
+
];
|
|
1559
|
+
var IMAGE_CONTROL_DESCRIPTORS = [
|
|
1560
|
+
{
|
|
1561
|
+
key: "tl",
|
|
1562
|
+
capability: "rotate",
|
|
1563
|
+
create: () => new Control({
|
|
1564
|
+
x: -0.5,
|
|
1565
|
+
y: -0.5,
|
|
1566
|
+
actionName: "rotate",
|
|
1567
|
+
actionHandler: controlsUtils.rotationWithSnapping,
|
|
1568
|
+
cursorStyleHandler: controlsUtils.rotationStyleHandler
|
|
1569
|
+
})
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
key: "br",
|
|
1573
|
+
capability: "scale",
|
|
1574
|
+
create: () => new Control({
|
|
1575
|
+
x: 0.5,
|
|
1576
|
+
y: 0.5,
|
|
1577
|
+
actionName: "scale",
|
|
1578
|
+
actionHandler: controlsUtils.scalingEqually,
|
|
1579
|
+
cursorStyleHandler: controlsUtils.scaleCursorStyleHandler
|
|
1580
|
+
})
|
|
1581
|
+
}
|
|
1582
|
+
];
|
|
1319
1583
|
var ImageTool = class {
|
|
1320
1584
|
constructor() {
|
|
1321
1585
|
this.id = "pooder.kit.image";
|
|
@@ -1332,7 +1596,9 @@ var ImageTool = class {
|
|
|
1332
1596
|
this.isImageSelectionActive = false;
|
|
1333
1597
|
this.focusedImageId = null;
|
|
1334
1598
|
this.renderSeq = 0;
|
|
1599
|
+
this.imageSpecs = [];
|
|
1335
1600
|
this.overlaySpecs = [];
|
|
1601
|
+
this.imageControlsByCapabilityKey = /* @__PURE__ */ new Map();
|
|
1336
1602
|
this.onToolActivated = (event) => {
|
|
1337
1603
|
const before = this.isToolActive;
|
|
1338
1604
|
this.syncToolActiveFromWorkbench(event.id);
|
|
@@ -1439,9 +1705,34 @@ var ImageTool = class {
|
|
|
1439
1705
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
1440
1706
|
this.id,
|
|
1441
1707
|
() => ({
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1708
|
+
passes: [
|
|
1709
|
+
{
|
|
1710
|
+
id: IMAGE_OBJECT_LAYER_ID,
|
|
1711
|
+
stack: 500,
|
|
1712
|
+
order: 0,
|
|
1713
|
+
visibility: {
|
|
1714
|
+
op: "not",
|
|
1715
|
+
expr: {
|
|
1716
|
+
op: "sessionActive",
|
|
1717
|
+
toolId: "pooder.kit.white-ink"
|
|
1718
|
+
}
|
|
1719
|
+
},
|
|
1720
|
+
objects: this.imageSpecs
|
|
1721
|
+
},
|
|
1722
|
+
{
|
|
1723
|
+
id: IMAGE_OVERLAY_LAYER_ID,
|
|
1724
|
+
stack: 800,
|
|
1725
|
+
order: 0,
|
|
1726
|
+
visibility: {
|
|
1727
|
+
op: "not",
|
|
1728
|
+
expr: {
|
|
1729
|
+
op: "sessionActive",
|
|
1730
|
+
toolId: "pooder.kit.white-ink"
|
|
1731
|
+
}
|
|
1732
|
+
},
|
|
1733
|
+
objects: this.overlaySpecs
|
|
1734
|
+
}
|
|
1735
|
+
]
|
|
1445
1736
|
}),
|
|
1446
1737
|
{ priority: 300 }
|
|
1447
1738
|
);
|
|
@@ -1472,7 +1763,10 @@ var ImageTool = class {
|
|
|
1472
1763
|
this.updateImages();
|
|
1473
1764
|
return;
|
|
1474
1765
|
}
|
|
1475
|
-
if (e.key.startsWith("size.") || e.key.startsWith("image.frame.")) {
|
|
1766
|
+
if (e.key.startsWith("size.") || e.key.startsWith("image.frame.") || e.key.startsWith("image.control.")) {
|
|
1767
|
+
if (e.key.startsWith("image.control.")) {
|
|
1768
|
+
this.imageControlsByCapabilityKey.clear();
|
|
1769
|
+
}
|
|
1476
1770
|
this.updateImages();
|
|
1477
1771
|
}
|
|
1478
1772
|
});
|
|
@@ -1498,7 +1792,9 @@ var ImageTool = class {
|
|
|
1498
1792
|
this.cropShapeHatchPattern = void 0;
|
|
1499
1793
|
this.cropShapeHatchPatternColor = void 0;
|
|
1500
1794
|
this.cropShapeHatchPatternKey = void 0;
|
|
1795
|
+
this.imageSpecs = [];
|
|
1501
1796
|
this.overlaySpecs = [];
|
|
1797
|
+
this.imageControlsByCapabilityKey.clear();
|
|
1502
1798
|
this.clearRenderedImages();
|
|
1503
1799
|
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
1504
1800
|
this.renderProducerDisposable = void 0;
|
|
@@ -1521,6 +1817,90 @@ var ImageTool = class {
|
|
|
1521
1817
|
isImageEditingVisible() {
|
|
1522
1818
|
return this.isToolActive || this.isImageSelectionActive || !!this.focusedImageId;
|
|
1523
1819
|
}
|
|
1820
|
+
getEnabledImageControlCapabilities() {
|
|
1821
|
+
return IMAGE_DEFAULT_CONTROL_CAPABILITIES;
|
|
1822
|
+
}
|
|
1823
|
+
getImageControls(capabilities) {
|
|
1824
|
+
const normalized = [...new Set(capabilities)].sort();
|
|
1825
|
+
const cacheKey = normalized.join("|");
|
|
1826
|
+
const cached = this.imageControlsByCapabilityKey.get(cacheKey);
|
|
1827
|
+
if (cached) {
|
|
1828
|
+
return cached;
|
|
1829
|
+
}
|
|
1830
|
+
const enabled = new Set(normalized);
|
|
1831
|
+
const controls = {};
|
|
1832
|
+
IMAGE_CONTROL_DESCRIPTORS.forEach((descriptor) => {
|
|
1833
|
+
if (!enabled.has(descriptor.capability)) return;
|
|
1834
|
+
controls[descriptor.key] = descriptor.create();
|
|
1835
|
+
});
|
|
1836
|
+
this.imageControlsByCapabilityKey.set(cacheKey, controls);
|
|
1837
|
+
return controls;
|
|
1838
|
+
}
|
|
1839
|
+
getImageControlVisualConfig() {
|
|
1840
|
+
var _a, _b, _c, _d;
|
|
1841
|
+
const cornerSizeRaw = Number(
|
|
1842
|
+
(_a = this.getConfig("image.control.cornerSize", 14)) != null ? _a : 14
|
|
1843
|
+
);
|
|
1844
|
+
const touchCornerSizeRaw = Number(
|
|
1845
|
+
(_b = this.getConfig("image.control.touchCornerSize", 24)) != null ? _b : 24
|
|
1846
|
+
);
|
|
1847
|
+
const borderScaleFactorRaw = Number(
|
|
1848
|
+
(_c = this.getConfig("image.control.borderScaleFactor", 1.5)) != null ? _c : 1.5
|
|
1849
|
+
);
|
|
1850
|
+
const paddingRaw = Number(
|
|
1851
|
+
(_d = this.getConfig("image.control.padding", 0)) != null ? _d : 0
|
|
1852
|
+
);
|
|
1853
|
+
const cornerStyleRaw = this.getConfig(
|
|
1854
|
+
"image.control.cornerStyle",
|
|
1855
|
+
"circle"
|
|
1856
|
+
) || "circle";
|
|
1857
|
+
const cornerStyle = cornerStyleRaw === "rect" ? "rect" : "circle";
|
|
1858
|
+
return {
|
|
1859
|
+
cornerSize: Number.isFinite(cornerSizeRaw) ? Math.max(4, Math.min(64, cornerSizeRaw)) : 14,
|
|
1860
|
+
touchCornerSize: Number.isFinite(touchCornerSizeRaw) ? Math.max(8, Math.min(96, touchCornerSizeRaw)) : 24,
|
|
1861
|
+
cornerStyle,
|
|
1862
|
+
cornerColor: this.getConfig("image.control.cornerColor", "#ffffff") || "#ffffff",
|
|
1863
|
+
cornerStrokeColor: this.getConfig("image.control.cornerStrokeColor", "#1677ff") || "#1677ff",
|
|
1864
|
+
transparentCorners: !!this.getConfig(
|
|
1865
|
+
"image.control.transparentCorners",
|
|
1866
|
+
false
|
|
1867
|
+
),
|
|
1868
|
+
borderColor: this.getConfig("image.control.borderColor", "#1677ff") || "#1677ff",
|
|
1869
|
+
borderScaleFactor: Number.isFinite(borderScaleFactorRaw) ? Math.max(0.5, Math.min(8, borderScaleFactorRaw)) : 1.5,
|
|
1870
|
+
padding: Number.isFinite(paddingRaw) ? Math.max(0, Math.min(64, paddingRaw)) : 0
|
|
1871
|
+
};
|
|
1872
|
+
}
|
|
1873
|
+
applyImageObjectInteractionState(obj) {
|
|
1874
|
+
var _a;
|
|
1875
|
+
if (!obj) return;
|
|
1876
|
+
const visible = this.isImageEditingVisible();
|
|
1877
|
+
const visual = this.getImageControlVisualConfig();
|
|
1878
|
+
obj.set({
|
|
1879
|
+
selectable: visible,
|
|
1880
|
+
evented: visible,
|
|
1881
|
+
hasControls: visible,
|
|
1882
|
+
hasBorders: visible,
|
|
1883
|
+
lockScalingFlip: true,
|
|
1884
|
+
cornerSize: visual.cornerSize,
|
|
1885
|
+
touchCornerSize: visual.touchCornerSize,
|
|
1886
|
+
cornerStyle: visual.cornerStyle,
|
|
1887
|
+
cornerColor: visual.cornerColor,
|
|
1888
|
+
cornerStrokeColor: visual.cornerStrokeColor,
|
|
1889
|
+
transparentCorners: visual.transparentCorners,
|
|
1890
|
+
borderColor: visual.borderColor,
|
|
1891
|
+
borderScaleFactor: visual.borderScaleFactor,
|
|
1892
|
+
padding: visual.padding
|
|
1893
|
+
});
|
|
1894
|
+
obj.controls = this.getImageControls(
|
|
1895
|
+
this.getEnabledImageControlCapabilities()
|
|
1896
|
+
);
|
|
1897
|
+
(_a = obj.setCoords) == null ? void 0 : _a.call(obj);
|
|
1898
|
+
}
|
|
1899
|
+
refreshImageObjectInteractionState() {
|
|
1900
|
+
this.getImageObjects().forEach(
|
|
1901
|
+
(obj) => this.applyImageObjectInteractionState(obj)
|
|
1902
|
+
);
|
|
1903
|
+
}
|
|
1524
1904
|
isDebugEnabled() {
|
|
1525
1905
|
return !!this.getConfig("image.debug", false);
|
|
1526
1906
|
}
|
|
@@ -1552,17 +1932,84 @@ var ImageTool = class {
|
|
|
1552
1932
|
],
|
|
1553
1933
|
[ContributionPointIds2.CONFIGURATIONS]: [
|
|
1554
1934
|
{
|
|
1555
|
-
id: "image.items",
|
|
1556
|
-
type: "array",
|
|
1557
|
-
label: "Images",
|
|
1558
|
-
default: []
|
|
1935
|
+
id: "image.items",
|
|
1936
|
+
type: "array",
|
|
1937
|
+
label: "Images",
|
|
1938
|
+
default: []
|
|
1939
|
+
},
|
|
1940
|
+
{
|
|
1941
|
+
id: "image.debug",
|
|
1942
|
+
type: "boolean",
|
|
1943
|
+
label: "Image Debug Log",
|
|
1944
|
+
default: false
|
|
1945
|
+
},
|
|
1946
|
+
{
|
|
1947
|
+
id: "image.control.cornerSize",
|
|
1948
|
+
type: "number",
|
|
1949
|
+
label: "Image Control Corner Size",
|
|
1950
|
+
min: 4,
|
|
1951
|
+
max: 64,
|
|
1952
|
+
step: 1,
|
|
1953
|
+
default: 14
|
|
1954
|
+
},
|
|
1955
|
+
{
|
|
1956
|
+
id: "image.control.touchCornerSize",
|
|
1957
|
+
type: "number",
|
|
1958
|
+
label: "Image Control Touch Corner Size",
|
|
1959
|
+
min: 8,
|
|
1960
|
+
max: 96,
|
|
1961
|
+
step: 1,
|
|
1962
|
+
default: 24
|
|
1963
|
+
},
|
|
1964
|
+
{
|
|
1965
|
+
id: "image.control.cornerStyle",
|
|
1966
|
+
type: "select",
|
|
1967
|
+
label: "Image Control Corner Style",
|
|
1968
|
+
options: ["circle", "rect"],
|
|
1969
|
+
default: "circle"
|
|
1559
1970
|
},
|
|
1560
1971
|
{
|
|
1561
|
-
id: "image.
|
|
1972
|
+
id: "image.control.cornerColor",
|
|
1973
|
+
type: "color",
|
|
1974
|
+
label: "Image Control Corner Color",
|
|
1975
|
+
default: "#ffffff"
|
|
1976
|
+
},
|
|
1977
|
+
{
|
|
1978
|
+
id: "image.control.cornerStrokeColor",
|
|
1979
|
+
type: "color",
|
|
1980
|
+
label: "Image Control Corner Stroke Color",
|
|
1981
|
+
default: "#1677ff"
|
|
1982
|
+
},
|
|
1983
|
+
{
|
|
1984
|
+
id: "image.control.transparentCorners",
|
|
1562
1985
|
type: "boolean",
|
|
1563
|
-
label: "Image
|
|
1986
|
+
label: "Image Control Transparent Corners",
|
|
1564
1987
|
default: false
|
|
1565
1988
|
},
|
|
1989
|
+
{
|
|
1990
|
+
id: "image.control.borderColor",
|
|
1991
|
+
type: "color",
|
|
1992
|
+
label: "Image Control Border Color",
|
|
1993
|
+
default: "#1677ff"
|
|
1994
|
+
},
|
|
1995
|
+
{
|
|
1996
|
+
id: "image.control.borderScaleFactor",
|
|
1997
|
+
type: "number",
|
|
1998
|
+
label: "Image Control Border Width",
|
|
1999
|
+
min: 0.5,
|
|
2000
|
+
max: 8,
|
|
2001
|
+
step: 0.1,
|
|
2002
|
+
default: 1.5
|
|
2003
|
+
},
|
|
2004
|
+
{
|
|
2005
|
+
id: "image.control.padding",
|
|
2006
|
+
type: "number",
|
|
2007
|
+
label: "Image Control Padding",
|
|
2008
|
+
min: 0,
|
|
2009
|
+
max: 64,
|
|
2010
|
+
step: 1,
|
|
2011
|
+
default: 0
|
|
2012
|
+
},
|
|
1566
2013
|
{
|
|
1567
2014
|
id: "image.frame.strokeColor",
|
|
1568
2015
|
type: "color",
|
|
@@ -1800,12 +2247,7 @@ var ImageTool = class {
|
|
|
1800
2247
|
} else {
|
|
1801
2248
|
const obj = this.getImageObject(id);
|
|
1802
2249
|
if (obj) {
|
|
1803
|
-
|
|
1804
|
-
selectable: true,
|
|
1805
|
-
evented: true,
|
|
1806
|
-
hasControls: true,
|
|
1807
|
-
hasBorders: true
|
|
1808
|
-
});
|
|
2250
|
+
this.applyImageObjectInteractionState(obj);
|
|
1809
2251
|
canvas.setActiveObject(obj);
|
|
1810
2252
|
}
|
|
1811
2253
|
}
|
|
@@ -1973,9 +2415,7 @@ var ImageTool = class {
|
|
|
1973
2415
|
}
|
|
1974
2416
|
getOverlayObjects() {
|
|
1975
2417
|
if (!this.canvasService) return [];
|
|
1976
|
-
return this.canvasService.
|
|
1977
|
-
IMAGE_OVERLAY_LAYER_ID
|
|
1978
|
-
);
|
|
2418
|
+
return this.canvasService.getPassObjects(IMAGE_OVERLAY_LAYER_ID);
|
|
1979
2419
|
}
|
|
1980
2420
|
getImageObject(id) {
|
|
1981
2421
|
return this.getImageObjects().find((obj) => {
|
|
@@ -1985,9 +2425,9 @@ var ImageTool = class {
|
|
|
1985
2425
|
}
|
|
1986
2426
|
clearRenderedImages() {
|
|
1987
2427
|
if (!this.canvasService) return;
|
|
1988
|
-
|
|
1989
|
-
this.
|
|
1990
|
-
this.canvasService.
|
|
2428
|
+
this.imageSpecs = [];
|
|
2429
|
+
this.overlaySpecs = [];
|
|
2430
|
+
this.canvasService.requestRenderFromProducers();
|
|
1991
2431
|
}
|
|
1992
2432
|
purgeSourceSizeCacheForItem(item) {
|
|
1993
2433
|
if (!item) return;
|
|
@@ -2015,6 +2455,29 @@ var ImageTool = class {
|
|
|
2015
2455
|
}
|
|
2016
2456
|
return { width: 1, height: 1 };
|
|
2017
2457
|
}
|
|
2458
|
+
async ensureSourceSize(src) {
|
|
2459
|
+
if (!src) return null;
|
|
2460
|
+
const cached = this.sourceSizeBySrc.get(src);
|
|
2461
|
+
if (cached) return cached;
|
|
2462
|
+
try {
|
|
2463
|
+
const image = await FabricImage2.fromURL(src, {
|
|
2464
|
+
crossOrigin: "anonymous"
|
|
2465
|
+
});
|
|
2466
|
+
const width = Number((image == null ? void 0 : image.width) || 0);
|
|
2467
|
+
const height = Number((image == null ? void 0 : image.height) || 0);
|
|
2468
|
+
if (width > 0 && height > 0) {
|
|
2469
|
+
const size = { width, height };
|
|
2470
|
+
this.sourceSizeBySrc.set(src, size);
|
|
2471
|
+
return size;
|
|
2472
|
+
}
|
|
2473
|
+
} catch (error) {
|
|
2474
|
+
this.debug("image:size:load-failed", {
|
|
2475
|
+
src,
|
|
2476
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2477
|
+
});
|
|
2478
|
+
}
|
|
2479
|
+
return null;
|
|
2480
|
+
}
|
|
2018
2481
|
getCoverScale(frame, size) {
|
|
2019
2482
|
const sw = Math.max(1, size.width);
|
|
2020
2483
|
const sh = Math.max(1, size.height);
|
|
@@ -2349,24 +2812,6 @@ var ImageTool = class {
|
|
|
2349
2812
|
opacity: render.opacity
|
|
2350
2813
|
};
|
|
2351
2814
|
}
|
|
2352
|
-
toScreenObjectProps(props) {
|
|
2353
|
-
if (!this.canvasService) return props;
|
|
2354
|
-
const next = { ...props };
|
|
2355
|
-
if (Number.isFinite(next.left) || Number.isFinite(next.top)) {
|
|
2356
|
-
const mapped = this.canvasService.toScreenPoint({
|
|
2357
|
-
x: Number.isFinite(next.left) ? Number(next.left) : 0,
|
|
2358
|
-
y: Number.isFinite(next.top) ? Number(next.top) : 0
|
|
2359
|
-
});
|
|
2360
|
-
if (Number.isFinite(next.left)) next.left = mapped.x;
|
|
2361
|
-
if (Number.isFinite(next.top)) next.top = mapped.y;
|
|
2362
|
-
}
|
|
2363
|
-
const sceneScale = this.canvasService.getSceneScale();
|
|
2364
|
-
const sx = Number.isFinite(next.scaleX) ? Number(next.scaleX) : 1;
|
|
2365
|
-
const sy = Number.isFinite(next.scaleY) ? Number(next.scaleY) : 1;
|
|
2366
|
-
next.scaleX = sx * sceneScale;
|
|
2367
|
-
next.scaleY = sy * sceneScale;
|
|
2368
|
-
return next;
|
|
2369
|
-
}
|
|
2370
2815
|
toSceneObjectScale(value) {
|
|
2371
2816
|
if (!this.canvasService) return value;
|
|
2372
2817
|
return value / this.canvasService.getSceneScale();
|
|
@@ -2377,104 +2822,27 @@ var ImageTool = class {
|
|
|
2377
2822
|
if (typeof obj.getSrc === "function") return obj.getSrc();
|
|
2378
2823
|
return (_a = obj == null ? void 0 : obj._originalElement) == null ? void 0 : _a.src;
|
|
2379
2824
|
}
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
});
|
|
2393
|
-
}
|
|
2394
|
-
async upsertImageObject(item, frame, seq) {
|
|
2395
|
-
if (!this.canvasService) return;
|
|
2396
|
-
const canvas = this.canvasService.canvas;
|
|
2397
|
-
const render = this.resolveRenderImageState(item);
|
|
2398
|
-
if (!render.src) return;
|
|
2399
|
-
let obj = this.getImageObject(item.id);
|
|
2400
|
-
const currentSrc = this.getCurrentSrc(obj);
|
|
2401
|
-
if (obj && currentSrc && currentSrc !== render.src) {
|
|
2402
|
-
canvas.remove(obj);
|
|
2403
|
-
obj = void 0;
|
|
2404
|
-
}
|
|
2405
|
-
if (!obj) {
|
|
2406
|
-
const created = await FabricImage2.fromURL(render.src, {
|
|
2407
|
-
crossOrigin: "anonymous"
|
|
2408
|
-
});
|
|
2409
|
-
if (seq !== this.renderSeq) return;
|
|
2410
|
-
created.set({
|
|
2825
|
+
async buildImageSpecs(items, frame) {
|
|
2826
|
+
const specs = [];
|
|
2827
|
+
for (const item of items) {
|
|
2828
|
+
const render = this.resolveRenderImageState(item);
|
|
2829
|
+
if (!render.src) continue;
|
|
2830
|
+
const ensured = await this.ensureSourceSize(render.src);
|
|
2831
|
+
const sourceSize = ensured || this.getSourceSize(render.src);
|
|
2832
|
+
const props = this.computeCanvasProps(render, sourceSize, frame);
|
|
2833
|
+
specs.push({
|
|
2834
|
+
id: item.id,
|
|
2835
|
+
type: "image",
|
|
2836
|
+
src: render.src,
|
|
2411
2837
|
data: {
|
|
2412
2838
|
id: item.id,
|
|
2413
2839
|
layerId: IMAGE_OBJECT_LAYER_ID,
|
|
2414
2840
|
type: "image-item"
|
|
2415
|
-
}
|
|
2841
|
+
},
|
|
2842
|
+
props
|
|
2416
2843
|
});
|
|
2417
|
-
canvas.add(created);
|
|
2418
|
-
obj = created;
|
|
2419
|
-
}
|
|
2420
|
-
this.rememberSourceSize(render.src, obj);
|
|
2421
|
-
const sourceSize = this.getSourceSize(render.src, obj);
|
|
2422
|
-
const props = this.computeCanvasProps(render, sourceSize, frame);
|
|
2423
|
-
const screenProps = this.toScreenObjectProps(props);
|
|
2424
|
-
obj.set({
|
|
2425
|
-
...screenProps,
|
|
2426
|
-
data: {
|
|
2427
|
-
...obj.data || {},
|
|
2428
|
-
id: item.id,
|
|
2429
|
-
layerId: IMAGE_OBJECT_LAYER_ID,
|
|
2430
|
-
type: "image-item"
|
|
2431
|
-
}
|
|
2432
|
-
});
|
|
2433
|
-
this.applyImageControlVisibility(obj);
|
|
2434
|
-
obj.setCoords();
|
|
2435
|
-
const resolver = this.loadResolvers.get(item.id);
|
|
2436
|
-
if (resolver) {
|
|
2437
|
-
resolver();
|
|
2438
|
-
this.loadResolvers.delete(item.id);
|
|
2439
|
-
}
|
|
2440
|
-
}
|
|
2441
|
-
syncImageZOrder(items) {
|
|
2442
|
-
if (!this.canvasService) return;
|
|
2443
|
-
const canvas = this.canvasService.canvas;
|
|
2444
|
-
const objects = canvas.getObjects();
|
|
2445
|
-
let insertIndex = 0;
|
|
2446
|
-
const backgroundLayer = this.canvasService.getLayer("background");
|
|
2447
|
-
if (backgroundLayer) {
|
|
2448
|
-
const bgIndex = objects.indexOf(backgroundLayer);
|
|
2449
|
-
if (bgIndex >= 0) insertIndex = bgIndex + 1;
|
|
2450
|
-
}
|
|
2451
|
-
items.forEach((item) => {
|
|
2452
|
-
const obj = this.getImageObject(item.id);
|
|
2453
|
-
if (!obj) return;
|
|
2454
|
-
canvas.moveObjectTo(obj, insertIndex);
|
|
2455
|
-
insertIndex += 1;
|
|
2456
|
-
});
|
|
2457
|
-
const overlayObjects = this.getOverlayObjects().sort((a, b) => {
|
|
2458
|
-
var _a, _b, _c, _d;
|
|
2459
|
-
const az = Number((_b = (_a = a == null ? void 0 : a.data) == null ? void 0 : _a.zIndex) != null ? _b : 0);
|
|
2460
|
-
const bz = Number((_d = (_c = b == null ? void 0 : b.data) == null ? void 0 : _c.zIndex) != null ? _d : 0);
|
|
2461
|
-
return az - bz;
|
|
2462
|
-
});
|
|
2463
|
-
overlayObjects.forEach((obj) => {
|
|
2464
|
-
canvas.bringObjectToFront(obj);
|
|
2465
|
-
});
|
|
2466
|
-
if (this.isDebugEnabled()) {
|
|
2467
|
-
const stack = canvas.getObjects().map((obj, index) => {
|
|
2468
|
-
var _a, _b, _c;
|
|
2469
|
-
return {
|
|
2470
|
-
index,
|
|
2471
|
-
id: (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id,
|
|
2472
|
-
layerId: (_b = obj == null ? void 0 : obj.data) == null ? void 0 : _b.layerId,
|
|
2473
|
-
zIndex: (_c = obj == null ? void 0 : obj.data) == null ? void 0 : _c.zIndex
|
|
2474
|
-
};
|
|
2475
|
-
}).filter((item) => item.layerId === IMAGE_OVERLAY_LAYER_ID);
|
|
2476
|
-
this.debug("overlay:stack", stack);
|
|
2477
2844
|
}
|
|
2845
|
+
return specs;
|
|
2478
2846
|
}
|
|
2479
2847
|
buildOverlaySpecs(frame, sceneGeometry) {
|
|
2480
2848
|
const visible = this.isImageEditingVisible();
|
|
@@ -2638,7 +3006,7 @@ var ImageTool = class {
|
|
|
2638
3006
|
evented: false
|
|
2639
3007
|
}
|
|
2640
3008
|
};
|
|
2641
|
-
const specs = [...mask, ...shapeOverlay, frameSpec];
|
|
3009
|
+
const specs = shapeOverlay.length > 0 ? [...mask, ...shapeOverlay] : [...mask, ...shapeOverlay, frameSpec];
|
|
2642
3010
|
this.debug("overlay:built", {
|
|
2643
3011
|
frame,
|
|
2644
3012
|
shape: sceneGeometry == null ? void 0 : sceneGeometry.shape,
|
|
@@ -2668,30 +3036,33 @@ var ImageTool = class {
|
|
|
2668
3036
|
skipRender: true
|
|
2669
3037
|
});
|
|
2670
3038
|
}
|
|
2671
|
-
this.
|
|
2672
|
-
var _a, _b;
|
|
2673
|
-
const id = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id;
|
|
2674
|
-
if (typeof id === "string" && !desiredIds.has(id)) {
|
|
2675
|
-
(_b = this.canvasService) == null ? void 0 : _b.canvas.remove(obj);
|
|
2676
|
-
}
|
|
2677
|
-
});
|
|
2678
|
-
for (const item of renderItems) {
|
|
2679
|
-
if (seq !== this.renderSeq) return;
|
|
2680
|
-
await this.upsertImageObject(item, frame, seq);
|
|
2681
|
-
}
|
|
3039
|
+
const imageSpecs = await this.buildImageSpecs(renderItems, frame);
|
|
2682
3040
|
if (seq !== this.renderSeq) return;
|
|
2683
|
-
this.syncImageZOrder(renderItems);
|
|
2684
3041
|
const sceneGeometry = await this.resolveSceneGeometryForOverlay();
|
|
2685
3042
|
if (seq !== this.renderSeq) return;
|
|
2686
|
-
|
|
2687
|
-
this.overlaySpecs =
|
|
3043
|
+
this.imageSpecs = imageSpecs;
|
|
3044
|
+
this.overlaySpecs = this.buildOverlaySpecs(frame, sceneGeometry);
|
|
2688
3045
|
await this.canvasService.flushRenderFromProducers();
|
|
2689
|
-
this.
|
|
3046
|
+
if (seq !== this.renderSeq) return;
|
|
3047
|
+
this.refreshImageObjectInteractionState();
|
|
3048
|
+
renderItems.forEach((item) => {
|
|
3049
|
+
if (!this.getImageObject(item.id)) return;
|
|
3050
|
+
const resolver = this.loadResolvers.get(item.id);
|
|
3051
|
+
if (!resolver) return;
|
|
3052
|
+
resolver();
|
|
3053
|
+
this.loadResolvers.delete(item.id);
|
|
3054
|
+
});
|
|
3055
|
+
if (this.focusedImageId && this.isToolActive) {
|
|
3056
|
+
this.setImageFocus(this.focusedImageId, {
|
|
3057
|
+
syncCanvasSelection: true,
|
|
3058
|
+
skipRender: true
|
|
3059
|
+
});
|
|
3060
|
+
}
|
|
2690
3061
|
const overlayCanvasCount = this.getOverlayObjects().length;
|
|
2691
3062
|
this.debug("render:done", {
|
|
2692
3063
|
seq,
|
|
2693
3064
|
renderCount: renderItems.length,
|
|
2694
|
-
overlayCount: overlaySpecs.length,
|
|
3065
|
+
overlayCount: this.overlaySpecs.length,
|
|
2695
3066
|
overlayCanvasCount,
|
|
2696
3067
|
isToolActive: this.isToolActive,
|
|
2697
3068
|
isImageSelectionActive: this.isImageSelectionActive,
|
|
@@ -4341,11 +4712,11 @@ var DielineTool = class {
|
|
|
4341
4712
|
style: "solid"
|
|
4342
4713
|
},
|
|
4343
4714
|
insideColor: "rgba(0,0,0,0)",
|
|
4344
|
-
outsideColor: "#ffffff",
|
|
4345
4715
|
showBleedLines: true,
|
|
4346
4716
|
features: []
|
|
4347
4717
|
};
|
|
4348
4718
|
this.specs = [];
|
|
4719
|
+
this.effects = [];
|
|
4349
4720
|
this.renderSeq = 0;
|
|
4350
4721
|
this.onCanvasResized = () => {
|
|
4351
4722
|
this.updateDieline();
|
|
@@ -4382,10 +4753,23 @@ var DielineTool = class {
|
|
|
4382
4753
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
4383
4754
|
this.id,
|
|
4384
4755
|
() => ({
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4756
|
+
passes: [
|
|
4757
|
+
{
|
|
4758
|
+
id: DIELINE_LAYER_ID,
|
|
4759
|
+
stack: 700,
|
|
4760
|
+
order: 0,
|
|
4761
|
+
replace: true,
|
|
4762
|
+
visibility: {
|
|
4763
|
+
op: "not",
|
|
4764
|
+
expr: {
|
|
4765
|
+
op: "activeToolIn",
|
|
4766
|
+
ids: ["pooder.kit.image", "pooder.kit.white-ink"]
|
|
4767
|
+
}
|
|
4768
|
+
},
|
|
4769
|
+
effects: this.effects,
|
|
4770
|
+
objects: this.specs
|
|
4771
|
+
}
|
|
4772
|
+
]
|
|
4389
4773
|
}),
|
|
4390
4774
|
{ priority: 250 }
|
|
4391
4775
|
);
|
|
@@ -4441,10 +4825,6 @@ var DielineTool = class {
|
|
|
4441
4825
|
s.offsetLine.style
|
|
4442
4826
|
);
|
|
4443
4827
|
s.insideColor = configService.get("dieline.insideColor", s.insideColor);
|
|
4444
|
-
s.outsideColor = configService.get(
|
|
4445
|
-
"dieline.outsideColor",
|
|
4446
|
-
s.outsideColor
|
|
4447
|
-
);
|
|
4448
4828
|
s.showBleedLines = configService.get(
|
|
4449
4829
|
"dieline.showBleedLines",
|
|
4450
4830
|
s.showBleedLines
|
|
@@ -4507,9 +4887,6 @@ var DielineTool = class {
|
|
|
4507
4887
|
case "dieline.insideColor":
|
|
4508
4888
|
s.insideColor = e.value;
|
|
4509
4889
|
break;
|
|
4510
|
-
case "dieline.outsideColor":
|
|
4511
|
-
s.outsideColor = e.value;
|
|
4512
|
-
break;
|
|
4513
4890
|
case "dieline.showBleedLines":
|
|
4514
4891
|
s.showBleedLines = e.value;
|
|
4515
4892
|
break;
|
|
@@ -4538,6 +4915,7 @@ var DielineTool = class {
|
|
|
4538
4915
|
context.eventBus.off("canvas:resized", this.onCanvasResized);
|
|
4539
4916
|
this.renderSeq += 1;
|
|
4540
4917
|
this.specs = [];
|
|
4918
|
+
this.effects = [];
|
|
4541
4919
|
(_a = this.renderProducerDisposable) == null ? void 0 : _a.dispose();
|
|
4542
4920
|
this.renderProducerDisposable = void 0;
|
|
4543
4921
|
if (this.canvasService) {
|
|
@@ -4654,12 +5032,6 @@ var DielineTool = class {
|
|
|
4654
5032
|
label: "Inside Color",
|
|
4655
5033
|
default: s.insideColor
|
|
4656
5034
|
},
|
|
4657
|
-
{
|
|
4658
|
-
id: "dieline.outsideColor",
|
|
4659
|
-
type: "color",
|
|
4660
|
-
label: "Outside Color",
|
|
4661
|
-
default: s.outsideColor
|
|
4662
|
-
},
|
|
4663
5035
|
{
|
|
4664
5036
|
id: "dieline.features",
|
|
4665
5037
|
type: "json",
|
|
@@ -4783,6 +5155,12 @@ var DielineTool = class {
|
|
|
4783
5155
|
"ConfigurationService"
|
|
4784
5156
|
);
|
|
4785
5157
|
}
|
|
5158
|
+
hasImageItems() {
|
|
5159
|
+
const configService = this.getConfigService();
|
|
5160
|
+
if (!configService) return false;
|
|
5161
|
+
const items = configService.get("image.items", []);
|
|
5162
|
+
return Array.isArray(items) && items.length > 0;
|
|
5163
|
+
}
|
|
4786
5164
|
syncSizeState(configService) {
|
|
4787
5165
|
const sizeState = readSizeState(configService);
|
|
4788
5166
|
this.state.width = sizeState.actualWidthMm;
|
|
@@ -4790,29 +5168,6 @@ var DielineTool = class {
|
|
|
4790
5168
|
this.state.padding = sizeState.viewPadding;
|
|
4791
5169
|
this.state.offset = sizeState.cutMode === "outset" ? sizeState.cutMarginMm : sizeState.cutMode === "inset" ? -sizeState.cutMarginMm : 0;
|
|
4792
5170
|
}
|
|
4793
|
-
bringFeatureMarkersToFront() {
|
|
4794
|
-
if (!this.canvasService) return;
|
|
4795
|
-
const canvas = this.canvasService.canvas;
|
|
4796
|
-
canvas.getObjects().filter((obj) => {
|
|
4797
|
-
var _a;
|
|
4798
|
-
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.type) === "feature-marker";
|
|
4799
|
-
}).forEach((obj) => canvas.bringObjectToFront(obj));
|
|
4800
|
-
}
|
|
4801
|
-
ensureLayerStacking() {
|
|
4802
|
-
if (!this.canvasService) return;
|
|
4803
|
-
const layer = this.canvasService.getLayer(DIELINE_LAYER_ID);
|
|
4804
|
-
if (!layer) return;
|
|
4805
|
-
const userLayer = this.canvasService.getLayer("user");
|
|
4806
|
-
if (userLayer) {
|
|
4807
|
-
const layerIndex = this.canvasService.canvas.getObjects().indexOf(layer);
|
|
4808
|
-
const userIndex = this.canvasService.canvas.getObjects().indexOf(userLayer);
|
|
4809
|
-
if (layerIndex < userIndex) {
|
|
4810
|
-
this.canvasService.canvas.moveObjectTo(layer, userIndex + 1);
|
|
4811
|
-
}
|
|
4812
|
-
return;
|
|
4813
|
-
}
|
|
4814
|
-
this.canvasService.canvas.bringObjectToFront(layer);
|
|
4815
|
-
}
|
|
4816
5171
|
buildDielineSpecs(sceneLayout) {
|
|
4817
5172
|
var _a, _b;
|
|
4818
5173
|
const {
|
|
@@ -4822,10 +5177,10 @@ var DielineTool = class {
|
|
|
4822
5177
|
mainLine,
|
|
4823
5178
|
offsetLine,
|
|
4824
5179
|
insideColor,
|
|
4825
|
-
outsideColor,
|
|
4826
5180
|
showBleedLines,
|
|
4827
5181
|
features
|
|
4828
5182
|
} = this.state;
|
|
5183
|
+
const hasImages = this.hasImageItems();
|
|
4829
5184
|
const canvasW = sceneLayout.canvasWidth || ((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 800;
|
|
4830
5185
|
const canvasH = sceneLayout.canvasHeight || ((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 600;
|
|
4831
5186
|
const scale = sceneLayout.scale;
|
|
@@ -4847,41 +5202,8 @@ var DielineTool = class {
|
|
|
4847
5202
|
radius: (f.radius || 0) * scale
|
|
4848
5203
|
}));
|
|
4849
5204
|
const cutFeatures = absoluteFeatures.filter((f) => !f.skipCut);
|
|
4850
|
-
const
|
|
4851
|
-
|
|
4852
|
-
canvasHeight: canvasH,
|
|
4853
|
-
shape,
|
|
4854
|
-
width: cutW,
|
|
4855
|
-
height: cutH,
|
|
4856
|
-
radius: cutR,
|
|
4857
|
-
x: cx,
|
|
4858
|
-
y: cy,
|
|
4859
|
-
features: cutFeatures,
|
|
4860
|
-
shapeStyle,
|
|
4861
|
-
pathData: this.state.pathData,
|
|
4862
|
-
customSourceWidthPx: this.state.customSourceWidthPx,
|
|
4863
|
-
customSourceHeightPx: this.state.customSourceHeightPx
|
|
4864
|
-
});
|
|
4865
|
-
const specs = [
|
|
4866
|
-
{
|
|
4867
|
-
id: "dieline.mask",
|
|
4868
|
-
type: "path",
|
|
4869
|
-
space: "screen",
|
|
4870
|
-
data: { id: "dieline.mask", type: "dieline" },
|
|
4871
|
-
props: {
|
|
4872
|
-
pathData: maskPathData,
|
|
4873
|
-
fill: outsideColor,
|
|
4874
|
-
stroke: null,
|
|
4875
|
-
selectable: false,
|
|
4876
|
-
evented: false,
|
|
4877
|
-
originX: "left",
|
|
4878
|
-
originY: "top",
|
|
4879
|
-
left: 0,
|
|
4880
|
-
top: 0
|
|
4881
|
-
}
|
|
4882
|
-
}
|
|
4883
|
-
];
|
|
4884
|
-
if (insideColor && insideColor !== "transparent" && insideColor !== "rgba(0,0,0,0)") {
|
|
5205
|
+
const specs = [];
|
|
5206
|
+
if (insideColor && insideColor !== "transparent" && insideColor !== "rgba(0,0,0,0)" && !hasImages) {
|
|
4885
5207
|
const productPathData = generateDielinePath({
|
|
4886
5208
|
shape,
|
|
4887
5209
|
width: cutW,
|
|
@@ -5035,6 +5357,77 @@ var DielineTool = class {
|
|
|
5035
5357
|
});
|
|
5036
5358
|
return specs;
|
|
5037
5359
|
}
|
|
5360
|
+
buildImageClipEffects(sceneLayout) {
|
|
5361
|
+
var _a, _b;
|
|
5362
|
+
const { shape, shapeStyle, radius, features } = this.state;
|
|
5363
|
+
const canvasW = sceneLayout.canvasWidth || ((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 800;
|
|
5364
|
+
const canvasH = sceneLayout.canvasHeight || ((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 600;
|
|
5365
|
+
const scale = sceneLayout.scale;
|
|
5366
|
+
const cx = sceneLayout.trimRect.centerX;
|
|
5367
|
+
const cy = sceneLayout.trimRect.centerY;
|
|
5368
|
+
const visualWidth = sceneLayout.trimRect.width;
|
|
5369
|
+
const visualRadius = radius * scale;
|
|
5370
|
+
const cutW = sceneLayout.cutRect.width;
|
|
5371
|
+
const cutH = sceneLayout.cutRect.height;
|
|
5372
|
+
const visualOffset = (cutW - visualWidth) / 2;
|
|
5373
|
+
const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
5374
|
+
const absoluteFeatures = (features || []).map((f) => ({
|
|
5375
|
+
...f,
|
|
5376
|
+
x: f.x,
|
|
5377
|
+
y: f.y,
|
|
5378
|
+
width: (f.width || 0) * scale,
|
|
5379
|
+
height: (f.height || 0) * scale,
|
|
5380
|
+
radius: (f.radius || 0) * scale
|
|
5381
|
+
}));
|
|
5382
|
+
const cutFeatures = absoluteFeatures.filter((f) => !f.skipCut);
|
|
5383
|
+
const clipPathData = generateDielinePath({
|
|
5384
|
+
shape,
|
|
5385
|
+
width: cutW,
|
|
5386
|
+
height: cutH,
|
|
5387
|
+
radius: cutR,
|
|
5388
|
+
x: cx,
|
|
5389
|
+
y: cy,
|
|
5390
|
+
features: cutFeatures,
|
|
5391
|
+
shapeStyle,
|
|
5392
|
+
pathData: this.state.pathData,
|
|
5393
|
+
customSourceWidthPx: this.state.customSourceWidthPx,
|
|
5394
|
+
customSourceHeightPx: this.state.customSourceHeightPx,
|
|
5395
|
+
canvasWidth: canvasW,
|
|
5396
|
+
canvasHeight: canvasH
|
|
5397
|
+
});
|
|
5398
|
+
if (!clipPathData) return [];
|
|
5399
|
+
return [
|
|
5400
|
+
{
|
|
5401
|
+
type: "clipPath",
|
|
5402
|
+
id: "dieline.clip.image",
|
|
5403
|
+
visibility: {
|
|
5404
|
+
op: "not",
|
|
5405
|
+
expr: { op: "anySessionActive" }
|
|
5406
|
+
},
|
|
5407
|
+
targetPassIds: [IMAGE_OBJECT_LAYER_ID2],
|
|
5408
|
+
source: {
|
|
5409
|
+
id: "dieline.effect.clip-path",
|
|
5410
|
+
type: "path",
|
|
5411
|
+
space: "screen",
|
|
5412
|
+
data: {
|
|
5413
|
+
id: "dieline.effect.clip-path",
|
|
5414
|
+
type: "dieline-effect",
|
|
5415
|
+
effect: "clipPath"
|
|
5416
|
+
},
|
|
5417
|
+
props: {
|
|
5418
|
+
pathData: clipPathData,
|
|
5419
|
+
fill: "#000000",
|
|
5420
|
+
stroke: null,
|
|
5421
|
+
originX: "left",
|
|
5422
|
+
originY: "top",
|
|
5423
|
+
selectable: false,
|
|
5424
|
+
evented: false,
|
|
5425
|
+
excludeFromExport: true
|
|
5426
|
+
}
|
|
5427
|
+
}
|
|
5428
|
+
}
|
|
5429
|
+
];
|
|
5430
|
+
}
|
|
5038
5431
|
updateDieline(_emitEvent = true) {
|
|
5039
5432
|
void this.updateDielineAsync();
|
|
5040
5433
|
}
|
|
@@ -5051,17 +5444,17 @@ var DielineTool = class {
|
|
|
5051
5444
|
if (!sceneLayout) {
|
|
5052
5445
|
if (seq !== this.renderSeq) return;
|
|
5053
5446
|
this.specs = [];
|
|
5447
|
+
this.effects = [];
|
|
5054
5448
|
await this.canvasService.flushRenderFromProducers();
|
|
5055
5449
|
return;
|
|
5056
5450
|
}
|
|
5057
5451
|
const nextSpecs = this.buildDielineSpecs(sceneLayout);
|
|
5452
|
+
const nextEffects = this.buildImageClipEffects(sceneLayout);
|
|
5058
5453
|
if (seq !== this.renderSeq) return;
|
|
5059
5454
|
this.specs = nextSpecs;
|
|
5455
|
+
this.effects = nextEffects;
|
|
5060
5456
|
await this.canvasService.flushRenderFromProducers();
|
|
5061
5457
|
if (seq !== this.renderSeq) return;
|
|
5062
|
-
this.ensureLayerStacking();
|
|
5063
|
-
this.bringFeatureMarkersToFront();
|
|
5064
|
-
this.canvasService.bringLayerToFront("ruler-overlay");
|
|
5065
5458
|
this.canvasService.requestRenderAll();
|
|
5066
5459
|
}
|
|
5067
5460
|
getGeometry() {
|
|
@@ -5496,9 +5889,14 @@ var FeatureTool = class {
|
|
|
5496
5889
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
5497
5890
|
this.id,
|
|
5498
5891
|
() => ({
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5892
|
+
passes: [
|
|
5893
|
+
{
|
|
5894
|
+
id: FEATURE_OVERLAY_LAYER_ID,
|
|
5895
|
+
stack: 880,
|
|
5896
|
+
order: 0,
|
|
5897
|
+
objects: this.specs
|
|
5898
|
+
}
|
|
5899
|
+
]
|
|
5502
5900
|
}),
|
|
5503
5901
|
{ priority: 350 }
|
|
5504
5902
|
);
|
|
@@ -5641,10 +6039,10 @@ var FeatureTool = class {
|
|
|
5641
6039
|
await this.refreshGeometry();
|
|
5642
6040
|
this.setWorkingFeatures(original);
|
|
5643
6041
|
this.hasWorkingChanges = false;
|
|
6042
|
+
this.clearFeatureSessionState();
|
|
5644
6043
|
this.redraw();
|
|
5645
6044
|
this.emitWorkingChange();
|
|
5646
6045
|
this.updateCommittedFeatures(original);
|
|
5647
|
-
this.clearFeatureSessionState();
|
|
5648
6046
|
return { ok: true };
|
|
5649
6047
|
}
|
|
5650
6048
|
},
|
|
@@ -5960,6 +6358,7 @@ var FeatureTool = class {
|
|
|
5960
6358
|
}
|
|
5961
6359
|
getDraggableMarkerTarget(target) {
|
|
5962
6360
|
var _a, _b;
|
|
6361
|
+
if (!this.isFeatureSessionActive || !this.isToolActive) return null;
|
|
5963
6362
|
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "feature-marker") return null;
|
|
5964
6363
|
if (((_b = target.data) == null ? void 0 : _b.markerRole) !== "handle") return null;
|
|
5965
6364
|
return target;
|
|
@@ -6082,18 +6481,12 @@ var FeatureTool = class {
|
|
|
6082
6481
|
if (seq !== this.renderSeq) return;
|
|
6083
6482
|
await this.canvasService.flushRenderFromProducers();
|
|
6084
6483
|
if (seq !== this.renderSeq) return;
|
|
6085
|
-
this.syncOverlayOrder();
|
|
6086
6484
|
if (options.enforceConstraints) {
|
|
6087
6485
|
this.enforceConstraints();
|
|
6088
6486
|
}
|
|
6089
6487
|
}
|
|
6090
|
-
syncOverlayOrder() {
|
|
6091
|
-
if (!this.canvasService) return;
|
|
6092
|
-
this.canvasService.bringLayerToFront(FEATURE_OVERLAY_LAYER_ID);
|
|
6093
|
-
this.canvasService.bringLayerToFront("ruler-overlay");
|
|
6094
|
-
}
|
|
6095
6488
|
buildFeatureSpecs() {
|
|
6096
|
-
if (!this.currentGeometry || this.workingFeatures.length === 0) {
|
|
6489
|
+
if (!this.isFeatureSessionActive || !this.currentGeometry || this.workingFeatures.length === 0) {
|
|
6097
6490
|
return [];
|
|
6098
6491
|
}
|
|
6099
6492
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -6163,11 +6556,12 @@ var FeatureTool = class {
|
|
|
6163
6556
|
const color = feature.color || (feature.operation === "add" ? "#00FF00" : "#FF0000");
|
|
6164
6557
|
const strokeDash = feature.strokeDash || (feature.operation === "subtract" ? [4, 4] : void 0);
|
|
6165
6558
|
const interactive = options.markerRole === "handle";
|
|
6559
|
+
const sessionVisible = this.isToolActive && this.isFeatureSessionActive;
|
|
6166
6560
|
const baseData = this.buildMarkerData(marker, options);
|
|
6167
6561
|
const commonProps = {
|
|
6168
|
-
visible:
|
|
6169
|
-
selectable: interactive &&
|
|
6170
|
-
evented: interactive &&
|
|
6562
|
+
visible: sessionVisible,
|
|
6563
|
+
selectable: interactive && sessionVisible,
|
|
6564
|
+
evented: interactive && sessionVisible,
|
|
6171
6565
|
hasControls: false,
|
|
6172
6566
|
hasBorders: false,
|
|
6173
6567
|
hoverCursor: interactive ? "move" : "default",
|
|
@@ -6232,7 +6626,7 @@ var FeatureTool = class {
|
|
|
6232
6626
|
markerOffsetY: -visualHeight / 2
|
|
6233
6627
|
},
|
|
6234
6628
|
props: {
|
|
6235
|
-
visible:
|
|
6629
|
+
visible: sessionVisible,
|
|
6236
6630
|
selectable: false,
|
|
6237
6631
|
evented: false,
|
|
6238
6632
|
width: visualWidth,
|
|
@@ -6402,9 +6796,14 @@ var FilmTool = class {
|
|
|
6402
6796
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
6403
6797
|
this.id,
|
|
6404
6798
|
() => ({
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6799
|
+
passes: [
|
|
6800
|
+
{
|
|
6801
|
+
id: FILM_LAYER_ID,
|
|
6802
|
+
stack: 1e3,
|
|
6803
|
+
order: 0,
|
|
6804
|
+
objects: this.specs
|
|
6805
|
+
}
|
|
6806
|
+
]
|
|
6408
6807
|
}),
|
|
6409
6808
|
{ priority: 500 }
|
|
6410
6809
|
);
|
|
@@ -6578,7 +6977,6 @@ var FilmTool = class {
|
|
|
6578
6977
|
this.specs = this.buildFilmSpecs(this.renderImageUrl, this.opacity);
|
|
6579
6978
|
await this.canvasService.flushRenderFromProducers();
|
|
6580
6979
|
if (seq !== this.renderSeq) return;
|
|
6581
|
-
this.canvasService.bringLayerToFront(FILM_LAYER_ID);
|
|
6582
6980
|
this.canvasService.requestRenderAll();
|
|
6583
6981
|
}
|
|
6584
6982
|
};
|
|
@@ -6729,10 +7127,22 @@ var RulerTool = class {
|
|
|
6729
7127
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
6730
7128
|
this.id,
|
|
6731
7129
|
() => ({
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
7130
|
+
passes: [
|
|
7131
|
+
{
|
|
7132
|
+
id: RULER_LAYER_ID,
|
|
7133
|
+
stack: 950,
|
|
7134
|
+
order: 0,
|
|
7135
|
+
replace: true,
|
|
7136
|
+
visibility: {
|
|
7137
|
+
op: "not",
|
|
7138
|
+
expr: {
|
|
7139
|
+
op: "activeToolIn",
|
|
7140
|
+
ids: ["pooder.kit.white-ink"]
|
|
7141
|
+
}
|
|
7142
|
+
},
|
|
7143
|
+
objects: this.specs
|
|
7144
|
+
}
|
|
7145
|
+
]
|
|
6736
7146
|
}),
|
|
6737
7147
|
{ priority: 400 }
|
|
6738
7148
|
);
|
|
@@ -7224,7 +7634,6 @@ var RulerTool = class {
|
|
|
7224
7634
|
this.specs = specs;
|
|
7225
7635
|
await this.canvasService.flushRenderFromProducers();
|
|
7226
7636
|
if (seq !== this.renderSeq) return;
|
|
7227
|
-
this.canvasService.bringLayerToFront(RULER_LAYER_ID);
|
|
7228
7637
|
this.canvasService.requestRenderAll();
|
|
7229
7638
|
this.log("render:done", { seq });
|
|
7230
7639
|
}
|
|
@@ -7238,7 +7647,6 @@ var WHITE_INK_OBJECT_LAYER_ID = "white-ink.user";
|
|
|
7238
7647
|
var WHITE_INK_COVER_LAYER_ID = "white-ink.cover";
|
|
7239
7648
|
var WHITE_INK_OVERLAY_LAYER_ID = "white-ink.overlay";
|
|
7240
7649
|
var IMAGE_OBJECT_LAYER_ID3 = "image.user";
|
|
7241
|
-
var IMAGE_OVERLAY_LAYER_ID2 = "image-overlay";
|
|
7242
7650
|
var WHITE_INK_DEBUG_KEY = "whiteInk.debug";
|
|
7243
7651
|
var WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY = "whiteInk.previewImageVisible";
|
|
7244
7652
|
var WHITE_INK_DEFAULT_OPACITY = 0.85;
|
|
@@ -7316,11 +7724,26 @@ var WhiteInkTool = class {
|
|
|
7316
7724
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
7317
7725
|
this.id,
|
|
7318
7726
|
() => ({
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7727
|
+
passes: [
|
|
7728
|
+
{
|
|
7729
|
+
id: WHITE_INK_COVER_LAYER_ID,
|
|
7730
|
+
stack: 220,
|
|
7731
|
+
order: 0,
|
|
7732
|
+
objects: this.coverSpecs
|
|
7733
|
+
},
|
|
7734
|
+
{
|
|
7735
|
+
id: WHITE_INK_OBJECT_LAYER_ID,
|
|
7736
|
+
stack: 221,
|
|
7737
|
+
order: 0,
|
|
7738
|
+
objects: this.whiteSpecs
|
|
7739
|
+
},
|
|
7740
|
+
{
|
|
7741
|
+
id: WHITE_INK_OVERLAY_LAYER_ID,
|
|
7742
|
+
stack: 790,
|
|
7743
|
+
order: 0,
|
|
7744
|
+
objects: this.overlaySpecs
|
|
7745
|
+
}
|
|
7746
|
+
]
|
|
7324
7747
|
}),
|
|
7325
7748
|
{ priority: 260 }
|
|
7326
7749
|
);
|
|
@@ -7399,7 +7822,6 @@ var WhiteInkTool = class {
|
|
|
7399
7822
|
(_a = this.dirtyTrackerDisposable) == null ? void 0 : _a.dispose();
|
|
7400
7823
|
this.dirtyTrackerDisposable = void 0;
|
|
7401
7824
|
this.clearRenderedWhiteInks();
|
|
7402
|
-
this.applyImageVisibilityForWhiteInk(false);
|
|
7403
7825
|
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
7404
7826
|
this.renderProducerDisposable = void 0;
|
|
7405
7827
|
if (this.canvasService) {
|
|
@@ -8243,22 +8665,6 @@ var WhiteInkTool = class {
|
|
|
8243
8665
|
}
|
|
8244
8666
|
];
|
|
8245
8667
|
}
|
|
8246
|
-
applyImageVisibilityForWhiteInk(previewActive) {
|
|
8247
|
-
if (!this.canvasService) return;
|
|
8248
|
-
const visible = !previewActive;
|
|
8249
|
-
let changed = false;
|
|
8250
|
-
this.canvasService.canvas.getObjects().forEach((obj) => {
|
|
8251
|
-
var _a, _b;
|
|
8252
|
-
if (((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.layerId) !== IMAGE_OBJECT_LAYER_ID3) return;
|
|
8253
|
-
if (obj.visible === visible) return;
|
|
8254
|
-
obj.set({ visible });
|
|
8255
|
-
(_b = obj.setCoords) == null ? void 0 : _b.call(obj);
|
|
8256
|
-
changed = true;
|
|
8257
|
-
});
|
|
8258
|
-
if (changed) {
|
|
8259
|
-
this.canvasService.requestRenderAll();
|
|
8260
|
-
}
|
|
8261
|
-
}
|
|
8262
8668
|
resolveRenderItems() {
|
|
8263
8669
|
if (this.isToolActive) {
|
|
8264
8670
|
return this.cloneItems(this.workingItems);
|
|
@@ -8277,57 +8683,11 @@ var WhiteInkTool = class {
|
|
|
8277
8683
|
]);
|
|
8278
8684
|
const scaleAdjust = this.computeWhiteScaleAdjust(imageSource, whiteSource);
|
|
8279
8685
|
return {
|
|
8280
|
-
whiteSrc: whiteMaskSrc || "",
|
|
8281
|
-
coverSrc: coverMaskSrc || "",
|
|
8282
|
-
whiteScaleAdjustX: scaleAdjust.x,
|
|
8283
|
-
whiteScaleAdjustY: scaleAdjust.y
|
|
8284
|
-
};
|
|
8285
|
-
}
|
|
8286
|
-
resolveDefaultInsertIndex(objects) {
|
|
8287
|
-
if (!this.canvasService) return 0;
|
|
8288
|
-
const backgroundLayer = this.canvasService.getLayer("background");
|
|
8289
|
-
if (!backgroundLayer) return 0;
|
|
8290
|
-
const bgIndex = objects.indexOf(backgroundLayer);
|
|
8291
|
-
if (bgIndex < 0) return 0;
|
|
8292
|
-
return bgIndex + 1;
|
|
8293
|
-
}
|
|
8294
|
-
syncZOrder() {
|
|
8295
|
-
if (!this.canvasService) return;
|
|
8296
|
-
const canvas = this.canvasService.canvas;
|
|
8297
|
-
const whiteObjects = this.canvasService.getRootLayerObjects(
|
|
8298
|
-
WHITE_INK_OBJECT_LAYER_ID
|
|
8299
|
-
);
|
|
8300
|
-
const coverObjects = this.canvasService.getRootLayerObjects(
|
|
8301
|
-
WHITE_INK_COVER_LAYER_ID
|
|
8302
|
-
);
|
|
8303
|
-
const frameObjects = this.canvasService.getRootLayerObjects(
|
|
8304
|
-
WHITE_INK_OVERLAY_LAYER_ID
|
|
8305
|
-
);
|
|
8306
|
-
const currentObjects = canvas.getObjects();
|
|
8307
|
-
const imageIndexes = currentObjects.map(
|
|
8308
|
-
(obj, index) => {
|
|
8309
|
-
var _a;
|
|
8310
|
-
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.layerId) === IMAGE_OBJECT_LAYER_ID3 ? index : -1;
|
|
8311
|
-
}
|
|
8312
|
-
).filter((index) => index >= 0);
|
|
8313
|
-
let whiteInsertIndex = imageIndexes.length ? Math.min(...imageIndexes) : this.resolveDefaultInsertIndex(currentObjects);
|
|
8314
|
-
let coverInsertIndex = whiteInsertIndex;
|
|
8315
|
-
coverObjects.forEach((obj) => {
|
|
8316
|
-
canvas.moveObjectTo(obj, coverInsertIndex);
|
|
8317
|
-
coverInsertIndex += 1;
|
|
8318
|
-
});
|
|
8319
|
-
whiteInsertIndex = coverInsertIndex;
|
|
8320
|
-
whiteObjects.forEach((obj) => {
|
|
8321
|
-
canvas.moveObjectTo(obj, whiteInsertIndex);
|
|
8322
|
-
whiteInsertIndex += 1;
|
|
8323
|
-
});
|
|
8324
|
-
frameObjects.forEach((obj) => canvas.bringObjectToFront(obj));
|
|
8325
|
-
canvas.getObjects().filter((obj) => {
|
|
8326
|
-
var _a;
|
|
8327
|
-
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.layerId) === IMAGE_OVERLAY_LAYER_ID2;
|
|
8328
|
-
}).forEach((obj) => canvas.bringObjectToFront(obj));
|
|
8329
|
-
this.canvasService.bringLayerToFront("dieline-overlay");
|
|
8330
|
-
this.canvasService.bringLayerToFront("ruler-overlay");
|
|
8686
|
+
whiteSrc: whiteMaskSrc || "",
|
|
8687
|
+
coverSrc: coverMaskSrc || "",
|
|
8688
|
+
whiteScaleAdjustX: scaleAdjust.x,
|
|
8689
|
+
whiteScaleAdjustY: scaleAdjust.y
|
|
8690
|
+
};
|
|
8331
8691
|
}
|
|
8332
8692
|
clearRenderedWhiteInks() {
|
|
8333
8693
|
if (!this.canvasService) return;
|
|
@@ -8360,7 +8720,6 @@ var WhiteInkTool = class {
|
|
|
8360
8720
|
this.syncToolActiveFromWorkbench();
|
|
8361
8721
|
const seq = ++this.renderSeq;
|
|
8362
8722
|
const previewActive = this.isPreviewActive();
|
|
8363
|
-
this.applyImageVisibilityForWhiteInk(previewActive);
|
|
8364
8723
|
const frame = this.getFrameRect();
|
|
8365
8724
|
const frameSpecs = this.buildFrameSpecs(frame);
|
|
8366
8725
|
let whiteSpecs = [];
|
|
@@ -8408,7 +8767,6 @@ var WhiteInkTool = class {
|
|
|
8408
8767
|
this.overlaySpecs = frameSpecs;
|
|
8409
8768
|
await this.canvasService.flushRenderFromProducers();
|
|
8410
8769
|
if (seq !== this.renderSeq) return;
|
|
8411
|
-
this.syncZOrder();
|
|
8412
8770
|
this.canvasService.requestRenderAll();
|
|
8413
8771
|
}
|
|
8414
8772
|
getMaskCacheKey(sourceUrl, tint) {
|
|
@@ -8593,62 +8951,12 @@ var SceneLayoutService = class {
|
|
|
8593
8951
|
}
|
|
8594
8952
|
};
|
|
8595
8953
|
|
|
8596
|
-
// src/extensions/sceneVisibility.ts
|
|
8597
|
-
import { WORKBENCH_SERVICE } from "@pooder/core";
|
|
8598
|
-
var CANVAS_SERVICE_ID2 = "CanvasService";
|
|
8599
|
-
var HIDDEN_DIELINE_TOOLS = /* @__PURE__ */ new Set(["pooder.kit.image", "pooder.kit.white-ink"]);
|
|
8600
|
-
var HIDDEN_RULER_TOOLS = /* @__PURE__ */ new Set(["pooder.kit.white-ink"]);
|
|
8601
|
-
var SceneVisibilityService = class {
|
|
8602
|
-
constructor() {
|
|
8603
|
-
this.activeToolId = null;
|
|
8604
|
-
this.onToolActivated = (e) => {
|
|
8605
|
-
this.activeToolId = e.id;
|
|
8606
|
-
this.apply();
|
|
8607
|
-
};
|
|
8608
|
-
this.onObjectAdded = () => {
|
|
8609
|
-
this.apply();
|
|
8610
|
-
};
|
|
8611
|
-
}
|
|
8612
|
-
init(context) {
|
|
8613
|
-
var _a, _b;
|
|
8614
|
-
if (this.context) {
|
|
8615
|
-
this.dispose(this.context);
|
|
8616
|
-
}
|
|
8617
|
-
const canvasService = context.get(CANVAS_SERVICE_ID2);
|
|
8618
|
-
if (!canvasService) {
|
|
8619
|
-
throw new Error("[SceneVisibilityService] CanvasService is required.");
|
|
8620
|
-
}
|
|
8621
|
-
this.context = context;
|
|
8622
|
-
this.canvasService = canvasService;
|
|
8623
|
-
this.activeToolId = (_b = (_a = context.get(WORKBENCH_SERVICE)) == null ? void 0 : _a.activeToolId) != null ? _b : null;
|
|
8624
|
-
context.eventBus.on("tool:activated", this.onToolActivated);
|
|
8625
|
-
context.eventBus.on("object:added", this.onObjectAdded);
|
|
8626
|
-
this.apply();
|
|
8627
|
-
}
|
|
8628
|
-
dispose(context) {
|
|
8629
|
-
var _a;
|
|
8630
|
-
const activeContext = (_a = this.context) != null ? _a : context;
|
|
8631
|
-
activeContext.eventBus.off("tool:activated", this.onToolActivated);
|
|
8632
|
-
activeContext.eventBus.off("object:added", this.onObjectAdded);
|
|
8633
|
-
this.context = void 0;
|
|
8634
|
-
this.activeToolId = null;
|
|
8635
|
-
this.canvasService = void 0;
|
|
8636
|
-
}
|
|
8637
|
-
apply() {
|
|
8638
|
-
if (!this.canvasService) return;
|
|
8639
|
-
const dielineLayer = this.canvasService.getLayer("dieline-overlay");
|
|
8640
|
-
if (dielineLayer) {
|
|
8641
|
-
const visible = !HIDDEN_DIELINE_TOOLS.has(this.activeToolId || "");
|
|
8642
|
-
this.canvasService.setLayerVisibility("dieline-overlay", visible);
|
|
8643
|
-
}
|
|
8644
|
-
const rulerVisible = !HIDDEN_RULER_TOOLS.has(this.activeToolId || "");
|
|
8645
|
-
this.canvasService.setLayerVisibility("ruler-overlay", rulerVisible);
|
|
8646
|
-
this.canvasService.requestRenderAll();
|
|
8647
|
-
}
|
|
8648
|
-
};
|
|
8649
|
-
|
|
8650
8954
|
// src/services/CanvasService.ts
|
|
8651
|
-
import { Canvas,
|
|
8955
|
+
import { Canvas, Rect, Path as Path2, Image as Image2, Text } from "fabric";
|
|
8956
|
+
import {
|
|
8957
|
+
TOOL_SESSION_SERVICE,
|
|
8958
|
+
WORKBENCH_SERVICE
|
|
8959
|
+
} from "@pooder/core";
|
|
8652
8960
|
|
|
8653
8961
|
// src/services/ViewportSystem.ts
|
|
8654
8962
|
var ViewportSystem = class {
|
|
@@ -8723,6 +9031,56 @@ var ViewportSystem = class {
|
|
|
8723
9031
|
}
|
|
8724
9032
|
};
|
|
8725
9033
|
|
|
9034
|
+
// src/services/visibility.ts
|
|
9035
|
+
function compareLayerObjectCount(actual, cmp, expected) {
|
|
9036
|
+
if (cmp === ">") return actual > expected;
|
|
9037
|
+
if (cmp === ">=") return actual >= expected;
|
|
9038
|
+
if (cmp === "<") return actual < expected;
|
|
9039
|
+
if (cmp === "<=") return actual <= expected;
|
|
9040
|
+
return actual === expected;
|
|
9041
|
+
}
|
|
9042
|
+
function layerState(context, layerId) {
|
|
9043
|
+
return context.layers.get(layerId) || { exists: false, objectCount: 0 };
|
|
9044
|
+
}
|
|
9045
|
+
function evaluateVisibilityExpr(expr, context) {
|
|
9046
|
+
var _a;
|
|
9047
|
+
if (!expr) return true;
|
|
9048
|
+
if (expr.op === "const") {
|
|
9049
|
+
return Boolean(expr.value);
|
|
9050
|
+
}
|
|
9051
|
+
if (expr.op === "activeToolIn") {
|
|
9052
|
+
const activeToolId = (_a = context.activeToolId) != null ? _a : null;
|
|
9053
|
+
return !!activeToolId && expr.ids.includes(activeToolId);
|
|
9054
|
+
}
|
|
9055
|
+
if (expr.op === "sessionActive") {
|
|
9056
|
+
const toolId = String(expr.toolId || "").trim();
|
|
9057
|
+
if (!toolId) return false;
|
|
9058
|
+
return context.isSessionActive ? context.isSessionActive(toolId) : false;
|
|
9059
|
+
}
|
|
9060
|
+
if (expr.op === "anySessionActive") {
|
|
9061
|
+
return context.hasAnyActiveSession ? context.hasAnyActiveSession() : false;
|
|
9062
|
+
}
|
|
9063
|
+
if (expr.op === "layerExists") {
|
|
9064
|
+
return layerState(context, expr.layerId).exists === true;
|
|
9065
|
+
}
|
|
9066
|
+
if (expr.op === "layerObjectCount") {
|
|
9067
|
+
const expected = Number(expr.value);
|
|
9068
|
+
if (!Number.isFinite(expected)) return false;
|
|
9069
|
+
const count = layerState(context, expr.layerId).objectCount;
|
|
9070
|
+
return compareLayerObjectCount(count, expr.cmp, expected);
|
|
9071
|
+
}
|
|
9072
|
+
if (expr.op === "not") {
|
|
9073
|
+
return !evaluateVisibilityExpr(expr.expr, context);
|
|
9074
|
+
}
|
|
9075
|
+
if (expr.op === "all") {
|
|
9076
|
+
return expr.exprs.every((item) => evaluateVisibilityExpr(item, context));
|
|
9077
|
+
}
|
|
9078
|
+
if (expr.op === "any") {
|
|
9079
|
+
return expr.exprs.some((item) => evaluateVisibilityExpr(item, context));
|
|
9080
|
+
}
|
|
9081
|
+
return true;
|
|
9082
|
+
}
|
|
9083
|
+
|
|
8726
9084
|
// src/services/CanvasService.ts
|
|
8727
9085
|
var CanvasService = class {
|
|
8728
9086
|
constructor(el, options) {
|
|
@@ -8731,8 +9089,48 @@ var CanvasService = class {
|
|
|
8731
9089
|
this.producerFlushRequested = false;
|
|
8732
9090
|
this.producerLoopPending = false;
|
|
8733
9091
|
this.producerLoopPromise = null;
|
|
8734
|
-
this.
|
|
8735
|
-
this.
|
|
9092
|
+
this.producerApplyInProgress = false;
|
|
9093
|
+
this.visibilityRefreshScheduled = false;
|
|
9094
|
+
this.managedProducerPassIds = /* @__PURE__ */ new Set();
|
|
9095
|
+
this.managedPassMetas = /* @__PURE__ */ new Map();
|
|
9096
|
+
this.managedPassEffects = [];
|
|
9097
|
+
this.canvasForwardersBound = false;
|
|
9098
|
+
this.forwardSelectionCreated = (e) => {
|
|
9099
|
+
var _a;
|
|
9100
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("selection:created", e);
|
|
9101
|
+
};
|
|
9102
|
+
this.forwardSelectionUpdated = (e) => {
|
|
9103
|
+
var _a;
|
|
9104
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("selection:updated", e);
|
|
9105
|
+
};
|
|
9106
|
+
this.forwardSelectionCleared = (e) => {
|
|
9107
|
+
var _a;
|
|
9108
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("selection:cleared", e);
|
|
9109
|
+
};
|
|
9110
|
+
this.forwardObjectModified = (e) => {
|
|
9111
|
+
var _a;
|
|
9112
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("object:modified", e);
|
|
9113
|
+
};
|
|
9114
|
+
this.forwardObjectAdded = (e) => {
|
|
9115
|
+
var _a;
|
|
9116
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("object:added", e);
|
|
9117
|
+
};
|
|
9118
|
+
this.forwardObjectRemoved = (e) => {
|
|
9119
|
+
var _a;
|
|
9120
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("object:removed", e);
|
|
9121
|
+
};
|
|
9122
|
+
this.onToolActivated = () => {
|
|
9123
|
+
this.applyManagedPassVisibility();
|
|
9124
|
+
void this.applyManagedPassEffects(void 0, { render: true });
|
|
9125
|
+
};
|
|
9126
|
+
this.onToolSessionChanged = () => {
|
|
9127
|
+
this.applyManagedPassVisibility();
|
|
9128
|
+
void this.applyManagedPassEffects(void 0, { render: true });
|
|
9129
|
+
};
|
|
9130
|
+
this.onCanvasObjectChanged = () => {
|
|
9131
|
+
if (this.producerApplyInProgress) return;
|
|
9132
|
+
this.scheduleManagedPassVisibilityRefresh();
|
|
9133
|
+
};
|
|
8736
9134
|
if (el instanceof Canvas) {
|
|
8737
9135
|
this.canvas = el;
|
|
8738
9136
|
} else {
|
|
@@ -8749,25 +9147,53 @@ var CanvasService = class {
|
|
|
8749
9147
|
this.setEventBus(options.eventBus);
|
|
8750
9148
|
}
|
|
8751
9149
|
}
|
|
9150
|
+
init(context) {
|
|
9151
|
+
if (this.context) {
|
|
9152
|
+
this.detachContextEvents(this.context.eventBus);
|
|
9153
|
+
}
|
|
9154
|
+
this.context = context;
|
|
9155
|
+
this.workbenchService = context.get(WORKBENCH_SERVICE);
|
|
9156
|
+
this.toolSessionService = context.get(TOOL_SESSION_SERVICE);
|
|
9157
|
+
this.setEventBus(context.eventBus);
|
|
9158
|
+
this.attachContextEvents(context.eventBus);
|
|
9159
|
+
}
|
|
9160
|
+
attachContextEvents(eventBus) {
|
|
9161
|
+
eventBus.on("tool:activated", this.onToolActivated);
|
|
9162
|
+
eventBus.on("tool:session:change", this.onToolSessionChanged);
|
|
9163
|
+
eventBus.on("object:added", this.onCanvasObjectChanged);
|
|
9164
|
+
eventBus.on("object:removed", this.onCanvasObjectChanged);
|
|
9165
|
+
}
|
|
9166
|
+
detachContextEvents(eventBus) {
|
|
9167
|
+
eventBus.off("tool:activated", this.onToolActivated);
|
|
9168
|
+
eventBus.off("tool:session:change", this.onToolSessionChanged);
|
|
9169
|
+
eventBus.off("object:added", this.onCanvasObjectChanged);
|
|
9170
|
+
eventBus.off("object:removed", this.onCanvasObjectChanged);
|
|
9171
|
+
}
|
|
8752
9172
|
setEventBus(eventBus) {
|
|
8753
9173
|
this.eventBus = eventBus;
|
|
8754
9174
|
this.setupEvents();
|
|
8755
9175
|
}
|
|
8756
9176
|
setupEvents() {
|
|
8757
|
-
if (
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
this.canvas.on("selection:
|
|
8761
|
-
this.canvas.on("
|
|
8762
|
-
this.canvas.on("
|
|
8763
|
-
this.canvas.on("object:
|
|
8764
|
-
this.
|
|
8765
|
-
this.canvas.on("object:removed", forward("object:removed"));
|
|
9177
|
+
if (this.canvasForwardersBound) return;
|
|
9178
|
+
this.canvas.on("selection:created", this.forwardSelectionCreated);
|
|
9179
|
+
this.canvas.on("selection:updated", this.forwardSelectionUpdated);
|
|
9180
|
+
this.canvas.on("selection:cleared", this.forwardSelectionCleared);
|
|
9181
|
+
this.canvas.on("object:modified", this.forwardObjectModified);
|
|
9182
|
+
this.canvas.on("object:added", this.forwardObjectAdded);
|
|
9183
|
+
this.canvas.on("object:removed", this.forwardObjectRemoved);
|
|
9184
|
+
this.canvasForwardersBound = true;
|
|
8766
9185
|
}
|
|
8767
9186
|
dispose() {
|
|
9187
|
+
if (this.context) {
|
|
9188
|
+
this.detachContextEvents(this.context.eventBus);
|
|
9189
|
+
}
|
|
8768
9190
|
this.renderProducers.clear();
|
|
8769
|
-
this.
|
|
8770
|
-
this.
|
|
9191
|
+
this.managedProducerPassIds.clear();
|
|
9192
|
+
this.managedPassMetas.clear();
|
|
9193
|
+
this.managedPassEffects = [];
|
|
9194
|
+
this.context = void 0;
|
|
9195
|
+
this.workbenchService = void 0;
|
|
9196
|
+
this.toolSessionService = void 0;
|
|
8771
9197
|
this.producerFlushRequested = false;
|
|
8772
9198
|
this.canvas.dispose();
|
|
8773
9199
|
}
|
|
@@ -8845,118 +9271,292 @@ var CanvasService = class {
|
|
|
8845
9271
|
return a.toolId.localeCompare(b.toolId);
|
|
8846
9272
|
});
|
|
8847
9273
|
}
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
9274
|
+
normalizePassSpecValue(spec) {
|
|
9275
|
+
const id = String(spec.id || "").trim();
|
|
9276
|
+
if (!id) return null;
|
|
9277
|
+
return {
|
|
9278
|
+
id,
|
|
9279
|
+
stack: Number.isFinite(spec.stack) ? Number(spec.stack) : 0,
|
|
9280
|
+
order: Number.isFinite(spec.order) ? Number(spec.order) : 0,
|
|
9281
|
+
replace: spec.replace !== false,
|
|
9282
|
+
visibility: spec.visibility,
|
|
9283
|
+
effects: Array.isArray(spec.effects) ? [...spec.effects] : [],
|
|
9284
|
+
objects: Array.isArray(spec.objects) ? [...spec.objects] : []
|
|
9285
|
+
};
|
|
9286
|
+
}
|
|
9287
|
+
normalizeClipPathEffectSpec(effect, passId, index) {
|
|
9288
|
+
if (!effect || effect.type !== "clipPath") return null;
|
|
9289
|
+
const source = effect.source;
|
|
9290
|
+
if (!source || typeof source !== "object") return null;
|
|
9291
|
+
const sourceId = String(source.id || "").trim();
|
|
9292
|
+
if (!sourceId) return null;
|
|
9293
|
+
const targetPassIds = Array.isArray(effect.targetPassIds) ? effect.targetPassIds.map((item) => String(item || "").trim()).filter((item) => item.length > 0) : [];
|
|
9294
|
+
if (!targetPassIds.length) return null;
|
|
9295
|
+
const customId = String(effect.id || "").trim();
|
|
9296
|
+
const key = customId || `${passId}.effect.clipPath.${index}`;
|
|
9297
|
+
return {
|
|
9298
|
+
type: "clipPath",
|
|
9299
|
+
key,
|
|
9300
|
+
visibility: effect.visibility,
|
|
9301
|
+
source: {
|
|
9302
|
+
...source,
|
|
9303
|
+
id: sourceId
|
|
9304
|
+
},
|
|
9305
|
+
targetPassIds
|
|
9306
|
+
};
|
|
9307
|
+
}
|
|
9308
|
+
mergePassSpec(map, rawSpec, producerId) {
|
|
9309
|
+
const normalized = this.normalizePassSpecValue(rawSpec);
|
|
9310
|
+
if (!normalized) return;
|
|
9311
|
+
const existing = map.get(normalized.id);
|
|
9312
|
+
if (!existing) {
|
|
9313
|
+
map.set(normalized.id, normalized);
|
|
9314
|
+
return;
|
|
9315
|
+
}
|
|
9316
|
+
existing.objects.push(...normalized.objects);
|
|
9317
|
+
existing.replace = existing.replace || normalized.replace;
|
|
9318
|
+
existing.stack = normalized.stack;
|
|
9319
|
+
existing.order = normalized.order;
|
|
9320
|
+
if (normalized.visibility !== void 0) {
|
|
9321
|
+
existing.visibility = normalized.visibility;
|
|
9322
|
+
}
|
|
9323
|
+
existing.effects.push(...normalized.effects);
|
|
9324
|
+
if (normalized.objects.length === 0 && normalized.effects.length === 0) {
|
|
9325
|
+
console.debug(
|
|
9326
|
+
`[CanvasService] pass "${normalized.id}" from producer "${producerId}" updated ordering/visibility only.`
|
|
9327
|
+
);
|
|
9328
|
+
}
|
|
9329
|
+
}
|
|
9330
|
+
comparePassMeta(a, b) {
|
|
9331
|
+
if (a.stack !== b.stack) return a.stack - b.stack;
|
|
9332
|
+
if (a.order !== b.order) return a.order - b.order;
|
|
9333
|
+
return a.id.localeCompare(b.id);
|
|
9334
|
+
}
|
|
9335
|
+
getPassObjectOrder(obj) {
|
|
9336
|
+
var _a;
|
|
9337
|
+
const raw = Number((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passOrder);
|
|
9338
|
+
return Number.isFinite(raw) ? raw : Number.MAX_SAFE_INTEGER;
|
|
9339
|
+
}
|
|
9340
|
+
getPassCanvasObjects(passId) {
|
|
9341
|
+
const all = this.canvas.getObjects();
|
|
9342
|
+
return all.filter((obj) => {
|
|
9343
|
+
var _a;
|
|
9344
|
+
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passId) === passId;
|
|
9345
|
+
}).sort((a, b) => {
|
|
9346
|
+
const orderA = this.getPassObjectOrder(a);
|
|
9347
|
+
const orderB = this.getPassObjectOrder(b);
|
|
9348
|
+
if (orderA !== orderB) return orderA - orderB;
|
|
9349
|
+
return all.indexOf(a) - all.indexOf(b);
|
|
9350
|
+
});
|
|
9351
|
+
}
|
|
9352
|
+
getPassObjects(passId) {
|
|
9353
|
+
return this.getPassCanvasObjects(passId);
|
|
9354
|
+
}
|
|
9355
|
+
getRootLayerObjects(layerId) {
|
|
9356
|
+
return this.getPassCanvasObjects(layerId);
|
|
9357
|
+
}
|
|
9358
|
+
isManagedPassObject(obj) {
|
|
9359
|
+
var _a;
|
|
9360
|
+
const passId = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passId;
|
|
9361
|
+
return typeof passId === "string" && this.managedPassMetas.has(passId);
|
|
9362
|
+
}
|
|
9363
|
+
syncManagedPassStacking(passes) {
|
|
9364
|
+
const orderedPasses = [...passes].sort((a, b) => this.comparePassMeta(a, b));
|
|
9365
|
+
if (!orderedPasses.length) return;
|
|
9366
|
+
const canvasObjects = this.canvas.getObjects();
|
|
9367
|
+
const managedObjects = canvasObjects.filter(
|
|
9368
|
+
(obj) => this.isManagedPassObject(obj)
|
|
9369
|
+
);
|
|
9370
|
+
if (!managedObjects.length) return;
|
|
9371
|
+
const firstManagedIndex = managedObjects.map((obj) => canvasObjects.indexOf(obj)).filter((index) => index >= 0).reduce((min, value) => Math.min(min, value), Number.MAX_SAFE_INTEGER);
|
|
9372
|
+
let targetIndex = Number.isFinite(firstManagedIndex) ? firstManagedIndex : 0;
|
|
9373
|
+
orderedPasses.forEach((meta) => {
|
|
9374
|
+
const objects = this.getPassCanvasObjects(meta.id);
|
|
9375
|
+
objects.forEach((obj) => {
|
|
9376
|
+
this.moveObjectInCanvas(obj, targetIndex);
|
|
9377
|
+
targetIndex += 1;
|
|
9378
|
+
});
|
|
9379
|
+
});
|
|
9380
|
+
}
|
|
9381
|
+
getPassRuntimeState() {
|
|
9382
|
+
const state = /* @__PURE__ */ new Map();
|
|
9383
|
+
const ensure = (passId) => {
|
|
9384
|
+
const id = String(passId || "").trim();
|
|
9385
|
+
if (!id) return { exists: false, objectCount: 0 };
|
|
9386
|
+
let item = state.get(id);
|
|
9387
|
+
if (!item) {
|
|
9388
|
+
item = { exists: false, objectCount: 0 };
|
|
9389
|
+
state.set(id, item);
|
|
9390
|
+
}
|
|
9391
|
+
return item;
|
|
9392
|
+
};
|
|
9393
|
+
this.canvas.getObjects().forEach((obj) => {
|
|
9394
|
+
var _a;
|
|
9395
|
+
const passId = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passId;
|
|
9396
|
+
if (typeof passId === "string") {
|
|
9397
|
+
const item = ensure(passId);
|
|
9398
|
+
item.exists = true;
|
|
9399
|
+
item.objectCount += 1;
|
|
9400
|
+
}
|
|
9401
|
+
});
|
|
9402
|
+
this.managedPassMetas.forEach((meta) => {
|
|
9403
|
+
const item = ensure(meta.id);
|
|
9404
|
+
item.exists = true;
|
|
9405
|
+
});
|
|
9406
|
+
return state;
|
|
9407
|
+
}
|
|
9408
|
+
isSessionActive(toolId) {
|
|
9409
|
+
if (!this.toolSessionService) return false;
|
|
9410
|
+
return this.toolSessionService.getState(toolId).status === "active";
|
|
9411
|
+
}
|
|
9412
|
+
hasAnyActiveSession() {
|
|
9413
|
+
var _a, _b;
|
|
9414
|
+
return (_b = (_a = this.toolSessionService) == null ? void 0 : _a.hasAnyActiveSession()) != null ? _b : false;
|
|
9415
|
+
}
|
|
9416
|
+
buildVisibilityEvalContext(layers) {
|
|
9417
|
+
var _a, _b;
|
|
9418
|
+
return {
|
|
9419
|
+
activeToolId: (_b = (_a = this.workbenchService) == null ? void 0 : _a.activeToolId) != null ? _b : null,
|
|
9420
|
+
isSessionActive: (toolId) => this.isSessionActive(toolId),
|
|
9421
|
+
hasAnyActiveSession: () => this.hasAnyActiveSession(),
|
|
9422
|
+
layers
|
|
9423
|
+
};
|
|
9424
|
+
}
|
|
9425
|
+
applyManagedPassVisibility(options = {}) {
|
|
9426
|
+
if (!this.managedPassMetas.size) return false;
|
|
9427
|
+
const layers = this.getPassRuntimeState();
|
|
9428
|
+
const context = this.buildVisibilityEvalContext(layers);
|
|
9429
|
+
let changed = false;
|
|
9430
|
+
this.managedPassMetas.forEach((meta) => {
|
|
9431
|
+
const visible = evaluateVisibilityExpr(meta.visibility, context);
|
|
9432
|
+
changed = this.setPassVisibility(meta.id, visible) || changed;
|
|
9433
|
+
});
|
|
9434
|
+
if (changed && options.render !== false) {
|
|
9435
|
+
this.requestRenderAll();
|
|
9436
|
+
}
|
|
9437
|
+
return changed;
|
|
9438
|
+
}
|
|
9439
|
+
scheduleManagedPassVisibilityRefresh() {
|
|
9440
|
+
if (this.visibilityRefreshScheduled) return;
|
|
9441
|
+
this.visibilityRefreshScheduled = true;
|
|
9442
|
+
void Promise.resolve().then(() => {
|
|
9443
|
+
this.visibilityRefreshScheduled = false;
|
|
9444
|
+
this.applyManagedPassVisibility();
|
|
8855
9445
|
});
|
|
8856
9446
|
}
|
|
8857
9447
|
async collectAndApplyProducerSpecs() {
|
|
8858
|
-
const
|
|
8859
|
-
const rootLayerSpecs = /* @__PURE__ */ new Map();
|
|
8860
|
-
const replaceLayerIds = /* @__PURE__ */ new Set();
|
|
8861
|
-
const replaceRootLayerIds = /* @__PURE__ */ new Set();
|
|
9448
|
+
const passes = /* @__PURE__ */ new Map();
|
|
8862
9449
|
const entries = this.sortedRenderProducerEntries();
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
if (layerId) replaceRootLayerIds.add(layerId);
|
|
8877
|
-
});
|
|
9450
|
+
this.producerApplyInProgress = true;
|
|
9451
|
+
try {
|
|
9452
|
+
for (const entry of entries) {
|
|
9453
|
+
try {
|
|
9454
|
+
const result = await entry.producer();
|
|
9455
|
+
if (!result) continue;
|
|
9456
|
+
const specs = Array.isArray(result.passes) ? result.passes : [];
|
|
9457
|
+
specs.forEach((spec) => this.mergePassSpec(passes, spec, entry.toolId));
|
|
9458
|
+
} catch (error) {
|
|
9459
|
+
console.error(
|
|
9460
|
+
`[CanvasService] render producer "${entry.toolId}" failed.`,
|
|
9461
|
+
error
|
|
9462
|
+
);
|
|
8878
9463
|
}
|
|
8879
|
-
} catch (error) {
|
|
8880
|
-
console.error(
|
|
8881
|
-
`[CanvasService] render producer "${entry.toolId}" failed.`,
|
|
8882
|
-
error
|
|
8883
|
-
);
|
|
8884
9464
|
}
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
9465
|
+
const nextPassIds = /* @__PURE__ */ new Set();
|
|
9466
|
+
const nextManagedPassMetas = /* @__PURE__ */ new Map();
|
|
9467
|
+
const nextEffects = [];
|
|
9468
|
+
for (const pass of passes.values()) {
|
|
9469
|
+
nextPassIds.add(pass.id);
|
|
9470
|
+
nextManagedPassMetas.set(pass.id, {
|
|
9471
|
+
id: pass.id,
|
|
9472
|
+
stack: pass.stack,
|
|
9473
|
+
order: pass.order,
|
|
9474
|
+
visibility: pass.visibility
|
|
9475
|
+
});
|
|
9476
|
+
await this.applyObjectSpecsToPass(pass.id, pass.objects, {
|
|
9477
|
+
render: false,
|
|
9478
|
+
replace: pass.replace
|
|
9479
|
+
});
|
|
9480
|
+
pass.effects.forEach((effect, index) => {
|
|
9481
|
+
const normalized = this.normalizeClipPathEffectSpec(
|
|
9482
|
+
effect,
|
|
9483
|
+
pass.id,
|
|
9484
|
+
index
|
|
9485
|
+
);
|
|
9486
|
+
if (!normalized) return;
|
|
9487
|
+
nextEffects.push(normalized);
|
|
9488
|
+
});
|
|
8894
9489
|
}
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
await this.applyObjectSpecsToContainer(layer, [], { render: false });
|
|
8902
|
-
}
|
|
8903
|
-
for (const [layerId, specs] of rootLayerSpecs.entries()) {
|
|
8904
|
-
if (replaceRootLayerIds.has(layerId)) {
|
|
8905
|
-
const existing = this.getRootLayerObjects(layerId);
|
|
8906
|
-
existing.forEach((obj) => this.canvas.remove(obj));
|
|
9490
|
+
for (const passId of this.managedProducerPassIds) {
|
|
9491
|
+
if (nextPassIds.has(passId)) continue;
|
|
9492
|
+
await this.applyObjectSpecsToPass(passId, [], {
|
|
9493
|
+
render: false,
|
|
9494
|
+
replace: true
|
|
9495
|
+
});
|
|
8907
9496
|
}
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
await this.
|
|
9497
|
+
this.managedProducerPassIds = nextPassIds;
|
|
9498
|
+
this.managedPassMetas = nextManagedPassMetas;
|
|
9499
|
+
this.managedPassEffects = nextEffects;
|
|
9500
|
+
this.syncManagedPassStacking(Array.from(nextManagedPassMetas.values()));
|
|
9501
|
+
await this.applyManagedPassEffects(nextEffects, { render: false });
|
|
9502
|
+
this.applyManagedPassVisibility({ render: false });
|
|
9503
|
+
} finally {
|
|
9504
|
+
this.producerApplyInProgress = false;
|
|
8913
9505
|
}
|
|
8914
|
-
this.managedProducerLayerIds = nextLayerIds;
|
|
8915
|
-
this.managedProducerRootLayerIds = nextRootLayerIds;
|
|
8916
9506
|
this.requestRenderAll();
|
|
8917
9507
|
}
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
createLayer(id, options = {}) {
|
|
8932
|
-
let layer = this.getLayer(id);
|
|
8933
|
-
if (!layer) {
|
|
8934
|
-
const defaultOptions = {
|
|
8935
|
-
selectable: false,
|
|
8936
|
-
evented: false,
|
|
8937
|
-
...options,
|
|
8938
|
-
data: { ...options.data, id }
|
|
8939
|
-
};
|
|
8940
|
-
layer = new Group([], defaultOptions);
|
|
8941
|
-
this.canvas.add(layer);
|
|
8942
|
-
}
|
|
8943
|
-
return layer;
|
|
8944
|
-
}
|
|
8945
|
-
/**
|
|
8946
|
-
* Find an object by ID, optionally within a specific layer.
|
|
8947
|
-
*/
|
|
8948
|
-
getObject(id, layerId) {
|
|
8949
|
-
if (layerId) {
|
|
8950
|
-
const layer = this.getLayer(layerId);
|
|
8951
|
-
if (!layer) return void 0;
|
|
8952
|
-
return layer.getObjects().find((obj) => {
|
|
8953
|
-
var _a;
|
|
8954
|
-
return ((_a = obj.data) == null ? void 0 : _a.id) === id;
|
|
9508
|
+
async applyManagedPassEffects(effects = this.managedPassEffects, options = {}) {
|
|
9509
|
+
const effectTargetMap = /* @__PURE__ */ new Map();
|
|
9510
|
+
const layers = this.getPassRuntimeState();
|
|
9511
|
+
const visibilityContext = this.buildVisibilityEvalContext(layers);
|
|
9512
|
+
for (const effect of effects) {
|
|
9513
|
+
if (effect.type !== "clipPath") continue;
|
|
9514
|
+
if (!evaluateVisibilityExpr(effect.visibility, visibilityContext)) {
|
|
9515
|
+
continue;
|
|
9516
|
+
}
|
|
9517
|
+
effect.targetPassIds.forEach((targetPassId) => {
|
|
9518
|
+
this.getPassCanvasObjects(targetPassId).forEach((obj) => {
|
|
9519
|
+
effectTargetMap.set(obj, effect);
|
|
9520
|
+
});
|
|
8955
9521
|
});
|
|
8956
9522
|
}
|
|
9523
|
+
const managedObjects = this.canvas.getObjects().filter(
|
|
9524
|
+
(obj) => this.isManagedPassObject(obj)
|
|
9525
|
+
);
|
|
9526
|
+
const effectTemplateCache = /* @__PURE__ */ new Map();
|
|
9527
|
+
for (const obj of managedObjects) {
|
|
9528
|
+
const targetEffect = effectTargetMap.get(obj);
|
|
9529
|
+
if (!targetEffect) {
|
|
9530
|
+
this.clearClipPathEffectFromObject(obj);
|
|
9531
|
+
continue;
|
|
9532
|
+
}
|
|
9533
|
+
let template = effectTemplateCache.get(targetEffect.key);
|
|
9534
|
+
if (template === void 0) {
|
|
9535
|
+
template = await this.createClipPathTemplate(targetEffect);
|
|
9536
|
+
effectTemplateCache.set(targetEffect.key, template);
|
|
9537
|
+
}
|
|
9538
|
+
if (!template) {
|
|
9539
|
+
this.clearClipPathEffectFromObject(obj);
|
|
9540
|
+
continue;
|
|
9541
|
+
}
|
|
9542
|
+
await this.applyClipPathEffectToObject(
|
|
9543
|
+
obj,
|
|
9544
|
+
template,
|
|
9545
|
+
targetEffect.key
|
|
9546
|
+
);
|
|
9547
|
+
}
|
|
9548
|
+
if (options.render !== false) {
|
|
9549
|
+
this.requestRenderAll();
|
|
9550
|
+
}
|
|
9551
|
+
}
|
|
9552
|
+
getObject(id, passId) {
|
|
9553
|
+
const normalizedId = String(id || "").trim();
|
|
9554
|
+
if (!normalizedId) return void 0;
|
|
8957
9555
|
return this.canvas.getObjects().find((obj) => {
|
|
8958
|
-
var _a;
|
|
8959
|
-
|
|
9556
|
+
var _a, _b;
|
|
9557
|
+
if (((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id) !== normalizedId) return false;
|
|
9558
|
+
if (!passId) return true;
|
|
9559
|
+
return ((_b = obj == null ? void 0 : obj.data) == null ? void 0 : _b.passId) === passId;
|
|
8960
9560
|
});
|
|
8961
9561
|
}
|
|
8962
9562
|
requestRenderAll() {
|
|
@@ -9142,114 +9742,187 @@ var CanvasService = class {
|
|
|
9142
9742
|
next.top = objectTop + objectHeight * this.originFactor(originY);
|
|
9143
9743
|
return next;
|
|
9144
9744
|
}
|
|
9145
|
-
|
|
9146
|
-
const
|
|
9147
|
-
|
|
9148
|
-
layer.set({ visible });
|
|
9149
|
-
}
|
|
9150
|
-
const objects = this.getRootLayerObjects(layerId);
|
|
9745
|
+
setPassVisibility(passId, visible) {
|
|
9746
|
+
const objects = this.getPassCanvasObjects(passId);
|
|
9747
|
+
let changed = false;
|
|
9151
9748
|
objects.forEach((obj) => {
|
|
9152
9749
|
var _a, _b;
|
|
9750
|
+
if (obj.visible === visible) return;
|
|
9153
9751
|
(_a = obj.set) == null ? void 0 : _a.call(obj, { visible });
|
|
9154
9752
|
(_b = obj.setCoords) == null ? void 0 : _b.call(obj);
|
|
9753
|
+
changed = true;
|
|
9155
9754
|
});
|
|
9755
|
+
return changed;
|
|
9156
9756
|
}
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
if (layer) {
|
|
9160
|
-
this.canvas.bringObjectToFront(layer);
|
|
9161
|
-
}
|
|
9162
|
-
const objects = this.getRootLayerObjects(layerId);
|
|
9163
|
-
objects.forEach((obj) => this.canvas.bringObjectToFront(obj));
|
|
9757
|
+
setLayerVisibility(layerId, visible) {
|
|
9758
|
+
return this.setPassVisibility(layerId, visible);
|
|
9164
9759
|
}
|
|
9165
|
-
|
|
9166
|
-
const
|
|
9167
|
-
|
|
9760
|
+
bringPassToFront(passId) {
|
|
9761
|
+
const objects = this.getPassCanvasObjects(passId);
|
|
9762
|
+
objects.forEach((obj) => this.canvas.bringObjectToFront(obj));
|
|
9168
9763
|
}
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
await this.applyObjectSpecsToContainer(layer, objects, options);
|
|
9764
|
+
bringLayerToFront(layerId) {
|
|
9765
|
+
this.bringPassToFront(layerId);
|
|
9172
9766
|
}
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9767
|
+
async applyPassSpec(spec, options = {}) {
|
|
9768
|
+
await this.applyObjectSpecsToPass(spec.id, spec.objects, {
|
|
9769
|
+
render: options.render,
|
|
9770
|
+
replace: spec.replace !== false
|
|
9177
9771
|
});
|
|
9178
9772
|
}
|
|
9179
|
-
async applyObjectSpecsToRootLayer(
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
var _a;
|
|
9184
|
-
const id = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id;
|
|
9185
|
-
if (typeof id === "string" && !desiredIds.has(id)) {
|
|
9186
|
-
this.canvas.remove(obj);
|
|
9187
|
-
}
|
|
9773
|
+
async applyObjectSpecsToRootLayer(passId, specs, options = {}) {
|
|
9774
|
+
await this.applyObjectSpecsToPass(passId, specs, {
|
|
9775
|
+
render: options.render,
|
|
9776
|
+
replace: true
|
|
9188
9777
|
});
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9778
|
+
}
|
|
9779
|
+
normalizeObjectSpecs(specs) {
|
|
9780
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9781
|
+
const normalized = [];
|
|
9782
|
+
(specs || []).forEach((spec) => {
|
|
9783
|
+
const id = String((spec == null ? void 0 : spec.id) || "").trim();
|
|
9784
|
+
if (!id || seen.has(id)) return;
|
|
9785
|
+
seen.add(id);
|
|
9786
|
+
normalized.push({
|
|
9787
|
+
...spec,
|
|
9788
|
+
id
|
|
9789
|
+
});
|
|
9194
9790
|
});
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
if (!current) {
|
|
9204
|
-
const created = await this.createFabricObject(spec);
|
|
9205
|
-
if (!created) continue;
|
|
9206
|
-
this.patchFabricObject(created, spec, { layerId });
|
|
9207
|
-
this.canvas.add(created);
|
|
9208
|
-
byId.set(spec.id, created);
|
|
9209
|
-
continue;
|
|
9210
|
-
}
|
|
9211
|
-
this.patchFabricObject(current, spec, { layerId });
|
|
9791
|
+
return normalized;
|
|
9792
|
+
}
|
|
9793
|
+
async cloneFabricObject(source) {
|
|
9794
|
+
const clone = source.clone;
|
|
9795
|
+
if (typeof clone !== "function") return void 0;
|
|
9796
|
+
const result = clone.call(source);
|
|
9797
|
+
if (!result || typeof result.then !== "function") {
|
|
9798
|
+
return void 0;
|
|
9212
9799
|
}
|
|
9213
|
-
|
|
9214
|
-
|
|
9800
|
+
try {
|
|
9801
|
+
const copied = await result;
|
|
9802
|
+
return copied;
|
|
9803
|
+
} catch (e) {
|
|
9804
|
+
return void 0;
|
|
9215
9805
|
}
|
|
9216
9806
|
}
|
|
9217
|
-
async
|
|
9218
|
-
|
|
9219
|
-
const
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9807
|
+
async createClipPathTemplate(effect) {
|
|
9808
|
+
var _a, _b;
|
|
9809
|
+
const source = effect.source;
|
|
9810
|
+
const sourceId = String(source.id || "").trim();
|
|
9811
|
+
if (!sourceId) return null;
|
|
9812
|
+
const template = await this.createFabricObject({
|
|
9813
|
+
...source,
|
|
9814
|
+
id: sourceId,
|
|
9815
|
+
data: {
|
|
9816
|
+
...source.data || {},
|
|
9817
|
+
id: sourceId,
|
|
9818
|
+
type: "clip-path-effect-template",
|
|
9819
|
+
effectKey: effect.key
|
|
9820
|
+
},
|
|
9821
|
+
props: {
|
|
9822
|
+
...source.props || {},
|
|
9823
|
+
selectable: false,
|
|
9824
|
+
evented: false,
|
|
9825
|
+
excludeFromExport: true
|
|
9225
9826
|
}
|
|
9226
9827
|
});
|
|
9828
|
+
if (!template) return null;
|
|
9829
|
+
(_a = template.set) == null ? void 0 : _a.call(template, {
|
|
9830
|
+
selectable: false,
|
|
9831
|
+
evented: false,
|
|
9832
|
+
excludeFromExport: true,
|
|
9833
|
+
absolutePositioned: true
|
|
9834
|
+
});
|
|
9835
|
+
(_b = template.setCoords) == null ? void 0 : _b.call(template);
|
|
9836
|
+
return template;
|
|
9837
|
+
}
|
|
9838
|
+
isClipPathEffectManaged(target) {
|
|
9839
|
+
return typeof (target == null ? void 0 : target.__pooderEffectClipKey) === "string";
|
|
9840
|
+
}
|
|
9841
|
+
clearClipPathEffectFromObject(target) {
|
|
9842
|
+
var _a, _b;
|
|
9843
|
+
if (!target) return;
|
|
9844
|
+
if (!this.isClipPathEffectManaged(target)) return;
|
|
9845
|
+
(_a = target.set) == null ? void 0 : _a.call(target, { clipPath: void 0 });
|
|
9846
|
+
(_b = target.setCoords) == null ? void 0 : _b.call(target);
|
|
9847
|
+
delete target.__pooderEffectClipKey;
|
|
9848
|
+
}
|
|
9849
|
+
async applyClipPathEffectToObject(target, clipTemplate, effectKey) {
|
|
9850
|
+
var _a, _b, _c, _d;
|
|
9851
|
+
if (!target) return;
|
|
9852
|
+
const clipPath = await this.cloneFabricObject(clipTemplate);
|
|
9853
|
+
if (!clipPath) {
|
|
9854
|
+
this.clearClipPathEffectFromObject(target);
|
|
9855
|
+
return;
|
|
9856
|
+
}
|
|
9857
|
+
(_a = clipPath.set) == null ? void 0 : _a.call(clipPath, {
|
|
9858
|
+
selectable: false,
|
|
9859
|
+
evented: false,
|
|
9860
|
+
excludeFromExport: true,
|
|
9861
|
+
absolutePositioned: true
|
|
9862
|
+
});
|
|
9863
|
+
(_b = clipPath.setCoords) == null ? void 0 : _b.call(clipPath);
|
|
9864
|
+
(_c = target.set) == null ? void 0 : _c.call(target, { clipPath });
|
|
9865
|
+
(_d = target.setCoords) == null ? void 0 : _d.call(target);
|
|
9866
|
+
target.__pooderEffectClipKey = effectKey;
|
|
9867
|
+
}
|
|
9868
|
+
async applyObjectSpecsToPass(passId, specs, options = {}) {
|
|
9869
|
+
const normalizedPassId = String(passId || "").trim();
|
|
9870
|
+
if (!normalizedPassId) return;
|
|
9871
|
+
const replace = options.replace !== false;
|
|
9872
|
+
const normalizedSpecs = this.normalizeObjectSpecs(specs);
|
|
9873
|
+
const desiredIds = new Set(normalizedSpecs.map((s) => s.id));
|
|
9874
|
+
const existing = this.getPassCanvasObjects(normalizedPassId);
|
|
9875
|
+
if (replace) {
|
|
9876
|
+
existing.forEach((obj) => {
|
|
9877
|
+
var _a;
|
|
9878
|
+
const id = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id;
|
|
9879
|
+
if (typeof id === "string" && !desiredIds.has(id)) {
|
|
9880
|
+
this.canvas.remove(obj);
|
|
9881
|
+
}
|
|
9882
|
+
});
|
|
9883
|
+
}
|
|
9227
9884
|
const byId = /* @__PURE__ */ new Map();
|
|
9228
|
-
|
|
9885
|
+
this.getPassCanvasObjects(normalizedPassId).forEach((obj) => {
|
|
9229
9886
|
var _a;
|
|
9230
9887
|
const id = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id;
|
|
9231
9888
|
if (typeof id === "string") byId.set(id, obj);
|
|
9232
9889
|
});
|
|
9233
|
-
for (let index = 0; index <
|
|
9234
|
-
const spec =
|
|
9890
|
+
for (let index = 0; index < normalizedSpecs.length; index += 1) {
|
|
9891
|
+
const spec = normalizedSpecs[index];
|
|
9235
9892
|
let current = byId.get(spec.id);
|
|
9236
|
-
if (
|
|
9237
|
-
|
|
9893
|
+
if (spec.type === "path") {
|
|
9894
|
+
const nextPathData = this.readPathDataFromSpec(spec);
|
|
9895
|
+
if (!nextPathData || !nextPathData.trim()) {
|
|
9896
|
+
if (current) {
|
|
9897
|
+
this.canvas.remove(current);
|
|
9898
|
+
byId.delete(spec.id);
|
|
9899
|
+
}
|
|
9900
|
+
continue;
|
|
9901
|
+
}
|
|
9902
|
+
}
|
|
9903
|
+
if (current && this.shouldRecreateObject(current, spec)) {
|
|
9904
|
+
this.canvas.remove(current);
|
|
9238
9905
|
byId.delete(spec.id);
|
|
9239
9906
|
current = void 0;
|
|
9240
9907
|
}
|
|
9241
9908
|
if (!current) {
|
|
9242
9909
|
const created = await this.createFabricObject(spec);
|
|
9243
9910
|
if (!created) continue;
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9911
|
+
this.patchFabricObject(created, spec, {
|
|
9912
|
+
passId: normalizedPassId,
|
|
9913
|
+
layerId: normalizedPassId,
|
|
9914
|
+
passOrder: index
|
|
9915
|
+
});
|
|
9916
|
+
this.canvas.add(created);
|
|
9917
|
+
byId.set(spec.id, created);
|
|
9918
|
+
continue;
|
|
9249
9919
|
}
|
|
9250
|
-
this.
|
|
9920
|
+
this.patchFabricObject(current, spec, {
|
|
9921
|
+
passId: normalizedPassId,
|
|
9922
|
+
layerId: normalizedPassId,
|
|
9923
|
+
passOrder: index
|
|
9924
|
+
});
|
|
9251
9925
|
}
|
|
9252
|
-
container.dirty = true;
|
|
9253
9926
|
if (options.render !== false) {
|
|
9254
9927
|
this.requestRenderAll();
|
|
9255
9928
|
}
|
|
@@ -9261,10 +9934,56 @@ var CanvasService = class {
|
|
|
9261
9934
|
...extraData || {},
|
|
9262
9935
|
id: spec.id
|
|
9263
9936
|
};
|
|
9937
|
+
nextData.__renderSourceKey = this.getSpecRenderSourceKey(spec);
|
|
9264
9938
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9265
9939
|
obj.set({ ...props, data: nextData });
|
|
9266
9940
|
obj.setCoords();
|
|
9267
9941
|
}
|
|
9942
|
+
readPathDataFromSpec(spec) {
|
|
9943
|
+
var _a, _b;
|
|
9944
|
+
if (spec.type !== "path") return void 0;
|
|
9945
|
+
const raw = ((_a = spec.props) == null ? void 0 : _a.path) || ((_b = spec.props) == null ? void 0 : _b.pathData);
|
|
9946
|
+
if (typeof raw !== "string") return void 0;
|
|
9947
|
+
return raw;
|
|
9948
|
+
}
|
|
9949
|
+
hashText(value) {
|
|
9950
|
+
let hash = 2166136261;
|
|
9951
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
9952
|
+
hash ^= value.charCodeAt(i);
|
|
9953
|
+
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
9954
|
+
}
|
|
9955
|
+
return (hash >>> 0).toString(16);
|
|
9956
|
+
}
|
|
9957
|
+
getSpecRenderSourceKey(spec) {
|
|
9958
|
+
var _a, _b;
|
|
9959
|
+
switch (spec.type) {
|
|
9960
|
+
case "path": {
|
|
9961
|
+
const pathData = this.readPathDataFromSpec(spec) || "";
|
|
9962
|
+
return `path:${this.hashText(pathData)}`;
|
|
9963
|
+
}
|
|
9964
|
+
case "image":
|
|
9965
|
+
return `image:${String(spec.src || "")}`;
|
|
9966
|
+
case "text":
|
|
9967
|
+
return `text:${String((_b = (_a = spec.props) == null ? void 0 : _a.text) != null ? _b : "")}`;
|
|
9968
|
+
case "rect":
|
|
9969
|
+
return "rect";
|
|
9970
|
+
default:
|
|
9971
|
+
return String(spec.type || "");
|
|
9972
|
+
}
|
|
9973
|
+
}
|
|
9974
|
+
shouldRecreateObject(current, spec) {
|
|
9975
|
+
var _a;
|
|
9976
|
+
if (!current) return true;
|
|
9977
|
+
const currentType = String((current == null ? void 0 : current.type) || "").toLowerCase();
|
|
9978
|
+
if (currentType !== spec.type) return true;
|
|
9979
|
+
const expectedKey = this.getSpecRenderSourceKey(spec);
|
|
9980
|
+
const currentKey = String(((_a = current == null ? void 0 : current.data) == null ? void 0 : _a.__renderSourceKey) || "");
|
|
9981
|
+
if (currentKey && expectedKey && currentKey !== expectedKey) return true;
|
|
9982
|
+
if (spec.type === "image" && spec.src && current.getSrc) {
|
|
9983
|
+
return current.getSrc() !== spec.src;
|
|
9984
|
+
}
|
|
9985
|
+
return false;
|
|
9986
|
+
}
|
|
9268
9987
|
resolveFabricProps(spec, props) {
|
|
9269
9988
|
const space = spec.space || "scene";
|
|
9270
9989
|
const next = this.resolveLayoutProps(spec, props);
|
|
@@ -9288,26 +10007,26 @@ var CanvasService = class {
|
|
|
9288
10007
|
next.scaleY = rawScaleY * sceneScale;
|
|
9289
10008
|
return next;
|
|
9290
10009
|
}
|
|
9291
|
-
|
|
10010
|
+
moveObjectInCanvas(obj, index) {
|
|
9292
10011
|
if (!obj) return;
|
|
9293
|
-
const moveObjectTo =
|
|
10012
|
+
const moveObjectTo = this.canvas.moveObjectTo;
|
|
9294
10013
|
if (typeof moveObjectTo === "function") {
|
|
9295
|
-
moveObjectTo.call(
|
|
10014
|
+
moveObjectTo.call(this.canvas, obj, index);
|
|
9296
10015
|
return;
|
|
9297
10016
|
}
|
|
9298
|
-
const list =
|
|
10017
|
+
const list = this.canvas._objects;
|
|
9299
10018
|
if (!Array.isArray(list)) return;
|
|
9300
10019
|
const from = list.indexOf(obj);
|
|
9301
10020
|
if (from < 0 || from === index) return;
|
|
9302
10021
|
list.splice(from, 1);
|
|
9303
10022
|
const target = Math.max(0, Math.min(index, list.length));
|
|
9304
10023
|
list.splice(target, 0, obj);
|
|
9305
|
-
if (typeof
|
|
9306
|
-
|
|
10024
|
+
if (typeof this.canvas._onStackOrderChanged === "function") {
|
|
10025
|
+
this.canvas._onStackOrderChanged();
|
|
9307
10026
|
}
|
|
9308
10027
|
}
|
|
9309
10028
|
async createFabricObject(spec) {
|
|
9310
|
-
var _a, _b
|
|
10029
|
+
var _a, _b;
|
|
9311
10030
|
if (spec.type === "rect") {
|
|
9312
10031
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9313
10032
|
const rect = new Rect({
|
|
@@ -9318,7 +10037,7 @@ var CanvasService = class {
|
|
|
9318
10037
|
return rect;
|
|
9319
10038
|
}
|
|
9320
10039
|
if (spec.type === "path") {
|
|
9321
|
-
const pathData =
|
|
10040
|
+
const pathData = this.readPathDataFromSpec(spec);
|
|
9322
10041
|
if (!pathData) return void 0;
|
|
9323
10042
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9324
10043
|
const path = new Path2(pathData, {
|
|
@@ -9340,7 +10059,7 @@ var CanvasService = class {
|
|
|
9340
10059
|
return image;
|
|
9341
10060
|
}
|
|
9342
10061
|
if (spec.type === "text") {
|
|
9343
|
-
const content = String((
|
|
10062
|
+
const content = String((_b = (_a = spec.props) == null ? void 0 : _a.text) != null ? _b : "");
|
|
9344
10063
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9345
10064
|
const text = new Text(content, {
|
|
9346
10065
|
...props,
|
|
@@ -9362,8 +10081,8 @@ export {
|
|
|
9362
10081
|
MirrorTool,
|
|
9363
10082
|
RulerTool,
|
|
9364
10083
|
SceneLayoutService,
|
|
9365
|
-
SceneVisibilityService,
|
|
9366
10084
|
SizeTool,
|
|
9367
10085
|
ViewportSystem,
|
|
9368
|
-
WhiteInkTool
|
|
10086
|
+
WhiteInkTool,
|
|
10087
|
+
evaluateVisibilityExpr
|
|
9369
10088
|
};
|