@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.js
CHANGED
|
@@ -39,294 +39,104 @@ __export(index_exports, {
|
|
|
39
39
|
MirrorTool: () => MirrorTool,
|
|
40
40
|
RulerTool: () => RulerTool,
|
|
41
41
|
SceneLayoutService: () => SceneLayoutService,
|
|
42
|
-
SceneVisibilityService: () => SceneVisibilityService,
|
|
43
42
|
SizeTool: () => SizeTool,
|
|
44
43
|
ViewportSystem: () => ViewportSystem,
|
|
45
|
-
WhiteInkTool: () => WhiteInkTool
|
|
44
|
+
WhiteInkTool: () => WhiteInkTool,
|
|
45
|
+
evaluateVisibilityExpr: () => evaluateVisibilityExpr
|
|
46
46
|
});
|
|
47
47
|
module.exports = __toCommonJS(index_exports);
|
|
48
48
|
|
|
49
49
|
// src/extensions/background.ts
|
|
50
50
|
var import_core = require("@pooder/core");
|
|
51
51
|
var import_fabric = require("fabric");
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
var
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
this.color = "";
|
|
64
|
-
this.url = "";
|
|
65
|
-
this.specs = [];
|
|
66
|
-
this.renderSeq = 0;
|
|
67
|
-
this.renderImageUrl = "";
|
|
68
|
-
this.sourceSizeBySrc = /* @__PURE__ */ new Map();
|
|
69
|
-
this.pendingSizeBySrc = /* @__PURE__ */ new Map();
|
|
70
|
-
this.onCanvasResized = () => {
|
|
71
|
-
this.updateBackground();
|
|
72
|
-
};
|
|
73
|
-
if (options) {
|
|
74
|
-
Object.assign(this, options);
|
|
52
|
+
|
|
53
|
+
// src/coordinate.ts
|
|
54
|
+
var Coordinate = class {
|
|
55
|
+
/**
|
|
56
|
+
* Calculate layout to fit content within container while preserving aspect ratio.
|
|
57
|
+
*/
|
|
58
|
+
static calculateLayout(container, content, padding = 0) {
|
|
59
|
+
const availableWidth = Math.max(0, container.width - padding * 2);
|
|
60
|
+
const availableHeight = Math.max(0, container.height - padding * 2);
|
|
61
|
+
if (content.width === 0 || content.height === 0) {
|
|
62
|
+
return { scale: 1, offsetX: 0, offsetY: 0, width: 0, height: 0 };
|
|
75
63
|
}
|
|
64
|
+
const scaleX = availableWidth / content.width;
|
|
65
|
+
const scaleY = availableHeight / content.height;
|
|
66
|
+
const scale = Math.min(scaleX, scaleY);
|
|
67
|
+
const width = content.width * scale;
|
|
68
|
+
const height = content.height * scale;
|
|
69
|
+
const offsetX = (container.width - width) / 2;
|
|
70
|
+
const offsetY = (container.height - height) / 2;
|
|
71
|
+
return { scale, offsetX, offsetY, width, height };
|
|
76
72
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
(_a = this.renderProducerDisposable) == null ? void 0 : _a.dispose();
|
|
85
|
-
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
86
|
-
this.id,
|
|
87
|
-
() => ({
|
|
88
|
-
layerSpecs: {
|
|
89
|
-
[BACKGROUND_LAYER_ID]: this.specs
|
|
90
|
-
}
|
|
91
|
-
}),
|
|
92
|
-
{ priority: 0 }
|
|
93
|
-
);
|
|
94
|
-
const configService = context.services.get(
|
|
95
|
-
"ConfigurationService"
|
|
96
|
-
);
|
|
97
|
-
if (configService) {
|
|
98
|
-
this.color = configService.get("background.color", this.color);
|
|
99
|
-
this.url = configService.get("background.url", this.url);
|
|
100
|
-
configService.onAnyChange((e) => {
|
|
101
|
-
if (e.key.startsWith("background.")) {
|
|
102
|
-
const prop = e.key.split(".")[1];
|
|
103
|
-
console.log(
|
|
104
|
-
`[BackgroundTool] Config change detected: ${e.key} -> ${e.value}, prop: ${prop}`
|
|
105
|
-
);
|
|
106
|
-
if (prop && prop in this) {
|
|
107
|
-
console.log(
|
|
108
|
-
`[BackgroundTool] Updating option ${prop} to ${e.value}`
|
|
109
|
-
);
|
|
110
|
-
this[prop] = e.value;
|
|
111
|
-
this.updateBackground();
|
|
112
|
-
} else {
|
|
113
|
-
console.warn(
|
|
114
|
-
`[BackgroundTool] Property ${prop} not found in options`
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
context.eventBus.on("canvas:resized", this.onCanvasResized);
|
|
121
|
-
this.updateBackground();
|
|
73
|
+
/**
|
|
74
|
+
* Convert an absolute value to a normalized value (0-1).
|
|
75
|
+
* @param value Absolute value (e.g., pixels)
|
|
76
|
+
* @param total Total dimension size (e.g., canvas width)
|
|
77
|
+
*/
|
|
78
|
+
static toNormalized(value, total) {
|
|
79
|
+
return total === 0 ? 0 : value / total;
|
|
122
80
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
this.renderProducerDisposable = void 0;
|
|
131
|
-
if (!this.canvasService) return;
|
|
132
|
-
const layer = this.canvasService.getLayer(BACKGROUND_LAYER_ID);
|
|
133
|
-
if (layer) {
|
|
134
|
-
this.canvasService.canvas.remove(layer);
|
|
135
|
-
}
|
|
136
|
-
void this.canvasService.flushRenderFromProducers();
|
|
137
|
-
this.canvasService.requestRenderAll();
|
|
138
|
-
this.canvasService = void 0;
|
|
81
|
+
/**
|
|
82
|
+
* Convert a normalized value (0-1) to an absolute value.
|
|
83
|
+
* @param normalized Normalized value (0-1)
|
|
84
|
+
* @param total Total dimension size (e.g., canvas width)
|
|
85
|
+
*/
|
|
86
|
+
static toAbsolute(normalized, total) {
|
|
87
|
+
return normalized * total;
|
|
139
88
|
}
|
|
140
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Normalize a point's coordinates.
|
|
91
|
+
*/
|
|
92
|
+
static normalizePoint(point, size) {
|
|
141
93
|
return {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
id: "background.color",
|
|
145
|
-
type: "color",
|
|
146
|
-
label: "Background Color",
|
|
147
|
-
default: ""
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
id: "background.url",
|
|
151
|
-
type: "string",
|
|
152
|
-
label: "Image URL",
|
|
153
|
-
default: ""
|
|
154
|
-
}
|
|
155
|
-
],
|
|
156
|
-
[import_core.ContributionPointIds.COMMANDS]: [
|
|
157
|
-
{
|
|
158
|
-
command: "reset",
|
|
159
|
-
title: "Reset Background",
|
|
160
|
-
handler: () => {
|
|
161
|
-
this.updateBackground();
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
command: "clear",
|
|
167
|
-
title: "Clear Background",
|
|
168
|
-
handler: () => {
|
|
169
|
-
this.color = "transparent";
|
|
170
|
-
this.url = "";
|
|
171
|
-
this.updateBackground();
|
|
172
|
-
return true;
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
command: "setBackgroundColor",
|
|
177
|
-
title: "Set Background Color",
|
|
178
|
-
handler: (color) => {
|
|
179
|
-
if (this.color === color) return true;
|
|
180
|
-
this.color = color;
|
|
181
|
-
this.updateBackground();
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
command: "setBackgroundImage",
|
|
187
|
-
title: "Set Background Image",
|
|
188
|
-
handler: (url) => {
|
|
189
|
-
if (this.url === url) return true;
|
|
190
|
-
this.url = url;
|
|
191
|
-
this.updateBackground();
|
|
192
|
-
return true;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
]
|
|
94
|
+
x: this.toNormalized(point.x, size.width),
|
|
95
|
+
y: this.toNormalized(point.y, size.height)
|
|
196
96
|
};
|
|
197
97
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Denormalize a point's coordinates to absolute pixels.
|
|
100
|
+
*/
|
|
101
|
+
static denormalizePoint(point, size) {
|
|
202
102
|
return {
|
|
203
|
-
|
|
204
|
-
|
|
103
|
+
x: this.toAbsolute(point.x, size.width),
|
|
104
|
+
y: this.toAbsolute(point.y, size.height)
|
|
205
105
|
};
|
|
206
106
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
props: {
|
|
220
|
-
left: 0,
|
|
221
|
-
top: 0,
|
|
222
|
-
width,
|
|
223
|
-
height,
|
|
224
|
-
originX: "left",
|
|
225
|
-
originY: "top",
|
|
226
|
-
fill: color,
|
|
227
|
-
selectable: false,
|
|
228
|
-
evented: false,
|
|
229
|
-
excludeFromExport: true
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
];
|
|
233
|
-
if (!imageUrl) {
|
|
234
|
-
return specs;
|
|
235
|
-
}
|
|
236
|
-
const sourceSize = this.sourceSizeBySrc.get(imageUrl);
|
|
237
|
-
const sourceWidth = Math.max(1, Number((sourceSize == null ? void 0 : sourceSize.width) || width));
|
|
238
|
-
const sourceHeight = Math.max(1, Number((sourceSize == null ? void 0 : sourceSize.height) || height));
|
|
239
|
-
const coverScale = Math.max(width / sourceWidth, height / sourceHeight);
|
|
240
|
-
specs.push({
|
|
241
|
-
id: BACKGROUND_IMAGE_ID,
|
|
242
|
-
type: "image",
|
|
243
|
-
src: imageUrl,
|
|
244
|
-
space: "screen",
|
|
245
|
-
data: {
|
|
246
|
-
id: BACKGROUND_IMAGE_ID,
|
|
247
|
-
layerId: BACKGROUND_LAYER_ID,
|
|
248
|
-
type: "background-image"
|
|
249
|
-
},
|
|
250
|
-
props: {
|
|
251
|
-
left: 0,
|
|
252
|
-
top: 0,
|
|
253
|
-
originX: "left",
|
|
254
|
-
originY: "top",
|
|
255
|
-
scaleX: coverScale,
|
|
256
|
-
scaleY: coverScale,
|
|
257
|
-
selectable: false,
|
|
258
|
-
evented: false,
|
|
259
|
-
excludeFromExport: true
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
return specs;
|
|
263
|
-
}
|
|
264
|
-
async ensureImageSize(src) {
|
|
265
|
-
if (!src) return null;
|
|
266
|
-
const cached = this.sourceSizeBySrc.get(src);
|
|
267
|
-
if (cached) return cached;
|
|
268
|
-
const pending = this.pendingSizeBySrc.get(src);
|
|
269
|
-
if (pending) {
|
|
270
|
-
return pending;
|
|
271
|
-
}
|
|
272
|
-
const task = this.loadImageSize(src);
|
|
273
|
-
this.pendingSizeBySrc.set(src, task);
|
|
274
|
-
try {
|
|
275
|
-
return await task;
|
|
276
|
-
} finally {
|
|
277
|
-
if (this.pendingSizeBySrc.get(src) === task) {
|
|
278
|
-
this.pendingSizeBySrc.delete(src);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
async loadImageSize(src) {
|
|
283
|
-
try {
|
|
284
|
-
const image = await import_fabric.FabricImage.fromURL(src, {
|
|
285
|
-
crossOrigin: "anonymous"
|
|
286
|
-
});
|
|
287
|
-
const width = Number((image == null ? void 0 : image.width) || 0);
|
|
288
|
-
const height = Number((image == null ? void 0 : image.height) || 0);
|
|
289
|
-
if (width > 0 && height > 0) {
|
|
290
|
-
const size = { width, height };
|
|
291
|
-
this.sourceSizeBySrc.set(src, size);
|
|
292
|
-
return size;
|
|
293
|
-
}
|
|
294
|
-
} catch (error) {
|
|
295
|
-
console.error("[BackgroundTool] Failed to load image", src, error);
|
|
107
|
+
static convertUnit(value, from, to) {
|
|
108
|
+
if (from === to) return value;
|
|
109
|
+
const toMM = {
|
|
110
|
+
px: 0.264583,
|
|
111
|
+
// 1px = 0.264583mm (96 DPI)
|
|
112
|
+
mm: 1,
|
|
113
|
+
cm: 10,
|
|
114
|
+
in: 25.4
|
|
115
|
+
};
|
|
116
|
+
const mmValue = value * (from === "px" ? toMM.px : toMM[from] || 1);
|
|
117
|
+
if (to === "px") {
|
|
118
|
+
return mmValue / toMM.px;
|
|
296
119
|
}
|
|
297
|
-
return
|
|
120
|
+
return mmValue / (toMM[to] || 1);
|
|
298
121
|
}
|
|
299
|
-
|
|
300
|
-
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// src/units.ts
|
|
125
|
+
function parseLengthToMm(input, defaultUnit) {
|
|
126
|
+
var _a, _b;
|
|
127
|
+
if (typeof input === "number") {
|
|
128
|
+
if (!Number.isFinite(input)) return 0;
|
|
129
|
+
return Coordinate.convertUnit(input, defaultUnit, "mm");
|
|
301
130
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
if (seq !== this.renderSeq) return;
|
|
312
|
-
if (loaded) {
|
|
313
|
-
this.renderImageUrl = nextUrl;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
this.specs = this.buildBackgroundSpecs(color, this.renderImageUrl);
|
|
317
|
-
await this.canvasService.flushRenderFromProducers();
|
|
318
|
-
if (seq !== this.renderSeq) return;
|
|
319
|
-
const layer = this.canvasService.getLayer(BACKGROUND_LAYER_ID);
|
|
320
|
-
if (layer) {
|
|
321
|
-
this.canvasService.canvas.sendObjectToBack(layer);
|
|
322
|
-
}
|
|
323
|
-
this.canvasService.requestRenderAll();
|
|
324
|
-
}
|
|
325
|
-
};
|
|
326
|
-
|
|
327
|
-
// src/extensions/image.ts
|
|
328
|
-
var import_core2 = require("@pooder/core");
|
|
329
|
-
var import_fabric2 = require("fabric");
|
|
131
|
+
const raw = input.trim();
|
|
132
|
+
if (!raw) return 0;
|
|
133
|
+
const match = raw.match(/^([+-]?\d+(?:\.\d+)?)\s*(px|mm|cm|in)?$/i);
|
|
134
|
+
if (!match) return 0;
|
|
135
|
+
const value = Number(match[1]);
|
|
136
|
+
if (!Number.isFinite(value)) return 0;
|
|
137
|
+
const unit = (_b = (_a = match[2]) == null ? void 0 : _a.toLowerCase()) != null ? _b : defaultUnit;
|
|
138
|
+
return Coordinate.convertUnit(value, unit, "mm");
|
|
139
|
+
}
|
|
330
140
|
|
|
331
141
|
// src/extensions/dielineShape.ts
|
|
332
142
|
var BUILTIN_DIELINE_SHAPES = [
|
|
@@ -343,7 +153,7 @@ var DEFAULT_HEART_SHAPE_PARAMS = {
|
|
|
343
153
|
tipSharpness: 0
|
|
344
154
|
};
|
|
345
155
|
var DEFAULT_DIELINE_SHAPE_STYLE = {
|
|
346
|
-
fitMode: "
|
|
156
|
+
fitMode: "stretch",
|
|
347
157
|
...DEFAULT_HEART_SHAPE_PARAMS
|
|
348
158
|
};
|
|
349
159
|
function isDielineShape(value) {
|
|
@@ -393,968 +203,1420 @@ function getHeartShapeParams(style) {
|
|
|
393
203
|
};
|
|
394
204
|
}
|
|
395
205
|
|
|
396
|
-
// src/extensions/
|
|
397
|
-
var
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
206
|
+
// src/extensions/sceneLayoutModel.ts
|
|
207
|
+
var DEFAULT_SIZE_STATE = {
|
|
208
|
+
unit: "mm",
|
|
209
|
+
actualWidthMm: 500,
|
|
210
|
+
actualHeightMm: 500,
|
|
211
|
+
constraintMode: "free",
|
|
212
|
+
aspectRatio: 1,
|
|
213
|
+
cutMode: "trim",
|
|
214
|
+
cutMarginMm: 0,
|
|
215
|
+
viewPadding: 140,
|
|
216
|
+
minMm: 10,
|
|
217
|
+
maxMm: 2e3,
|
|
218
|
+
stepMm: 0.1
|
|
219
|
+
};
|
|
220
|
+
function clamp(value, min, max) {
|
|
221
|
+
return Math.max(min, Math.min(max, value));
|
|
406
222
|
}
|
|
407
|
-
function
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
if (s.outsideAbove) score++;
|
|
411
|
-
}
|
|
412
|
-
return score;
|
|
223
|
+
function roundToStep(value, step) {
|
|
224
|
+
if (!Number.isFinite(step) || step <= 0) return value;
|
|
225
|
+
return Math.round(value / step) * step;
|
|
413
226
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
if (!Number.isFinite(start) || !Number.isFinite(end)) return 0;
|
|
419
|
-
const s = (start % total + total) % total;
|
|
420
|
-
const e = (end % total + total) % total;
|
|
421
|
-
return e >= s ? e - s : total - s + e;
|
|
227
|
+
function sanitizeMmValue(valueMm, limits) {
|
|
228
|
+
if (!Number.isFinite(valueMm)) return limits.minMm;
|
|
229
|
+
const rounded = roundToStep(valueMm, limits.stepMm);
|
|
230
|
+
return clamp(rounded, limits.minMm, limits.maxMm);
|
|
422
231
|
}
|
|
423
|
-
function
|
|
424
|
-
if (
|
|
425
|
-
|
|
426
|
-
const n = Math.max(0, Math.floor(count));
|
|
427
|
-
if (n <= 0) return [];
|
|
428
|
-
const dist = wrappedDistance(total, start, end);
|
|
429
|
-
if (n === 1) return [(start % total + total) % total];
|
|
430
|
-
const step = dist / (n - 1);
|
|
431
|
-
const offsets = [];
|
|
432
|
-
for (let i = 0; i < n; i++) {
|
|
433
|
-
const raw = start + step * i;
|
|
434
|
-
const wrapped = (raw % total + total) % total;
|
|
435
|
-
offsets.push(wrapped);
|
|
436
|
-
}
|
|
437
|
-
return offsets;
|
|
232
|
+
function normalizeUnit(value) {
|
|
233
|
+
if (value === "cm" || value === "in") return value;
|
|
234
|
+
return "mm";
|
|
438
235
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
const { x, y, width, height } = geometry;
|
|
443
|
-
const left = x - width / 2;
|
|
444
|
-
const top = y - height / 2;
|
|
445
|
-
return {
|
|
446
|
-
x: left + feature.x * width,
|
|
447
|
-
y: top + feature.y * height
|
|
448
|
-
};
|
|
236
|
+
function normalizeConstraintMode(value) {
|
|
237
|
+
if (value === "lockAspect" || value === "equal") return value;
|
|
238
|
+
return "free";
|
|
449
239
|
}
|
|
450
|
-
function
|
|
451
|
-
if (
|
|
452
|
-
|
|
453
|
-
} else {
|
|
454
|
-
import_paper.default.view.viewSize = new import_paper.default.Size(width, height);
|
|
455
|
-
}
|
|
240
|
+
function normalizeCutMode(value) {
|
|
241
|
+
if (value === "outset" || value === "inset") return value;
|
|
242
|
+
return "trim";
|
|
456
243
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
let result = shape;
|
|
460
|
-
if (typeof result.resolveCrossings === "function") result = result.resolveCrossings();
|
|
461
|
-
if (typeof result.reduce === "function") result = result.reduce({});
|
|
462
|
-
if (typeof result.reorient === "function") result = result.reorient(true, true);
|
|
463
|
-
if (typeof result.reduce === "function") result = result.reduce({});
|
|
464
|
-
return result;
|
|
244
|
+
function toMm(value, fromUnit) {
|
|
245
|
+
return Coordinate.convertUnit(value, fromUnit, "mm");
|
|
465
246
|
}
|
|
466
|
-
function
|
|
467
|
-
return
|
|
247
|
+
function fromMm(valueMm, toUnit) {
|
|
248
|
+
return Coordinate.convertUnit(valueMm, "mm", toUnit);
|
|
468
249
|
}
|
|
469
|
-
function
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
if (validHits.length === 0) return null;
|
|
480
|
-
validHits.sort((a, b) => b.point.y - a.point.y);
|
|
481
|
-
const flags = validHits.map((h) => {
|
|
482
|
-
const above = h.point.add(new import_paper.default.Point(0, -delta));
|
|
483
|
-
const below = h.point.add(new import_paper.default.Point(0, delta));
|
|
484
|
-
return {
|
|
485
|
-
insideAbove: mainShape.contains(above),
|
|
486
|
-
insideBelow: mainShape.contains(below)
|
|
487
|
-
};
|
|
488
|
-
});
|
|
489
|
-
const idx = pickExitIndex(flags);
|
|
490
|
-
if (idx < 0) return null;
|
|
491
|
-
if (isBridgeDebugEnabled()) {
|
|
492
|
-
console.debug("Geometry: Bridge ray", {
|
|
493
|
-
x,
|
|
494
|
-
validHits: validHits.length,
|
|
495
|
-
idx,
|
|
496
|
-
delta,
|
|
497
|
-
overlap,
|
|
498
|
-
op
|
|
499
|
-
});
|
|
250
|
+
function resolvePaddingPx(raw, containerWidth, containerHeight) {
|
|
251
|
+
if (typeof raw === "number") return Math.max(0, raw);
|
|
252
|
+
if (typeof raw === "string") {
|
|
253
|
+
if (raw.endsWith("%")) {
|
|
254
|
+
const percent = parseFloat(raw) / 100;
|
|
255
|
+
if (!Number.isFinite(percent)) return 0;
|
|
256
|
+
return Math.max(0, Math.min(containerWidth, containerHeight) * percent);
|
|
257
|
+
}
|
|
258
|
+
const fixed = parseFloat(raw);
|
|
259
|
+
return Number.isFinite(fixed) ? Math.max(0, fixed) : 0;
|
|
500
260
|
}
|
|
501
|
-
|
|
502
|
-
return { point: hit.point, location: hit };
|
|
261
|
+
return 0;
|
|
503
262
|
}
|
|
504
|
-
function
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
pointsA.map((p) => ({
|
|
508
|
-
outsideAbove: !mainShape.contains(p.add(new import_paper.default.Point(0, -delta)))
|
|
509
|
-
}))
|
|
263
|
+
function readSizeState(configService) {
|
|
264
|
+
const unit = normalizeUnit(
|
|
265
|
+
configService.get("size.unit", DEFAULT_SIZE_STATE.unit)
|
|
510
266
|
);
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
}))
|
|
267
|
+
const minMm = Math.max(
|
|
268
|
+
0.1,
|
|
269
|
+
Number(configService.get("size.minMm", DEFAULT_SIZE_STATE.minMm))
|
|
515
270
|
);
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
console.debug("Geometry: Bridge chain", {
|
|
520
|
-
scoreA,
|
|
521
|
-
scoreB,
|
|
522
|
-
lenA: pointsA.length,
|
|
523
|
-
lenB: pointsB.length,
|
|
524
|
-
ratioA,
|
|
525
|
-
ratioB,
|
|
526
|
-
delta,
|
|
527
|
-
overlap,
|
|
528
|
-
op
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
const ratioEps = 1e-6;
|
|
532
|
-
if (Math.abs(ratioA - ratioB) > ratioEps) {
|
|
533
|
-
return ratioA > ratioB ? pointsA : pointsB;
|
|
534
|
-
}
|
|
535
|
-
if (scoreA !== scoreB) return scoreA > scoreB ? pointsA : pointsB;
|
|
536
|
-
return pointsA.length <= pointsB.length ? pointsA : pointsB;
|
|
537
|
-
}
|
|
538
|
-
function fitPathItemToRect(item, rect, fitMode) {
|
|
539
|
-
const { left, top, width, height } = rect;
|
|
540
|
-
const bounds = item.bounds;
|
|
541
|
-
if (width <= 0 || height <= 0 || !Number.isFinite(bounds.width) || !Number.isFinite(bounds.height) || bounds.width <= 0 || bounds.height <= 0) {
|
|
542
|
-
item.position = new import_paper.default.Point(left + width / 2, top + height / 2);
|
|
543
|
-
return item;
|
|
544
|
-
}
|
|
545
|
-
item.translate(new import_paper.default.Point(-bounds.left, -bounds.top));
|
|
546
|
-
if (fitMode === "stretch") {
|
|
547
|
-
item.scale(width / bounds.width, height / bounds.height, new import_paper.default.Point(0, 0));
|
|
548
|
-
item.translate(new import_paper.default.Point(left, top));
|
|
549
|
-
return item;
|
|
550
|
-
}
|
|
551
|
-
const uniformScale = Math.min(width / bounds.width, height / bounds.height);
|
|
552
|
-
item.scale(uniformScale, uniformScale, new import_paper.default.Point(0, 0));
|
|
553
|
-
const scaledWidth = bounds.width * uniformScale;
|
|
554
|
-
const scaledHeight = bounds.height * uniformScale;
|
|
555
|
-
item.translate(
|
|
556
|
-
new import_paper.default.Point(
|
|
557
|
-
left + (width - scaledWidth) / 2,
|
|
558
|
-
top + (height - scaledHeight) / 2
|
|
559
|
-
)
|
|
271
|
+
const maxMm = Math.max(
|
|
272
|
+
minMm,
|
|
273
|
+
Number(configService.get("size.maxMm", DEFAULT_SIZE_STATE.maxMm))
|
|
560
274
|
);
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
const { lobeSpread, notchDepth, tipSharpness } = params;
|
|
565
|
-
const halfSpread = 0.22 + lobeSpread * 0.18;
|
|
566
|
-
const notchY = 0.06 + notchDepth * 0.2;
|
|
567
|
-
const shoulderY = 0.24 + notchDepth * 0.2;
|
|
568
|
-
const topLift = 0.12 + (1 - notchDepth) * 0.06;
|
|
569
|
-
const topY = notchY - topLift;
|
|
570
|
-
const sideCtrlY = shoulderY - (0.18 - notchDepth * 0.08);
|
|
571
|
-
const lowerCtrlY = 0.58 + (1 - tipSharpness) * 0.16;
|
|
572
|
-
const tipCtrlX = 0.34 - tipSharpness * 0.2;
|
|
573
|
-
const notchCtrlX = 0.06 + lobeSpread * 0.06;
|
|
574
|
-
const lobeCtrlX = 0.1 + lobeSpread * 0.08;
|
|
575
|
-
const notchCtrlY = notchY - topLift * 0.45;
|
|
576
|
-
const xPeakL = 0.5 - halfSpread;
|
|
577
|
-
const xPeakR = 0.5 + halfSpread;
|
|
578
|
-
const heartPath = new import_paper.default.Path({ insert: false });
|
|
579
|
-
heartPath.moveTo(new import_paper.default.Point(0.5, notchY));
|
|
580
|
-
heartPath.cubicCurveTo(
|
|
581
|
-
new import_paper.default.Point(0.5 - notchCtrlX, notchCtrlY),
|
|
582
|
-
new import_paper.default.Point(xPeakL + lobeCtrlX, topY),
|
|
583
|
-
new import_paper.default.Point(xPeakL, topY)
|
|
275
|
+
const stepMm = Math.max(
|
|
276
|
+
1e-3,
|
|
277
|
+
Number(configService.get("size.stepMm", DEFAULT_SIZE_STATE.stepMm))
|
|
584
278
|
);
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
279
|
+
const actualWidthMm = sanitizeMmValue(
|
|
280
|
+
parseLengthToMm(
|
|
281
|
+
configService.get("size.actualWidthMm", DEFAULT_SIZE_STATE.actualWidthMm),
|
|
282
|
+
"mm"
|
|
283
|
+
),
|
|
284
|
+
{ minMm, maxMm, stepMm }
|
|
589
285
|
);
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
286
|
+
const actualHeightMm = sanitizeMmValue(
|
|
287
|
+
parseLengthToMm(
|
|
288
|
+
configService.get(
|
|
289
|
+
"size.actualHeightMm",
|
|
290
|
+
DEFAULT_SIZE_STATE.actualHeightMm
|
|
291
|
+
),
|
|
292
|
+
"mm"
|
|
293
|
+
),
|
|
294
|
+
{ minMm, maxMm, stepMm }
|
|
594
295
|
);
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
new import_paper.default.Point(1, lowerCtrlY),
|
|
598
|
-
new import_paper.default.Point(1, shoulderY)
|
|
296
|
+
const aspectRaw = Number(
|
|
297
|
+
configService.get("size.aspectRatio", DEFAULT_SIZE_STATE.aspectRatio)
|
|
599
298
|
);
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
299
|
+
const aspectRatio = Number.isFinite(aspectRaw) && aspectRaw > 0 ? aspectRaw : actualWidthMm / Math.max(1e-3, actualHeightMm);
|
|
300
|
+
const cutMarginMm = Math.max(
|
|
301
|
+
0,
|
|
302
|
+
parseLengthToMm(
|
|
303
|
+
configService.get("size.cutMarginMm", DEFAULT_SIZE_STATE.cutMarginMm),
|
|
304
|
+
"mm"
|
|
305
|
+
)
|
|
604
306
|
);
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
new import_paper.default.Point(0.5, notchY)
|
|
307
|
+
const viewPadding = configService.get(
|
|
308
|
+
"size.viewPadding",
|
|
309
|
+
DEFAULT_SIZE_STATE.viewPadding
|
|
609
310
|
);
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
311
|
+
return {
|
|
312
|
+
unit,
|
|
313
|
+
actualWidthMm,
|
|
314
|
+
actualHeightMm,
|
|
315
|
+
constraintMode: normalizeConstraintMode(
|
|
316
|
+
configService.get(
|
|
317
|
+
"size.constraintMode",
|
|
318
|
+
DEFAULT_SIZE_STATE.constraintMode
|
|
319
|
+
)
|
|
320
|
+
),
|
|
321
|
+
aspectRatio,
|
|
322
|
+
cutMode: normalizeCutMode(
|
|
323
|
+
configService.get("size.cutMode", DEFAULT_SIZE_STATE.cutMode)
|
|
324
|
+
),
|
|
325
|
+
cutMarginMm,
|
|
326
|
+
viewPadding,
|
|
327
|
+
minMm,
|
|
328
|
+
maxMm,
|
|
329
|
+
stepMm
|
|
330
|
+
};
|
|
623
331
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
point: [x - width / 2, y - height / 2],
|
|
629
|
-
size: [Math.max(0, width), Math.max(0, height)],
|
|
630
|
-
radius: Math.max(0, radius)
|
|
631
|
-
});
|
|
632
|
-
},
|
|
633
|
-
circle: (options) => {
|
|
634
|
-
const { x, y, width, height } = options;
|
|
635
|
-
const r = Math.min(width, height) / 2;
|
|
636
|
-
return new import_paper.default.Path.Circle({
|
|
637
|
-
center: new import_paper.default.Point(x, y),
|
|
638
|
-
radius: Math.max(0, r)
|
|
639
|
-
});
|
|
640
|
-
},
|
|
641
|
-
ellipse: (options) => {
|
|
642
|
-
const { x, y, width, height } = options;
|
|
643
|
-
return new import_paper.default.Path.Ellipse({
|
|
644
|
-
center: new import_paper.default.Point(x, y),
|
|
645
|
-
radius: [Math.max(0, width / 2), Math.max(0, height / 2)]
|
|
646
|
-
});
|
|
647
|
-
},
|
|
648
|
-
heart: createHeartBaseShape
|
|
649
|
-
};
|
|
650
|
-
function createCustomBaseShape(options) {
|
|
651
|
-
var _a;
|
|
652
|
-
const {
|
|
653
|
-
pathData,
|
|
654
|
-
customSourceWidthPx,
|
|
655
|
-
customSourceHeightPx,
|
|
656
|
-
x,
|
|
657
|
-
y,
|
|
332
|
+
function rectByCenter(centerX, centerY, width, height) {
|
|
333
|
+
return {
|
|
334
|
+
left: centerX - width / 2,
|
|
335
|
+
top: centerY - height / 2,
|
|
658
336
|
width,
|
|
659
|
-
height
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
}
|
|
664
|
-
const center = new import_paper.default.Point(x, y);
|
|
665
|
-
const hasMultipleSubPaths = ((_a = (pathData.match(/[Mm]/g) || []).length) != null ? _a : 0) > 1;
|
|
666
|
-
const path = hasMultipleSubPaths ? new import_paper.default.CompoundPath(pathData) : (() => {
|
|
667
|
-
const single = new import_paper.default.Path();
|
|
668
|
-
single.pathData = pathData;
|
|
669
|
-
return single;
|
|
670
|
-
})();
|
|
671
|
-
const sourceWidth = Number(customSourceWidthPx != null ? customSourceWidthPx : 0);
|
|
672
|
-
const sourceHeight = Number(customSourceHeightPx != null ? customSourceHeightPx : 0);
|
|
673
|
-
if (Number.isFinite(sourceWidth) && Number.isFinite(sourceHeight) && sourceWidth > 0 && sourceHeight > 0 && width > 0 && height > 0) {
|
|
674
|
-
const targetLeft = x - width / 2;
|
|
675
|
-
const targetTop = y - height / 2;
|
|
676
|
-
path.scale(width / sourceWidth, height / sourceHeight, new import_paper.default.Point(0, 0));
|
|
677
|
-
path.translate(new import_paper.default.Point(targetLeft, targetTop));
|
|
678
|
-
return path;
|
|
679
|
-
}
|
|
680
|
-
if (width > 0 && height > 0 && path.bounds.width > 0 && path.bounds.height > 0) {
|
|
681
|
-
path.position = center;
|
|
682
|
-
path.scale(width / path.bounds.width, height / path.bounds.height);
|
|
683
|
-
return path;
|
|
684
|
-
}
|
|
685
|
-
path.position = center;
|
|
686
|
-
return path;
|
|
687
|
-
}
|
|
688
|
-
function createBaseShape(options) {
|
|
689
|
-
const { shape } = options;
|
|
690
|
-
if (shape === "custom") {
|
|
691
|
-
const customShape = createCustomBaseShape(options);
|
|
692
|
-
if (customShape) return customShape;
|
|
693
|
-
return BUILTIN_SHAPE_BUILDERS[DEFAULT_DIELINE_SHAPE](options);
|
|
694
|
-
}
|
|
695
|
-
return BUILTIN_SHAPE_BUILDERS[shape](options);
|
|
337
|
+
height,
|
|
338
|
+
centerX,
|
|
339
|
+
centerY
|
|
340
|
+
};
|
|
696
341
|
}
|
|
697
|
-
function
|
|
698
|
-
if (
|
|
699
|
-
return
|
|
342
|
+
function getCutSizeMm(size) {
|
|
343
|
+
if (size.cutMode === "trim") {
|
|
344
|
+
return { widthMm: size.actualWidthMm, heightMm: size.actualHeightMm };
|
|
700
345
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
let bestDistance = Infinity;
|
|
708
|
-
for (const child of children) {
|
|
709
|
-
const location = child.getNearestLocation(anchor);
|
|
710
|
-
const point = location == null ? void 0 : location.point;
|
|
711
|
-
if (!point) continue;
|
|
712
|
-
const distance = point.getDistance(anchor);
|
|
713
|
-
if (distance < bestDistance) {
|
|
714
|
-
bestDistance = distance;
|
|
715
|
-
best = child;
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
return best;
|
|
346
|
+
const delta = size.cutMarginMm * 2;
|
|
347
|
+
if (size.cutMode === "outset") {
|
|
348
|
+
return {
|
|
349
|
+
widthMm: size.actualWidthMm + delta,
|
|
350
|
+
heightMm: size.actualHeightMm + delta
|
|
351
|
+
};
|
|
719
352
|
}
|
|
720
|
-
return
|
|
353
|
+
return {
|
|
354
|
+
widthMm: Math.max(size.minMm, size.actualWidthMm - delta),
|
|
355
|
+
heightMm: Math.max(size.minMm, size.actualHeightMm - delta)
|
|
356
|
+
};
|
|
721
357
|
}
|
|
722
|
-
function
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
radius: r
|
|
732
|
-
});
|
|
733
|
-
} else {
|
|
734
|
-
const r = feature.radius || 5;
|
|
735
|
-
item = new import_paper.default.Path.Circle({
|
|
736
|
-
center,
|
|
737
|
-
radius: r
|
|
738
|
-
});
|
|
358
|
+
function computeSceneLayout(canvasService, size) {
|
|
359
|
+
const canvasWidth = canvasService.canvas.width || 0;
|
|
360
|
+
const canvasHeight = canvasService.canvas.height || 0;
|
|
361
|
+
if (canvasWidth <= 0 || canvasHeight <= 0) return null;
|
|
362
|
+
const { widthMm: cutWidthMm, heightMm: cutHeightMm } = getCutSizeMm(size);
|
|
363
|
+
const viewWidthMm = Math.max(size.actualWidthMm, cutWidthMm);
|
|
364
|
+
const viewHeightMm = Math.max(size.actualHeightMm, cutHeightMm);
|
|
365
|
+
if (!Number.isFinite(viewWidthMm) || !Number.isFinite(viewHeightMm) || viewWidthMm <= 0 || viewHeightMm <= 0) {
|
|
366
|
+
return null;
|
|
739
367
|
}
|
|
740
|
-
|
|
741
|
-
|
|
368
|
+
const paddingPx = resolvePaddingPx(
|
|
369
|
+
size.viewPadding,
|
|
370
|
+
canvasWidth,
|
|
371
|
+
canvasHeight
|
|
372
|
+
);
|
|
373
|
+
canvasService.viewport.updateContainer(canvasWidth, canvasHeight);
|
|
374
|
+
canvasService.viewport.setPadding(paddingPx);
|
|
375
|
+
canvasService.viewport.updatePhysical(viewWidthMm, viewHeightMm);
|
|
376
|
+
const layout = canvasService.viewport.layout;
|
|
377
|
+
if (!Number.isFinite(layout.scale) || !Number.isFinite(layout.offsetX) || !Number.isFinite(layout.offsetY) || layout.scale <= 0) {
|
|
378
|
+
return null;
|
|
742
379
|
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
const
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
const xRight = itemBounds.right - inset;
|
|
772
|
-
const bridgeBasePath = resolveBridgeBasePath(mainShape, center);
|
|
773
|
-
const canBridge = !!bridgeBasePath && xRight - xLeft > eps;
|
|
774
|
-
if (canBridge && bridgeBasePath) {
|
|
775
|
-
const leftHit = getExitHit({
|
|
776
|
-
mainShape: bridgeBasePath,
|
|
777
|
-
x: xLeft,
|
|
778
|
-
bridgeBottom,
|
|
779
|
-
toY,
|
|
780
|
-
eps,
|
|
781
|
-
delta,
|
|
782
|
-
overlap,
|
|
783
|
-
op: f.operation
|
|
784
|
-
});
|
|
785
|
-
const rightHit = getExitHit({
|
|
786
|
-
mainShape: bridgeBasePath,
|
|
787
|
-
x: xRight,
|
|
788
|
-
bridgeBottom,
|
|
789
|
-
toY,
|
|
790
|
-
eps,
|
|
791
|
-
delta,
|
|
792
|
-
overlap,
|
|
793
|
-
op: f.operation
|
|
794
|
-
});
|
|
795
|
-
if (leftHit && rightHit) {
|
|
796
|
-
const pathLength = bridgeBasePath.length;
|
|
797
|
-
const leftOffset = leftHit.location.offset;
|
|
798
|
-
const rightOffset = rightHit.location.offset;
|
|
799
|
-
const distanceA = wrappedDistance(pathLength, leftOffset, rightOffset);
|
|
800
|
-
const distanceB = wrappedDistance(pathLength, rightOffset, leftOffset);
|
|
801
|
-
const countFor = (d) => Math.max(8, Math.min(80, Math.ceil(d / 6)));
|
|
802
|
-
const offsetsA = sampleWrappedOffsets(
|
|
803
|
-
pathLength,
|
|
804
|
-
leftOffset,
|
|
805
|
-
rightOffset,
|
|
806
|
-
countFor(distanceA)
|
|
807
|
-
);
|
|
808
|
-
const offsetsB = sampleWrappedOffsets(
|
|
809
|
-
pathLength,
|
|
810
|
-
rightOffset,
|
|
811
|
-
leftOffset,
|
|
812
|
-
countFor(distanceB)
|
|
813
|
-
);
|
|
814
|
-
const pointsA = offsetsA.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
815
|
-
const pointsB = offsetsB.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
816
|
-
if (pointsA.length >= 2 && pointsB.length >= 2) {
|
|
817
|
-
let topBase = selectOuterChain({
|
|
818
|
-
mainShape: bridgeBasePath,
|
|
819
|
-
pointsA,
|
|
820
|
-
pointsB,
|
|
821
|
-
delta,
|
|
822
|
-
overlap,
|
|
823
|
-
op: f.operation
|
|
824
|
-
});
|
|
825
|
-
const dist2 = (a, b) => {
|
|
826
|
-
const dx = a.x - b.x;
|
|
827
|
-
const dy = a.y - b.y;
|
|
828
|
-
return dx * dx + dy * dy;
|
|
829
|
-
};
|
|
830
|
-
if (dist2(topBase[0], leftHit.point) > dist2(topBase[0], rightHit.point)) {
|
|
831
|
-
topBase = topBase.slice().reverse();
|
|
832
|
-
}
|
|
833
|
-
topBase = topBase.slice();
|
|
834
|
-
topBase[0] = leftHit.point;
|
|
835
|
-
topBase[topBase.length - 1] = rightHit.point;
|
|
836
|
-
const capShiftY = f.operation === "subtract" ? -Math.max(overlap * 2, delta) : overlap;
|
|
837
|
-
const topPoints = topBase.map(
|
|
838
|
-
(p) => p.add(new import_paper.default.Point(0, capShiftY))
|
|
839
|
-
);
|
|
840
|
-
const bridgeBottomY = bridgeBottom + overlap * 2;
|
|
841
|
-
const bridgePoly = new import_paper.default.Path({ insert: false });
|
|
842
|
-
for (const p of topPoints) bridgePoly.add(p);
|
|
843
|
-
bridgePoly.add(new import_paper.default.Point(xRight, bridgeBottomY));
|
|
844
|
-
bridgePoly.add(new import_paper.default.Point(xLeft, bridgeBottomY));
|
|
845
|
-
bridgePoly.closed = true;
|
|
846
|
-
const unitedItem = item.unite(bridgePoly);
|
|
847
|
-
item.remove();
|
|
848
|
-
bridgePoly.remove();
|
|
849
|
-
if (f.operation === "add") {
|
|
850
|
-
adds.push(unitedItem);
|
|
851
|
-
} else {
|
|
852
|
-
subtracts.push(unitedItem);
|
|
853
|
-
}
|
|
854
|
-
return;
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
if (f.operation === "add") {
|
|
859
|
-
adds.push(item);
|
|
860
|
-
} else {
|
|
861
|
-
subtracts.push(item);
|
|
862
|
-
}
|
|
863
|
-
} else {
|
|
864
|
-
if (f.operation === "add") {
|
|
865
|
-
adds.push(item);
|
|
866
|
-
} else {
|
|
867
|
-
subtracts.push(item);
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
} else {
|
|
871
|
-
if (f.operation === "add") {
|
|
872
|
-
adds.push(item);
|
|
873
|
-
} else {
|
|
874
|
-
subtracts.push(item);
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
});
|
|
878
|
-
if (adds.length > 0) {
|
|
879
|
-
for (const item of adds) {
|
|
880
|
-
try {
|
|
881
|
-
const temp = mainShape.unite(item);
|
|
882
|
-
mainShape.remove();
|
|
883
|
-
item.remove();
|
|
884
|
-
mainShape = normalizePathItem(temp);
|
|
885
|
-
} catch (e) {
|
|
886
|
-
console.error("Geometry: Failed to unite feature", e);
|
|
887
|
-
item.remove();
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
if (subtracts.length > 0) {
|
|
892
|
-
for (const item of subtracts) {
|
|
893
|
-
try {
|
|
894
|
-
const temp = mainShape.subtract(item);
|
|
895
|
-
mainShape.remove();
|
|
896
|
-
item.remove();
|
|
897
|
-
mainShape = normalizePathItem(temp);
|
|
898
|
-
} catch (e) {
|
|
899
|
-
console.error("Geometry: Failed to subtract feature", e);
|
|
900
|
-
item.remove();
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
return mainShape;
|
|
380
|
+
const centerX = layout.offsetX + layout.width / 2;
|
|
381
|
+
const centerY = layout.offsetY + layout.height / 2;
|
|
382
|
+
const trimWidthPx = size.actualWidthMm * layout.scale;
|
|
383
|
+
const trimHeightPx = size.actualHeightMm * layout.scale;
|
|
384
|
+
const cutWidthPx = cutWidthMm * layout.scale;
|
|
385
|
+
const cutHeightPx = cutHeightMm * layout.scale;
|
|
386
|
+
const trimRect = rectByCenter(centerX, centerY, trimWidthPx, trimHeightPx);
|
|
387
|
+
const cutRect = rectByCenter(centerX, centerY, cutWidthPx, cutHeightPx);
|
|
388
|
+
const bleedRect = rectByCenter(
|
|
389
|
+
centerX,
|
|
390
|
+
centerY,
|
|
391
|
+
Math.max(trimWidthPx, cutWidthPx),
|
|
392
|
+
Math.max(trimHeightPx, cutHeightPx)
|
|
393
|
+
);
|
|
394
|
+
return {
|
|
395
|
+
scale: layout.scale,
|
|
396
|
+
canvasWidth,
|
|
397
|
+
canvasHeight,
|
|
398
|
+
trimRect,
|
|
399
|
+
cutRect,
|
|
400
|
+
bleedRect,
|
|
401
|
+
trimWidthMm: size.actualWidthMm,
|
|
402
|
+
trimHeightMm: size.actualHeightMm,
|
|
403
|
+
cutWidthMm,
|
|
404
|
+
cutHeightMm,
|
|
405
|
+
cutMode: size.cutMode,
|
|
406
|
+
cutMarginMm: size.cutMarginMm
|
|
407
|
+
};
|
|
906
408
|
}
|
|
907
|
-
function
|
|
908
|
-
const
|
|
909
|
-
(
|
|
409
|
+
function buildSceneGeometry(configService, layout) {
|
|
410
|
+
const radiusMm = parseLengthToMm(
|
|
411
|
+
configService.get("dieline.radius", 0),
|
|
412
|
+
"mm"
|
|
910
413
|
);
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
414
|
+
const offset = (layout.cutRect.width - layout.trimRect.width) / 2;
|
|
415
|
+
const sourceWidth = Number(configService.get("dieline.customSourceWidthPx", 0));
|
|
416
|
+
const sourceHeight = Number(
|
|
417
|
+
configService.get("dieline.customSourceHeightPx", 0)
|
|
418
|
+
);
|
|
419
|
+
const shapeStyle = normalizeShapeStyle(
|
|
420
|
+
configService.get("dieline.shapeStyle", DEFAULT_DIELINE_SHAPE_STYLE)
|
|
421
|
+
);
|
|
422
|
+
return {
|
|
423
|
+
shape: normalizeDielineShape(
|
|
424
|
+
configService.get("dieline.shape", DEFAULT_DIELINE_SHAPE)
|
|
425
|
+
),
|
|
426
|
+
shapeStyle,
|
|
427
|
+
unit: "px",
|
|
428
|
+
x: layout.trimRect.centerX,
|
|
429
|
+
y: layout.trimRect.centerY,
|
|
430
|
+
width: layout.trimRect.width,
|
|
431
|
+
height: layout.trimRect.height,
|
|
432
|
+
radius: radiusMm * layout.scale,
|
|
433
|
+
offset,
|
|
434
|
+
scale: layout.scale,
|
|
435
|
+
pathData: configService.get("dieline.pathData"),
|
|
436
|
+
customSourceWidthPx: Number.isFinite(sourceWidth) && sourceWidth > 0 ? sourceWidth : void 0,
|
|
437
|
+
customSourceHeightPx: Number.isFinite(sourceHeight) && sourceHeight > 0 ? sourceHeight : void 0
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// src/extensions/background.ts
|
|
442
|
+
var BACKGROUND_LAYER_ID = "background";
|
|
443
|
+
var BACKGROUND_CONFIG_KEY = "background.config";
|
|
444
|
+
var DEFAULT_WIDTH = 800;
|
|
445
|
+
var DEFAULT_HEIGHT = 600;
|
|
446
|
+
var DEFAULT_BACKGROUND_CONFIG = {
|
|
447
|
+
version: 1,
|
|
448
|
+
layers: [
|
|
449
|
+
{
|
|
450
|
+
id: "base-color",
|
|
451
|
+
kind: "color",
|
|
452
|
+
anchor: "viewport",
|
|
453
|
+
fit: "cover",
|
|
454
|
+
opacity: 1,
|
|
455
|
+
order: 0,
|
|
456
|
+
enabled: true,
|
|
457
|
+
exportable: false,
|
|
458
|
+
color: "#fff"
|
|
932
459
|
}
|
|
460
|
+
]
|
|
461
|
+
};
|
|
462
|
+
function clampOpacity(value, fallback) {
|
|
463
|
+
const numeric = Number(value);
|
|
464
|
+
if (!Number.isFinite(numeric)) {
|
|
465
|
+
return Math.max(0, Math.min(1, fallback));
|
|
933
466
|
}
|
|
934
|
-
return
|
|
467
|
+
return Math.max(0, Math.min(1, numeric));
|
|
935
468
|
}
|
|
936
|
-
function
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
const perimeter = getPerimeterShape(options);
|
|
942
|
-
const finalShape = applySurfaceFeatures(perimeter, options.features, options);
|
|
943
|
-
const pathData = finalShape.pathData;
|
|
944
|
-
finalShape.remove();
|
|
945
|
-
return pathData;
|
|
469
|
+
function normalizeLayerKind(value, fallback) {
|
|
470
|
+
if (value === "color" || value === "image") {
|
|
471
|
+
return value;
|
|
472
|
+
}
|
|
473
|
+
return fallback;
|
|
946
474
|
}
|
|
947
|
-
function
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
point: [0, 0],
|
|
953
|
-
size: [canvasWidth, canvasHeight]
|
|
954
|
-
});
|
|
955
|
-
const perimeter = getPerimeterShape(options);
|
|
956
|
-
const mainShape = applySurfaceFeatures(perimeter, options.features, options);
|
|
957
|
-
const finalMask = maskRect.subtract(mainShape);
|
|
958
|
-
maskRect.remove();
|
|
959
|
-
mainShape.remove();
|
|
960
|
-
const pathData = finalMask.pathData;
|
|
961
|
-
finalMask.remove();
|
|
962
|
-
return pathData;
|
|
475
|
+
function normalizeFitMode2(value, fallback) {
|
|
476
|
+
if (value === "contain" || value === "cover" || value === "stretch") {
|
|
477
|
+
return value;
|
|
478
|
+
}
|
|
479
|
+
return fallback;
|
|
963
480
|
}
|
|
964
|
-
function
|
|
965
|
-
|
|
966
|
-
const
|
|
967
|
-
|
|
968
|
-
import_paper.default.project.activeLayer.removeChildren();
|
|
969
|
-
const pOriginal = getPerimeterShape(originalOptions);
|
|
970
|
-
const shapeOriginal = applySurfaceFeatures(
|
|
971
|
-
pOriginal,
|
|
972
|
-
originalOptions.features,
|
|
973
|
-
originalOptions
|
|
974
|
-
);
|
|
975
|
-
const pOffset = getPerimeterShape(offsetOptions);
|
|
976
|
-
const shapeOffset = applySurfaceFeatures(
|
|
977
|
-
pOffset,
|
|
978
|
-
offsetOptions.features,
|
|
979
|
-
offsetOptions
|
|
980
|
-
);
|
|
981
|
-
let bleedZone;
|
|
982
|
-
if (offset > 0) {
|
|
983
|
-
bleedZone = shapeOffset.subtract(shapeOriginal);
|
|
984
|
-
} else {
|
|
985
|
-
bleedZone = shapeOriginal.subtract(shapeOffset);
|
|
986
|
-
}
|
|
987
|
-
const pathData = bleedZone.pathData;
|
|
988
|
-
shapeOriginal.remove();
|
|
989
|
-
shapeOffset.remove();
|
|
990
|
-
bleedZone.remove();
|
|
991
|
-
return pathData;
|
|
481
|
+
function normalizeAnchor(value, fallback) {
|
|
482
|
+
if (typeof value !== "string") return fallback;
|
|
483
|
+
const trimmed = value.trim();
|
|
484
|
+
return trimmed || fallback;
|
|
992
485
|
}
|
|
993
|
-
function
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
486
|
+
function normalizeOrder(value, fallback) {
|
|
487
|
+
const numeric = Number(value);
|
|
488
|
+
if (!Number.isFinite(numeric)) return fallback;
|
|
489
|
+
return numeric;
|
|
490
|
+
}
|
|
491
|
+
function normalizeLayer(raw, index, fallback) {
|
|
492
|
+
const fallbackLayer = fallback || {
|
|
493
|
+
id: `layer-${index + 1}`,
|
|
494
|
+
kind: "image",
|
|
495
|
+
anchor: "viewport",
|
|
496
|
+
fit: "contain",
|
|
497
|
+
opacity: 1,
|
|
498
|
+
order: index,
|
|
499
|
+
enabled: true,
|
|
500
|
+
exportable: false,
|
|
501
|
+
src: ""
|
|
502
|
+
};
|
|
503
|
+
if (!raw || typeof raw !== "object") {
|
|
504
|
+
return { ...fallbackLayer };
|
|
505
|
+
}
|
|
506
|
+
const input = raw;
|
|
507
|
+
const kind = normalizeLayerKind(input.kind, fallbackLayer.kind);
|
|
508
|
+
return {
|
|
509
|
+
id: typeof input.id === "string" && input.id.trim().length > 0 ? input.id.trim() : fallbackLayer.id,
|
|
510
|
+
kind,
|
|
511
|
+
anchor: normalizeAnchor(input.anchor, fallbackLayer.anchor),
|
|
512
|
+
fit: normalizeFitMode2(input.fit, fallbackLayer.fit),
|
|
513
|
+
opacity: clampOpacity(input.opacity, fallbackLayer.opacity),
|
|
514
|
+
order: normalizeOrder(input.order, fallbackLayer.order),
|
|
515
|
+
enabled: typeof input.enabled === "boolean" ? input.enabled : fallbackLayer.enabled,
|
|
516
|
+
exportable: typeof input.exportable === "boolean" ? input.exportable : fallbackLayer.exportable,
|
|
517
|
+
color: kind === "color" ? typeof input.color === "string" ? input.color : typeof fallbackLayer.color === "string" ? fallbackLayer.color : "#ffffff" : void 0,
|
|
518
|
+
src: kind === "image" ? typeof input.src === "string" ? input.src.trim() : typeof fallbackLayer.src === "string" ? fallbackLayer.src : "" : void 0
|
|
1001
519
|
};
|
|
1002
|
-
shape.remove();
|
|
1003
|
-
return result;
|
|
1004
520
|
}
|
|
1005
|
-
function
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
const
|
|
1010
|
-
const
|
|
1011
|
-
const
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
521
|
+
function normalizeConfig(raw) {
|
|
522
|
+
if (!raw || typeof raw !== "object") {
|
|
523
|
+
return cloneConfig(DEFAULT_BACKGROUND_CONFIG);
|
|
524
|
+
}
|
|
525
|
+
const input = raw;
|
|
526
|
+
const version = Number.isFinite(Number(input.version)) ? Number(input.version) : DEFAULT_BACKGROUND_CONFIG.version;
|
|
527
|
+
const baseLayers = Array.isArray(input.layers) ? input.layers.map((layer, index) => normalizeLayer(layer, index)) : cloneConfig(DEFAULT_BACKGROUND_CONFIG).layers;
|
|
528
|
+
const uniqueLayers = [];
|
|
529
|
+
const seen = /* @__PURE__ */ new Set();
|
|
530
|
+
baseLayers.forEach((layer, index) => {
|
|
531
|
+
let nextId = layer.id || `layer-${index + 1}`;
|
|
532
|
+
let serial = 1;
|
|
533
|
+
while (seen.has(nextId)) {
|
|
534
|
+
serial += 1;
|
|
535
|
+
nextId = `${layer.id || `layer-${index + 1}`}-${serial}`;
|
|
536
|
+
}
|
|
537
|
+
seen.add(nextId);
|
|
538
|
+
uniqueLayers.push({ ...layer, id: nextId });
|
|
539
|
+
});
|
|
540
|
+
return {
|
|
541
|
+
version,
|
|
542
|
+
layers: uniqueLayers
|
|
1015
543
|
};
|
|
1016
|
-
shape.remove();
|
|
1017
|
-
return result;
|
|
1018
544
|
}
|
|
1019
|
-
function
|
|
1020
|
-
const path = new import_paper.default.Path();
|
|
1021
|
-
path.pathData = pathData;
|
|
1022
|
-
const bounds = path.bounds;
|
|
1023
|
-
path.remove();
|
|
545
|
+
function cloneConfig(config) {
|
|
1024
546
|
return {
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
width: bounds.width,
|
|
1028
|
-
height: bounds.height
|
|
547
|
+
version: config.version,
|
|
548
|
+
layers: (config.layers || []).map((layer) => ({ ...layer }))
|
|
1029
549
|
};
|
|
1030
550
|
}
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
551
|
+
function mergeConfig(base, patch) {
|
|
552
|
+
const merged = {
|
|
553
|
+
version: patch.version === void 0 ? base.version : Number.isFinite(Number(patch.version)) ? Number(patch.version) : base.version,
|
|
554
|
+
layers: Array.isArray(patch.layers) ? patch.layers.map((layer, index) => normalizeLayer(layer, index)) : base.layers.map((layer) => ({ ...layer }))
|
|
555
|
+
};
|
|
556
|
+
return normalizeConfig(merged);
|
|
557
|
+
}
|
|
558
|
+
function configSignature(config) {
|
|
559
|
+
return JSON.stringify(config);
|
|
560
|
+
}
|
|
561
|
+
var BackgroundTool = class {
|
|
562
|
+
constructor(options) {
|
|
563
|
+
this.id = "pooder.kit.background";
|
|
564
|
+
this.metadata = {
|
|
565
|
+
name: "BackgroundTool"
|
|
566
|
+
};
|
|
567
|
+
this.config = cloneConfig(DEFAULT_BACKGROUND_CONFIG);
|
|
568
|
+
this.specs = [];
|
|
569
|
+
this.renderSeq = 0;
|
|
570
|
+
this.latestSceneLayout = null;
|
|
571
|
+
this.sourceSizeBySrc = /* @__PURE__ */ new Map();
|
|
572
|
+
this.pendingSizeBySrc = /* @__PURE__ */ new Map();
|
|
573
|
+
this.onCanvasResized = () => {
|
|
574
|
+
this.latestSceneLayout = null;
|
|
575
|
+
this.updateBackground();
|
|
576
|
+
};
|
|
577
|
+
this.onSceneLayoutChanged = (layout) => {
|
|
578
|
+
this.latestSceneLayout = layout;
|
|
579
|
+
this.updateBackground();
|
|
580
|
+
};
|
|
581
|
+
if (options && typeof options === "object") {
|
|
582
|
+
this.config = mergeConfig(this.config, options);
|
|
1042
583
|
}
|
|
1043
|
-
const scaleX = availableWidth / content.width;
|
|
1044
|
-
const scaleY = availableHeight / content.height;
|
|
1045
|
-
const scale = Math.min(scaleX, scaleY);
|
|
1046
|
-
const width = content.width * scale;
|
|
1047
|
-
const height = content.height * scale;
|
|
1048
|
-
const offsetX = (container.width - width) / 2;
|
|
1049
|
-
const offsetY = (container.height - height) / 2;
|
|
1050
|
-
return { scale, offsetX, offsetY, width, height };
|
|
1051
584
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
585
|
+
activate(context) {
|
|
586
|
+
var _a, _b;
|
|
587
|
+
this.canvasService = context.services.get("CanvasService");
|
|
588
|
+
if (!this.canvasService) {
|
|
589
|
+
console.warn("CanvasService not found for BackgroundTool");
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
this.configService = context.services.get(
|
|
593
|
+
"ConfigurationService"
|
|
594
|
+
);
|
|
595
|
+
if (this.configService) {
|
|
596
|
+
this.config = normalizeConfig(
|
|
597
|
+
this.configService.get(
|
|
598
|
+
BACKGROUND_CONFIG_KEY,
|
|
599
|
+
DEFAULT_BACKGROUND_CONFIG
|
|
600
|
+
)
|
|
601
|
+
);
|
|
602
|
+
(_a = this.configChangeDisposable) == null ? void 0 : _a.dispose();
|
|
603
|
+
this.configChangeDisposable = this.configService.onAnyChange(
|
|
604
|
+
(e) => {
|
|
605
|
+
if (e.key === BACKGROUND_CONFIG_KEY) {
|
|
606
|
+
this.config = normalizeConfig(e.value);
|
|
607
|
+
this.updateBackground();
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
if (e.key.startsWith("size.")) {
|
|
611
|
+
this.latestSceneLayout = null;
|
|
612
|
+
this.updateBackground();
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
618
|
+
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
619
|
+
this.id,
|
|
620
|
+
() => ({
|
|
621
|
+
passes: [
|
|
622
|
+
{
|
|
623
|
+
id: BACKGROUND_LAYER_ID,
|
|
624
|
+
stack: 0,
|
|
625
|
+
order: 0,
|
|
626
|
+
objects: this.specs
|
|
627
|
+
}
|
|
628
|
+
]
|
|
629
|
+
}),
|
|
630
|
+
{ priority: 0 }
|
|
631
|
+
);
|
|
632
|
+
context.eventBus.on("canvas:resized", this.onCanvasResized);
|
|
633
|
+
context.eventBus.on("scene:layout:change", this.onSceneLayoutChanged);
|
|
634
|
+
this.updateBackground();
|
|
1059
635
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
636
|
+
deactivate(context) {
|
|
637
|
+
var _a, _b;
|
|
638
|
+
context.eventBus.off("canvas:resized", this.onCanvasResized);
|
|
639
|
+
context.eventBus.off("scene:layout:change", this.onSceneLayoutChanged);
|
|
640
|
+
this.renderSeq += 1;
|
|
641
|
+
this.specs = [];
|
|
642
|
+
this.latestSceneLayout = null;
|
|
643
|
+
(_a = this.configChangeDisposable) == null ? void 0 : _a.dispose();
|
|
644
|
+
this.configChangeDisposable = void 0;
|
|
645
|
+
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
646
|
+
this.renderProducerDisposable = void 0;
|
|
647
|
+
if (!this.canvasService) return;
|
|
648
|
+
void this.canvasService.flushRenderFromProducers();
|
|
649
|
+
this.canvasService.requestRenderAll();
|
|
650
|
+
this.canvasService = void 0;
|
|
651
|
+
this.configService = void 0;
|
|
1067
652
|
}
|
|
1068
|
-
|
|
1069
|
-
* Normalize a point's coordinates.
|
|
1070
|
-
*/
|
|
1071
|
-
static normalizePoint(point, size) {
|
|
653
|
+
contribute() {
|
|
1072
654
|
return {
|
|
1073
|
-
|
|
1074
|
-
|
|
655
|
+
[import_core.ContributionPointIds.CONFIGURATIONS]: [
|
|
656
|
+
{
|
|
657
|
+
id: BACKGROUND_CONFIG_KEY,
|
|
658
|
+
type: "json",
|
|
659
|
+
label: "Background Config",
|
|
660
|
+
default: cloneConfig(DEFAULT_BACKGROUND_CONFIG)
|
|
661
|
+
}
|
|
662
|
+
],
|
|
663
|
+
[import_core.ContributionPointIds.COMMANDS]: [
|
|
664
|
+
{
|
|
665
|
+
command: "background.getConfig",
|
|
666
|
+
title: "Get Background Config",
|
|
667
|
+
handler: () => cloneConfig(this.config)
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
command: "background.resetConfig",
|
|
671
|
+
title: "Reset Background Config",
|
|
672
|
+
handler: () => {
|
|
673
|
+
this.commitConfig(cloneConfig(DEFAULT_BACKGROUND_CONFIG));
|
|
674
|
+
return true;
|
|
675
|
+
}
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
command: "background.replaceConfig",
|
|
679
|
+
title: "Replace Background Config",
|
|
680
|
+
handler: (config) => {
|
|
681
|
+
this.commitConfig(normalizeConfig(config));
|
|
682
|
+
return true;
|
|
683
|
+
}
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
command: "background.patchConfig",
|
|
687
|
+
title: "Patch Background Config",
|
|
688
|
+
handler: (patch) => {
|
|
689
|
+
this.commitConfig(mergeConfig(this.config, patch || {}));
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
command: "background.upsertLayer",
|
|
695
|
+
title: "Upsert Background Layer",
|
|
696
|
+
handler: (layer) => {
|
|
697
|
+
const normalized = normalizeLayer(layer, 0);
|
|
698
|
+
const existingIndex = this.config.layers.findIndex(
|
|
699
|
+
(item) => item.id === normalized.id
|
|
700
|
+
);
|
|
701
|
+
const nextLayers = [...this.config.layers];
|
|
702
|
+
if (existingIndex >= 0) {
|
|
703
|
+
nextLayers[existingIndex] = normalizeLayer(
|
|
704
|
+
{ ...nextLayers[existingIndex], ...layer },
|
|
705
|
+
existingIndex,
|
|
706
|
+
nextLayers[existingIndex]
|
|
707
|
+
);
|
|
708
|
+
} else {
|
|
709
|
+
nextLayers.push(
|
|
710
|
+
normalizeLayer(
|
|
711
|
+
{
|
|
712
|
+
...normalized,
|
|
713
|
+
order: Number.isFinite(Number(layer.order)) ? Number(layer.order) : nextLayers.length
|
|
714
|
+
},
|
|
715
|
+
nextLayers.length
|
|
716
|
+
)
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
this.commitConfig(
|
|
720
|
+
normalizeConfig({
|
|
721
|
+
...this.config,
|
|
722
|
+
layers: nextLayers
|
|
723
|
+
})
|
|
724
|
+
);
|
|
725
|
+
return true;
|
|
726
|
+
}
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
command: "background.removeLayer",
|
|
730
|
+
title: "Remove Background Layer",
|
|
731
|
+
handler: (id) => {
|
|
732
|
+
const nextLayers = this.config.layers.filter(
|
|
733
|
+
(layer) => layer.id !== id
|
|
734
|
+
);
|
|
735
|
+
this.commitConfig(
|
|
736
|
+
normalizeConfig({
|
|
737
|
+
...this.config,
|
|
738
|
+
layers: nextLayers
|
|
739
|
+
})
|
|
740
|
+
);
|
|
741
|
+
return true;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
]
|
|
1075
745
|
};
|
|
1076
746
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
747
|
+
commitConfig(next) {
|
|
748
|
+
const normalized = normalizeConfig(next);
|
|
749
|
+
if (configSignature(normalized) === configSignature(this.config)) {
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
if (this.configService) {
|
|
753
|
+
this.configService.update(BACKGROUND_CONFIG_KEY, cloneConfig(normalized));
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
this.config = normalized;
|
|
757
|
+
this.updateBackground();
|
|
758
|
+
}
|
|
759
|
+
getViewportRect() {
|
|
760
|
+
var _a, _b;
|
|
761
|
+
const width = Number(((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 0);
|
|
762
|
+
const height = Number(((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 0);
|
|
1081
763
|
return {
|
|
1082
|
-
|
|
1083
|
-
|
|
764
|
+
left: 0,
|
|
765
|
+
top: 0,
|
|
766
|
+
width: width > 0 ? width : DEFAULT_WIDTH,
|
|
767
|
+
height: height > 0 ? height : DEFAULT_HEIGHT
|
|
1084
768
|
};
|
|
1085
769
|
}
|
|
1086
|
-
|
|
1087
|
-
if (
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
770
|
+
resolveSceneLayout() {
|
|
771
|
+
if (this.latestSceneLayout) return this.latestSceneLayout;
|
|
772
|
+
if (!this.canvasService || !this.configService) return null;
|
|
773
|
+
const layout = computeSceneLayout(
|
|
774
|
+
this.canvasService,
|
|
775
|
+
readSizeState(this.configService)
|
|
776
|
+
);
|
|
777
|
+
this.latestSceneLayout = layout;
|
|
778
|
+
return layout;
|
|
779
|
+
}
|
|
780
|
+
resolveFocusRect() {
|
|
781
|
+
const layout = this.resolveSceneLayout();
|
|
782
|
+
if (!layout) return null;
|
|
783
|
+
return {
|
|
784
|
+
left: layout.trimRect.left,
|
|
785
|
+
top: layout.trimRect.top,
|
|
786
|
+
width: layout.trimRect.width,
|
|
787
|
+
height: layout.trimRect.height
|
|
1094
788
|
};
|
|
1095
|
-
const mmValue = value * (from === "px" ? toMM.px : toMM[from] || 1);
|
|
1096
|
-
if (to === "px") {
|
|
1097
|
-
return mmValue / toMM.px;
|
|
1098
|
-
}
|
|
1099
|
-
return mmValue / (toMM[to] || 1);
|
|
1100
789
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
return
|
|
790
|
+
resolveAnchorRect(anchor) {
|
|
791
|
+
if (anchor === "focus") {
|
|
792
|
+
return this.resolveFocusRect() || this.getViewportRect();
|
|
793
|
+
}
|
|
794
|
+
if (anchor !== "viewport") {
|
|
795
|
+
return this.getViewportRect();
|
|
796
|
+
}
|
|
797
|
+
return this.getViewportRect();
|
|
798
|
+
}
|
|
799
|
+
resolveImagePlacement(target, sourceSize, fit) {
|
|
800
|
+
const targetWidth = Math.max(1, Number(target.width || 0));
|
|
801
|
+
const targetHeight = Math.max(1, Number(target.height || 0));
|
|
802
|
+
const sourceWidth = Math.max(1, Number(sourceSize.width || 0));
|
|
803
|
+
const sourceHeight = Math.max(1, Number(sourceSize.height || 0));
|
|
804
|
+
if (fit === "stretch") {
|
|
805
|
+
return {
|
|
806
|
+
left: target.left,
|
|
807
|
+
top: target.top,
|
|
808
|
+
scaleX: targetWidth / sourceWidth,
|
|
809
|
+
scaleY: targetHeight / sourceHeight
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
const scale = fit === "contain" ? Math.min(targetWidth / sourceWidth, targetHeight / sourceHeight) : Math.max(targetWidth / sourceWidth, targetHeight / sourceHeight);
|
|
813
|
+
const renderWidth = sourceWidth * scale;
|
|
814
|
+
const renderHeight = sourceHeight * scale;
|
|
815
|
+
return {
|
|
816
|
+
left: target.left + (targetWidth - renderWidth) / 2,
|
|
817
|
+
top: target.top + (targetHeight - renderHeight) / 2,
|
|
818
|
+
scaleX: scale,
|
|
819
|
+
scaleY: scale
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
buildColorLayerSpec(layer) {
|
|
823
|
+
const rect = this.resolveAnchorRect(layer.anchor);
|
|
824
|
+
return {
|
|
825
|
+
id: `background.layer.${layer.id}.color`,
|
|
826
|
+
type: "rect",
|
|
827
|
+
space: "screen",
|
|
828
|
+
data: {
|
|
829
|
+
id: `background.layer.${layer.id}.color`,
|
|
830
|
+
layerId: BACKGROUND_LAYER_ID,
|
|
831
|
+
type: "background-layer",
|
|
832
|
+
layerRef: layer.id,
|
|
833
|
+
layerKind: layer.kind
|
|
834
|
+
},
|
|
835
|
+
props: {
|
|
836
|
+
left: rect.left,
|
|
837
|
+
top: rect.top,
|
|
838
|
+
width: rect.width,
|
|
839
|
+
height: rect.height,
|
|
840
|
+
originX: "left",
|
|
841
|
+
originY: "top",
|
|
842
|
+
fill: layer.color || "transparent",
|
|
843
|
+
opacity: layer.opacity,
|
|
844
|
+
selectable: false,
|
|
845
|
+
evented: false,
|
|
846
|
+
excludeFromExport: !layer.exportable
|
|
847
|
+
}
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
buildImageLayerSpec(layer) {
|
|
851
|
+
const src = String(layer.src || "").trim();
|
|
852
|
+
if (!src) return [];
|
|
853
|
+
const sourceSize = this.sourceSizeBySrc.get(src);
|
|
854
|
+
if (!sourceSize) return [];
|
|
855
|
+
const rect = this.resolveAnchorRect(layer.anchor);
|
|
856
|
+
const placement = this.resolveImagePlacement(rect, sourceSize, layer.fit);
|
|
857
|
+
return [
|
|
858
|
+
{
|
|
859
|
+
id: `background.layer.${layer.id}.image`,
|
|
860
|
+
type: "image",
|
|
861
|
+
src,
|
|
862
|
+
space: "screen",
|
|
863
|
+
data: {
|
|
864
|
+
id: `background.layer.${layer.id}.image`,
|
|
865
|
+
layerId: BACKGROUND_LAYER_ID,
|
|
866
|
+
type: "background-layer",
|
|
867
|
+
layerRef: layer.id,
|
|
868
|
+
layerKind: layer.kind
|
|
869
|
+
},
|
|
870
|
+
props: {
|
|
871
|
+
left: placement.left,
|
|
872
|
+
top: placement.top,
|
|
873
|
+
originX: "left",
|
|
874
|
+
originY: "top",
|
|
875
|
+
scaleX: placement.scaleX,
|
|
876
|
+
scaleY: placement.scaleY,
|
|
877
|
+
opacity: layer.opacity,
|
|
878
|
+
selectable: false,
|
|
879
|
+
evented: false,
|
|
880
|
+
excludeFromExport: !layer.exportable
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
];
|
|
884
|
+
}
|
|
885
|
+
buildBackgroundSpecs(config) {
|
|
886
|
+
const activeLayers = (config.layers || []).filter((layer) => layer.enabled).map((layer, index) => ({ layer, index })).sort((a, b) => {
|
|
887
|
+
if (a.layer.order !== b.layer.order) {
|
|
888
|
+
return a.layer.order - b.layer.order;
|
|
889
|
+
}
|
|
890
|
+
return a.index - b.index;
|
|
891
|
+
});
|
|
892
|
+
const specs = [];
|
|
893
|
+
activeLayers.forEach(({ layer }) => {
|
|
894
|
+
if (layer.kind === "color") {
|
|
895
|
+
specs.push(this.buildColorLayerSpec(layer));
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
specs.push(...this.buildImageLayerSpec(layer));
|
|
899
|
+
});
|
|
900
|
+
return specs;
|
|
901
|
+
}
|
|
902
|
+
collectActiveImageUrls(config) {
|
|
903
|
+
const urls = /* @__PURE__ */ new Set();
|
|
904
|
+
(config.layers || []).forEach((layer) => {
|
|
905
|
+
if (!layer.enabled || layer.kind !== "image") return;
|
|
906
|
+
const src = String(layer.src || "").trim();
|
|
907
|
+
if (!src) return;
|
|
908
|
+
urls.add(src);
|
|
909
|
+
});
|
|
910
|
+
return Array.from(urls);
|
|
911
|
+
}
|
|
912
|
+
async ensureImageSize(src) {
|
|
913
|
+
if (!src) return null;
|
|
914
|
+
const cached = this.sourceSizeBySrc.get(src);
|
|
915
|
+
if (cached) return cached;
|
|
916
|
+
const pending = this.pendingSizeBySrc.get(src);
|
|
917
|
+
if (pending) {
|
|
918
|
+
return pending;
|
|
919
|
+
}
|
|
920
|
+
const task = this.loadImageSize(src);
|
|
921
|
+
this.pendingSizeBySrc.set(src, task);
|
|
922
|
+
try {
|
|
923
|
+
return await task;
|
|
924
|
+
} finally {
|
|
925
|
+
if (this.pendingSizeBySrc.get(src) === task) {
|
|
926
|
+
this.pendingSizeBySrc.delete(src);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
async loadImageSize(src) {
|
|
931
|
+
try {
|
|
932
|
+
const image = await import_fabric.FabricImage.fromURL(src, {
|
|
933
|
+
crossOrigin: "anonymous"
|
|
934
|
+
});
|
|
935
|
+
const width = Number((image == null ? void 0 : image.width) || 0);
|
|
936
|
+
const height = Number((image == null ? void 0 : image.height) || 0);
|
|
937
|
+
if (width > 0 && height > 0) {
|
|
938
|
+
const size = { width, height };
|
|
939
|
+
this.sourceSizeBySrc.set(src, size);
|
|
940
|
+
return size;
|
|
941
|
+
}
|
|
942
|
+
} catch (error) {
|
|
943
|
+
console.error("[BackgroundTool] Failed to load image", src, error);
|
|
944
|
+
}
|
|
945
|
+
return null;
|
|
946
|
+
}
|
|
947
|
+
updateBackground() {
|
|
948
|
+
void this.updateBackgroundAsync();
|
|
949
|
+
}
|
|
950
|
+
async updateBackgroundAsync() {
|
|
951
|
+
if (!this.canvasService) return;
|
|
952
|
+
const seq = ++this.renderSeq;
|
|
953
|
+
const currentConfig = cloneConfig(this.config);
|
|
954
|
+
const activeUrls = this.collectActiveImageUrls(currentConfig);
|
|
955
|
+
if (activeUrls.length > 0) {
|
|
956
|
+
await Promise.all(activeUrls.map((url) => this.ensureImageSize(url)));
|
|
957
|
+
if (seq !== this.renderSeq) return;
|
|
958
|
+
}
|
|
959
|
+
this.specs = this.buildBackgroundSpecs(currentConfig);
|
|
960
|
+
await this.canvasService.flushRenderFromProducers();
|
|
961
|
+
if (seq !== this.renderSeq) return;
|
|
962
|
+
this.canvasService.requestRenderAll();
|
|
1109
963
|
}
|
|
1110
|
-
const raw = input.trim();
|
|
1111
|
-
if (!raw) return 0;
|
|
1112
|
-
const match = raw.match(/^([+-]?\d+(?:\.\d+)?)\s*(px|mm|cm|in)?$/i);
|
|
1113
|
-
if (!match) return 0;
|
|
1114
|
-
const value = Number(match[1]);
|
|
1115
|
-
if (!Number.isFinite(value)) return 0;
|
|
1116
|
-
const unit = (_b = (_a = match[2]) == null ? void 0 : _a.toLowerCase()) != null ? _b : defaultUnit;
|
|
1117
|
-
return Coordinate.convertUnit(value, unit, "mm");
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
// src/extensions/sceneLayoutModel.ts
|
|
1121
|
-
var DEFAULT_SIZE_STATE = {
|
|
1122
|
-
unit: "mm",
|
|
1123
|
-
actualWidthMm: 500,
|
|
1124
|
-
actualHeightMm: 500,
|
|
1125
|
-
constraintMode: "free",
|
|
1126
|
-
aspectRatio: 1,
|
|
1127
|
-
cutMode: "trim",
|
|
1128
|
-
cutMarginMm: 0,
|
|
1129
|
-
viewPadding: 140,
|
|
1130
|
-
minMm: 10,
|
|
1131
|
-
maxMm: 2e3,
|
|
1132
|
-
stepMm: 0.1
|
|
1133
964
|
};
|
|
1134
|
-
|
|
1135
|
-
|
|
965
|
+
|
|
966
|
+
// src/extensions/image.ts
|
|
967
|
+
var import_core2 = require("@pooder/core");
|
|
968
|
+
var import_fabric2 = require("fabric");
|
|
969
|
+
|
|
970
|
+
// src/extensions/geometry.ts
|
|
971
|
+
var import_paper = __toESM(require("paper"));
|
|
972
|
+
|
|
973
|
+
// src/extensions/bridgeSelection.ts
|
|
974
|
+
function pickExitIndex(hits) {
|
|
975
|
+
for (let i = 0; i < hits.length; i++) {
|
|
976
|
+
const h = hits[i];
|
|
977
|
+
if (h.insideBelow && !h.insideAbove) return i;
|
|
978
|
+
}
|
|
979
|
+
return -1;
|
|
1136
980
|
}
|
|
1137
|
-
function
|
|
1138
|
-
|
|
1139
|
-
|
|
981
|
+
function scoreOutsideAbove(samples) {
|
|
982
|
+
let score = 0;
|
|
983
|
+
for (const s of samples) {
|
|
984
|
+
if (s.outsideAbove) score++;
|
|
985
|
+
}
|
|
986
|
+
return score;
|
|
1140
987
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
988
|
+
|
|
989
|
+
// src/extensions/wrappedOffsets.ts
|
|
990
|
+
function wrappedDistance(total, start, end) {
|
|
991
|
+
if (!Number.isFinite(total) || total <= 0) return 0;
|
|
992
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return 0;
|
|
993
|
+
const s = (start % total + total) % total;
|
|
994
|
+
const e = (end % total + total) % total;
|
|
995
|
+
return e >= s ? e - s : total - s + e;
|
|
1145
996
|
}
|
|
1146
|
-
function
|
|
1147
|
-
if (
|
|
1148
|
-
return
|
|
997
|
+
function sampleWrappedOffsets(total, start, end, count) {
|
|
998
|
+
if (!Number.isFinite(total) || total <= 0) return [];
|
|
999
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return [];
|
|
1000
|
+
const n = Math.max(0, Math.floor(count));
|
|
1001
|
+
if (n <= 0) return [];
|
|
1002
|
+
const dist = wrappedDistance(total, start, end);
|
|
1003
|
+
if (n === 1) return [(start % total + total) % total];
|
|
1004
|
+
const step = dist / (n - 1);
|
|
1005
|
+
const offsets = [];
|
|
1006
|
+
for (let i = 0; i < n; i++) {
|
|
1007
|
+
const raw = start + step * i;
|
|
1008
|
+
const wrapped = (raw % total + total) % total;
|
|
1009
|
+
offsets.push(wrapped);
|
|
1010
|
+
}
|
|
1011
|
+
return offsets;
|
|
1149
1012
|
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1013
|
+
|
|
1014
|
+
// src/extensions/geometry.ts
|
|
1015
|
+
function resolveFeaturePosition(feature, geometry) {
|
|
1016
|
+
const { x, y, width, height } = geometry;
|
|
1017
|
+
const left = x - width / 2;
|
|
1018
|
+
const top = y - height / 2;
|
|
1019
|
+
return {
|
|
1020
|
+
x: left + feature.x * width,
|
|
1021
|
+
y: top + feature.y * height
|
|
1022
|
+
};
|
|
1153
1023
|
}
|
|
1154
|
-
function
|
|
1155
|
-
if (
|
|
1156
|
-
|
|
1024
|
+
function ensurePaper(width, height) {
|
|
1025
|
+
if (!import_paper.default.project) {
|
|
1026
|
+
import_paper.default.setup(new import_paper.default.Size(width, height));
|
|
1027
|
+
} else {
|
|
1028
|
+
import_paper.default.view.viewSize = new import_paper.default.Size(width, height);
|
|
1029
|
+
}
|
|
1157
1030
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1031
|
+
var isBridgeDebugEnabled = () => Boolean(globalThis.__POODER_BRIDGE_DEBUG__);
|
|
1032
|
+
function normalizePathItem(shape) {
|
|
1033
|
+
let result = shape;
|
|
1034
|
+
if (typeof result.resolveCrossings === "function") result = result.resolveCrossings();
|
|
1035
|
+
if (typeof result.reduce === "function") result = result.reduce({});
|
|
1036
|
+
if (typeof result.reorient === "function") result = result.reorient(true, true);
|
|
1037
|
+
if (typeof result.reduce === "function") result = result.reduce({});
|
|
1038
|
+
return result;
|
|
1160
1039
|
}
|
|
1161
|
-
function
|
|
1162
|
-
return
|
|
1040
|
+
function getBridgeDelta(itemBounds, overlap) {
|
|
1041
|
+
return Math.max(overlap, Math.min(5, Math.max(1, itemBounds.height * 0.02)));
|
|
1163
1042
|
}
|
|
1164
|
-
function
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1043
|
+
function getExitHit(args) {
|
|
1044
|
+
const { mainShape, x, bridgeBottom, toY, eps, delta, overlap, op } = args;
|
|
1045
|
+
const ray = new import_paper.default.Path.Line({
|
|
1046
|
+
from: [x, bridgeBottom],
|
|
1047
|
+
to: [x, toY],
|
|
1048
|
+
insert: false
|
|
1049
|
+
});
|
|
1050
|
+
const intersections = mainShape.getIntersections(ray) || [];
|
|
1051
|
+
ray.remove();
|
|
1052
|
+
const validHits = intersections.filter((i) => i.point.y < bridgeBottom - eps);
|
|
1053
|
+
if (validHits.length === 0) return null;
|
|
1054
|
+
validHits.sort((a, b) => b.point.y - a.point.y);
|
|
1055
|
+
const flags = validHits.map((h) => {
|
|
1056
|
+
const above = h.point.add(new import_paper.default.Point(0, -delta));
|
|
1057
|
+
const below = h.point.add(new import_paper.default.Point(0, delta));
|
|
1058
|
+
return {
|
|
1059
|
+
insideAbove: mainShape.contains(above),
|
|
1060
|
+
insideBelow: mainShape.contains(below)
|
|
1061
|
+
};
|
|
1062
|
+
});
|
|
1063
|
+
const idx = pickExitIndex(flags);
|
|
1064
|
+
if (idx < 0) return null;
|
|
1065
|
+
if (isBridgeDebugEnabled()) {
|
|
1066
|
+
console.debug("Geometry: Bridge ray", {
|
|
1067
|
+
x,
|
|
1068
|
+
validHits: validHits.length,
|
|
1069
|
+
idx,
|
|
1070
|
+
delta,
|
|
1071
|
+
overlap,
|
|
1072
|
+
op
|
|
1073
|
+
});
|
|
1174
1074
|
}
|
|
1175
|
-
|
|
1075
|
+
const hit = validHits[idx];
|
|
1076
|
+
return { point: hit.point, location: hit };
|
|
1176
1077
|
}
|
|
1177
|
-
function
|
|
1178
|
-
const
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
Number(configService.get("size.minMm", DEFAULT_SIZE_STATE.minMm))
|
|
1078
|
+
function selectOuterChain(args) {
|
|
1079
|
+
const { mainShape, pointsA, pointsB, delta, overlap, op } = args;
|
|
1080
|
+
const scoreA = scoreOutsideAbove(
|
|
1081
|
+
pointsA.map((p) => ({
|
|
1082
|
+
outsideAbove: !mainShape.contains(p.add(new import_paper.default.Point(0, -delta)))
|
|
1083
|
+
}))
|
|
1184
1084
|
);
|
|
1185
|
-
const
|
|
1186
|
-
|
|
1187
|
-
|
|
1085
|
+
const scoreB = scoreOutsideAbove(
|
|
1086
|
+
pointsB.map((p) => ({
|
|
1087
|
+
outsideAbove: !mainShape.contains(p.add(new import_paper.default.Point(0, -delta)))
|
|
1088
|
+
}))
|
|
1188
1089
|
);
|
|
1189
|
-
const
|
|
1190
|
-
|
|
1191
|
-
|
|
1090
|
+
const ratioA = scoreA / pointsA.length;
|
|
1091
|
+
const ratioB = scoreB / pointsB.length;
|
|
1092
|
+
if (isBridgeDebugEnabled()) {
|
|
1093
|
+
console.debug("Geometry: Bridge chain", {
|
|
1094
|
+
scoreA,
|
|
1095
|
+
scoreB,
|
|
1096
|
+
lenA: pointsA.length,
|
|
1097
|
+
lenB: pointsB.length,
|
|
1098
|
+
ratioA,
|
|
1099
|
+
ratioB,
|
|
1100
|
+
delta,
|
|
1101
|
+
overlap,
|
|
1102
|
+
op
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
const ratioEps = 1e-6;
|
|
1106
|
+
if (Math.abs(ratioA - ratioB) > ratioEps) {
|
|
1107
|
+
return ratioA > ratioB ? pointsA : pointsB;
|
|
1108
|
+
}
|
|
1109
|
+
if (scoreA !== scoreB) return scoreA > scoreB ? pointsA : pointsB;
|
|
1110
|
+
return pointsA.length <= pointsB.length ? pointsA : pointsB;
|
|
1111
|
+
}
|
|
1112
|
+
function fitPathItemToRect(item, rect, fitMode) {
|
|
1113
|
+
const { left, top, width, height } = rect;
|
|
1114
|
+
const bounds = item.bounds;
|
|
1115
|
+
if (width <= 0 || height <= 0 || !Number.isFinite(bounds.width) || !Number.isFinite(bounds.height) || bounds.width <= 0 || bounds.height <= 0) {
|
|
1116
|
+
item.position = new import_paper.default.Point(left + width / 2, top + height / 2);
|
|
1117
|
+
return item;
|
|
1118
|
+
}
|
|
1119
|
+
item.translate(new import_paper.default.Point(-bounds.left, -bounds.top));
|
|
1120
|
+
if (fitMode === "stretch") {
|
|
1121
|
+
item.scale(width / bounds.width, height / bounds.height, new import_paper.default.Point(0, 0));
|
|
1122
|
+
item.translate(new import_paper.default.Point(left, top));
|
|
1123
|
+
return item;
|
|
1124
|
+
}
|
|
1125
|
+
const uniformScale = Math.min(width / bounds.width, height / bounds.height);
|
|
1126
|
+
item.scale(uniformScale, uniformScale, new import_paper.default.Point(0, 0));
|
|
1127
|
+
const scaledWidth = bounds.width * uniformScale;
|
|
1128
|
+
const scaledHeight = bounds.height * uniformScale;
|
|
1129
|
+
item.translate(
|
|
1130
|
+
new import_paper.default.Point(
|
|
1131
|
+
left + (width - scaledWidth) / 2,
|
|
1132
|
+
top + (height - scaledHeight) / 2
|
|
1133
|
+
)
|
|
1192
1134
|
);
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1135
|
+
return item;
|
|
1136
|
+
}
|
|
1137
|
+
function createNormalizedHeartPath(params) {
|
|
1138
|
+
const { lobeSpread, notchDepth, tipSharpness } = params;
|
|
1139
|
+
const halfSpread = 0.22 + lobeSpread * 0.18;
|
|
1140
|
+
const notchY = 0.06 + notchDepth * 0.2;
|
|
1141
|
+
const shoulderY = 0.24 + notchDepth * 0.2;
|
|
1142
|
+
const topLift = 0.12 + (1 - notchDepth) * 0.06;
|
|
1143
|
+
const topY = notchY - topLift;
|
|
1144
|
+
const sideCtrlY = shoulderY - (0.18 - notchDepth * 0.08);
|
|
1145
|
+
const lowerCtrlY = 0.58 + (1 - tipSharpness) * 0.16;
|
|
1146
|
+
const tipCtrlX = 0.34 - tipSharpness * 0.2;
|
|
1147
|
+
const notchCtrlX = 0.06 + lobeSpread * 0.06;
|
|
1148
|
+
const lobeCtrlX = 0.1 + lobeSpread * 0.08;
|
|
1149
|
+
const notchCtrlY = notchY - topLift * 0.45;
|
|
1150
|
+
const xPeakL = 0.5 - halfSpread;
|
|
1151
|
+
const xPeakR = 0.5 + halfSpread;
|
|
1152
|
+
const heartPath = new import_paper.default.Path({ insert: false });
|
|
1153
|
+
heartPath.moveTo(new import_paper.default.Point(0.5, notchY));
|
|
1154
|
+
heartPath.cubicCurveTo(
|
|
1155
|
+
new import_paper.default.Point(0.5 - notchCtrlX, notchCtrlY),
|
|
1156
|
+
new import_paper.default.Point(xPeakL + lobeCtrlX, topY),
|
|
1157
|
+
new import_paper.default.Point(xPeakL, topY)
|
|
1199
1158
|
);
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
DEFAULT_SIZE_STATE.actualHeightMm
|
|
1205
|
-
),
|
|
1206
|
-
"mm"
|
|
1207
|
-
),
|
|
1208
|
-
{ minMm, maxMm, stepMm }
|
|
1159
|
+
heartPath.cubicCurveTo(
|
|
1160
|
+
new import_paper.default.Point(xPeakL - lobeCtrlX, topY),
|
|
1161
|
+
new import_paper.default.Point(0, sideCtrlY),
|
|
1162
|
+
new import_paper.default.Point(0, shoulderY)
|
|
1209
1163
|
);
|
|
1210
|
-
|
|
1211
|
-
|
|
1164
|
+
heartPath.cubicCurveTo(
|
|
1165
|
+
new import_paper.default.Point(0, lowerCtrlY),
|
|
1166
|
+
new import_paper.default.Point(tipCtrlX, 1),
|
|
1167
|
+
new import_paper.default.Point(0.5, 1)
|
|
1212
1168
|
);
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
configService.get("size.cutMarginMm", DEFAULT_SIZE_STATE.cutMarginMm),
|
|
1218
|
-
"mm"
|
|
1219
|
-
)
|
|
1169
|
+
heartPath.cubicCurveTo(
|
|
1170
|
+
new import_paper.default.Point(1 - tipCtrlX, 1),
|
|
1171
|
+
new import_paper.default.Point(1, lowerCtrlY),
|
|
1172
|
+
new import_paper.default.Point(1, shoulderY)
|
|
1220
1173
|
);
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1174
|
+
heartPath.cubicCurveTo(
|
|
1175
|
+
new import_paper.default.Point(1, sideCtrlY),
|
|
1176
|
+
new import_paper.default.Point(xPeakR + lobeCtrlX, topY),
|
|
1177
|
+
new import_paper.default.Point(xPeakR, topY)
|
|
1224
1178
|
);
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
DEFAULT_SIZE_STATE.constraintMode
|
|
1233
|
-
)
|
|
1234
|
-
),
|
|
1235
|
-
aspectRatio,
|
|
1236
|
-
cutMode: normalizeCutMode(
|
|
1237
|
-
configService.get("size.cutMode", DEFAULT_SIZE_STATE.cutMode)
|
|
1238
|
-
),
|
|
1239
|
-
cutMarginMm,
|
|
1240
|
-
viewPadding,
|
|
1241
|
-
minMm,
|
|
1242
|
-
maxMm,
|
|
1243
|
-
stepMm
|
|
1244
|
-
};
|
|
1179
|
+
heartPath.cubicCurveTo(
|
|
1180
|
+
new import_paper.default.Point(xPeakR - lobeCtrlX, topY),
|
|
1181
|
+
new import_paper.default.Point(0.5 + notchCtrlX, notchCtrlY),
|
|
1182
|
+
new import_paper.default.Point(0.5, notchY)
|
|
1183
|
+
);
|
|
1184
|
+
heartPath.closed = true;
|
|
1185
|
+
return heartPath;
|
|
1245
1186
|
}
|
|
1246
|
-
function
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1187
|
+
function createHeartBaseShape(options) {
|
|
1188
|
+
const { x, y, width, height } = options;
|
|
1189
|
+
const w = Math.max(0, width);
|
|
1190
|
+
const h = Math.max(0, height);
|
|
1191
|
+
const left = x - w / 2;
|
|
1192
|
+
const top = y - h / 2;
|
|
1193
|
+
const fitMode = getShapeFitMode(options.shapeStyle);
|
|
1194
|
+
const heartParams = getHeartShapeParams(options.shapeStyle);
|
|
1195
|
+
const rawHeart = createNormalizedHeartPath(heartParams);
|
|
1196
|
+
return fitPathItemToRect(rawHeart, { left, top, width: w, height: h }, fitMode);
|
|
1197
|
+
}
|
|
1198
|
+
var BUILTIN_SHAPE_BUILDERS = {
|
|
1199
|
+
rect: (options) => {
|
|
1200
|
+
const { x, y, width, height, radius } = options;
|
|
1201
|
+
return new import_paper.default.Path.Rectangle({
|
|
1202
|
+
point: [x - width / 2, y - height / 2],
|
|
1203
|
+
size: [Math.max(0, width), Math.max(0, height)],
|
|
1204
|
+
radius: Math.max(0, radius)
|
|
1205
|
+
});
|
|
1206
|
+
},
|
|
1207
|
+
circle: (options) => {
|
|
1208
|
+
const { x, y, width, height } = options;
|
|
1209
|
+
const r = Math.min(width, height) / 2;
|
|
1210
|
+
return new import_paper.default.Path.Circle({
|
|
1211
|
+
center: new import_paper.default.Point(x, y),
|
|
1212
|
+
radius: Math.max(0, r)
|
|
1213
|
+
});
|
|
1214
|
+
},
|
|
1215
|
+
ellipse: (options) => {
|
|
1216
|
+
const { x, y, width, height } = options;
|
|
1217
|
+
return new import_paper.default.Path.Ellipse({
|
|
1218
|
+
center: new import_paper.default.Point(x, y),
|
|
1219
|
+
radius: [Math.max(0, width / 2), Math.max(0, height / 2)]
|
|
1220
|
+
});
|
|
1221
|
+
},
|
|
1222
|
+
heart: createHeartBaseShape
|
|
1223
|
+
};
|
|
1224
|
+
function createCustomBaseShape(options) {
|
|
1225
|
+
var _a;
|
|
1226
|
+
const {
|
|
1227
|
+
pathData,
|
|
1228
|
+
customSourceWidthPx,
|
|
1229
|
+
customSourceHeightPx,
|
|
1230
|
+
x,
|
|
1231
|
+
y,
|
|
1250
1232
|
width,
|
|
1251
|
-
height
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1233
|
+
height
|
|
1234
|
+
} = options;
|
|
1235
|
+
if (typeof pathData !== "string" || pathData.trim().length === 0) {
|
|
1236
|
+
return null;
|
|
1237
|
+
}
|
|
1238
|
+
const center = new import_paper.default.Point(x, y);
|
|
1239
|
+
const hasMultipleSubPaths = ((_a = (pathData.match(/[Mm]/g) || []).length) != null ? _a : 0) > 1;
|
|
1240
|
+
const path = hasMultipleSubPaths ? new import_paper.default.CompoundPath(pathData) : (() => {
|
|
1241
|
+
const single = new import_paper.default.Path();
|
|
1242
|
+
single.pathData = pathData;
|
|
1243
|
+
return single;
|
|
1244
|
+
})();
|
|
1245
|
+
const sourceWidth = Number(customSourceWidthPx != null ? customSourceWidthPx : 0);
|
|
1246
|
+
const sourceHeight = Number(customSourceHeightPx != null ? customSourceHeightPx : 0);
|
|
1247
|
+
if (Number.isFinite(sourceWidth) && Number.isFinite(sourceHeight) && sourceWidth > 0 && sourceHeight > 0 && width > 0 && height > 0) {
|
|
1248
|
+
const targetLeft = x - width / 2;
|
|
1249
|
+
const targetTop = y - height / 2;
|
|
1250
|
+
path.scale(width / sourceWidth, height / sourceHeight, new import_paper.default.Point(0, 0));
|
|
1251
|
+
path.translate(new import_paper.default.Point(targetLeft, targetTop));
|
|
1252
|
+
return path;
|
|
1253
|
+
}
|
|
1254
|
+
if (width > 0 && height > 0 && path.bounds.width > 0 && path.bounds.height > 0) {
|
|
1255
|
+
path.position = center;
|
|
1256
|
+
path.scale(width / path.bounds.width, height / path.bounds.height);
|
|
1257
|
+
return path;
|
|
1258
|
+
}
|
|
1259
|
+
path.position = center;
|
|
1260
|
+
return path;
|
|
1255
1261
|
}
|
|
1256
|
-
function
|
|
1257
|
-
|
|
1258
|
-
|
|
1262
|
+
function createBaseShape(options) {
|
|
1263
|
+
const { shape } = options;
|
|
1264
|
+
if (shape === "custom") {
|
|
1265
|
+
const customShape = createCustomBaseShape(options);
|
|
1266
|
+
if (customShape) return customShape;
|
|
1267
|
+
return BUILTIN_SHAPE_BUILDERS[DEFAULT_DIELINE_SHAPE](options);
|
|
1259
1268
|
}
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1269
|
+
return BUILTIN_SHAPE_BUILDERS[shape](options);
|
|
1270
|
+
}
|
|
1271
|
+
function resolveBridgeBasePath(shape, anchor) {
|
|
1272
|
+
if (shape instanceof import_paper.default.Path) {
|
|
1273
|
+
return shape;
|
|
1274
|
+
}
|
|
1275
|
+
if (shape instanceof import_paper.default.CompoundPath) {
|
|
1276
|
+
const children = (shape.children || []).filter(
|
|
1277
|
+
(child) => child instanceof import_paper.default.Path
|
|
1278
|
+
);
|
|
1279
|
+
if (!children.length) return null;
|
|
1280
|
+
let best = children[0];
|
|
1281
|
+
let bestDistance = Infinity;
|
|
1282
|
+
for (const child of children) {
|
|
1283
|
+
const location = child.getNearestLocation(anchor);
|
|
1284
|
+
const point = location == null ? void 0 : location.point;
|
|
1285
|
+
if (!point) continue;
|
|
1286
|
+
const distance = point.getDistance(anchor);
|
|
1287
|
+
if (distance < bestDistance) {
|
|
1288
|
+
bestDistance = distance;
|
|
1289
|
+
best = child;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
return best;
|
|
1293
|
+
}
|
|
1294
|
+
return null;
|
|
1295
|
+
}
|
|
1296
|
+
function createFeatureItem(feature, center) {
|
|
1297
|
+
let item;
|
|
1298
|
+
if (feature.shape === "rect") {
|
|
1299
|
+
const w = feature.width || 10;
|
|
1300
|
+
const h = feature.height || 10;
|
|
1301
|
+
const r = feature.radius || 0;
|
|
1302
|
+
item = new import_paper.default.Path.Rectangle({
|
|
1303
|
+
point: [center.x - w / 2, center.y - h / 2],
|
|
1304
|
+
size: [w, h],
|
|
1305
|
+
radius: r
|
|
1306
|
+
});
|
|
1307
|
+
} else {
|
|
1308
|
+
const r = feature.radius || 5;
|
|
1309
|
+
item = new import_paper.default.Path.Circle({
|
|
1310
|
+
center,
|
|
1311
|
+
radius: r
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
if (feature.rotation) {
|
|
1315
|
+
item.rotate(feature.rotation, center);
|
|
1316
|
+
}
|
|
1317
|
+
return item;
|
|
1318
|
+
}
|
|
1319
|
+
function getPerimeterShape(options) {
|
|
1320
|
+
let mainShape = createBaseShape(options);
|
|
1321
|
+
const { features } = options;
|
|
1322
|
+
if (features && features.length > 0) {
|
|
1323
|
+
const edgeFeatures = features.filter(
|
|
1324
|
+
(f) => !f.renderBehavior || f.renderBehavior === "edge"
|
|
1325
|
+
);
|
|
1326
|
+
const adds = [];
|
|
1327
|
+
const subtracts = [];
|
|
1328
|
+
edgeFeatures.forEach((f) => {
|
|
1329
|
+
const pos = resolveFeaturePosition(f, options);
|
|
1330
|
+
const center = new import_paper.default.Point(pos.x, pos.y);
|
|
1331
|
+
const item = createFeatureItem(f, center);
|
|
1332
|
+
if (f.bridge && f.bridge.type === "vertical") {
|
|
1333
|
+
const itemBounds = item.bounds;
|
|
1334
|
+
const mainBounds = mainShape.bounds;
|
|
1335
|
+
const bridgeTop = mainBounds.top;
|
|
1336
|
+
const bridgeBottom = itemBounds.top;
|
|
1337
|
+
if (bridgeBottom > bridgeTop) {
|
|
1338
|
+
const overlap = 2;
|
|
1339
|
+
const rayPadding = 10;
|
|
1340
|
+
const eps = 0.1;
|
|
1341
|
+
const delta = getBridgeDelta(itemBounds, overlap);
|
|
1342
|
+
const toY = bridgeTop - rayPadding;
|
|
1343
|
+
const inset = Math.min(1, Math.max(0, itemBounds.width * 0.01));
|
|
1344
|
+
const xLeft = itemBounds.left + inset;
|
|
1345
|
+
const xRight = itemBounds.right - inset;
|
|
1346
|
+
const bridgeBasePath = resolveBridgeBasePath(mainShape, center);
|
|
1347
|
+
const canBridge = !!bridgeBasePath && xRight - xLeft > eps;
|
|
1348
|
+
if (canBridge && bridgeBasePath) {
|
|
1349
|
+
const leftHit = getExitHit({
|
|
1350
|
+
mainShape: bridgeBasePath,
|
|
1351
|
+
x: xLeft,
|
|
1352
|
+
bridgeBottom,
|
|
1353
|
+
toY,
|
|
1354
|
+
eps,
|
|
1355
|
+
delta,
|
|
1356
|
+
overlap,
|
|
1357
|
+
op: f.operation
|
|
1358
|
+
});
|
|
1359
|
+
const rightHit = getExitHit({
|
|
1360
|
+
mainShape: bridgeBasePath,
|
|
1361
|
+
x: xRight,
|
|
1362
|
+
bridgeBottom,
|
|
1363
|
+
toY,
|
|
1364
|
+
eps,
|
|
1365
|
+
delta,
|
|
1366
|
+
overlap,
|
|
1367
|
+
op: f.operation
|
|
1368
|
+
});
|
|
1369
|
+
if (leftHit && rightHit) {
|
|
1370
|
+
const pathLength = bridgeBasePath.length;
|
|
1371
|
+
const leftOffset = leftHit.location.offset;
|
|
1372
|
+
const rightOffset = rightHit.location.offset;
|
|
1373
|
+
const distanceA = wrappedDistance(pathLength, leftOffset, rightOffset);
|
|
1374
|
+
const distanceB = wrappedDistance(pathLength, rightOffset, leftOffset);
|
|
1375
|
+
const countFor = (d) => Math.max(8, Math.min(80, Math.ceil(d / 6)));
|
|
1376
|
+
const offsetsA = sampleWrappedOffsets(
|
|
1377
|
+
pathLength,
|
|
1378
|
+
leftOffset,
|
|
1379
|
+
rightOffset,
|
|
1380
|
+
countFor(distanceA)
|
|
1381
|
+
);
|
|
1382
|
+
const offsetsB = sampleWrappedOffsets(
|
|
1383
|
+
pathLength,
|
|
1384
|
+
rightOffset,
|
|
1385
|
+
leftOffset,
|
|
1386
|
+
countFor(distanceB)
|
|
1387
|
+
);
|
|
1388
|
+
const pointsA = offsetsA.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
1389
|
+
const pointsB = offsetsB.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
1390
|
+
if (pointsA.length >= 2 && pointsB.length >= 2) {
|
|
1391
|
+
let topBase = selectOuterChain({
|
|
1392
|
+
mainShape: bridgeBasePath,
|
|
1393
|
+
pointsA,
|
|
1394
|
+
pointsB,
|
|
1395
|
+
delta,
|
|
1396
|
+
overlap,
|
|
1397
|
+
op: f.operation
|
|
1398
|
+
});
|
|
1399
|
+
const dist2 = (a, b) => {
|
|
1400
|
+
const dx = a.x - b.x;
|
|
1401
|
+
const dy = a.y - b.y;
|
|
1402
|
+
return dx * dx + dy * dy;
|
|
1403
|
+
};
|
|
1404
|
+
if (dist2(topBase[0], leftHit.point) > dist2(topBase[0], rightHit.point)) {
|
|
1405
|
+
topBase = topBase.slice().reverse();
|
|
1406
|
+
}
|
|
1407
|
+
topBase = topBase.slice();
|
|
1408
|
+
topBase[0] = leftHit.point;
|
|
1409
|
+
topBase[topBase.length - 1] = rightHit.point;
|
|
1410
|
+
const capShiftY = f.operation === "subtract" ? -Math.max(overlap * 2, delta) : overlap;
|
|
1411
|
+
const topPoints = topBase.map(
|
|
1412
|
+
(p) => p.add(new import_paper.default.Point(0, capShiftY))
|
|
1413
|
+
);
|
|
1414
|
+
const bridgeBottomY = bridgeBottom + overlap * 2;
|
|
1415
|
+
const bridgePoly = new import_paper.default.Path({ insert: false });
|
|
1416
|
+
for (const p of topPoints) bridgePoly.add(p);
|
|
1417
|
+
bridgePoly.add(new import_paper.default.Point(xRight, bridgeBottomY));
|
|
1418
|
+
bridgePoly.add(new import_paper.default.Point(xLeft, bridgeBottomY));
|
|
1419
|
+
bridgePoly.closed = true;
|
|
1420
|
+
const unitedItem = item.unite(bridgePoly);
|
|
1421
|
+
item.remove();
|
|
1422
|
+
bridgePoly.remove();
|
|
1423
|
+
if (f.operation === "add") {
|
|
1424
|
+
adds.push(unitedItem);
|
|
1425
|
+
} else {
|
|
1426
|
+
subtracts.push(unitedItem);
|
|
1427
|
+
}
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
if (f.operation === "add") {
|
|
1433
|
+
adds.push(item);
|
|
1434
|
+
} else {
|
|
1435
|
+
subtracts.push(item);
|
|
1436
|
+
}
|
|
1437
|
+
} else {
|
|
1438
|
+
if (f.operation === "add") {
|
|
1439
|
+
adds.push(item);
|
|
1440
|
+
} else {
|
|
1441
|
+
subtracts.push(item);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
} else {
|
|
1445
|
+
if (f.operation === "add") {
|
|
1446
|
+
adds.push(item);
|
|
1447
|
+
} else {
|
|
1448
|
+
subtracts.push(item);
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
});
|
|
1452
|
+
if (adds.length > 0) {
|
|
1453
|
+
for (const item of adds) {
|
|
1454
|
+
try {
|
|
1455
|
+
const temp = mainShape.unite(item);
|
|
1456
|
+
mainShape.remove();
|
|
1457
|
+
item.remove();
|
|
1458
|
+
mainShape = normalizePathItem(temp);
|
|
1459
|
+
} catch (e) {
|
|
1460
|
+
console.error("Geometry: Failed to unite feature", e);
|
|
1461
|
+
item.remove();
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
if (subtracts.length > 0) {
|
|
1466
|
+
for (const item of subtracts) {
|
|
1467
|
+
try {
|
|
1468
|
+
const temp = mainShape.subtract(item);
|
|
1469
|
+
mainShape.remove();
|
|
1470
|
+
item.remove();
|
|
1471
|
+
mainShape = normalizePathItem(temp);
|
|
1472
|
+
} catch (e) {
|
|
1473
|
+
console.error("Geometry: Failed to subtract feature", e);
|
|
1474
|
+
item.remove();
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1266
1478
|
}
|
|
1267
|
-
return
|
|
1268
|
-
widthMm: Math.max(size.minMm, size.actualWidthMm - delta),
|
|
1269
|
-
heightMm: Math.max(size.minMm, size.actualHeightMm - delta)
|
|
1270
|
-
};
|
|
1479
|
+
return mainShape;
|
|
1271
1480
|
}
|
|
1272
|
-
function
|
|
1273
|
-
const
|
|
1274
|
-
|
|
1275
|
-
if (canvasWidth <= 0 || canvasHeight <= 0) return null;
|
|
1276
|
-
const { widthMm: cutWidthMm, heightMm: cutHeightMm } = getCutSizeMm(size);
|
|
1277
|
-
const viewWidthMm = Math.max(size.actualWidthMm, cutWidthMm);
|
|
1278
|
-
const viewHeightMm = Math.max(size.actualHeightMm, cutHeightMm);
|
|
1279
|
-
if (!Number.isFinite(viewWidthMm) || !Number.isFinite(viewHeightMm) || viewWidthMm <= 0 || viewHeightMm <= 0) {
|
|
1280
|
-
return null;
|
|
1281
|
-
}
|
|
1282
|
-
const paddingPx = resolvePaddingPx(
|
|
1283
|
-
size.viewPadding,
|
|
1284
|
-
canvasWidth,
|
|
1285
|
-
canvasHeight
|
|
1481
|
+
function applySurfaceFeatures(shape, features, options) {
|
|
1482
|
+
const surfaceFeatures = features.filter(
|
|
1483
|
+
(f) => f.renderBehavior === "surface"
|
|
1286
1484
|
);
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1485
|
+
if (surfaceFeatures.length === 0) return shape;
|
|
1486
|
+
let result = shape;
|
|
1487
|
+
for (const f of surfaceFeatures) {
|
|
1488
|
+
const pos = resolveFeaturePosition(f, options);
|
|
1489
|
+
const center = new import_paper.default.Point(pos.x, pos.y);
|
|
1490
|
+
const item = createFeatureItem(f, center);
|
|
1491
|
+
try {
|
|
1492
|
+
if (f.operation === "add") {
|
|
1493
|
+
const temp = result.unite(item);
|
|
1494
|
+
result.remove();
|
|
1495
|
+
item.remove();
|
|
1496
|
+
result = normalizePathItem(temp);
|
|
1497
|
+
} else {
|
|
1498
|
+
const temp = result.subtract(item);
|
|
1499
|
+
result.remove();
|
|
1500
|
+
item.remove();
|
|
1501
|
+
result = normalizePathItem(temp);
|
|
1502
|
+
}
|
|
1503
|
+
} catch (e) {
|
|
1504
|
+
console.error("Geometry: Failed to apply surface feature", e);
|
|
1505
|
+
item.remove();
|
|
1506
|
+
}
|
|
1293
1507
|
}
|
|
1294
|
-
|
|
1295
|
-
const centerY = layout.offsetY + layout.height / 2;
|
|
1296
|
-
const trimWidthPx = size.actualWidthMm * layout.scale;
|
|
1297
|
-
const trimHeightPx = size.actualHeightMm * layout.scale;
|
|
1298
|
-
const cutWidthPx = cutWidthMm * layout.scale;
|
|
1299
|
-
const cutHeightPx = cutHeightMm * layout.scale;
|
|
1300
|
-
const trimRect = rectByCenter(centerX, centerY, trimWidthPx, trimHeightPx);
|
|
1301
|
-
const cutRect = rectByCenter(centerX, centerY, cutWidthPx, cutHeightPx);
|
|
1302
|
-
const bleedRect = rectByCenter(
|
|
1303
|
-
centerX,
|
|
1304
|
-
centerY,
|
|
1305
|
-
Math.max(trimWidthPx, cutWidthPx),
|
|
1306
|
-
Math.max(trimHeightPx, cutHeightPx)
|
|
1307
|
-
);
|
|
1308
|
-
return {
|
|
1309
|
-
scale: layout.scale,
|
|
1310
|
-
canvasWidth,
|
|
1311
|
-
canvasHeight,
|
|
1312
|
-
trimRect,
|
|
1313
|
-
cutRect,
|
|
1314
|
-
bleedRect,
|
|
1315
|
-
trimWidthMm: size.actualWidthMm,
|
|
1316
|
-
trimHeightMm: size.actualHeightMm,
|
|
1317
|
-
cutWidthMm,
|
|
1318
|
-
cutHeightMm,
|
|
1319
|
-
cutMode: size.cutMode,
|
|
1320
|
-
cutMarginMm: size.cutMarginMm
|
|
1321
|
-
};
|
|
1508
|
+
return result;
|
|
1322
1509
|
}
|
|
1323
|
-
function
|
|
1324
|
-
const
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
);
|
|
1328
|
-
const
|
|
1329
|
-
const
|
|
1330
|
-
const
|
|
1331
|
-
|
|
1510
|
+
function generateDielinePath(options) {
|
|
1511
|
+
const paperWidth = options.canvasWidth || options.width * 2 || 2e3;
|
|
1512
|
+
const paperHeight = options.canvasHeight || options.height * 2 || 2e3;
|
|
1513
|
+
ensurePaper(paperWidth, paperHeight);
|
|
1514
|
+
import_paper.default.project.activeLayer.removeChildren();
|
|
1515
|
+
const perimeter = getPerimeterShape(options);
|
|
1516
|
+
const finalShape = applySurfaceFeatures(perimeter, options.features, options);
|
|
1517
|
+
const pathData = finalShape.pathData;
|
|
1518
|
+
finalShape.remove();
|
|
1519
|
+
return pathData;
|
|
1520
|
+
}
|
|
1521
|
+
function generateBleedZonePath(originalOptions, offsetOptions, offset) {
|
|
1522
|
+
const paperWidth = originalOptions.canvasWidth || originalOptions.width * 2 || 2e3;
|
|
1523
|
+
const paperHeight = originalOptions.canvasHeight || originalOptions.height * 2 || 2e3;
|
|
1524
|
+
ensurePaper(paperWidth, paperHeight);
|
|
1525
|
+
import_paper.default.project.activeLayer.removeChildren();
|
|
1526
|
+
const pOriginal = getPerimeterShape(originalOptions);
|
|
1527
|
+
const shapeOriginal = applySurfaceFeatures(
|
|
1528
|
+
pOriginal,
|
|
1529
|
+
originalOptions.features,
|
|
1530
|
+
originalOptions
|
|
1332
1531
|
);
|
|
1333
|
-
const
|
|
1334
|
-
|
|
1532
|
+
const pOffset = getPerimeterShape(offsetOptions);
|
|
1533
|
+
const shapeOffset = applySurfaceFeatures(
|
|
1534
|
+
pOffset,
|
|
1535
|
+
offsetOptions.features,
|
|
1536
|
+
offsetOptions
|
|
1335
1537
|
);
|
|
1538
|
+
let bleedZone;
|
|
1539
|
+
if (offset > 0) {
|
|
1540
|
+
bleedZone = shapeOffset.subtract(shapeOriginal);
|
|
1541
|
+
} else {
|
|
1542
|
+
bleedZone = shapeOriginal.subtract(shapeOffset);
|
|
1543
|
+
}
|
|
1544
|
+
const pathData = bleedZone.pathData;
|
|
1545
|
+
shapeOriginal.remove();
|
|
1546
|
+
shapeOffset.remove();
|
|
1547
|
+
bleedZone.remove();
|
|
1548
|
+
return pathData;
|
|
1549
|
+
}
|
|
1550
|
+
function getLowestPointOnDieline(options) {
|
|
1551
|
+
ensurePaper(options.width * 2, options.height * 2);
|
|
1552
|
+
import_paper.default.project.activeLayer.removeChildren();
|
|
1553
|
+
const shape = createBaseShape(options);
|
|
1554
|
+
const bounds = shape.bounds;
|
|
1555
|
+
const result = {
|
|
1556
|
+
x: bounds.center.x,
|
|
1557
|
+
y: bounds.bottom
|
|
1558
|
+
};
|
|
1559
|
+
shape.remove();
|
|
1560
|
+
return result;
|
|
1561
|
+
}
|
|
1562
|
+
function getNearestPointOnDieline(point, options) {
|
|
1563
|
+
ensurePaper(options.width * 2, options.height * 2);
|
|
1564
|
+
import_paper.default.project.activeLayer.removeChildren();
|
|
1565
|
+
const shape = createBaseShape(options);
|
|
1566
|
+
const p = new import_paper.default.Point(point.x, point.y);
|
|
1567
|
+
const location = shape.getNearestLocation(p);
|
|
1568
|
+
const result = {
|
|
1569
|
+
x: location.point.x,
|
|
1570
|
+
y: location.point.y,
|
|
1571
|
+
normal: location.normal ? { x: location.normal.x, y: location.normal.y } : void 0
|
|
1572
|
+
};
|
|
1573
|
+
shape.remove();
|
|
1574
|
+
return result;
|
|
1575
|
+
}
|
|
1576
|
+
function getPathBounds(pathData) {
|
|
1577
|
+
const path = new import_paper.default.Path();
|
|
1578
|
+
path.pathData = pathData;
|
|
1579
|
+
const bounds = path.bounds;
|
|
1580
|
+
path.remove();
|
|
1336
1581
|
return {
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
unit: "px",
|
|
1342
|
-
x: layout.trimRect.centerX,
|
|
1343
|
-
y: layout.trimRect.centerY,
|
|
1344
|
-
width: layout.trimRect.width,
|
|
1345
|
-
height: layout.trimRect.height,
|
|
1346
|
-
radius: radiusMm * layout.scale,
|
|
1347
|
-
offset,
|
|
1348
|
-
scale: layout.scale,
|
|
1349
|
-
pathData: configService.get("dieline.pathData"),
|
|
1350
|
-
customSourceWidthPx: Number.isFinite(sourceWidth) && sourceWidth > 0 ? sourceWidth : void 0,
|
|
1351
|
-
customSourceHeightPx: Number.isFinite(sourceHeight) && sourceHeight > 0 ? sourceHeight : void 0
|
|
1582
|
+
x: bounds.x,
|
|
1583
|
+
y: bounds.y,
|
|
1584
|
+
width: bounds.width,
|
|
1585
|
+
height: bounds.height
|
|
1352
1586
|
};
|
|
1353
1587
|
}
|
|
1354
1588
|
|
|
1355
1589
|
// src/extensions/image.ts
|
|
1356
1590
|
var IMAGE_OBJECT_LAYER_ID = "image.user";
|
|
1357
1591
|
var IMAGE_OVERLAY_LAYER_ID = "image-overlay";
|
|
1592
|
+
var IMAGE_DEFAULT_CONTROL_CAPABILITIES = [
|
|
1593
|
+
"rotate",
|
|
1594
|
+
"scale"
|
|
1595
|
+
];
|
|
1596
|
+
var IMAGE_CONTROL_DESCRIPTORS = [
|
|
1597
|
+
{
|
|
1598
|
+
key: "tl",
|
|
1599
|
+
capability: "rotate",
|
|
1600
|
+
create: () => new import_fabric2.Control({
|
|
1601
|
+
x: -0.5,
|
|
1602
|
+
y: -0.5,
|
|
1603
|
+
actionName: "rotate",
|
|
1604
|
+
actionHandler: import_fabric2.controlsUtils.rotationWithSnapping,
|
|
1605
|
+
cursorStyleHandler: import_fabric2.controlsUtils.rotationStyleHandler
|
|
1606
|
+
})
|
|
1607
|
+
},
|
|
1608
|
+
{
|
|
1609
|
+
key: "br",
|
|
1610
|
+
capability: "scale",
|
|
1611
|
+
create: () => new import_fabric2.Control({
|
|
1612
|
+
x: 0.5,
|
|
1613
|
+
y: 0.5,
|
|
1614
|
+
actionName: "scale",
|
|
1615
|
+
actionHandler: import_fabric2.controlsUtils.scalingEqually,
|
|
1616
|
+
cursorStyleHandler: import_fabric2.controlsUtils.scaleCursorStyleHandler
|
|
1617
|
+
})
|
|
1618
|
+
}
|
|
1619
|
+
];
|
|
1358
1620
|
var ImageTool = class {
|
|
1359
1621
|
constructor() {
|
|
1360
1622
|
this.id = "pooder.kit.image";
|
|
@@ -1371,7 +1633,9 @@ var ImageTool = class {
|
|
|
1371
1633
|
this.isImageSelectionActive = false;
|
|
1372
1634
|
this.focusedImageId = null;
|
|
1373
1635
|
this.renderSeq = 0;
|
|
1636
|
+
this.imageSpecs = [];
|
|
1374
1637
|
this.overlaySpecs = [];
|
|
1638
|
+
this.imageControlsByCapabilityKey = /* @__PURE__ */ new Map();
|
|
1375
1639
|
this.onToolActivated = (event) => {
|
|
1376
1640
|
const before = this.isToolActive;
|
|
1377
1641
|
this.syncToolActiveFromWorkbench(event.id);
|
|
@@ -1478,9 +1742,34 @@ var ImageTool = class {
|
|
|
1478
1742
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
1479
1743
|
this.id,
|
|
1480
1744
|
() => ({
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1745
|
+
passes: [
|
|
1746
|
+
{
|
|
1747
|
+
id: IMAGE_OBJECT_LAYER_ID,
|
|
1748
|
+
stack: 500,
|
|
1749
|
+
order: 0,
|
|
1750
|
+
visibility: {
|
|
1751
|
+
op: "not",
|
|
1752
|
+
expr: {
|
|
1753
|
+
op: "sessionActive",
|
|
1754
|
+
toolId: "pooder.kit.white-ink"
|
|
1755
|
+
}
|
|
1756
|
+
},
|
|
1757
|
+
objects: this.imageSpecs
|
|
1758
|
+
},
|
|
1759
|
+
{
|
|
1760
|
+
id: IMAGE_OVERLAY_LAYER_ID,
|
|
1761
|
+
stack: 800,
|
|
1762
|
+
order: 0,
|
|
1763
|
+
visibility: {
|
|
1764
|
+
op: "not",
|
|
1765
|
+
expr: {
|
|
1766
|
+
op: "sessionActive",
|
|
1767
|
+
toolId: "pooder.kit.white-ink"
|
|
1768
|
+
}
|
|
1769
|
+
},
|
|
1770
|
+
objects: this.overlaySpecs
|
|
1771
|
+
}
|
|
1772
|
+
]
|
|
1484
1773
|
}),
|
|
1485
1774
|
{ priority: 300 }
|
|
1486
1775
|
);
|
|
@@ -1511,7 +1800,10 @@ var ImageTool = class {
|
|
|
1511
1800
|
this.updateImages();
|
|
1512
1801
|
return;
|
|
1513
1802
|
}
|
|
1514
|
-
if (e.key.startsWith("size.") || e.key.startsWith("image.frame.")) {
|
|
1803
|
+
if (e.key.startsWith("size.") || e.key.startsWith("image.frame.") || e.key.startsWith("image.control.")) {
|
|
1804
|
+
if (e.key.startsWith("image.control.")) {
|
|
1805
|
+
this.imageControlsByCapabilityKey.clear();
|
|
1806
|
+
}
|
|
1515
1807
|
this.updateImages();
|
|
1516
1808
|
}
|
|
1517
1809
|
});
|
|
@@ -1537,7 +1829,9 @@ var ImageTool = class {
|
|
|
1537
1829
|
this.cropShapeHatchPattern = void 0;
|
|
1538
1830
|
this.cropShapeHatchPatternColor = void 0;
|
|
1539
1831
|
this.cropShapeHatchPatternKey = void 0;
|
|
1832
|
+
this.imageSpecs = [];
|
|
1540
1833
|
this.overlaySpecs = [];
|
|
1834
|
+
this.imageControlsByCapabilityKey.clear();
|
|
1541
1835
|
this.clearRenderedImages();
|
|
1542
1836
|
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
1543
1837
|
this.renderProducerDisposable = void 0;
|
|
@@ -1560,6 +1854,90 @@ var ImageTool = class {
|
|
|
1560
1854
|
isImageEditingVisible() {
|
|
1561
1855
|
return this.isToolActive || this.isImageSelectionActive || !!this.focusedImageId;
|
|
1562
1856
|
}
|
|
1857
|
+
getEnabledImageControlCapabilities() {
|
|
1858
|
+
return IMAGE_DEFAULT_CONTROL_CAPABILITIES;
|
|
1859
|
+
}
|
|
1860
|
+
getImageControls(capabilities) {
|
|
1861
|
+
const normalized = [...new Set(capabilities)].sort();
|
|
1862
|
+
const cacheKey = normalized.join("|");
|
|
1863
|
+
const cached = this.imageControlsByCapabilityKey.get(cacheKey);
|
|
1864
|
+
if (cached) {
|
|
1865
|
+
return cached;
|
|
1866
|
+
}
|
|
1867
|
+
const enabled = new Set(normalized);
|
|
1868
|
+
const controls = {};
|
|
1869
|
+
IMAGE_CONTROL_DESCRIPTORS.forEach((descriptor) => {
|
|
1870
|
+
if (!enabled.has(descriptor.capability)) return;
|
|
1871
|
+
controls[descriptor.key] = descriptor.create();
|
|
1872
|
+
});
|
|
1873
|
+
this.imageControlsByCapabilityKey.set(cacheKey, controls);
|
|
1874
|
+
return controls;
|
|
1875
|
+
}
|
|
1876
|
+
getImageControlVisualConfig() {
|
|
1877
|
+
var _a, _b, _c, _d;
|
|
1878
|
+
const cornerSizeRaw = Number(
|
|
1879
|
+
(_a = this.getConfig("image.control.cornerSize", 14)) != null ? _a : 14
|
|
1880
|
+
);
|
|
1881
|
+
const touchCornerSizeRaw = Number(
|
|
1882
|
+
(_b = this.getConfig("image.control.touchCornerSize", 24)) != null ? _b : 24
|
|
1883
|
+
);
|
|
1884
|
+
const borderScaleFactorRaw = Number(
|
|
1885
|
+
(_c = this.getConfig("image.control.borderScaleFactor", 1.5)) != null ? _c : 1.5
|
|
1886
|
+
);
|
|
1887
|
+
const paddingRaw = Number(
|
|
1888
|
+
(_d = this.getConfig("image.control.padding", 0)) != null ? _d : 0
|
|
1889
|
+
);
|
|
1890
|
+
const cornerStyleRaw = this.getConfig(
|
|
1891
|
+
"image.control.cornerStyle",
|
|
1892
|
+
"circle"
|
|
1893
|
+
) || "circle";
|
|
1894
|
+
const cornerStyle = cornerStyleRaw === "rect" ? "rect" : "circle";
|
|
1895
|
+
return {
|
|
1896
|
+
cornerSize: Number.isFinite(cornerSizeRaw) ? Math.max(4, Math.min(64, cornerSizeRaw)) : 14,
|
|
1897
|
+
touchCornerSize: Number.isFinite(touchCornerSizeRaw) ? Math.max(8, Math.min(96, touchCornerSizeRaw)) : 24,
|
|
1898
|
+
cornerStyle,
|
|
1899
|
+
cornerColor: this.getConfig("image.control.cornerColor", "#ffffff") || "#ffffff",
|
|
1900
|
+
cornerStrokeColor: this.getConfig("image.control.cornerStrokeColor", "#1677ff") || "#1677ff",
|
|
1901
|
+
transparentCorners: !!this.getConfig(
|
|
1902
|
+
"image.control.transparentCorners",
|
|
1903
|
+
false
|
|
1904
|
+
),
|
|
1905
|
+
borderColor: this.getConfig("image.control.borderColor", "#1677ff") || "#1677ff",
|
|
1906
|
+
borderScaleFactor: Number.isFinite(borderScaleFactorRaw) ? Math.max(0.5, Math.min(8, borderScaleFactorRaw)) : 1.5,
|
|
1907
|
+
padding: Number.isFinite(paddingRaw) ? Math.max(0, Math.min(64, paddingRaw)) : 0
|
|
1908
|
+
};
|
|
1909
|
+
}
|
|
1910
|
+
applyImageObjectInteractionState(obj) {
|
|
1911
|
+
var _a;
|
|
1912
|
+
if (!obj) return;
|
|
1913
|
+
const visible = this.isImageEditingVisible();
|
|
1914
|
+
const visual = this.getImageControlVisualConfig();
|
|
1915
|
+
obj.set({
|
|
1916
|
+
selectable: visible,
|
|
1917
|
+
evented: visible,
|
|
1918
|
+
hasControls: visible,
|
|
1919
|
+
hasBorders: visible,
|
|
1920
|
+
lockScalingFlip: true,
|
|
1921
|
+
cornerSize: visual.cornerSize,
|
|
1922
|
+
touchCornerSize: visual.touchCornerSize,
|
|
1923
|
+
cornerStyle: visual.cornerStyle,
|
|
1924
|
+
cornerColor: visual.cornerColor,
|
|
1925
|
+
cornerStrokeColor: visual.cornerStrokeColor,
|
|
1926
|
+
transparentCorners: visual.transparentCorners,
|
|
1927
|
+
borderColor: visual.borderColor,
|
|
1928
|
+
borderScaleFactor: visual.borderScaleFactor,
|
|
1929
|
+
padding: visual.padding
|
|
1930
|
+
});
|
|
1931
|
+
obj.controls = this.getImageControls(
|
|
1932
|
+
this.getEnabledImageControlCapabilities()
|
|
1933
|
+
);
|
|
1934
|
+
(_a = obj.setCoords) == null ? void 0 : _a.call(obj);
|
|
1935
|
+
}
|
|
1936
|
+
refreshImageObjectInteractionState() {
|
|
1937
|
+
this.getImageObjects().forEach(
|
|
1938
|
+
(obj) => this.applyImageObjectInteractionState(obj)
|
|
1939
|
+
);
|
|
1940
|
+
}
|
|
1563
1941
|
isDebugEnabled() {
|
|
1564
1942
|
return !!this.getConfig("image.debug", false);
|
|
1565
1943
|
}
|
|
@@ -1591,17 +1969,84 @@ var ImageTool = class {
|
|
|
1591
1969
|
],
|
|
1592
1970
|
[import_core2.ContributionPointIds.CONFIGURATIONS]: [
|
|
1593
1971
|
{
|
|
1594
|
-
id: "image.items",
|
|
1595
|
-
type: "array",
|
|
1596
|
-
label: "Images",
|
|
1597
|
-
default: []
|
|
1972
|
+
id: "image.items",
|
|
1973
|
+
type: "array",
|
|
1974
|
+
label: "Images",
|
|
1975
|
+
default: []
|
|
1976
|
+
},
|
|
1977
|
+
{
|
|
1978
|
+
id: "image.debug",
|
|
1979
|
+
type: "boolean",
|
|
1980
|
+
label: "Image Debug Log",
|
|
1981
|
+
default: false
|
|
1982
|
+
},
|
|
1983
|
+
{
|
|
1984
|
+
id: "image.control.cornerSize",
|
|
1985
|
+
type: "number",
|
|
1986
|
+
label: "Image Control Corner Size",
|
|
1987
|
+
min: 4,
|
|
1988
|
+
max: 64,
|
|
1989
|
+
step: 1,
|
|
1990
|
+
default: 14
|
|
1991
|
+
},
|
|
1992
|
+
{
|
|
1993
|
+
id: "image.control.touchCornerSize",
|
|
1994
|
+
type: "number",
|
|
1995
|
+
label: "Image Control Touch Corner Size",
|
|
1996
|
+
min: 8,
|
|
1997
|
+
max: 96,
|
|
1998
|
+
step: 1,
|
|
1999
|
+
default: 24
|
|
2000
|
+
},
|
|
2001
|
+
{
|
|
2002
|
+
id: "image.control.cornerStyle",
|
|
2003
|
+
type: "select",
|
|
2004
|
+
label: "Image Control Corner Style",
|
|
2005
|
+
options: ["circle", "rect"],
|
|
2006
|
+
default: "circle"
|
|
1598
2007
|
},
|
|
1599
2008
|
{
|
|
1600
|
-
id: "image.
|
|
2009
|
+
id: "image.control.cornerColor",
|
|
2010
|
+
type: "color",
|
|
2011
|
+
label: "Image Control Corner Color",
|
|
2012
|
+
default: "#ffffff"
|
|
2013
|
+
},
|
|
2014
|
+
{
|
|
2015
|
+
id: "image.control.cornerStrokeColor",
|
|
2016
|
+
type: "color",
|
|
2017
|
+
label: "Image Control Corner Stroke Color",
|
|
2018
|
+
default: "#1677ff"
|
|
2019
|
+
},
|
|
2020
|
+
{
|
|
2021
|
+
id: "image.control.transparentCorners",
|
|
1601
2022
|
type: "boolean",
|
|
1602
|
-
label: "Image
|
|
2023
|
+
label: "Image Control Transparent Corners",
|
|
1603
2024
|
default: false
|
|
1604
2025
|
},
|
|
2026
|
+
{
|
|
2027
|
+
id: "image.control.borderColor",
|
|
2028
|
+
type: "color",
|
|
2029
|
+
label: "Image Control Border Color",
|
|
2030
|
+
default: "#1677ff"
|
|
2031
|
+
},
|
|
2032
|
+
{
|
|
2033
|
+
id: "image.control.borderScaleFactor",
|
|
2034
|
+
type: "number",
|
|
2035
|
+
label: "Image Control Border Width",
|
|
2036
|
+
min: 0.5,
|
|
2037
|
+
max: 8,
|
|
2038
|
+
step: 0.1,
|
|
2039
|
+
default: 1.5
|
|
2040
|
+
},
|
|
2041
|
+
{
|
|
2042
|
+
id: "image.control.padding",
|
|
2043
|
+
type: "number",
|
|
2044
|
+
label: "Image Control Padding",
|
|
2045
|
+
min: 0,
|
|
2046
|
+
max: 64,
|
|
2047
|
+
step: 1,
|
|
2048
|
+
default: 0
|
|
2049
|
+
},
|
|
1605
2050
|
{
|
|
1606
2051
|
id: "image.frame.strokeColor",
|
|
1607
2052
|
type: "color",
|
|
@@ -1839,12 +2284,7 @@ var ImageTool = class {
|
|
|
1839
2284
|
} else {
|
|
1840
2285
|
const obj = this.getImageObject(id);
|
|
1841
2286
|
if (obj) {
|
|
1842
|
-
|
|
1843
|
-
selectable: true,
|
|
1844
|
-
evented: true,
|
|
1845
|
-
hasControls: true,
|
|
1846
|
-
hasBorders: true
|
|
1847
|
-
});
|
|
2287
|
+
this.applyImageObjectInteractionState(obj);
|
|
1848
2288
|
canvas.setActiveObject(obj);
|
|
1849
2289
|
}
|
|
1850
2290
|
}
|
|
@@ -2012,9 +2452,7 @@ var ImageTool = class {
|
|
|
2012
2452
|
}
|
|
2013
2453
|
getOverlayObjects() {
|
|
2014
2454
|
if (!this.canvasService) return [];
|
|
2015
|
-
return this.canvasService.
|
|
2016
|
-
IMAGE_OVERLAY_LAYER_ID
|
|
2017
|
-
);
|
|
2455
|
+
return this.canvasService.getPassObjects(IMAGE_OVERLAY_LAYER_ID);
|
|
2018
2456
|
}
|
|
2019
2457
|
getImageObject(id) {
|
|
2020
2458
|
return this.getImageObjects().find((obj) => {
|
|
@@ -2024,9 +2462,9 @@ var ImageTool = class {
|
|
|
2024
2462
|
}
|
|
2025
2463
|
clearRenderedImages() {
|
|
2026
2464
|
if (!this.canvasService) return;
|
|
2027
|
-
|
|
2028
|
-
this.
|
|
2029
|
-
this.canvasService.
|
|
2465
|
+
this.imageSpecs = [];
|
|
2466
|
+
this.overlaySpecs = [];
|
|
2467
|
+
this.canvasService.requestRenderFromProducers();
|
|
2030
2468
|
}
|
|
2031
2469
|
purgeSourceSizeCacheForItem(item) {
|
|
2032
2470
|
if (!item) return;
|
|
@@ -2054,6 +2492,29 @@ var ImageTool = class {
|
|
|
2054
2492
|
}
|
|
2055
2493
|
return { width: 1, height: 1 };
|
|
2056
2494
|
}
|
|
2495
|
+
async ensureSourceSize(src) {
|
|
2496
|
+
if (!src) return null;
|
|
2497
|
+
const cached = this.sourceSizeBySrc.get(src);
|
|
2498
|
+
if (cached) return cached;
|
|
2499
|
+
try {
|
|
2500
|
+
const image = await import_fabric2.Image.fromURL(src, {
|
|
2501
|
+
crossOrigin: "anonymous"
|
|
2502
|
+
});
|
|
2503
|
+
const width = Number((image == null ? void 0 : image.width) || 0);
|
|
2504
|
+
const height = Number((image == null ? void 0 : image.height) || 0);
|
|
2505
|
+
if (width > 0 && height > 0) {
|
|
2506
|
+
const size = { width, height };
|
|
2507
|
+
this.sourceSizeBySrc.set(src, size);
|
|
2508
|
+
return size;
|
|
2509
|
+
}
|
|
2510
|
+
} catch (error) {
|
|
2511
|
+
this.debug("image:size:load-failed", {
|
|
2512
|
+
src,
|
|
2513
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2514
|
+
});
|
|
2515
|
+
}
|
|
2516
|
+
return null;
|
|
2517
|
+
}
|
|
2057
2518
|
getCoverScale(frame, size) {
|
|
2058
2519
|
const sw = Math.max(1, size.width);
|
|
2059
2520
|
const sh = Math.max(1, size.height);
|
|
@@ -2388,24 +2849,6 @@ var ImageTool = class {
|
|
|
2388
2849
|
opacity: render.opacity
|
|
2389
2850
|
};
|
|
2390
2851
|
}
|
|
2391
|
-
toScreenObjectProps(props) {
|
|
2392
|
-
if (!this.canvasService) return props;
|
|
2393
|
-
const next = { ...props };
|
|
2394
|
-
if (Number.isFinite(next.left) || Number.isFinite(next.top)) {
|
|
2395
|
-
const mapped = this.canvasService.toScreenPoint({
|
|
2396
|
-
x: Number.isFinite(next.left) ? Number(next.left) : 0,
|
|
2397
|
-
y: Number.isFinite(next.top) ? Number(next.top) : 0
|
|
2398
|
-
});
|
|
2399
|
-
if (Number.isFinite(next.left)) next.left = mapped.x;
|
|
2400
|
-
if (Number.isFinite(next.top)) next.top = mapped.y;
|
|
2401
|
-
}
|
|
2402
|
-
const sceneScale = this.canvasService.getSceneScale();
|
|
2403
|
-
const sx = Number.isFinite(next.scaleX) ? Number(next.scaleX) : 1;
|
|
2404
|
-
const sy = Number.isFinite(next.scaleY) ? Number(next.scaleY) : 1;
|
|
2405
|
-
next.scaleX = sx * sceneScale;
|
|
2406
|
-
next.scaleY = sy * sceneScale;
|
|
2407
|
-
return next;
|
|
2408
|
-
}
|
|
2409
2852
|
toSceneObjectScale(value) {
|
|
2410
2853
|
if (!this.canvasService) return value;
|
|
2411
2854
|
return value / this.canvasService.getSceneScale();
|
|
@@ -2416,104 +2859,27 @@ var ImageTool = class {
|
|
|
2416
2859
|
if (typeof obj.getSrc === "function") return obj.getSrc();
|
|
2417
2860
|
return (_a = obj == null ? void 0 : obj._originalElement) == null ? void 0 : _a.src;
|
|
2418
2861
|
}
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
});
|
|
2432
|
-
}
|
|
2433
|
-
async upsertImageObject(item, frame, seq) {
|
|
2434
|
-
if (!this.canvasService) return;
|
|
2435
|
-
const canvas = this.canvasService.canvas;
|
|
2436
|
-
const render = this.resolveRenderImageState(item);
|
|
2437
|
-
if (!render.src) return;
|
|
2438
|
-
let obj = this.getImageObject(item.id);
|
|
2439
|
-
const currentSrc = this.getCurrentSrc(obj);
|
|
2440
|
-
if (obj && currentSrc && currentSrc !== render.src) {
|
|
2441
|
-
canvas.remove(obj);
|
|
2442
|
-
obj = void 0;
|
|
2443
|
-
}
|
|
2444
|
-
if (!obj) {
|
|
2445
|
-
const created = await import_fabric2.Image.fromURL(render.src, {
|
|
2446
|
-
crossOrigin: "anonymous"
|
|
2447
|
-
});
|
|
2448
|
-
if (seq !== this.renderSeq) return;
|
|
2449
|
-
created.set({
|
|
2862
|
+
async buildImageSpecs(items, frame) {
|
|
2863
|
+
const specs = [];
|
|
2864
|
+
for (const item of items) {
|
|
2865
|
+
const render = this.resolveRenderImageState(item);
|
|
2866
|
+
if (!render.src) continue;
|
|
2867
|
+
const ensured = await this.ensureSourceSize(render.src);
|
|
2868
|
+
const sourceSize = ensured || this.getSourceSize(render.src);
|
|
2869
|
+
const props = this.computeCanvasProps(render, sourceSize, frame);
|
|
2870
|
+
specs.push({
|
|
2871
|
+
id: item.id,
|
|
2872
|
+
type: "image",
|
|
2873
|
+
src: render.src,
|
|
2450
2874
|
data: {
|
|
2451
2875
|
id: item.id,
|
|
2452
2876
|
layerId: IMAGE_OBJECT_LAYER_ID,
|
|
2453
2877
|
type: "image-item"
|
|
2454
|
-
}
|
|
2878
|
+
},
|
|
2879
|
+
props
|
|
2455
2880
|
});
|
|
2456
|
-
canvas.add(created);
|
|
2457
|
-
obj = created;
|
|
2458
|
-
}
|
|
2459
|
-
this.rememberSourceSize(render.src, obj);
|
|
2460
|
-
const sourceSize = this.getSourceSize(render.src, obj);
|
|
2461
|
-
const props = this.computeCanvasProps(render, sourceSize, frame);
|
|
2462
|
-
const screenProps = this.toScreenObjectProps(props);
|
|
2463
|
-
obj.set({
|
|
2464
|
-
...screenProps,
|
|
2465
|
-
data: {
|
|
2466
|
-
...obj.data || {},
|
|
2467
|
-
id: item.id,
|
|
2468
|
-
layerId: IMAGE_OBJECT_LAYER_ID,
|
|
2469
|
-
type: "image-item"
|
|
2470
|
-
}
|
|
2471
|
-
});
|
|
2472
|
-
this.applyImageControlVisibility(obj);
|
|
2473
|
-
obj.setCoords();
|
|
2474
|
-
const resolver = this.loadResolvers.get(item.id);
|
|
2475
|
-
if (resolver) {
|
|
2476
|
-
resolver();
|
|
2477
|
-
this.loadResolvers.delete(item.id);
|
|
2478
|
-
}
|
|
2479
|
-
}
|
|
2480
|
-
syncImageZOrder(items) {
|
|
2481
|
-
if (!this.canvasService) return;
|
|
2482
|
-
const canvas = this.canvasService.canvas;
|
|
2483
|
-
const objects = canvas.getObjects();
|
|
2484
|
-
let insertIndex = 0;
|
|
2485
|
-
const backgroundLayer = this.canvasService.getLayer("background");
|
|
2486
|
-
if (backgroundLayer) {
|
|
2487
|
-
const bgIndex = objects.indexOf(backgroundLayer);
|
|
2488
|
-
if (bgIndex >= 0) insertIndex = bgIndex + 1;
|
|
2489
|
-
}
|
|
2490
|
-
items.forEach((item) => {
|
|
2491
|
-
const obj = this.getImageObject(item.id);
|
|
2492
|
-
if (!obj) return;
|
|
2493
|
-
canvas.moveObjectTo(obj, insertIndex);
|
|
2494
|
-
insertIndex += 1;
|
|
2495
|
-
});
|
|
2496
|
-
const overlayObjects = this.getOverlayObjects().sort((a, b) => {
|
|
2497
|
-
var _a, _b, _c, _d;
|
|
2498
|
-
const az = Number((_b = (_a = a == null ? void 0 : a.data) == null ? void 0 : _a.zIndex) != null ? _b : 0);
|
|
2499
|
-
const bz = Number((_d = (_c = b == null ? void 0 : b.data) == null ? void 0 : _c.zIndex) != null ? _d : 0);
|
|
2500
|
-
return az - bz;
|
|
2501
|
-
});
|
|
2502
|
-
overlayObjects.forEach((obj) => {
|
|
2503
|
-
canvas.bringObjectToFront(obj);
|
|
2504
|
-
});
|
|
2505
|
-
if (this.isDebugEnabled()) {
|
|
2506
|
-
const stack = canvas.getObjects().map((obj, index) => {
|
|
2507
|
-
var _a, _b, _c;
|
|
2508
|
-
return {
|
|
2509
|
-
index,
|
|
2510
|
-
id: (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id,
|
|
2511
|
-
layerId: (_b = obj == null ? void 0 : obj.data) == null ? void 0 : _b.layerId,
|
|
2512
|
-
zIndex: (_c = obj == null ? void 0 : obj.data) == null ? void 0 : _c.zIndex
|
|
2513
|
-
};
|
|
2514
|
-
}).filter((item) => item.layerId === IMAGE_OVERLAY_LAYER_ID);
|
|
2515
|
-
this.debug("overlay:stack", stack);
|
|
2516
2881
|
}
|
|
2882
|
+
return specs;
|
|
2517
2883
|
}
|
|
2518
2884
|
buildOverlaySpecs(frame, sceneGeometry) {
|
|
2519
2885
|
const visible = this.isImageEditingVisible();
|
|
@@ -2677,7 +3043,7 @@ var ImageTool = class {
|
|
|
2677
3043
|
evented: false
|
|
2678
3044
|
}
|
|
2679
3045
|
};
|
|
2680
|
-
const specs = [...mask, ...shapeOverlay, frameSpec];
|
|
3046
|
+
const specs = shapeOverlay.length > 0 ? [...mask, ...shapeOverlay] : [...mask, ...shapeOverlay, frameSpec];
|
|
2681
3047
|
this.debug("overlay:built", {
|
|
2682
3048
|
frame,
|
|
2683
3049
|
shape: sceneGeometry == null ? void 0 : sceneGeometry.shape,
|
|
@@ -2707,30 +3073,33 @@ var ImageTool = class {
|
|
|
2707
3073
|
skipRender: true
|
|
2708
3074
|
});
|
|
2709
3075
|
}
|
|
2710
|
-
this.
|
|
2711
|
-
var _a, _b;
|
|
2712
|
-
const id = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id;
|
|
2713
|
-
if (typeof id === "string" && !desiredIds.has(id)) {
|
|
2714
|
-
(_b = this.canvasService) == null ? void 0 : _b.canvas.remove(obj);
|
|
2715
|
-
}
|
|
2716
|
-
});
|
|
2717
|
-
for (const item of renderItems) {
|
|
2718
|
-
if (seq !== this.renderSeq) return;
|
|
2719
|
-
await this.upsertImageObject(item, frame, seq);
|
|
2720
|
-
}
|
|
3076
|
+
const imageSpecs = await this.buildImageSpecs(renderItems, frame);
|
|
2721
3077
|
if (seq !== this.renderSeq) return;
|
|
2722
|
-
this.syncImageZOrder(renderItems);
|
|
2723
3078
|
const sceneGeometry = await this.resolveSceneGeometryForOverlay();
|
|
2724
3079
|
if (seq !== this.renderSeq) return;
|
|
2725
|
-
|
|
2726
|
-
this.overlaySpecs =
|
|
3080
|
+
this.imageSpecs = imageSpecs;
|
|
3081
|
+
this.overlaySpecs = this.buildOverlaySpecs(frame, sceneGeometry);
|
|
2727
3082
|
await this.canvasService.flushRenderFromProducers();
|
|
2728
|
-
this.
|
|
3083
|
+
if (seq !== this.renderSeq) return;
|
|
3084
|
+
this.refreshImageObjectInteractionState();
|
|
3085
|
+
renderItems.forEach((item) => {
|
|
3086
|
+
if (!this.getImageObject(item.id)) return;
|
|
3087
|
+
const resolver = this.loadResolvers.get(item.id);
|
|
3088
|
+
if (!resolver) return;
|
|
3089
|
+
resolver();
|
|
3090
|
+
this.loadResolvers.delete(item.id);
|
|
3091
|
+
});
|
|
3092
|
+
if (this.focusedImageId && this.isToolActive) {
|
|
3093
|
+
this.setImageFocus(this.focusedImageId, {
|
|
3094
|
+
syncCanvasSelection: true,
|
|
3095
|
+
skipRender: true
|
|
3096
|
+
});
|
|
3097
|
+
}
|
|
2729
3098
|
const overlayCanvasCount = this.getOverlayObjects().length;
|
|
2730
3099
|
this.debug("render:done", {
|
|
2731
3100
|
seq,
|
|
2732
3101
|
renderCount: renderItems.length,
|
|
2733
|
-
overlayCount: overlaySpecs.length,
|
|
3102
|
+
overlayCount: this.overlaySpecs.length,
|
|
2734
3103
|
overlayCanvasCount,
|
|
2735
3104
|
isToolActive: this.isToolActive,
|
|
2736
3105
|
isImageSelectionActive: this.isImageSelectionActive,
|
|
@@ -4376,11 +4745,11 @@ var DielineTool = class {
|
|
|
4376
4745
|
style: "solid"
|
|
4377
4746
|
},
|
|
4378
4747
|
insideColor: "rgba(0,0,0,0)",
|
|
4379
|
-
outsideColor: "#ffffff",
|
|
4380
4748
|
showBleedLines: true,
|
|
4381
4749
|
features: []
|
|
4382
4750
|
};
|
|
4383
4751
|
this.specs = [];
|
|
4752
|
+
this.effects = [];
|
|
4384
4753
|
this.renderSeq = 0;
|
|
4385
4754
|
this.onCanvasResized = () => {
|
|
4386
4755
|
this.updateDieline();
|
|
@@ -4417,10 +4786,23 @@ var DielineTool = class {
|
|
|
4417
4786
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
4418
4787
|
this.id,
|
|
4419
4788
|
() => ({
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4789
|
+
passes: [
|
|
4790
|
+
{
|
|
4791
|
+
id: DIELINE_LAYER_ID,
|
|
4792
|
+
stack: 700,
|
|
4793
|
+
order: 0,
|
|
4794
|
+
replace: true,
|
|
4795
|
+
visibility: {
|
|
4796
|
+
op: "not",
|
|
4797
|
+
expr: {
|
|
4798
|
+
op: "activeToolIn",
|
|
4799
|
+
ids: ["pooder.kit.image", "pooder.kit.white-ink"]
|
|
4800
|
+
}
|
|
4801
|
+
},
|
|
4802
|
+
effects: this.effects,
|
|
4803
|
+
objects: this.specs
|
|
4804
|
+
}
|
|
4805
|
+
]
|
|
4424
4806
|
}),
|
|
4425
4807
|
{ priority: 250 }
|
|
4426
4808
|
);
|
|
@@ -4476,10 +4858,6 @@ var DielineTool = class {
|
|
|
4476
4858
|
s.offsetLine.style
|
|
4477
4859
|
);
|
|
4478
4860
|
s.insideColor = configService.get("dieline.insideColor", s.insideColor);
|
|
4479
|
-
s.outsideColor = configService.get(
|
|
4480
|
-
"dieline.outsideColor",
|
|
4481
|
-
s.outsideColor
|
|
4482
|
-
);
|
|
4483
4861
|
s.showBleedLines = configService.get(
|
|
4484
4862
|
"dieline.showBleedLines",
|
|
4485
4863
|
s.showBleedLines
|
|
@@ -4542,9 +4920,6 @@ var DielineTool = class {
|
|
|
4542
4920
|
case "dieline.insideColor":
|
|
4543
4921
|
s.insideColor = e.value;
|
|
4544
4922
|
break;
|
|
4545
|
-
case "dieline.outsideColor":
|
|
4546
|
-
s.outsideColor = e.value;
|
|
4547
|
-
break;
|
|
4548
4923
|
case "dieline.showBleedLines":
|
|
4549
4924
|
s.showBleedLines = e.value;
|
|
4550
4925
|
break;
|
|
@@ -4573,6 +4948,7 @@ var DielineTool = class {
|
|
|
4573
4948
|
context.eventBus.off("canvas:resized", this.onCanvasResized);
|
|
4574
4949
|
this.renderSeq += 1;
|
|
4575
4950
|
this.specs = [];
|
|
4951
|
+
this.effects = [];
|
|
4576
4952
|
(_a = this.renderProducerDisposable) == null ? void 0 : _a.dispose();
|
|
4577
4953
|
this.renderProducerDisposable = void 0;
|
|
4578
4954
|
if (this.canvasService) {
|
|
@@ -4689,12 +5065,6 @@ var DielineTool = class {
|
|
|
4689
5065
|
label: "Inside Color",
|
|
4690
5066
|
default: s.insideColor
|
|
4691
5067
|
},
|
|
4692
|
-
{
|
|
4693
|
-
id: "dieline.outsideColor",
|
|
4694
|
-
type: "color",
|
|
4695
|
-
label: "Outside Color",
|
|
4696
|
-
default: s.outsideColor
|
|
4697
|
-
},
|
|
4698
5068
|
{
|
|
4699
5069
|
id: "dieline.features",
|
|
4700
5070
|
type: "json",
|
|
@@ -4818,6 +5188,12 @@ var DielineTool = class {
|
|
|
4818
5188
|
"ConfigurationService"
|
|
4819
5189
|
);
|
|
4820
5190
|
}
|
|
5191
|
+
hasImageItems() {
|
|
5192
|
+
const configService = this.getConfigService();
|
|
5193
|
+
if (!configService) return false;
|
|
5194
|
+
const items = configService.get("image.items", []);
|
|
5195
|
+
return Array.isArray(items) && items.length > 0;
|
|
5196
|
+
}
|
|
4821
5197
|
syncSizeState(configService) {
|
|
4822
5198
|
const sizeState = readSizeState(configService);
|
|
4823
5199
|
this.state.width = sizeState.actualWidthMm;
|
|
@@ -4825,29 +5201,6 @@ var DielineTool = class {
|
|
|
4825
5201
|
this.state.padding = sizeState.viewPadding;
|
|
4826
5202
|
this.state.offset = sizeState.cutMode === "outset" ? sizeState.cutMarginMm : sizeState.cutMode === "inset" ? -sizeState.cutMarginMm : 0;
|
|
4827
5203
|
}
|
|
4828
|
-
bringFeatureMarkersToFront() {
|
|
4829
|
-
if (!this.canvasService) return;
|
|
4830
|
-
const canvas = this.canvasService.canvas;
|
|
4831
|
-
canvas.getObjects().filter((obj) => {
|
|
4832
|
-
var _a;
|
|
4833
|
-
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.type) === "feature-marker";
|
|
4834
|
-
}).forEach((obj) => canvas.bringObjectToFront(obj));
|
|
4835
|
-
}
|
|
4836
|
-
ensureLayerStacking() {
|
|
4837
|
-
if (!this.canvasService) return;
|
|
4838
|
-
const layer = this.canvasService.getLayer(DIELINE_LAYER_ID);
|
|
4839
|
-
if (!layer) return;
|
|
4840
|
-
const userLayer = this.canvasService.getLayer("user");
|
|
4841
|
-
if (userLayer) {
|
|
4842
|
-
const layerIndex = this.canvasService.canvas.getObjects().indexOf(layer);
|
|
4843
|
-
const userIndex = this.canvasService.canvas.getObjects().indexOf(userLayer);
|
|
4844
|
-
if (layerIndex < userIndex) {
|
|
4845
|
-
this.canvasService.canvas.moveObjectTo(layer, userIndex + 1);
|
|
4846
|
-
}
|
|
4847
|
-
return;
|
|
4848
|
-
}
|
|
4849
|
-
this.canvasService.canvas.bringObjectToFront(layer);
|
|
4850
|
-
}
|
|
4851
5204
|
buildDielineSpecs(sceneLayout) {
|
|
4852
5205
|
var _a, _b;
|
|
4853
5206
|
const {
|
|
@@ -4857,10 +5210,10 @@ var DielineTool = class {
|
|
|
4857
5210
|
mainLine,
|
|
4858
5211
|
offsetLine,
|
|
4859
5212
|
insideColor,
|
|
4860
|
-
outsideColor,
|
|
4861
5213
|
showBleedLines,
|
|
4862
5214
|
features
|
|
4863
5215
|
} = this.state;
|
|
5216
|
+
const hasImages = this.hasImageItems();
|
|
4864
5217
|
const canvasW = sceneLayout.canvasWidth || ((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 800;
|
|
4865
5218
|
const canvasH = sceneLayout.canvasHeight || ((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 600;
|
|
4866
5219
|
const scale = sceneLayout.scale;
|
|
@@ -4882,41 +5235,8 @@ var DielineTool = class {
|
|
|
4882
5235
|
radius: (f.radius || 0) * scale
|
|
4883
5236
|
}));
|
|
4884
5237
|
const cutFeatures = absoluteFeatures.filter((f) => !f.skipCut);
|
|
4885
|
-
const
|
|
4886
|
-
|
|
4887
|
-
canvasHeight: canvasH,
|
|
4888
|
-
shape,
|
|
4889
|
-
width: cutW,
|
|
4890
|
-
height: cutH,
|
|
4891
|
-
radius: cutR,
|
|
4892
|
-
x: cx,
|
|
4893
|
-
y: cy,
|
|
4894
|
-
features: cutFeatures,
|
|
4895
|
-
shapeStyle,
|
|
4896
|
-
pathData: this.state.pathData,
|
|
4897
|
-
customSourceWidthPx: this.state.customSourceWidthPx,
|
|
4898
|
-
customSourceHeightPx: this.state.customSourceHeightPx
|
|
4899
|
-
});
|
|
4900
|
-
const specs = [
|
|
4901
|
-
{
|
|
4902
|
-
id: "dieline.mask",
|
|
4903
|
-
type: "path",
|
|
4904
|
-
space: "screen",
|
|
4905
|
-
data: { id: "dieline.mask", type: "dieline" },
|
|
4906
|
-
props: {
|
|
4907
|
-
pathData: maskPathData,
|
|
4908
|
-
fill: outsideColor,
|
|
4909
|
-
stroke: null,
|
|
4910
|
-
selectable: false,
|
|
4911
|
-
evented: false,
|
|
4912
|
-
originX: "left",
|
|
4913
|
-
originY: "top",
|
|
4914
|
-
left: 0,
|
|
4915
|
-
top: 0
|
|
4916
|
-
}
|
|
4917
|
-
}
|
|
4918
|
-
];
|
|
4919
|
-
if (insideColor && insideColor !== "transparent" && insideColor !== "rgba(0,0,0,0)") {
|
|
5238
|
+
const specs = [];
|
|
5239
|
+
if (insideColor && insideColor !== "transparent" && insideColor !== "rgba(0,0,0,0)" && !hasImages) {
|
|
4920
5240
|
const productPathData = generateDielinePath({
|
|
4921
5241
|
shape,
|
|
4922
5242
|
width: cutW,
|
|
@@ -5070,6 +5390,77 @@ var DielineTool = class {
|
|
|
5070
5390
|
});
|
|
5071
5391
|
return specs;
|
|
5072
5392
|
}
|
|
5393
|
+
buildImageClipEffects(sceneLayout) {
|
|
5394
|
+
var _a, _b;
|
|
5395
|
+
const { shape, shapeStyle, radius, features } = this.state;
|
|
5396
|
+
const canvasW = sceneLayout.canvasWidth || ((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 800;
|
|
5397
|
+
const canvasH = sceneLayout.canvasHeight || ((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 600;
|
|
5398
|
+
const scale = sceneLayout.scale;
|
|
5399
|
+
const cx = sceneLayout.trimRect.centerX;
|
|
5400
|
+
const cy = sceneLayout.trimRect.centerY;
|
|
5401
|
+
const visualWidth = sceneLayout.trimRect.width;
|
|
5402
|
+
const visualRadius = radius * scale;
|
|
5403
|
+
const cutW = sceneLayout.cutRect.width;
|
|
5404
|
+
const cutH = sceneLayout.cutRect.height;
|
|
5405
|
+
const visualOffset = (cutW - visualWidth) / 2;
|
|
5406
|
+
const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
5407
|
+
const absoluteFeatures = (features || []).map((f) => ({
|
|
5408
|
+
...f,
|
|
5409
|
+
x: f.x,
|
|
5410
|
+
y: f.y,
|
|
5411
|
+
width: (f.width || 0) * scale,
|
|
5412
|
+
height: (f.height || 0) * scale,
|
|
5413
|
+
radius: (f.radius || 0) * scale
|
|
5414
|
+
}));
|
|
5415
|
+
const cutFeatures = absoluteFeatures.filter((f) => !f.skipCut);
|
|
5416
|
+
const clipPathData = generateDielinePath({
|
|
5417
|
+
shape,
|
|
5418
|
+
width: cutW,
|
|
5419
|
+
height: cutH,
|
|
5420
|
+
radius: cutR,
|
|
5421
|
+
x: cx,
|
|
5422
|
+
y: cy,
|
|
5423
|
+
features: cutFeatures,
|
|
5424
|
+
shapeStyle,
|
|
5425
|
+
pathData: this.state.pathData,
|
|
5426
|
+
customSourceWidthPx: this.state.customSourceWidthPx,
|
|
5427
|
+
customSourceHeightPx: this.state.customSourceHeightPx,
|
|
5428
|
+
canvasWidth: canvasW,
|
|
5429
|
+
canvasHeight: canvasH
|
|
5430
|
+
});
|
|
5431
|
+
if (!clipPathData) return [];
|
|
5432
|
+
return [
|
|
5433
|
+
{
|
|
5434
|
+
type: "clipPath",
|
|
5435
|
+
id: "dieline.clip.image",
|
|
5436
|
+
visibility: {
|
|
5437
|
+
op: "not",
|
|
5438
|
+
expr: { op: "anySessionActive" }
|
|
5439
|
+
},
|
|
5440
|
+
targetPassIds: [IMAGE_OBJECT_LAYER_ID2],
|
|
5441
|
+
source: {
|
|
5442
|
+
id: "dieline.effect.clip-path",
|
|
5443
|
+
type: "path",
|
|
5444
|
+
space: "screen",
|
|
5445
|
+
data: {
|
|
5446
|
+
id: "dieline.effect.clip-path",
|
|
5447
|
+
type: "dieline-effect",
|
|
5448
|
+
effect: "clipPath"
|
|
5449
|
+
},
|
|
5450
|
+
props: {
|
|
5451
|
+
pathData: clipPathData,
|
|
5452
|
+
fill: "#000000",
|
|
5453
|
+
stroke: null,
|
|
5454
|
+
originX: "left",
|
|
5455
|
+
originY: "top",
|
|
5456
|
+
selectable: false,
|
|
5457
|
+
evented: false,
|
|
5458
|
+
excludeFromExport: true
|
|
5459
|
+
}
|
|
5460
|
+
}
|
|
5461
|
+
}
|
|
5462
|
+
];
|
|
5463
|
+
}
|
|
5073
5464
|
updateDieline(_emitEvent = true) {
|
|
5074
5465
|
void this.updateDielineAsync();
|
|
5075
5466
|
}
|
|
@@ -5086,17 +5477,17 @@ var DielineTool = class {
|
|
|
5086
5477
|
if (!sceneLayout) {
|
|
5087
5478
|
if (seq !== this.renderSeq) return;
|
|
5088
5479
|
this.specs = [];
|
|
5480
|
+
this.effects = [];
|
|
5089
5481
|
await this.canvasService.flushRenderFromProducers();
|
|
5090
5482
|
return;
|
|
5091
5483
|
}
|
|
5092
5484
|
const nextSpecs = this.buildDielineSpecs(sceneLayout);
|
|
5485
|
+
const nextEffects = this.buildImageClipEffects(sceneLayout);
|
|
5093
5486
|
if (seq !== this.renderSeq) return;
|
|
5094
5487
|
this.specs = nextSpecs;
|
|
5488
|
+
this.effects = nextEffects;
|
|
5095
5489
|
await this.canvasService.flushRenderFromProducers();
|
|
5096
5490
|
if (seq !== this.renderSeq) return;
|
|
5097
|
-
this.ensureLayerStacking();
|
|
5098
|
-
this.bringFeatureMarkersToFront();
|
|
5099
|
-
this.canvasService.bringLayerToFront("ruler-overlay");
|
|
5100
5491
|
this.canvasService.requestRenderAll();
|
|
5101
5492
|
}
|
|
5102
5493
|
getGeometry() {
|
|
@@ -5529,9 +5920,14 @@ var FeatureTool = class {
|
|
|
5529
5920
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
5530
5921
|
this.id,
|
|
5531
5922
|
() => ({
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5923
|
+
passes: [
|
|
5924
|
+
{
|
|
5925
|
+
id: FEATURE_OVERLAY_LAYER_ID,
|
|
5926
|
+
stack: 880,
|
|
5927
|
+
order: 0,
|
|
5928
|
+
objects: this.specs
|
|
5929
|
+
}
|
|
5930
|
+
]
|
|
5535
5931
|
}),
|
|
5536
5932
|
{ priority: 350 }
|
|
5537
5933
|
);
|
|
@@ -5674,10 +6070,10 @@ var FeatureTool = class {
|
|
|
5674
6070
|
await this.refreshGeometry();
|
|
5675
6071
|
this.setWorkingFeatures(original);
|
|
5676
6072
|
this.hasWorkingChanges = false;
|
|
6073
|
+
this.clearFeatureSessionState();
|
|
5677
6074
|
this.redraw();
|
|
5678
6075
|
this.emitWorkingChange();
|
|
5679
6076
|
this.updateCommittedFeatures(original);
|
|
5680
|
-
this.clearFeatureSessionState();
|
|
5681
6077
|
return { ok: true };
|
|
5682
6078
|
}
|
|
5683
6079
|
},
|
|
@@ -5993,6 +6389,7 @@ var FeatureTool = class {
|
|
|
5993
6389
|
}
|
|
5994
6390
|
getDraggableMarkerTarget(target) {
|
|
5995
6391
|
var _a, _b;
|
|
6392
|
+
if (!this.isFeatureSessionActive || !this.isToolActive) return null;
|
|
5996
6393
|
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "feature-marker") return null;
|
|
5997
6394
|
if (((_b = target.data) == null ? void 0 : _b.markerRole) !== "handle") return null;
|
|
5998
6395
|
return target;
|
|
@@ -6115,18 +6512,12 @@ var FeatureTool = class {
|
|
|
6115
6512
|
if (seq !== this.renderSeq) return;
|
|
6116
6513
|
await this.canvasService.flushRenderFromProducers();
|
|
6117
6514
|
if (seq !== this.renderSeq) return;
|
|
6118
|
-
this.syncOverlayOrder();
|
|
6119
6515
|
if (options.enforceConstraints) {
|
|
6120
6516
|
this.enforceConstraints();
|
|
6121
6517
|
}
|
|
6122
6518
|
}
|
|
6123
|
-
syncOverlayOrder() {
|
|
6124
|
-
if (!this.canvasService) return;
|
|
6125
|
-
this.canvasService.bringLayerToFront(FEATURE_OVERLAY_LAYER_ID);
|
|
6126
|
-
this.canvasService.bringLayerToFront("ruler-overlay");
|
|
6127
|
-
}
|
|
6128
6519
|
buildFeatureSpecs() {
|
|
6129
|
-
if (!this.currentGeometry || this.workingFeatures.length === 0) {
|
|
6520
|
+
if (!this.isFeatureSessionActive || !this.currentGeometry || this.workingFeatures.length === 0) {
|
|
6130
6521
|
return [];
|
|
6131
6522
|
}
|
|
6132
6523
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -6196,11 +6587,12 @@ var FeatureTool = class {
|
|
|
6196
6587
|
const color = feature.color || (feature.operation === "add" ? "#00FF00" : "#FF0000");
|
|
6197
6588
|
const strokeDash = feature.strokeDash || (feature.operation === "subtract" ? [4, 4] : void 0);
|
|
6198
6589
|
const interactive = options.markerRole === "handle";
|
|
6590
|
+
const sessionVisible = this.isToolActive && this.isFeatureSessionActive;
|
|
6199
6591
|
const baseData = this.buildMarkerData(marker, options);
|
|
6200
6592
|
const commonProps = {
|
|
6201
|
-
visible:
|
|
6202
|
-
selectable: interactive &&
|
|
6203
|
-
evented: interactive &&
|
|
6593
|
+
visible: sessionVisible,
|
|
6594
|
+
selectable: interactive && sessionVisible,
|
|
6595
|
+
evented: interactive && sessionVisible,
|
|
6204
6596
|
hasControls: false,
|
|
6205
6597
|
hasBorders: false,
|
|
6206
6598
|
hoverCursor: interactive ? "move" : "default",
|
|
@@ -6265,7 +6657,7 @@ var FeatureTool = class {
|
|
|
6265
6657
|
markerOffsetY: -visualHeight / 2
|
|
6266
6658
|
},
|
|
6267
6659
|
props: {
|
|
6268
|
-
visible:
|
|
6660
|
+
visible: sessionVisible,
|
|
6269
6661
|
selectable: false,
|
|
6270
6662
|
evented: false,
|
|
6271
6663
|
width: visualWidth,
|
|
@@ -6433,9 +6825,14 @@ var FilmTool = class {
|
|
|
6433
6825
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
6434
6826
|
this.id,
|
|
6435
6827
|
() => ({
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6828
|
+
passes: [
|
|
6829
|
+
{
|
|
6830
|
+
id: FILM_LAYER_ID,
|
|
6831
|
+
stack: 1e3,
|
|
6832
|
+
order: 0,
|
|
6833
|
+
objects: this.specs
|
|
6834
|
+
}
|
|
6835
|
+
]
|
|
6439
6836
|
}),
|
|
6440
6837
|
{ priority: 500 }
|
|
6441
6838
|
);
|
|
@@ -6609,7 +7006,6 @@ var FilmTool = class {
|
|
|
6609
7006
|
this.specs = this.buildFilmSpecs(this.renderImageUrl, this.opacity);
|
|
6610
7007
|
await this.canvasService.flushRenderFromProducers();
|
|
6611
7008
|
if (seq !== this.renderSeq) return;
|
|
6612
|
-
this.canvasService.bringLayerToFront(FILM_LAYER_ID);
|
|
6613
7009
|
this.canvasService.requestRenderAll();
|
|
6614
7010
|
}
|
|
6615
7011
|
};
|
|
@@ -6756,10 +7152,22 @@ var RulerTool = class {
|
|
|
6756
7152
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
6757
7153
|
this.id,
|
|
6758
7154
|
() => ({
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
7155
|
+
passes: [
|
|
7156
|
+
{
|
|
7157
|
+
id: RULER_LAYER_ID,
|
|
7158
|
+
stack: 950,
|
|
7159
|
+
order: 0,
|
|
7160
|
+
replace: true,
|
|
7161
|
+
visibility: {
|
|
7162
|
+
op: "not",
|
|
7163
|
+
expr: {
|
|
7164
|
+
op: "activeToolIn",
|
|
7165
|
+
ids: ["pooder.kit.white-ink"]
|
|
7166
|
+
}
|
|
7167
|
+
},
|
|
7168
|
+
objects: this.specs
|
|
7169
|
+
}
|
|
7170
|
+
]
|
|
6763
7171
|
}),
|
|
6764
7172
|
{ priority: 400 }
|
|
6765
7173
|
);
|
|
@@ -7251,7 +7659,6 @@ var RulerTool = class {
|
|
|
7251
7659
|
this.specs = specs;
|
|
7252
7660
|
await this.canvasService.flushRenderFromProducers();
|
|
7253
7661
|
if (seq !== this.renderSeq) return;
|
|
7254
|
-
this.canvasService.bringLayerToFront(RULER_LAYER_ID);
|
|
7255
7662
|
this.canvasService.requestRenderAll();
|
|
7256
7663
|
this.log("render:done", { seq });
|
|
7257
7664
|
}
|
|
@@ -7263,7 +7670,6 @@ var WHITE_INK_OBJECT_LAYER_ID = "white-ink.user";
|
|
|
7263
7670
|
var WHITE_INK_COVER_LAYER_ID = "white-ink.cover";
|
|
7264
7671
|
var WHITE_INK_OVERLAY_LAYER_ID = "white-ink.overlay";
|
|
7265
7672
|
var IMAGE_OBJECT_LAYER_ID3 = "image.user";
|
|
7266
|
-
var IMAGE_OVERLAY_LAYER_ID2 = "image-overlay";
|
|
7267
7673
|
var WHITE_INK_DEBUG_KEY = "whiteInk.debug";
|
|
7268
7674
|
var WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY = "whiteInk.previewImageVisible";
|
|
7269
7675
|
var WHITE_INK_DEFAULT_OPACITY = 0.85;
|
|
@@ -7341,11 +7747,26 @@ var WhiteInkTool = class {
|
|
|
7341
7747
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
7342
7748
|
this.id,
|
|
7343
7749
|
() => ({
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7750
|
+
passes: [
|
|
7751
|
+
{
|
|
7752
|
+
id: WHITE_INK_COVER_LAYER_ID,
|
|
7753
|
+
stack: 220,
|
|
7754
|
+
order: 0,
|
|
7755
|
+
objects: this.coverSpecs
|
|
7756
|
+
},
|
|
7757
|
+
{
|
|
7758
|
+
id: WHITE_INK_OBJECT_LAYER_ID,
|
|
7759
|
+
stack: 221,
|
|
7760
|
+
order: 0,
|
|
7761
|
+
objects: this.whiteSpecs
|
|
7762
|
+
},
|
|
7763
|
+
{
|
|
7764
|
+
id: WHITE_INK_OVERLAY_LAYER_ID,
|
|
7765
|
+
stack: 790,
|
|
7766
|
+
order: 0,
|
|
7767
|
+
objects: this.overlaySpecs
|
|
7768
|
+
}
|
|
7769
|
+
]
|
|
7349
7770
|
}),
|
|
7350
7771
|
{ priority: 260 }
|
|
7351
7772
|
);
|
|
@@ -7424,7 +7845,6 @@ var WhiteInkTool = class {
|
|
|
7424
7845
|
(_a = this.dirtyTrackerDisposable) == null ? void 0 : _a.dispose();
|
|
7425
7846
|
this.dirtyTrackerDisposable = void 0;
|
|
7426
7847
|
this.clearRenderedWhiteInks();
|
|
7427
|
-
this.applyImageVisibilityForWhiteInk(false);
|
|
7428
7848
|
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
7429
7849
|
this.renderProducerDisposable = void 0;
|
|
7430
7850
|
if (this.canvasService) {
|
|
@@ -8268,22 +8688,6 @@ var WhiteInkTool = class {
|
|
|
8268
8688
|
}
|
|
8269
8689
|
];
|
|
8270
8690
|
}
|
|
8271
|
-
applyImageVisibilityForWhiteInk(previewActive) {
|
|
8272
|
-
if (!this.canvasService) return;
|
|
8273
|
-
const visible = !previewActive;
|
|
8274
|
-
let changed = false;
|
|
8275
|
-
this.canvasService.canvas.getObjects().forEach((obj) => {
|
|
8276
|
-
var _a, _b;
|
|
8277
|
-
if (((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.layerId) !== IMAGE_OBJECT_LAYER_ID3) return;
|
|
8278
|
-
if (obj.visible === visible) return;
|
|
8279
|
-
obj.set({ visible });
|
|
8280
|
-
(_b = obj.setCoords) == null ? void 0 : _b.call(obj);
|
|
8281
|
-
changed = true;
|
|
8282
|
-
});
|
|
8283
|
-
if (changed) {
|
|
8284
|
-
this.canvasService.requestRenderAll();
|
|
8285
|
-
}
|
|
8286
|
-
}
|
|
8287
8691
|
resolveRenderItems() {
|
|
8288
8692
|
if (this.isToolActive) {
|
|
8289
8693
|
return this.cloneItems(this.workingItems);
|
|
@@ -8302,57 +8706,11 @@ var WhiteInkTool = class {
|
|
|
8302
8706
|
]);
|
|
8303
8707
|
const scaleAdjust = this.computeWhiteScaleAdjust(imageSource, whiteSource);
|
|
8304
8708
|
return {
|
|
8305
|
-
whiteSrc: whiteMaskSrc || "",
|
|
8306
|
-
coverSrc: coverMaskSrc || "",
|
|
8307
|
-
whiteScaleAdjustX: scaleAdjust.x,
|
|
8308
|
-
whiteScaleAdjustY: scaleAdjust.y
|
|
8309
|
-
};
|
|
8310
|
-
}
|
|
8311
|
-
resolveDefaultInsertIndex(objects) {
|
|
8312
|
-
if (!this.canvasService) return 0;
|
|
8313
|
-
const backgroundLayer = this.canvasService.getLayer("background");
|
|
8314
|
-
if (!backgroundLayer) return 0;
|
|
8315
|
-
const bgIndex = objects.indexOf(backgroundLayer);
|
|
8316
|
-
if (bgIndex < 0) return 0;
|
|
8317
|
-
return bgIndex + 1;
|
|
8318
|
-
}
|
|
8319
|
-
syncZOrder() {
|
|
8320
|
-
if (!this.canvasService) return;
|
|
8321
|
-
const canvas = this.canvasService.canvas;
|
|
8322
|
-
const whiteObjects = this.canvasService.getRootLayerObjects(
|
|
8323
|
-
WHITE_INK_OBJECT_LAYER_ID
|
|
8324
|
-
);
|
|
8325
|
-
const coverObjects = this.canvasService.getRootLayerObjects(
|
|
8326
|
-
WHITE_INK_COVER_LAYER_ID
|
|
8327
|
-
);
|
|
8328
|
-
const frameObjects = this.canvasService.getRootLayerObjects(
|
|
8329
|
-
WHITE_INK_OVERLAY_LAYER_ID
|
|
8330
|
-
);
|
|
8331
|
-
const currentObjects = canvas.getObjects();
|
|
8332
|
-
const imageIndexes = currentObjects.map(
|
|
8333
|
-
(obj, index) => {
|
|
8334
|
-
var _a;
|
|
8335
|
-
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.layerId) === IMAGE_OBJECT_LAYER_ID3 ? index : -1;
|
|
8336
|
-
}
|
|
8337
|
-
).filter((index) => index >= 0);
|
|
8338
|
-
let whiteInsertIndex = imageIndexes.length ? Math.min(...imageIndexes) : this.resolveDefaultInsertIndex(currentObjects);
|
|
8339
|
-
let coverInsertIndex = whiteInsertIndex;
|
|
8340
|
-
coverObjects.forEach((obj) => {
|
|
8341
|
-
canvas.moveObjectTo(obj, coverInsertIndex);
|
|
8342
|
-
coverInsertIndex += 1;
|
|
8343
|
-
});
|
|
8344
|
-
whiteInsertIndex = coverInsertIndex;
|
|
8345
|
-
whiteObjects.forEach((obj) => {
|
|
8346
|
-
canvas.moveObjectTo(obj, whiteInsertIndex);
|
|
8347
|
-
whiteInsertIndex += 1;
|
|
8348
|
-
});
|
|
8349
|
-
frameObjects.forEach((obj) => canvas.bringObjectToFront(obj));
|
|
8350
|
-
canvas.getObjects().filter((obj) => {
|
|
8351
|
-
var _a;
|
|
8352
|
-
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.layerId) === IMAGE_OVERLAY_LAYER_ID2;
|
|
8353
|
-
}).forEach((obj) => canvas.bringObjectToFront(obj));
|
|
8354
|
-
this.canvasService.bringLayerToFront("dieline-overlay");
|
|
8355
|
-
this.canvasService.bringLayerToFront("ruler-overlay");
|
|
8709
|
+
whiteSrc: whiteMaskSrc || "",
|
|
8710
|
+
coverSrc: coverMaskSrc || "",
|
|
8711
|
+
whiteScaleAdjustX: scaleAdjust.x,
|
|
8712
|
+
whiteScaleAdjustY: scaleAdjust.y
|
|
8713
|
+
};
|
|
8356
8714
|
}
|
|
8357
8715
|
clearRenderedWhiteInks() {
|
|
8358
8716
|
if (!this.canvasService) return;
|
|
@@ -8385,7 +8743,6 @@ var WhiteInkTool = class {
|
|
|
8385
8743
|
this.syncToolActiveFromWorkbench();
|
|
8386
8744
|
const seq = ++this.renderSeq;
|
|
8387
8745
|
const previewActive = this.isPreviewActive();
|
|
8388
|
-
this.applyImageVisibilityForWhiteInk(previewActive);
|
|
8389
8746
|
const frame = this.getFrameRect();
|
|
8390
8747
|
const frameSpecs = this.buildFrameSpecs(frame);
|
|
8391
8748
|
let whiteSpecs = [];
|
|
@@ -8433,7 +8790,6 @@ var WhiteInkTool = class {
|
|
|
8433
8790
|
this.overlaySpecs = frameSpecs;
|
|
8434
8791
|
await this.canvasService.flushRenderFromProducers();
|
|
8435
8792
|
if (seq !== this.renderSeq) return;
|
|
8436
|
-
this.syncZOrder();
|
|
8437
8793
|
this.canvasService.requestRenderAll();
|
|
8438
8794
|
}
|
|
8439
8795
|
getMaskCacheKey(sourceUrl, tint) {
|
|
@@ -8615,62 +8971,9 @@ var SceneLayoutService = class {
|
|
|
8615
8971
|
}
|
|
8616
8972
|
};
|
|
8617
8973
|
|
|
8618
|
-
// src/extensions/sceneVisibility.ts
|
|
8619
|
-
var import_core11 = require("@pooder/core");
|
|
8620
|
-
var CANVAS_SERVICE_ID2 = "CanvasService";
|
|
8621
|
-
var HIDDEN_DIELINE_TOOLS = /* @__PURE__ */ new Set(["pooder.kit.image", "pooder.kit.white-ink"]);
|
|
8622
|
-
var HIDDEN_RULER_TOOLS = /* @__PURE__ */ new Set(["pooder.kit.white-ink"]);
|
|
8623
|
-
var SceneVisibilityService = class {
|
|
8624
|
-
constructor() {
|
|
8625
|
-
this.activeToolId = null;
|
|
8626
|
-
this.onToolActivated = (e) => {
|
|
8627
|
-
this.activeToolId = e.id;
|
|
8628
|
-
this.apply();
|
|
8629
|
-
};
|
|
8630
|
-
this.onObjectAdded = () => {
|
|
8631
|
-
this.apply();
|
|
8632
|
-
};
|
|
8633
|
-
}
|
|
8634
|
-
init(context) {
|
|
8635
|
-
var _a, _b;
|
|
8636
|
-
if (this.context) {
|
|
8637
|
-
this.dispose(this.context);
|
|
8638
|
-
}
|
|
8639
|
-
const canvasService = context.get(CANVAS_SERVICE_ID2);
|
|
8640
|
-
if (!canvasService) {
|
|
8641
|
-
throw new Error("[SceneVisibilityService] CanvasService is required.");
|
|
8642
|
-
}
|
|
8643
|
-
this.context = context;
|
|
8644
|
-
this.canvasService = canvasService;
|
|
8645
|
-
this.activeToolId = (_b = (_a = context.get(import_core11.WORKBENCH_SERVICE)) == null ? void 0 : _a.activeToolId) != null ? _b : null;
|
|
8646
|
-
context.eventBus.on("tool:activated", this.onToolActivated);
|
|
8647
|
-
context.eventBus.on("object:added", this.onObjectAdded);
|
|
8648
|
-
this.apply();
|
|
8649
|
-
}
|
|
8650
|
-
dispose(context) {
|
|
8651
|
-
var _a;
|
|
8652
|
-
const activeContext = (_a = this.context) != null ? _a : context;
|
|
8653
|
-
activeContext.eventBus.off("tool:activated", this.onToolActivated);
|
|
8654
|
-
activeContext.eventBus.off("object:added", this.onObjectAdded);
|
|
8655
|
-
this.context = void 0;
|
|
8656
|
-
this.activeToolId = null;
|
|
8657
|
-
this.canvasService = void 0;
|
|
8658
|
-
}
|
|
8659
|
-
apply() {
|
|
8660
|
-
if (!this.canvasService) return;
|
|
8661
|
-
const dielineLayer = this.canvasService.getLayer("dieline-overlay");
|
|
8662
|
-
if (dielineLayer) {
|
|
8663
|
-
const visible = !HIDDEN_DIELINE_TOOLS.has(this.activeToolId || "");
|
|
8664
|
-
this.canvasService.setLayerVisibility("dieline-overlay", visible);
|
|
8665
|
-
}
|
|
8666
|
-
const rulerVisible = !HIDDEN_RULER_TOOLS.has(this.activeToolId || "");
|
|
8667
|
-
this.canvasService.setLayerVisibility("ruler-overlay", rulerVisible);
|
|
8668
|
-
this.canvasService.requestRenderAll();
|
|
8669
|
-
}
|
|
8670
|
-
};
|
|
8671
|
-
|
|
8672
8974
|
// src/services/CanvasService.ts
|
|
8673
8975
|
var import_fabric5 = require("fabric");
|
|
8976
|
+
var import_core11 = require("@pooder/core");
|
|
8674
8977
|
|
|
8675
8978
|
// src/services/ViewportSystem.ts
|
|
8676
8979
|
var ViewportSystem = class {
|
|
@@ -8745,6 +9048,56 @@ var ViewportSystem = class {
|
|
|
8745
9048
|
}
|
|
8746
9049
|
};
|
|
8747
9050
|
|
|
9051
|
+
// src/services/visibility.ts
|
|
9052
|
+
function compareLayerObjectCount(actual, cmp, expected) {
|
|
9053
|
+
if (cmp === ">") return actual > expected;
|
|
9054
|
+
if (cmp === ">=") return actual >= expected;
|
|
9055
|
+
if (cmp === "<") return actual < expected;
|
|
9056
|
+
if (cmp === "<=") return actual <= expected;
|
|
9057
|
+
return actual === expected;
|
|
9058
|
+
}
|
|
9059
|
+
function layerState(context, layerId) {
|
|
9060
|
+
return context.layers.get(layerId) || { exists: false, objectCount: 0 };
|
|
9061
|
+
}
|
|
9062
|
+
function evaluateVisibilityExpr(expr, context) {
|
|
9063
|
+
var _a;
|
|
9064
|
+
if (!expr) return true;
|
|
9065
|
+
if (expr.op === "const") {
|
|
9066
|
+
return Boolean(expr.value);
|
|
9067
|
+
}
|
|
9068
|
+
if (expr.op === "activeToolIn") {
|
|
9069
|
+
const activeToolId = (_a = context.activeToolId) != null ? _a : null;
|
|
9070
|
+
return !!activeToolId && expr.ids.includes(activeToolId);
|
|
9071
|
+
}
|
|
9072
|
+
if (expr.op === "sessionActive") {
|
|
9073
|
+
const toolId = String(expr.toolId || "").trim();
|
|
9074
|
+
if (!toolId) return false;
|
|
9075
|
+
return context.isSessionActive ? context.isSessionActive(toolId) : false;
|
|
9076
|
+
}
|
|
9077
|
+
if (expr.op === "anySessionActive") {
|
|
9078
|
+
return context.hasAnyActiveSession ? context.hasAnyActiveSession() : false;
|
|
9079
|
+
}
|
|
9080
|
+
if (expr.op === "layerExists") {
|
|
9081
|
+
return layerState(context, expr.layerId).exists === true;
|
|
9082
|
+
}
|
|
9083
|
+
if (expr.op === "layerObjectCount") {
|
|
9084
|
+
const expected = Number(expr.value);
|
|
9085
|
+
if (!Number.isFinite(expected)) return false;
|
|
9086
|
+
const count = layerState(context, expr.layerId).objectCount;
|
|
9087
|
+
return compareLayerObjectCount(count, expr.cmp, expected);
|
|
9088
|
+
}
|
|
9089
|
+
if (expr.op === "not") {
|
|
9090
|
+
return !evaluateVisibilityExpr(expr.expr, context);
|
|
9091
|
+
}
|
|
9092
|
+
if (expr.op === "all") {
|
|
9093
|
+
return expr.exprs.every((item) => evaluateVisibilityExpr(item, context));
|
|
9094
|
+
}
|
|
9095
|
+
if (expr.op === "any") {
|
|
9096
|
+
return expr.exprs.some((item) => evaluateVisibilityExpr(item, context));
|
|
9097
|
+
}
|
|
9098
|
+
return true;
|
|
9099
|
+
}
|
|
9100
|
+
|
|
8748
9101
|
// src/services/CanvasService.ts
|
|
8749
9102
|
var CanvasService = class {
|
|
8750
9103
|
constructor(el, options) {
|
|
@@ -8753,8 +9106,48 @@ var CanvasService = class {
|
|
|
8753
9106
|
this.producerFlushRequested = false;
|
|
8754
9107
|
this.producerLoopPending = false;
|
|
8755
9108
|
this.producerLoopPromise = null;
|
|
8756
|
-
this.
|
|
8757
|
-
this.
|
|
9109
|
+
this.producerApplyInProgress = false;
|
|
9110
|
+
this.visibilityRefreshScheduled = false;
|
|
9111
|
+
this.managedProducerPassIds = /* @__PURE__ */ new Set();
|
|
9112
|
+
this.managedPassMetas = /* @__PURE__ */ new Map();
|
|
9113
|
+
this.managedPassEffects = [];
|
|
9114
|
+
this.canvasForwardersBound = false;
|
|
9115
|
+
this.forwardSelectionCreated = (e) => {
|
|
9116
|
+
var _a;
|
|
9117
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("selection:created", e);
|
|
9118
|
+
};
|
|
9119
|
+
this.forwardSelectionUpdated = (e) => {
|
|
9120
|
+
var _a;
|
|
9121
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("selection:updated", e);
|
|
9122
|
+
};
|
|
9123
|
+
this.forwardSelectionCleared = (e) => {
|
|
9124
|
+
var _a;
|
|
9125
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("selection:cleared", e);
|
|
9126
|
+
};
|
|
9127
|
+
this.forwardObjectModified = (e) => {
|
|
9128
|
+
var _a;
|
|
9129
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("object:modified", e);
|
|
9130
|
+
};
|
|
9131
|
+
this.forwardObjectAdded = (e) => {
|
|
9132
|
+
var _a;
|
|
9133
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("object:added", e);
|
|
9134
|
+
};
|
|
9135
|
+
this.forwardObjectRemoved = (e) => {
|
|
9136
|
+
var _a;
|
|
9137
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("object:removed", e);
|
|
9138
|
+
};
|
|
9139
|
+
this.onToolActivated = () => {
|
|
9140
|
+
this.applyManagedPassVisibility();
|
|
9141
|
+
void this.applyManagedPassEffects(void 0, { render: true });
|
|
9142
|
+
};
|
|
9143
|
+
this.onToolSessionChanged = () => {
|
|
9144
|
+
this.applyManagedPassVisibility();
|
|
9145
|
+
void this.applyManagedPassEffects(void 0, { render: true });
|
|
9146
|
+
};
|
|
9147
|
+
this.onCanvasObjectChanged = () => {
|
|
9148
|
+
if (this.producerApplyInProgress) return;
|
|
9149
|
+
this.scheduleManagedPassVisibilityRefresh();
|
|
9150
|
+
};
|
|
8758
9151
|
if (el instanceof import_fabric5.Canvas) {
|
|
8759
9152
|
this.canvas = el;
|
|
8760
9153
|
} else {
|
|
@@ -8771,25 +9164,53 @@ var CanvasService = class {
|
|
|
8771
9164
|
this.setEventBus(options.eventBus);
|
|
8772
9165
|
}
|
|
8773
9166
|
}
|
|
9167
|
+
init(context) {
|
|
9168
|
+
if (this.context) {
|
|
9169
|
+
this.detachContextEvents(this.context.eventBus);
|
|
9170
|
+
}
|
|
9171
|
+
this.context = context;
|
|
9172
|
+
this.workbenchService = context.get(import_core11.WORKBENCH_SERVICE);
|
|
9173
|
+
this.toolSessionService = context.get(import_core11.TOOL_SESSION_SERVICE);
|
|
9174
|
+
this.setEventBus(context.eventBus);
|
|
9175
|
+
this.attachContextEvents(context.eventBus);
|
|
9176
|
+
}
|
|
9177
|
+
attachContextEvents(eventBus) {
|
|
9178
|
+
eventBus.on("tool:activated", this.onToolActivated);
|
|
9179
|
+
eventBus.on("tool:session:change", this.onToolSessionChanged);
|
|
9180
|
+
eventBus.on("object:added", this.onCanvasObjectChanged);
|
|
9181
|
+
eventBus.on("object:removed", this.onCanvasObjectChanged);
|
|
9182
|
+
}
|
|
9183
|
+
detachContextEvents(eventBus) {
|
|
9184
|
+
eventBus.off("tool:activated", this.onToolActivated);
|
|
9185
|
+
eventBus.off("tool:session:change", this.onToolSessionChanged);
|
|
9186
|
+
eventBus.off("object:added", this.onCanvasObjectChanged);
|
|
9187
|
+
eventBus.off("object:removed", this.onCanvasObjectChanged);
|
|
9188
|
+
}
|
|
8774
9189
|
setEventBus(eventBus) {
|
|
8775
9190
|
this.eventBus = eventBus;
|
|
8776
9191
|
this.setupEvents();
|
|
8777
9192
|
}
|
|
8778
9193
|
setupEvents() {
|
|
8779
|
-
if (
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
this.canvas.on("selection:
|
|
8783
|
-
this.canvas.on("
|
|
8784
|
-
this.canvas.on("
|
|
8785
|
-
this.canvas.on("object:
|
|
8786
|
-
this.
|
|
8787
|
-
this.canvas.on("object:removed", forward("object:removed"));
|
|
9194
|
+
if (this.canvasForwardersBound) return;
|
|
9195
|
+
this.canvas.on("selection:created", this.forwardSelectionCreated);
|
|
9196
|
+
this.canvas.on("selection:updated", this.forwardSelectionUpdated);
|
|
9197
|
+
this.canvas.on("selection:cleared", this.forwardSelectionCleared);
|
|
9198
|
+
this.canvas.on("object:modified", this.forwardObjectModified);
|
|
9199
|
+
this.canvas.on("object:added", this.forwardObjectAdded);
|
|
9200
|
+
this.canvas.on("object:removed", this.forwardObjectRemoved);
|
|
9201
|
+
this.canvasForwardersBound = true;
|
|
8788
9202
|
}
|
|
8789
9203
|
dispose() {
|
|
9204
|
+
if (this.context) {
|
|
9205
|
+
this.detachContextEvents(this.context.eventBus);
|
|
9206
|
+
}
|
|
8790
9207
|
this.renderProducers.clear();
|
|
8791
|
-
this.
|
|
8792
|
-
this.
|
|
9208
|
+
this.managedProducerPassIds.clear();
|
|
9209
|
+
this.managedPassMetas.clear();
|
|
9210
|
+
this.managedPassEffects = [];
|
|
9211
|
+
this.context = void 0;
|
|
9212
|
+
this.workbenchService = void 0;
|
|
9213
|
+
this.toolSessionService = void 0;
|
|
8793
9214
|
this.producerFlushRequested = false;
|
|
8794
9215
|
this.canvas.dispose();
|
|
8795
9216
|
}
|
|
@@ -8867,118 +9288,292 @@ var CanvasService = class {
|
|
|
8867
9288
|
return a.toolId.localeCompare(b.toolId);
|
|
8868
9289
|
});
|
|
8869
9290
|
}
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
9291
|
+
normalizePassSpecValue(spec) {
|
|
9292
|
+
const id = String(spec.id || "").trim();
|
|
9293
|
+
if (!id) return null;
|
|
9294
|
+
return {
|
|
9295
|
+
id,
|
|
9296
|
+
stack: Number.isFinite(spec.stack) ? Number(spec.stack) : 0,
|
|
9297
|
+
order: Number.isFinite(spec.order) ? Number(spec.order) : 0,
|
|
9298
|
+
replace: spec.replace !== false,
|
|
9299
|
+
visibility: spec.visibility,
|
|
9300
|
+
effects: Array.isArray(spec.effects) ? [...spec.effects] : [],
|
|
9301
|
+
objects: Array.isArray(spec.objects) ? [...spec.objects] : []
|
|
9302
|
+
};
|
|
9303
|
+
}
|
|
9304
|
+
normalizeClipPathEffectSpec(effect, passId, index) {
|
|
9305
|
+
if (!effect || effect.type !== "clipPath") return null;
|
|
9306
|
+
const source = effect.source;
|
|
9307
|
+
if (!source || typeof source !== "object") return null;
|
|
9308
|
+
const sourceId = String(source.id || "").trim();
|
|
9309
|
+
if (!sourceId) return null;
|
|
9310
|
+
const targetPassIds = Array.isArray(effect.targetPassIds) ? effect.targetPassIds.map((item) => String(item || "").trim()).filter((item) => item.length > 0) : [];
|
|
9311
|
+
if (!targetPassIds.length) return null;
|
|
9312
|
+
const customId = String(effect.id || "").trim();
|
|
9313
|
+
const key = customId || `${passId}.effect.clipPath.${index}`;
|
|
9314
|
+
return {
|
|
9315
|
+
type: "clipPath",
|
|
9316
|
+
key,
|
|
9317
|
+
visibility: effect.visibility,
|
|
9318
|
+
source: {
|
|
9319
|
+
...source,
|
|
9320
|
+
id: sourceId
|
|
9321
|
+
},
|
|
9322
|
+
targetPassIds
|
|
9323
|
+
};
|
|
9324
|
+
}
|
|
9325
|
+
mergePassSpec(map, rawSpec, producerId) {
|
|
9326
|
+
const normalized = this.normalizePassSpecValue(rawSpec);
|
|
9327
|
+
if (!normalized) return;
|
|
9328
|
+
const existing = map.get(normalized.id);
|
|
9329
|
+
if (!existing) {
|
|
9330
|
+
map.set(normalized.id, normalized);
|
|
9331
|
+
return;
|
|
9332
|
+
}
|
|
9333
|
+
existing.objects.push(...normalized.objects);
|
|
9334
|
+
existing.replace = existing.replace || normalized.replace;
|
|
9335
|
+
existing.stack = normalized.stack;
|
|
9336
|
+
existing.order = normalized.order;
|
|
9337
|
+
if (normalized.visibility !== void 0) {
|
|
9338
|
+
existing.visibility = normalized.visibility;
|
|
9339
|
+
}
|
|
9340
|
+
existing.effects.push(...normalized.effects);
|
|
9341
|
+
if (normalized.objects.length === 0 && normalized.effects.length === 0) {
|
|
9342
|
+
console.debug(
|
|
9343
|
+
`[CanvasService] pass "${normalized.id}" from producer "${producerId}" updated ordering/visibility only.`
|
|
9344
|
+
);
|
|
9345
|
+
}
|
|
9346
|
+
}
|
|
9347
|
+
comparePassMeta(a, b) {
|
|
9348
|
+
if (a.stack !== b.stack) return a.stack - b.stack;
|
|
9349
|
+
if (a.order !== b.order) return a.order - b.order;
|
|
9350
|
+
return a.id.localeCompare(b.id);
|
|
9351
|
+
}
|
|
9352
|
+
getPassObjectOrder(obj) {
|
|
9353
|
+
var _a;
|
|
9354
|
+
const raw = Number((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passOrder);
|
|
9355
|
+
return Number.isFinite(raw) ? raw : Number.MAX_SAFE_INTEGER;
|
|
9356
|
+
}
|
|
9357
|
+
getPassCanvasObjects(passId) {
|
|
9358
|
+
const all = this.canvas.getObjects();
|
|
9359
|
+
return all.filter((obj) => {
|
|
9360
|
+
var _a;
|
|
9361
|
+
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passId) === passId;
|
|
9362
|
+
}).sort((a, b) => {
|
|
9363
|
+
const orderA = this.getPassObjectOrder(a);
|
|
9364
|
+
const orderB = this.getPassObjectOrder(b);
|
|
9365
|
+
if (orderA !== orderB) return orderA - orderB;
|
|
9366
|
+
return all.indexOf(a) - all.indexOf(b);
|
|
9367
|
+
});
|
|
9368
|
+
}
|
|
9369
|
+
getPassObjects(passId) {
|
|
9370
|
+
return this.getPassCanvasObjects(passId);
|
|
9371
|
+
}
|
|
9372
|
+
getRootLayerObjects(layerId) {
|
|
9373
|
+
return this.getPassCanvasObjects(layerId);
|
|
9374
|
+
}
|
|
9375
|
+
isManagedPassObject(obj) {
|
|
9376
|
+
var _a;
|
|
9377
|
+
const passId = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passId;
|
|
9378
|
+
return typeof passId === "string" && this.managedPassMetas.has(passId);
|
|
9379
|
+
}
|
|
9380
|
+
syncManagedPassStacking(passes) {
|
|
9381
|
+
const orderedPasses = [...passes].sort((a, b) => this.comparePassMeta(a, b));
|
|
9382
|
+
if (!orderedPasses.length) return;
|
|
9383
|
+
const canvasObjects = this.canvas.getObjects();
|
|
9384
|
+
const managedObjects = canvasObjects.filter(
|
|
9385
|
+
(obj) => this.isManagedPassObject(obj)
|
|
9386
|
+
);
|
|
9387
|
+
if (!managedObjects.length) return;
|
|
9388
|
+
const firstManagedIndex = managedObjects.map((obj) => canvasObjects.indexOf(obj)).filter((index) => index >= 0).reduce((min, value) => Math.min(min, value), Number.MAX_SAFE_INTEGER);
|
|
9389
|
+
let targetIndex = Number.isFinite(firstManagedIndex) ? firstManagedIndex : 0;
|
|
9390
|
+
orderedPasses.forEach((meta) => {
|
|
9391
|
+
const objects = this.getPassCanvasObjects(meta.id);
|
|
9392
|
+
objects.forEach((obj) => {
|
|
9393
|
+
this.moveObjectInCanvas(obj, targetIndex);
|
|
9394
|
+
targetIndex += 1;
|
|
9395
|
+
});
|
|
9396
|
+
});
|
|
9397
|
+
}
|
|
9398
|
+
getPassRuntimeState() {
|
|
9399
|
+
const state = /* @__PURE__ */ new Map();
|
|
9400
|
+
const ensure = (passId) => {
|
|
9401
|
+
const id = String(passId || "").trim();
|
|
9402
|
+
if (!id) return { exists: false, objectCount: 0 };
|
|
9403
|
+
let item = state.get(id);
|
|
9404
|
+
if (!item) {
|
|
9405
|
+
item = { exists: false, objectCount: 0 };
|
|
9406
|
+
state.set(id, item);
|
|
9407
|
+
}
|
|
9408
|
+
return item;
|
|
9409
|
+
};
|
|
9410
|
+
this.canvas.getObjects().forEach((obj) => {
|
|
9411
|
+
var _a;
|
|
9412
|
+
const passId = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passId;
|
|
9413
|
+
if (typeof passId === "string") {
|
|
9414
|
+
const item = ensure(passId);
|
|
9415
|
+
item.exists = true;
|
|
9416
|
+
item.objectCount += 1;
|
|
9417
|
+
}
|
|
9418
|
+
});
|
|
9419
|
+
this.managedPassMetas.forEach((meta) => {
|
|
9420
|
+
const item = ensure(meta.id);
|
|
9421
|
+
item.exists = true;
|
|
9422
|
+
});
|
|
9423
|
+
return state;
|
|
9424
|
+
}
|
|
9425
|
+
isSessionActive(toolId) {
|
|
9426
|
+
if (!this.toolSessionService) return false;
|
|
9427
|
+
return this.toolSessionService.getState(toolId).status === "active";
|
|
9428
|
+
}
|
|
9429
|
+
hasAnyActiveSession() {
|
|
9430
|
+
var _a, _b;
|
|
9431
|
+
return (_b = (_a = this.toolSessionService) == null ? void 0 : _a.hasAnyActiveSession()) != null ? _b : false;
|
|
9432
|
+
}
|
|
9433
|
+
buildVisibilityEvalContext(layers) {
|
|
9434
|
+
var _a, _b;
|
|
9435
|
+
return {
|
|
9436
|
+
activeToolId: (_b = (_a = this.workbenchService) == null ? void 0 : _a.activeToolId) != null ? _b : null,
|
|
9437
|
+
isSessionActive: (toolId) => this.isSessionActive(toolId),
|
|
9438
|
+
hasAnyActiveSession: () => this.hasAnyActiveSession(),
|
|
9439
|
+
layers
|
|
9440
|
+
};
|
|
9441
|
+
}
|
|
9442
|
+
applyManagedPassVisibility(options = {}) {
|
|
9443
|
+
if (!this.managedPassMetas.size) return false;
|
|
9444
|
+
const layers = this.getPassRuntimeState();
|
|
9445
|
+
const context = this.buildVisibilityEvalContext(layers);
|
|
9446
|
+
let changed = false;
|
|
9447
|
+
this.managedPassMetas.forEach((meta) => {
|
|
9448
|
+
const visible = evaluateVisibilityExpr(meta.visibility, context);
|
|
9449
|
+
changed = this.setPassVisibility(meta.id, visible) || changed;
|
|
9450
|
+
});
|
|
9451
|
+
if (changed && options.render !== false) {
|
|
9452
|
+
this.requestRenderAll();
|
|
9453
|
+
}
|
|
9454
|
+
return changed;
|
|
9455
|
+
}
|
|
9456
|
+
scheduleManagedPassVisibilityRefresh() {
|
|
9457
|
+
if (this.visibilityRefreshScheduled) return;
|
|
9458
|
+
this.visibilityRefreshScheduled = true;
|
|
9459
|
+
void Promise.resolve().then(() => {
|
|
9460
|
+
this.visibilityRefreshScheduled = false;
|
|
9461
|
+
this.applyManagedPassVisibility();
|
|
8877
9462
|
});
|
|
8878
9463
|
}
|
|
8879
9464
|
async collectAndApplyProducerSpecs() {
|
|
8880
|
-
const
|
|
8881
|
-
const rootLayerSpecs = /* @__PURE__ */ new Map();
|
|
8882
|
-
const replaceLayerIds = /* @__PURE__ */ new Set();
|
|
8883
|
-
const replaceRootLayerIds = /* @__PURE__ */ new Set();
|
|
9465
|
+
const passes = /* @__PURE__ */ new Map();
|
|
8884
9466
|
const entries = this.sortedRenderProducerEntries();
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
if (layerId) replaceRootLayerIds.add(layerId);
|
|
8899
|
-
});
|
|
9467
|
+
this.producerApplyInProgress = true;
|
|
9468
|
+
try {
|
|
9469
|
+
for (const entry of entries) {
|
|
9470
|
+
try {
|
|
9471
|
+
const result = await entry.producer();
|
|
9472
|
+
if (!result) continue;
|
|
9473
|
+
const specs = Array.isArray(result.passes) ? result.passes : [];
|
|
9474
|
+
specs.forEach((spec) => this.mergePassSpec(passes, spec, entry.toolId));
|
|
9475
|
+
} catch (error) {
|
|
9476
|
+
console.error(
|
|
9477
|
+
`[CanvasService] render producer "${entry.toolId}" failed.`,
|
|
9478
|
+
error
|
|
9479
|
+
);
|
|
8900
9480
|
}
|
|
8901
|
-
} catch (error) {
|
|
8902
|
-
console.error(
|
|
8903
|
-
`[CanvasService] render producer "${entry.toolId}" failed.`,
|
|
8904
|
-
error
|
|
8905
|
-
);
|
|
8906
9481
|
}
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
9482
|
+
const nextPassIds = /* @__PURE__ */ new Set();
|
|
9483
|
+
const nextManagedPassMetas = /* @__PURE__ */ new Map();
|
|
9484
|
+
const nextEffects = [];
|
|
9485
|
+
for (const pass of passes.values()) {
|
|
9486
|
+
nextPassIds.add(pass.id);
|
|
9487
|
+
nextManagedPassMetas.set(pass.id, {
|
|
9488
|
+
id: pass.id,
|
|
9489
|
+
stack: pass.stack,
|
|
9490
|
+
order: pass.order,
|
|
9491
|
+
visibility: pass.visibility
|
|
9492
|
+
});
|
|
9493
|
+
await this.applyObjectSpecsToPass(pass.id, pass.objects, {
|
|
9494
|
+
render: false,
|
|
9495
|
+
replace: pass.replace
|
|
9496
|
+
});
|
|
9497
|
+
pass.effects.forEach((effect, index) => {
|
|
9498
|
+
const normalized = this.normalizeClipPathEffectSpec(
|
|
9499
|
+
effect,
|
|
9500
|
+
pass.id,
|
|
9501
|
+
index
|
|
9502
|
+
);
|
|
9503
|
+
if (!normalized) return;
|
|
9504
|
+
nextEffects.push(normalized);
|
|
9505
|
+
});
|
|
8916
9506
|
}
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
await this.applyObjectSpecsToContainer(layer, [], { render: false });
|
|
8924
|
-
}
|
|
8925
|
-
for (const [layerId, specs] of rootLayerSpecs.entries()) {
|
|
8926
|
-
if (replaceRootLayerIds.has(layerId)) {
|
|
8927
|
-
const existing = this.getRootLayerObjects(layerId);
|
|
8928
|
-
existing.forEach((obj) => this.canvas.remove(obj));
|
|
9507
|
+
for (const passId of this.managedProducerPassIds) {
|
|
9508
|
+
if (nextPassIds.has(passId)) continue;
|
|
9509
|
+
await this.applyObjectSpecsToPass(passId, [], {
|
|
9510
|
+
render: false,
|
|
9511
|
+
replace: true
|
|
9512
|
+
});
|
|
8929
9513
|
}
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
await this.
|
|
9514
|
+
this.managedProducerPassIds = nextPassIds;
|
|
9515
|
+
this.managedPassMetas = nextManagedPassMetas;
|
|
9516
|
+
this.managedPassEffects = nextEffects;
|
|
9517
|
+
this.syncManagedPassStacking(Array.from(nextManagedPassMetas.values()));
|
|
9518
|
+
await this.applyManagedPassEffects(nextEffects, { render: false });
|
|
9519
|
+
this.applyManagedPassVisibility({ render: false });
|
|
9520
|
+
} finally {
|
|
9521
|
+
this.producerApplyInProgress = false;
|
|
8935
9522
|
}
|
|
8936
|
-
this.managedProducerLayerIds = nextLayerIds;
|
|
8937
|
-
this.managedProducerRootLayerIds = nextRootLayerIds;
|
|
8938
9523
|
this.requestRenderAll();
|
|
8939
9524
|
}
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
8952
|
-
|
|
8953
|
-
createLayer(id, options = {}) {
|
|
8954
|
-
let layer = this.getLayer(id);
|
|
8955
|
-
if (!layer) {
|
|
8956
|
-
const defaultOptions = {
|
|
8957
|
-
selectable: false,
|
|
8958
|
-
evented: false,
|
|
8959
|
-
...options,
|
|
8960
|
-
data: { ...options.data, id }
|
|
8961
|
-
};
|
|
8962
|
-
layer = new import_fabric5.Group([], defaultOptions);
|
|
8963
|
-
this.canvas.add(layer);
|
|
8964
|
-
}
|
|
8965
|
-
return layer;
|
|
8966
|
-
}
|
|
8967
|
-
/**
|
|
8968
|
-
* Find an object by ID, optionally within a specific layer.
|
|
8969
|
-
*/
|
|
8970
|
-
getObject(id, layerId) {
|
|
8971
|
-
if (layerId) {
|
|
8972
|
-
const layer = this.getLayer(layerId);
|
|
8973
|
-
if (!layer) return void 0;
|
|
8974
|
-
return layer.getObjects().find((obj) => {
|
|
8975
|
-
var _a;
|
|
8976
|
-
return ((_a = obj.data) == null ? void 0 : _a.id) === id;
|
|
9525
|
+
async applyManagedPassEffects(effects = this.managedPassEffects, options = {}) {
|
|
9526
|
+
const effectTargetMap = /* @__PURE__ */ new Map();
|
|
9527
|
+
const layers = this.getPassRuntimeState();
|
|
9528
|
+
const visibilityContext = this.buildVisibilityEvalContext(layers);
|
|
9529
|
+
for (const effect of effects) {
|
|
9530
|
+
if (effect.type !== "clipPath") continue;
|
|
9531
|
+
if (!evaluateVisibilityExpr(effect.visibility, visibilityContext)) {
|
|
9532
|
+
continue;
|
|
9533
|
+
}
|
|
9534
|
+
effect.targetPassIds.forEach((targetPassId) => {
|
|
9535
|
+
this.getPassCanvasObjects(targetPassId).forEach((obj) => {
|
|
9536
|
+
effectTargetMap.set(obj, effect);
|
|
9537
|
+
});
|
|
8977
9538
|
});
|
|
8978
9539
|
}
|
|
9540
|
+
const managedObjects = this.canvas.getObjects().filter(
|
|
9541
|
+
(obj) => this.isManagedPassObject(obj)
|
|
9542
|
+
);
|
|
9543
|
+
const effectTemplateCache = /* @__PURE__ */ new Map();
|
|
9544
|
+
for (const obj of managedObjects) {
|
|
9545
|
+
const targetEffect = effectTargetMap.get(obj);
|
|
9546
|
+
if (!targetEffect) {
|
|
9547
|
+
this.clearClipPathEffectFromObject(obj);
|
|
9548
|
+
continue;
|
|
9549
|
+
}
|
|
9550
|
+
let template = effectTemplateCache.get(targetEffect.key);
|
|
9551
|
+
if (template === void 0) {
|
|
9552
|
+
template = await this.createClipPathTemplate(targetEffect);
|
|
9553
|
+
effectTemplateCache.set(targetEffect.key, template);
|
|
9554
|
+
}
|
|
9555
|
+
if (!template) {
|
|
9556
|
+
this.clearClipPathEffectFromObject(obj);
|
|
9557
|
+
continue;
|
|
9558
|
+
}
|
|
9559
|
+
await this.applyClipPathEffectToObject(
|
|
9560
|
+
obj,
|
|
9561
|
+
template,
|
|
9562
|
+
targetEffect.key
|
|
9563
|
+
);
|
|
9564
|
+
}
|
|
9565
|
+
if (options.render !== false) {
|
|
9566
|
+
this.requestRenderAll();
|
|
9567
|
+
}
|
|
9568
|
+
}
|
|
9569
|
+
getObject(id, passId) {
|
|
9570
|
+
const normalizedId = String(id || "").trim();
|
|
9571
|
+
if (!normalizedId) return void 0;
|
|
8979
9572
|
return this.canvas.getObjects().find((obj) => {
|
|
8980
|
-
var _a;
|
|
8981
|
-
|
|
9573
|
+
var _a, _b;
|
|
9574
|
+
if (((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id) !== normalizedId) return false;
|
|
9575
|
+
if (!passId) return true;
|
|
9576
|
+
return ((_b = obj == null ? void 0 : obj.data) == null ? void 0 : _b.passId) === passId;
|
|
8982
9577
|
});
|
|
8983
9578
|
}
|
|
8984
9579
|
requestRenderAll() {
|
|
@@ -9164,114 +9759,187 @@ var CanvasService = class {
|
|
|
9164
9759
|
next.top = objectTop + objectHeight * this.originFactor(originY);
|
|
9165
9760
|
return next;
|
|
9166
9761
|
}
|
|
9167
|
-
|
|
9168
|
-
const
|
|
9169
|
-
|
|
9170
|
-
layer.set({ visible });
|
|
9171
|
-
}
|
|
9172
|
-
const objects = this.getRootLayerObjects(layerId);
|
|
9762
|
+
setPassVisibility(passId, visible) {
|
|
9763
|
+
const objects = this.getPassCanvasObjects(passId);
|
|
9764
|
+
let changed = false;
|
|
9173
9765
|
objects.forEach((obj) => {
|
|
9174
9766
|
var _a, _b;
|
|
9767
|
+
if (obj.visible === visible) return;
|
|
9175
9768
|
(_a = obj.set) == null ? void 0 : _a.call(obj, { visible });
|
|
9176
9769
|
(_b = obj.setCoords) == null ? void 0 : _b.call(obj);
|
|
9770
|
+
changed = true;
|
|
9177
9771
|
});
|
|
9772
|
+
return changed;
|
|
9178
9773
|
}
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
if (layer) {
|
|
9182
|
-
this.canvas.bringObjectToFront(layer);
|
|
9183
|
-
}
|
|
9184
|
-
const objects = this.getRootLayerObjects(layerId);
|
|
9185
|
-
objects.forEach((obj) => this.canvas.bringObjectToFront(obj));
|
|
9774
|
+
setLayerVisibility(layerId, visible) {
|
|
9775
|
+
return this.setPassVisibility(layerId, visible);
|
|
9186
9776
|
}
|
|
9187
|
-
|
|
9188
|
-
const
|
|
9189
|
-
|
|
9777
|
+
bringPassToFront(passId) {
|
|
9778
|
+
const objects = this.getPassCanvasObjects(passId);
|
|
9779
|
+
objects.forEach((obj) => this.canvas.bringObjectToFront(obj));
|
|
9190
9780
|
}
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
await this.applyObjectSpecsToContainer(layer, objects, options);
|
|
9781
|
+
bringLayerToFront(layerId) {
|
|
9782
|
+
this.bringPassToFront(layerId);
|
|
9194
9783
|
}
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9784
|
+
async applyPassSpec(spec, options = {}) {
|
|
9785
|
+
await this.applyObjectSpecsToPass(spec.id, spec.objects, {
|
|
9786
|
+
render: options.render,
|
|
9787
|
+
replace: spec.replace !== false
|
|
9199
9788
|
});
|
|
9200
9789
|
}
|
|
9201
|
-
async applyObjectSpecsToRootLayer(
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
var _a;
|
|
9206
|
-
const id = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id;
|
|
9207
|
-
if (typeof id === "string" && !desiredIds.has(id)) {
|
|
9208
|
-
this.canvas.remove(obj);
|
|
9209
|
-
}
|
|
9790
|
+
async applyObjectSpecsToRootLayer(passId, specs, options = {}) {
|
|
9791
|
+
await this.applyObjectSpecsToPass(passId, specs, {
|
|
9792
|
+
render: options.render,
|
|
9793
|
+
replace: true
|
|
9210
9794
|
});
|
|
9211
|
-
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9795
|
+
}
|
|
9796
|
+
normalizeObjectSpecs(specs) {
|
|
9797
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9798
|
+
const normalized = [];
|
|
9799
|
+
(specs || []).forEach((spec) => {
|
|
9800
|
+
const id = String((spec == null ? void 0 : spec.id) || "").trim();
|
|
9801
|
+
if (!id || seen.has(id)) return;
|
|
9802
|
+
seen.add(id);
|
|
9803
|
+
normalized.push({
|
|
9804
|
+
...spec,
|
|
9805
|
+
id
|
|
9806
|
+
});
|
|
9216
9807
|
});
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
if (!current) {
|
|
9226
|
-
const created = await this.createFabricObject(spec);
|
|
9227
|
-
if (!created) continue;
|
|
9228
|
-
this.patchFabricObject(created, spec, { layerId });
|
|
9229
|
-
this.canvas.add(created);
|
|
9230
|
-
byId.set(spec.id, created);
|
|
9231
|
-
continue;
|
|
9232
|
-
}
|
|
9233
|
-
this.patchFabricObject(current, spec, { layerId });
|
|
9808
|
+
return normalized;
|
|
9809
|
+
}
|
|
9810
|
+
async cloneFabricObject(source) {
|
|
9811
|
+
const clone = source.clone;
|
|
9812
|
+
if (typeof clone !== "function") return void 0;
|
|
9813
|
+
const result = clone.call(source);
|
|
9814
|
+
if (!result || typeof result.then !== "function") {
|
|
9815
|
+
return void 0;
|
|
9234
9816
|
}
|
|
9235
|
-
|
|
9236
|
-
|
|
9817
|
+
try {
|
|
9818
|
+
const copied = await result;
|
|
9819
|
+
return copied;
|
|
9820
|
+
} catch (e) {
|
|
9821
|
+
return void 0;
|
|
9237
9822
|
}
|
|
9238
9823
|
}
|
|
9239
|
-
async
|
|
9240
|
-
|
|
9241
|
-
const
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9824
|
+
async createClipPathTemplate(effect) {
|
|
9825
|
+
var _a, _b;
|
|
9826
|
+
const source = effect.source;
|
|
9827
|
+
const sourceId = String(source.id || "").trim();
|
|
9828
|
+
if (!sourceId) return null;
|
|
9829
|
+
const template = await this.createFabricObject({
|
|
9830
|
+
...source,
|
|
9831
|
+
id: sourceId,
|
|
9832
|
+
data: {
|
|
9833
|
+
...source.data || {},
|
|
9834
|
+
id: sourceId,
|
|
9835
|
+
type: "clip-path-effect-template",
|
|
9836
|
+
effectKey: effect.key
|
|
9837
|
+
},
|
|
9838
|
+
props: {
|
|
9839
|
+
...source.props || {},
|
|
9840
|
+
selectable: false,
|
|
9841
|
+
evented: false,
|
|
9842
|
+
excludeFromExport: true
|
|
9247
9843
|
}
|
|
9248
9844
|
});
|
|
9845
|
+
if (!template) return null;
|
|
9846
|
+
(_a = template.set) == null ? void 0 : _a.call(template, {
|
|
9847
|
+
selectable: false,
|
|
9848
|
+
evented: false,
|
|
9849
|
+
excludeFromExport: true,
|
|
9850
|
+
absolutePositioned: true
|
|
9851
|
+
});
|
|
9852
|
+
(_b = template.setCoords) == null ? void 0 : _b.call(template);
|
|
9853
|
+
return template;
|
|
9854
|
+
}
|
|
9855
|
+
isClipPathEffectManaged(target) {
|
|
9856
|
+
return typeof (target == null ? void 0 : target.__pooderEffectClipKey) === "string";
|
|
9857
|
+
}
|
|
9858
|
+
clearClipPathEffectFromObject(target) {
|
|
9859
|
+
var _a, _b;
|
|
9860
|
+
if (!target) return;
|
|
9861
|
+
if (!this.isClipPathEffectManaged(target)) return;
|
|
9862
|
+
(_a = target.set) == null ? void 0 : _a.call(target, { clipPath: void 0 });
|
|
9863
|
+
(_b = target.setCoords) == null ? void 0 : _b.call(target);
|
|
9864
|
+
delete target.__pooderEffectClipKey;
|
|
9865
|
+
}
|
|
9866
|
+
async applyClipPathEffectToObject(target, clipTemplate, effectKey) {
|
|
9867
|
+
var _a, _b, _c, _d;
|
|
9868
|
+
if (!target) return;
|
|
9869
|
+
const clipPath = await this.cloneFabricObject(clipTemplate);
|
|
9870
|
+
if (!clipPath) {
|
|
9871
|
+
this.clearClipPathEffectFromObject(target);
|
|
9872
|
+
return;
|
|
9873
|
+
}
|
|
9874
|
+
(_a = clipPath.set) == null ? void 0 : _a.call(clipPath, {
|
|
9875
|
+
selectable: false,
|
|
9876
|
+
evented: false,
|
|
9877
|
+
excludeFromExport: true,
|
|
9878
|
+
absolutePositioned: true
|
|
9879
|
+
});
|
|
9880
|
+
(_b = clipPath.setCoords) == null ? void 0 : _b.call(clipPath);
|
|
9881
|
+
(_c = target.set) == null ? void 0 : _c.call(target, { clipPath });
|
|
9882
|
+
(_d = target.setCoords) == null ? void 0 : _d.call(target);
|
|
9883
|
+
target.__pooderEffectClipKey = effectKey;
|
|
9884
|
+
}
|
|
9885
|
+
async applyObjectSpecsToPass(passId, specs, options = {}) {
|
|
9886
|
+
const normalizedPassId = String(passId || "").trim();
|
|
9887
|
+
if (!normalizedPassId) return;
|
|
9888
|
+
const replace = options.replace !== false;
|
|
9889
|
+
const normalizedSpecs = this.normalizeObjectSpecs(specs);
|
|
9890
|
+
const desiredIds = new Set(normalizedSpecs.map((s) => s.id));
|
|
9891
|
+
const existing = this.getPassCanvasObjects(normalizedPassId);
|
|
9892
|
+
if (replace) {
|
|
9893
|
+
existing.forEach((obj) => {
|
|
9894
|
+
var _a;
|
|
9895
|
+
const id = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id;
|
|
9896
|
+
if (typeof id === "string" && !desiredIds.has(id)) {
|
|
9897
|
+
this.canvas.remove(obj);
|
|
9898
|
+
}
|
|
9899
|
+
});
|
|
9900
|
+
}
|
|
9249
9901
|
const byId = /* @__PURE__ */ new Map();
|
|
9250
|
-
|
|
9902
|
+
this.getPassCanvasObjects(normalizedPassId).forEach((obj) => {
|
|
9251
9903
|
var _a;
|
|
9252
9904
|
const id = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id;
|
|
9253
9905
|
if (typeof id === "string") byId.set(id, obj);
|
|
9254
9906
|
});
|
|
9255
|
-
for (let index = 0; index <
|
|
9256
|
-
const spec =
|
|
9907
|
+
for (let index = 0; index < normalizedSpecs.length; index += 1) {
|
|
9908
|
+
const spec = normalizedSpecs[index];
|
|
9257
9909
|
let current = byId.get(spec.id);
|
|
9258
|
-
if (
|
|
9259
|
-
|
|
9910
|
+
if (spec.type === "path") {
|
|
9911
|
+
const nextPathData = this.readPathDataFromSpec(spec);
|
|
9912
|
+
if (!nextPathData || !nextPathData.trim()) {
|
|
9913
|
+
if (current) {
|
|
9914
|
+
this.canvas.remove(current);
|
|
9915
|
+
byId.delete(spec.id);
|
|
9916
|
+
}
|
|
9917
|
+
continue;
|
|
9918
|
+
}
|
|
9919
|
+
}
|
|
9920
|
+
if (current && this.shouldRecreateObject(current, spec)) {
|
|
9921
|
+
this.canvas.remove(current);
|
|
9260
9922
|
byId.delete(spec.id);
|
|
9261
9923
|
current = void 0;
|
|
9262
9924
|
}
|
|
9263
9925
|
if (!current) {
|
|
9264
9926
|
const created = await this.createFabricObject(spec);
|
|
9265
9927
|
if (!created) continue;
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9928
|
+
this.patchFabricObject(created, spec, {
|
|
9929
|
+
passId: normalizedPassId,
|
|
9930
|
+
layerId: normalizedPassId,
|
|
9931
|
+
passOrder: index
|
|
9932
|
+
});
|
|
9933
|
+
this.canvas.add(created);
|
|
9934
|
+
byId.set(spec.id, created);
|
|
9935
|
+
continue;
|
|
9271
9936
|
}
|
|
9272
|
-
this.
|
|
9937
|
+
this.patchFabricObject(current, spec, {
|
|
9938
|
+
passId: normalizedPassId,
|
|
9939
|
+
layerId: normalizedPassId,
|
|
9940
|
+
passOrder: index
|
|
9941
|
+
});
|
|
9273
9942
|
}
|
|
9274
|
-
container.dirty = true;
|
|
9275
9943
|
if (options.render !== false) {
|
|
9276
9944
|
this.requestRenderAll();
|
|
9277
9945
|
}
|
|
@@ -9283,10 +9951,56 @@ var CanvasService = class {
|
|
|
9283
9951
|
...extraData || {},
|
|
9284
9952
|
id: spec.id
|
|
9285
9953
|
};
|
|
9954
|
+
nextData.__renderSourceKey = this.getSpecRenderSourceKey(spec);
|
|
9286
9955
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9287
9956
|
obj.set({ ...props, data: nextData });
|
|
9288
9957
|
obj.setCoords();
|
|
9289
9958
|
}
|
|
9959
|
+
readPathDataFromSpec(spec) {
|
|
9960
|
+
var _a, _b;
|
|
9961
|
+
if (spec.type !== "path") return void 0;
|
|
9962
|
+
const raw = ((_a = spec.props) == null ? void 0 : _a.path) || ((_b = spec.props) == null ? void 0 : _b.pathData);
|
|
9963
|
+
if (typeof raw !== "string") return void 0;
|
|
9964
|
+
return raw;
|
|
9965
|
+
}
|
|
9966
|
+
hashText(value) {
|
|
9967
|
+
let hash = 2166136261;
|
|
9968
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
9969
|
+
hash ^= value.charCodeAt(i);
|
|
9970
|
+
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
9971
|
+
}
|
|
9972
|
+
return (hash >>> 0).toString(16);
|
|
9973
|
+
}
|
|
9974
|
+
getSpecRenderSourceKey(spec) {
|
|
9975
|
+
var _a, _b;
|
|
9976
|
+
switch (spec.type) {
|
|
9977
|
+
case "path": {
|
|
9978
|
+
const pathData = this.readPathDataFromSpec(spec) || "";
|
|
9979
|
+
return `path:${this.hashText(pathData)}`;
|
|
9980
|
+
}
|
|
9981
|
+
case "image":
|
|
9982
|
+
return `image:${String(spec.src || "")}`;
|
|
9983
|
+
case "text":
|
|
9984
|
+
return `text:${String((_b = (_a = spec.props) == null ? void 0 : _a.text) != null ? _b : "")}`;
|
|
9985
|
+
case "rect":
|
|
9986
|
+
return "rect";
|
|
9987
|
+
default:
|
|
9988
|
+
return String(spec.type || "");
|
|
9989
|
+
}
|
|
9990
|
+
}
|
|
9991
|
+
shouldRecreateObject(current, spec) {
|
|
9992
|
+
var _a;
|
|
9993
|
+
if (!current) return true;
|
|
9994
|
+
const currentType = String((current == null ? void 0 : current.type) || "").toLowerCase();
|
|
9995
|
+
if (currentType !== spec.type) return true;
|
|
9996
|
+
const expectedKey = this.getSpecRenderSourceKey(spec);
|
|
9997
|
+
const currentKey = String(((_a = current == null ? void 0 : current.data) == null ? void 0 : _a.__renderSourceKey) || "");
|
|
9998
|
+
if (currentKey && expectedKey && currentKey !== expectedKey) return true;
|
|
9999
|
+
if (spec.type === "image" && spec.src && current.getSrc) {
|
|
10000
|
+
return current.getSrc() !== spec.src;
|
|
10001
|
+
}
|
|
10002
|
+
return false;
|
|
10003
|
+
}
|
|
9290
10004
|
resolveFabricProps(spec, props) {
|
|
9291
10005
|
const space = spec.space || "scene";
|
|
9292
10006
|
const next = this.resolveLayoutProps(spec, props);
|
|
@@ -9310,26 +10024,26 @@ var CanvasService = class {
|
|
|
9310
10024
|
next.scaleY = rawScaleY * sceneScale;
|
|
9311
10025
|
return next;
|
|
9312
10026
|
}
|
|
9313
|
-
|
|
10027
|
+
moveObjectInCanvas(obj, index) {
|
|
9314
10028
|
if (!obj) return;
|
|
9315
|
-
const moveObjectTo =
|
|
10029
|
+
const moveObjectTo = this.canvas.moveObjectTo;
|
|
9316
10030
|
if (typeof moveObjectTo === "function") {
|
|
9317
|
-
moveObjectTo.call(
|
|
10031
|
+
moveObjectTo.call(this.canvas, obj, index);
|
|
9318
10032
|
return;
|
|
9319
10033
|
}
|
|
9320
|
-
const list =
|
|
10034
|
+
const list = this.canvas._objects;
|
|
9321
10035
|
if (!Array.isArray(list)) return;
|
|
9322
10036
|
const from = list.indexOf(obj);
|
|
9323
10037
|
if (from < 0 || from === index) return;
|
|
9324
10038
|
list.splice(from, 1);
|
|
9325
10039
|
const target = Math.max(0, Math.min(index, list.length));
|
|
9326
10040
|
list.splice(target, 0, obj);
|
|
9327
|
-
if (typeof
|
|
9328
|
-
|
|
10041
|
+
if (typeof this.canvas._onStackOrderChanged === "function") {
|
|
10042
|
+
this.canvas._onStackOrderChanged();
|
|
9329
10043
|
}
|
|
9330
10044
|
}
|
|
9331
10045
|
async createFabricObject(spec) {
|
|
9332
|
-
var _a, _b
|
|
10046
|
+
var _a, _b;
|
|
9333
10047
|
if (spec.type === "rect") {
|
|
9334
10048
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9335
10049
|
const rect = new import_fabric5.Rect({
|
|
@@ -9340,7 +10054,7 @@ var CanvasService = class {
|
|
|
9340
10054
|
return rect;
|
|
9341
10055
|
}
|
|
9342
10056
|
if (spec.type === "path") {
|
|
9343
|
-
const pathData =
|
|
10057
|
+
const pathData = this.readPathDataFromSpec(spec);
|
|
9344
10058
|
if (!pathData) return void 0;
|
|
9345
10059
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9346
10060
|
const path = new import_fabric5.Path(pathData, {
|
|
@@ -9362,7 +10076,7 @@ var CanvasService = class {
|
|
|
9362
10076
|
return image;
|
|
9363
10077
|
}
|
|
9364
10078
|
if (spec.type === "text") {
|
|
9365
|
-
const content = String((
|
|
10079
|
+
const content = String((_b = (_a = spec.props) == null ? void 0 : _a.text) != null ? _b : "");
|
|
9366
10080
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9367
10081
|
const text = new import_fabric5.Text(content, {
|
|
9368
10082
|
...props,
|
|
@@ -9385,8 +10099,8 @@ var CanvasService = class {
|
|
|
9385
10099
|
MirrorTool,
|
|
9386
10100
|
RulerTool,
|
|
9387
10101
|
SceneLayoutService,
|
|
9388
|
-
SceneVisibilityService,
|
|
9389
10102
|
SizeTool,
|
|
9390
10103
|
ViewportSystem,
|
|
9391
|
-
WhiteInkTool
|
|
10104
|
+
WhiteInkTool,
|
|
10105
|
+
evaluateVisibilityExpr
|
|
9392
10106
|
});
|