@pooder/kit 5.4.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.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 +931 -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 +1613 -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 +1011 -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 +54 -0
- package/.test-dist/src/units.js +30 -0
- package/.test-dist/tests/run.js +148 -0
- package/CHANGELOG.md +6 -0
- package/dist/index.d.mts +150 -62
- package/dist/index.d.ts +150 -62
- package/dist/index.js +2219 -1714
- package/dist/index.mjs +2226 -1718
- 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 +1169 -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 +2007 -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 +1286 -832
- package/src/services/ViewportSystem.ts +95 -95
- package/src/services/index.ts +4 -3
- package/src/services/renderSpec.ts +83 -53
- package/src/services/visibility.ts +78 -0
- package/src/units.ts +27 -27
- package/tests/run.ts +253 -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,962 +203,1386 @@ 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
|
-
|
|
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
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
const hit = validHits[idx];
|
|
1076
|
+
return { point: hit.point, location: hit };
|
|
1077
|
+
}
|
|
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
|
+
}))
|
|
1084
|
+
);
|
|
1085
|
+
const scoreB = scoreOutsideAbove(
|
|
1086
|
+
pointsB.map((p) => ({
|
|
1087
|
+
outsideAbove: !mainShape.contains(p.add(new import_paper.default.Point(0, -delta)))
|
|
1088
|
+
}))
|
|
1089
|
+
);
|
|
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
|
+
)
|
|
1134
|
+
);
|
|
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)
|
|
1158
|
+
);
|
|
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)
|
|
1163
|
+
);
|
|
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)
|
|
1168
|
+
);
|
|
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)
|
|
1173
|
+
);
|
|
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)
|
|
1178
|
+
);
|
|
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;
|
|
1186
|
+
}
|
|
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,
|
|
1232
|
+
width,
|
|
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;
|
|
1261
|
+
}
|
|
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);
|
|
1268
|
+
}
|
|
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
|
+
}
|
|
1171
1477
|
}
|
|
1172
|
-
const fixed = parseFloat(raw);
|
|
1173
|
-
return Number.isFinite(fixed) ? Math.max(0, fixed) : 0;
|
|
1174
1478
|
}
|
|
1175
|
-
return
|
|
1479
|
+
return mainShape;
|
|
1176
1480
|
}
|
|
1177
|
-
function
|
|
1178
|
-
const
|
|
1179
|
-
|
|
1180
|
-
);
|
|
1181
|
-
const minMm = Math.max(
|
|
1182
|
-
0.1,
|
|
1183
|
-
Number(configService.get("size.minMm", DEFAULT_SIZE_STATE.minMm))
|
|
1184
|
-
);
|
|
1185
|
-
const maxMm = Math.max(
|
|
1186
|
-
minMm,
|
|
1187
|
-
Number(configService.get("size.maxMm", DEFAULT_SIZE_STATE.maxMm))
|
|
1188
|
-
);
|
|
1189
|
-
const stepMm = Math.max(
|
|
1190
|
-
1e-3,
|
|
1191
|
-
Number(configService.get("size.stepMm", DEFAULT_SIZE_STATE.stepMm))
|
|
1192
|
-
);
|
|
1193
|
-
const actualWidthMm = sanitizeMmValue(
|
|
1194
|
-
parseLengthToMm(
|
|
1195
|
-
configService.get("size.actualWidthMm", DEFAULT_SIZE_STATE.actualWidthMm),
|
|
1196
|
-
"mm"
|
|
1197
|
-
),
|
|
1198
|
-
{ minMm, maxMm, stepMm }
|
|
1199
|
-
);
|
|
1200
|
-
const actualHeightMm = sanitizeMmValue(
|
|
1201
|
-
parseLengthToMm(
|
|
1202
|
-
configService.get(
|
|
1203
|
-
"size.actualHeightMm",
|
|
1204
|
-
DEFAULT_SIZE_STATE.actualHeightMm
|
|
1205
|
-
),
|
|
1206
|
-
"mm"
|
|
1207
|
-
),
|
|
1208
|
-
{ minMm, maxMm, stepMm }
|
|
1209
|
-
);
|
|
1210
|
-
const aspectRaw = Number(
|
|
1211
|
-
configService.get("size.aspectRatio", DEFAULT_SIZE_STATE.aspectRatio)
|
|
1212
|
-
);
|
|
1213
|
-
const aspectRatio = Number.isFinite(aspectRaw) && aspectRaw > 0 ? aspectRaw : actualWidthMm / Math.max(1e-3, actualHeightMm);
|
|
1214
|
-
const cutMarginMm = Math.max(
|
|
1215
|
-
0,
|
|
1216
|
-
parseLengthToMm(
|
|
1217
|
-
configService.get("size.cutMarginMm", DEFAULT_SIZE_STATE.cutMarginMm),
|
|
1218
|
-
"mm"
|
|
1219
|
-
)
|
|
1220
|
-
);
|
|
1221
|
-
const viewPadding = configService.get(
|
|
1222
|
-
"size.viewPadding",
|
|
1223
|
-
DEFAULT_SIZE_STATE.viewPadding
|
|
1481
|
+
function applySurfaceFeatures(shape, features, options) {
|
|
1482
|
+
const surfaceFeatures = features.filter(
|
|
1483
|
+
(f) => f.renderBehavior === "surface"
|
|
1224
1484
|
);
|
|
1225
|
-
return
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
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
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
return result;
|
|
1245
1509
|
}
|
|
1246
|
-
function
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
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;
|
|
1255
1520
|
}
|
|
1256
|
-
function
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
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
|
|
1531
|
+
);
|
|
1532
|
+
const pOffset = getPerimeterShape(offsetOptions);
|
|
1533
|
+
const shapeOffset = applySurfaceFeatures(
|
|
1534
|
+
pOffset,
|
|
1535
|
+
offsetOptions.features,
|
|
1536
|
+
offsetOptions
|
|
1537
|
+
);
|
|
1538
|
+
let bleedZone;
|
|
1539
|
+
if (offset > 0) {
|
|
1540
|
+
bleedZone = shapeOffset.subtract(shapeOriginal);
|
|
1541
|
+
} else {
|
|
1542
|
+
bleedZone = shapeOriginal.subtract(shapeOffset);
|
|
1266
1543
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
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
|
|
1270
1558
|
};
|
|
1559
|
+
shape.remove();
|
|
1560
|
+
return result;
|
|
1271
1561
|
}
|
|
1272
|
-
function
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
const
|
|
1277
|
-
const
|
|
1278
|
-
const
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
const paddingPx = resolvePaddingPx(
|
|
1283
|
-
size.viewPadding,
|
|
1284
|
-
canvasWidth,
|
|
1285
|
-
canvasHeight
|
|
1286
|
-
);
|
|
1287
|
-
canvasService.viewport.updateContainer(canvasWidth, canvasHeight);
|
|
1288
|
-
canvasService.viewport.setPadding(paddingPx);
|
|
1289
|
-
canvasService.viewport.updatePhysical(viewWidthMm, viewHeightMm);
|
|
1290
|
-
const layout = canvasService.viewport.layout;
|
|
1291
|
-
if (!Number.isFinite(layout.scale) || !Number.isFinite(layout.offsetX) || !Number.isFinite(layout.offsetY) || layout.scale <= 0) {
|
|
1292
|
-
return null;
|
|
1293
|
-
}
|
|
1294
|
-
const centerX = layout.offsetX + layout.width / 2;
|
|
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
|
|
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
|
|
1321
1572
|
};
|
|
1573
|
+
shape.remove();
|
|
1574
|
+
return result;
|
|
1322
1575
|
}
|
|
1323
|
-
function
|
|
1324
|
-
const
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
);
|
|
1328
|
-
const offset = (layout.cutRect.width - layout.trimRect.width) / 2;
|
|
1329
|
-
const sourceWidth = Number(configService.get("dieline.customSourceWidthPx", 0));
|
|
1330
|
-
const sourceHeight = Number(
|
|
1331
|
-
configService.get("dieline.customSourceHeightPx", 0)
|
|
1332
|
-
);
|
|
1333
|
-
const shapeStyle = normalizeShapeStyle(
|
|
1334
|
-
configService.get("dieline.shapeStyle", DEFAULT_DIELINE_SHAPE_STYLE)
|
|
1335
|
-
);
|
|
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
|
|
|
@@ -1371,6 +1605,7 @@ var ImageTool = class {
|
|
|
1371
1605
|
this.isImageSelectionActive = false;
|
|
1372
1606
|
this.focusedImageId = null;
|
|
1373
1607
|
this.renderSeq = 0;
|
|
1608
|
+
this.imageSpecs = [];
|
|
1374
1609
|
this.overlaySpecs = [];
|
|
1375
1610
|
this.onToolActivated = (event) => {
|
|
1376
1611
|
const before = this.isToolActive;
|
|
@@ -1478,9 +1713,34 @@ var ImageTool = class {
|
|
|
1478
1713
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
1479
1714
|
this.id,
|
|
1480
1715
|
() => ({
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1716
|
+
passes: [
|
|
1717
|
+
{
|
|
1718
|
+
id: IMAGE_OBJECT_LAYER_ID,
|
|
1719
|
+
stack: 500,
|
|
1720
|
+
order: 0,
|
|
1721
|
+
visibility: {
|
|
1722
|
+
op: "not",
|
|
1723
|
+
expr: {
|
|
1724
|
+
op: "sessionActive",
|
|
1725
|
+
toolId: "pooder.kit.white-ink"
|
|
1726
|
+
}
|
|
1727
|
+
},
|
|
1728
|
+
objects: this.imageSpecs
|
|
1729
|
+
},
|
|
1730
|
+
{
|
|
1731
|
+
id: IMAGE_OVERLAY_LAYER_ID,
|
|
1732
|
+
stack: 800,
|
|
1733
|
+
order: 0,
|
|
1734
|
+
visibility: {
|
|
1735
|
+
op: "not",
|
|
1736
|
+
expr: {
|
|
1737
|
+
op: "sessionActive",
|
|
1738
|
+
toolId: "pooder.kit.white-ink"
|
|
1739
|
+
}
|
|
1740
|
+
},
|
|
1741
|
+
objects: this.overlaySpecs
|
|
1742
|
+
}
|
|
1743
|
+
]
|
|
1484
1744
|
}),
|
|
1485
1745
|
{ priority: 300 }
|
|
1486
1746
|
);
|
|
@@ -1537,6 +1797,7 @@ var ImageTool = class {
|
|
|
1537
1797
|
this.cropShapeHatchPattern = void 0;
|
|
1538
1798
|
this.cropShapeHatchPatternColor = void 0;
|
|
1539
1799
|
this.cropShapeHatchPatternKey = void 0;
|
|
1800
|
+
this.imageSpecs = [];
|
|
1540
1801
|
this.overlaySpecs = [];
|
|
1541
1802
|
this.clearRenderedImages();
|
|
1542
1803
|
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
@@ -2012,9 +2273,7 @@ var ImageTool = class {
|
|
|
2012
2273
|
}
|
|
2013
2274
|
getOverlayObjects() {
|
|
2014
2275
|
if (!this.canvasService) return [];
|
|
2015
|
-
return this.canvasService.
|
|
2016
|
-
IMAGE_OVERLAY_LAYER_ID
|
|
2017
|
-
);
|
|
2276
|
+
return this.canvasService.getPassObjects(IMAGE_OVERLAY_LAYER_ID);
|
|
2018
2277
|
}
|
|
2019
2278
|
getImageObject(id) {
|
|
2020
2279
|
return this.getImageObjects().find((obj) => {
|
|
@@ -2024,9 +2283,9 @@ var ImageTool = class {
|
|
|
2024
2283
|
}
|
|
2025
2284
|
clearRenderedImages() {
|
|
2026
2285
|
if (!this.canvasService) return;
|
|
2027
|
-
|
|
2028
|
-
this.
|
|
2029
|
-
this.canvasService.
|
|
2286
|
+
this.imageSpecs = [];
|
|
2287
|
+
this.overlaySpecs = [];
|
|
2288
|
+
this.canvasService.requestRenderFromProducers();
|
|
2030
2289
|
}
|
|
2031
2290
|
purgeSourceSizeCacheForItem(item) {
|
|
2032
2291
|
if (!item) return;
|
|
@@ -2054,6 +2313,29 @@ var ImageTool = class {
|
|
|
2054
2313
|
}
|
|
2055
2314
|
return { width: 1, height: 1 };
|
|
2056
2315
|
}
|
|
2316
|
+
async ensureSourceSize(src) {
|
|
2317
|
+
if (!src) return null;
|
|
2318
|
+
const cached = this.sourceSizeBySrc.get(src);
|
|
2319
|
+
if (cached) return cached;
|
|
2320
|
+
try {
|
|
2321
|
+
const image = await import_fabric2.Image.fromURL(src, {
|
|
2322
|
+
crossOrigin: "anonymous"
|
|
2323
|
+
});
|
|
2324
|
+
const width = Number((image == null ? void 0 : image.width) || 0);
|
|
2325
|
+
const height = Number((image == null ? void 0 : image.height) || 0);
|
|
2326
|
+
if (width > 0 && height > 0) {
|
|
2327
|
+
const size = { width, height };
|
|
2328
|
+
this.sourceSizeBySrc.set(src, size);
|
|
2329
|
+
return size;
|
|
2330
|
+
}
|
|
2331
|
+
} catch (error) {
|
|
2332
|
+
this.debug("image:size:load-failed", {
|
|
2333
|
+
src,
|
|
2334
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2335
|
+
});
|
|
2336
|
+
}
|
|
2337
|
+
return null;
|
|
2338
|
+
}
|
|
2057
2339
|
getCoverScale(frame, size) {
|
|
2058
2340
|
const sw = Math.max(1, size.width);
|
|
2059
2341
|
const sh = Math.max(1, size.height);
|
|
@@ -2388,24 +2670,6 @@ var ImageTool = class {
|
|
|
2388
2670
|
opacity: render.opacity
|
|
2389
2671
|
};
|
|
2390
2672
|
}
|
|
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
2673
|
toSceneObjectScale(value) {
|
|
2410
2674
|
if (!this.canvasService) return value;
|
|
2411
2675
|
return value / this.canvasService.getSceneScale();
|
|
@@ -2416,104 +2680,27 @@ var ImageTool = class {
|
|
|
2416
2680
|
if (typeof obj.getSrc === "function") return obj.getSrc();
|
|
2417
2681
|
return (_a = obj == null ? void 0 : obj._originalElement) == null ? void 0 : _a.src;
|
|
2418
2682
|
}
|
|
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({
|
|
2683
|
+
async buildImageSpecs(items, frame) {
|
|
2684
|
+
const specs = [];
|
|
2685
|
+
for (const item of items) {
|
|
2686
|
+
const render = this.resolveRenderImageState(item);
|
|
2687
|
+
if (!render.src) continue;
|
|
2688
|
+
const ensured = await this.ensureSourceSize(render.src);
|
|
2689
|
+
const sourceSize = ensured || this.getSourceSize(render.src);
|
|
2690
|
+
const props = this.computeCanvasProps(render, sourceSize, frame);
|
|
2691
|
+
specs.push({
|
|
2692
|
+
id: item.id,
|
|
2693
|
+
type: "image",
|
|
2694
|
+
src: render.src,
|
|
2450
2695
|
data: {
|
|
2451
2696
|
id: item.id,
|
|
2452
2697
|
layerId: IMAGE_OBJECT_LAYER_ID,
|
|
2453
2698
|
type: "image-item"
|
|
2454
|
-
}
|
|
2699
|
+
},
|
|
2700
|
+
props
|
|
2455
2701
|
});
|
|
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
2702
|
}
|
|
2703
|
+
return specs;
|
|
2517
2704
|
}
|
|
2518
2705
|
buildOverlaySpecs(frame, sceneGeometry) {
|
|
2519
2706
|
const visible = this.isImageEditingVisible();
|
|
@@ -2677,7 +2864,7 @@ var ImageTool = class {
|
|
|
2677
2864
|
evented: false
|
|
2678
2865
|
}
|
|
2679
2866
|
};
|
|
2680
|
-
const specs = [...mask, ...shapeOverlay, frameSpec];
|
|
2867
|
+
const specs = shapeOverlay.length > 0 ? [...mask, ...shapeOverlay] : [...mask, ...shapeOverlay, frameSpec];
|
|
2681
2868
|
this.debug("overlay:built", {
|
|
2682
2869
|
frame,
|
|
2683
2870
|
shape: sceneGeometry == null ? void 0 : sceneGeometry.shape,
|
|
@@ -2707,30 +2894,32 @@ var ImageTool = class {
|
|
|
2707
2894
|
skipRender: true
|
|
2708
2895
|
});
|
|
2709
2896
|
}
|
|
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
|
-
}
|
|
2897
|
+
const imageSpecs = await this.buildImageSpecs(renderItems, frame);
|
|
2721
2898
|
if (seq !== this.renderSeq) return;
|
|
2722
|
-
this.syncImageZOrder(renderItems);
|
|
2723
2899
|
const sceneGeometry = await this.resolveSceneGeometryForOverlay();
|
|
2724
2900
|
if (seq !== this.renderSeq) return;
|
|
2725
|
-
|
|
2726
|
-
this.overlaySpecs =
|
|
2901
|
+
this.imageSpecs = imageSpecs;
|
|
2902
|
+
this.overlaySpecs = this.buildOverlaySpecs(frame, sceneGeometry);
|
|
2727
2903
|
await this.canvasService.flushRenderFromProducers();
|
|
2728
|
-
this.
|
|
2904
|
+
if (seq !== this.renderSeq) return;
|
|
2905
|
+
renderItems.forEach((item) => {
|
|
2906
|
+
if (!this.getImageObject(item.id)) return;
|
|
2907
|
+
const resolver = this.loadResolvers.get(item.id);
|
|
2908
|
+
if (!resolver) return;
|
|
2909
|
+
resolver();
|
|
2910
|
+
this.loadResolvers.delete(item.id);
|
|
2911
|
+
});
|
|
2912
|
+
if (this.focusedImageId && this.isToolActive) {
|
|
2913
|
+
this.setImageFocus(this.focusedImageId, {
|
|
2914
|
+
syncCanvasSelection: true,
|
|
2915
|
+
skipRender: true
|
|
2916
|
+
});
|
|
2917
|
+
}
|
|
2729
2918
|
const overlayCanvasCount = this.getOverlayObjects().length;
|
|
2730
2919
|
this.debug("render:done", {
|
|
2731
2920
|
seq,
|
|
2732
2921
|
renderCount: renderItems.length,
|
|
2733
|
-
overlayCount: overlaySpecs.length,
|
|
2922
|
+
overlayCount: this.overlaySpecs.length,
|
|
2734
2923
|
overlayCanvasCount,
|
|
2735
2924
|
isToolActive: this.isToolActive,
|
|
2736
2925
|
isImageSelectionActive: this.isImageSelectionActive,
|
|
@@ -4376,11 +4565,11 @@ var DielineTool = class {
|
|
|
4376
4565
|
style: "solid"
|
|
4377
4566
|
},
|
|
4378
4567
|
insideColor: "rgba(0,0,0,0)",
|
|
4379
|
-
outsideColor: "#ffffff",
|
|
4380
4568
|
showBleedLines: true,
|
|
4381
4569
|
features: []
|
|
4382
4570
|
};
|
|
4383
4571
|
this.specs = [];
|
|
4572
|
+
this.effects = [];
|
|
4384
4573
|
this.renderSeq = 0;
|
|
4385
4574
|
this.onCanvasResized = () => {
|
|
4386
4575
|
this.updateDieline();
|
|
@@ -4417,10 +4606,23 @@ var DielineTool = class {
|
|
|
4417
4606
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
4418
4607
|
this.id,
|
|
4419
4608
|
() => ({
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4609
|
+
passes: [
|
|
4610
|
+
{
|
|
4611
|
+
id: DIELINE_LAYER_ID,
|
|
4612
|
+
stack: 700,
|
|
4613
|
+
order: 0,
|
|
4614
|
+
replace: true,
|
|
4615
|
+
visibility: {
|
|
4616
|
+
op: "not",
|
|
4617
|
+
expr: {
|
|
4618
|
+
op: "activeToolIn",
|
|
4619
|
+
ids: ["pooder.kit.image", "pooder.kit.white-ink"]
|
|
4620
|
+
}
|
|
4621
|
+
},
|
|
4622
|
+
effects: this.effects,
|
|
4623
|
+
objects: this.specs
|
|
4624
|
+
}
|
|
4625
|
+
]
|
|
4424
4626
|
}),
|
|
4425
4627
|
{ priority: 250 }
|
|
4426
4628
|
);
|
|
@@ -4476,10 +4678,6 @@ var DielineTool = class {
|
|
|
4476
4678
|
s.offsetLine.style
|
|
4477
4679
|
);
|
|
4478
4680
|
s.insideColor = configService.get("dieline.insideColor", s.insideColor);
|
|
4479
|
-
s.outsideColor = configService.get(
|
|
4480
|
-
"dieline.outsideColor",
|
|
4481
|
-
s.outsideColor
|
|
4482
|
-
);
|
|
4483
4681
|
s.showBleedLines = configService.get(
|
|
4484
4682
|
"dieline.showBleedLines",
|
|
4485
4683
|
s.showBleedLines
|
|
@@ -4542,9 +4740,6 @@ var DielineTool = class {
|
|
|
4542
4740
|
case "dieline.insideColor":
|
|
4543
4741
|
s.insideColor = e.value;
|
|
4544
4742
|
break;
|
|
4545
|
-
case "dieline.outsideColor":
|
|
4546
|
-
s.outsideColor = e.value;
|
|
4547
|
-
break;
|
|
4548
4743
|
case "dieline.showBleedLines":
|
|
4549
4744
|
s.showBleedLines = e.value;
|
|
4550
4745
|
break;
|
|
@@ -4573,6 +4768,7 @@ var DielineTool = class {
|
|
|
4573
4768
|
context.eventBus.off("canvas:resized", this.onCanvasResized);
|
|
4574
4769
|
this.renderSeq += 1;
|
|
4575
4770
|
this.specs = [];
|
|
4771
|
+
this.effects = [];
|
|
4576
4772
|
(_a = this.renderProducerDisposable) == null ? void 0 : _a.dispose();
|
|
4577
4773
|
this.renderProducerDisposable = void 0;
|
|
4578
4774
|
if (this.canvasService) {
|
|
@@ -4689,12 +4885,6 @@ var DielineTool = class {
|
|
|
4689
4885
|
label: "Inside Color",
|
|
4690
4886
|
default: s.insideColor
|
|
4691
4887
|
},
|
|
4692
|
-
{
|
|
4693
|
-
id: "dieline.outsideColor",
|
|
4694
|
-
type: "color",
|
|
4695
|
-
label: "Outside Color",
|
|
4696
|
-
default: s.outsideColor
|
|
4697
|
-
},
|
|
4698
4888
|
{
|
|
4699
4889
|
id: "dieline.features",
|
|
4700
4890
|
type: "json",
|
|
@@ -4818,6 +5008,12 @@ var DielineTool = class {
|
|
|
4818
5008
|
"ConfigurationService"
|
|
4819
5009
|
);
|
|
4820
5010
|
}
|
|
5011
|
+
hasImageItems() {
|
|
5012
|
+
const configService = this.getConfigService();
|
|
5013
|
+
if (!configService) return false;
|
|
5014
|
+
const items = configService.get("image.items", []);
|
|
5015
|
+
return Array.isArray(items) && items.length > 0;
|
|
5016
|
+
}
|
|
4821
5017
|
syncSizeState(configService) {
|
|
4822
5018
|
const sizeState = readSizeState(configService);
|
|
4823
5019
|
this.state.width = sizeState.actualWidthMm;
|
|
@@ -4825,29 +5021,6 @@ var DielineTool = class {
|
|
|
4825
5021
|
this.state.padding = sizeState.viewPadding;
|
|
4826
5022
|
this.state.offset = sizeState.cutMode === "outset" ? sizeState.cutMarginMm : sizeState.cutMode === "inset" ? -sizeState.cutMarginMm : 0;
|
|
4827
5023
|
}
|
|
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
5024
|
buildDielineSpecs(sceneLayout) {
|
|
4852
5025
|
var _a, _b;
|
|
4853
5026
|
const {
|
|
@@ -4857,10 +5030,10 @@ var DielineTool = class {
|
|
|
4857
5030
|
mainLine,
|
|
4858
5031
|
offsetLine,
|
|
4859
5032
|
insideColor,
|
|
4860
|
-
outsideColor,
|
|
4861
5033
|
showBleedLines,
|
|
4862
5034
|
features
|
|
4863
5035
|
} = this.state;
|
|
5036
|
+
const hasImages = this.hasImageItems();
|
|
4864
5037
|
const canvasW = sceneLayout.canvasWidth || ((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 800;
|
|
4865
5038
|
const canvasH = sceneLayout.canvasHeight || ((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 600;
|
|
4866
5039
|
const scale = sceneLayout.scale;
|
|
@@ -4882,41 +5055,8 @@ var DielineTool = class {
|
|
|
4882
5055
|
radius: (f.radius || 0) * scale
|
|
4883
5056
|
}));
|
|
4884
5057
|
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)") {
|
|
5058
|
+
const specs = [];
|
|
5059
|
+
if (insideColor && insideColor !== "transparent" && insideColor !== "rgba(0,0,0,0)" && !hasImages) {
|
|
4920
5060
|
const productPathData = generateDielinePath({
|
|
4921
5061
|
shape,
|
|
4922
5062
|
width: cutW,
|
|
@@ -5070,6 +5210,73 @@ var DielineTool = class {
|
|
|
5070
5210
|
});
|
|
5071
5211
|
return specs;
|
|
5072
5212
|
}
|
|
5213
|
+
buildImageClipEffects(sceneLayout) {
|
|
5214
|
+
var _a, _b;
|
|
5215
|
+
const { shape, shapeStyle, radius, features } = this.state;
|
|
5216
|
+
const canvasW = sceneLayout.canvasWidth || ((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 800;
|
|
5217
|
+
const canvasH = sceneLayout.canvasHeight || ((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 600;
|
|
5218
|
+
const scale = sceneLayout.scale;
|
|
5219
|
+
const cx = sceneLayout.trimRect.centerX;
|
|
5220
|
+
const cy = sceneLayout.trimRect.centerY;
|
|
5221
|
+
const visualWidth = sceneLayout.trimRect.width;
|
|
5222
|
+
const visualRadius = radius * scale;
|
|
5223
|
+
const cutW = sceneLayout.cutRect.width;
|
|
5224
|
+
const cutH = sceneLayout.cutRect.height;
|
|
5225
|
+
const visualOffset = (cutW - visualWidth) / 2;
|
|
5226
|
+
const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
5227
|
+
const absoluteFeatures = (features || []).map((f) => ({
|
|
5228
|
+
...f,
|
|
5229
|
+
x: f.x,
|
|
5230
|
+
y: f.y,
|
|
5231
|
+
width: (f.width || 0) * scale,
|
|
5232
|
+
height: (f.height || 0) * scale,
|
|
5233
|
+
radius: (f.radius || 0) * scale
|
|
5234
|
+
}));
|
|
5235
|
+
const cutFeatures = absoluteFeatures.filter((f) => !f.skipCut);
|
|
5236
|
+
const clipPathData = generateDielinePath({
|
|
5237
|
+
shape,
|
|
5238
|
+
width: cutW,
|
|
5239
|
+
height: cutH,
|
|
5240
|
+
radius: cutR,
|
|
5241
|
+
x: cx,
|
|
5242
|
+
y: cy,
|
|
5243
|
+
features: cutFeatures,
|
|
5244
|
+
shapeStyle,
|
|
5245
|
+
pathData: this.state.pathData,
|
|
5246
|
+
customSourceWidthPx: this.state.customSourceWidthPx,
|
|
5247
|
+
customSourceHeightPx: this.state.customSourceHeightPx,
|
|
5248
|
+
canvasWidth: canvasW,
|
|
5249
|
+
canvasHeight: canvasH
|
|
5250
|
+
});
|
|
5251
|
+
if (!clipPathData) return [];
|
|
5252
|
+
return [
|
|
5253
|
+
{
|
|
5254
|
+
type: "clipPath",
|
|
5255
|
+
id: "dieline.clip.image",
|
|
5256
|
+
targetPassIds: [IMAGE_OBJECT_LAYER_ID2],
|
|
5257
|
+
source: {
|
|
5258
|
+
id: "dieline.effect.clip-path",
|
|
5259
|
+
type: "path",
|
|
5260
|
+
space: "screen",
|
|
5261
|
+
data: {
|
|
5262
|
+
id: "dieline.effect.clip-path",
|
|
5263
|
+
type: "dieline-effect",
|
|
5264
|
+
effect: "clipPath"
|
|
5265
|
+
},
|
|
5266
|
+
props: {
|
|
5267
|
+
pathData: clipPathData,
|
|
5268
|
+
fill: "#000000",
|
|
5269
|
+
stroke: null,
|
|
5270
|
+
originX: "left",
|
|
5271
|
+
originY: "top",
|
|
5272
|
+
selectable: false,
|
|
5273
|
+
evented: false,
|
|
5274
|
+
excludeFromExport: true
|
|
5275
|
+
}
|
|
5276
|
+
}
|
|
5277
|
+
}
|
|
5278
|
+
];
|
|
5279
|
+
}
|
|
5073
5280
|
updateDieline(_emitEvent = true) {
|
|
5074
5281
|
void this.updateDielineAsync();
|
|
5075
5282
|
}
|
|
@@ -5086,17 +5293,17 @@ var DielineTool = class {
|
|
|
5086
5293
|
if (!sceneLayout) {
|
|
5087
5294
|
if (seq !== this.renderSeq) return;
|
|
5088
5295
|
this.specs = [];
|
|
5296
|
+
this.effects = [];
|
|
5089
5297
|
await this.canvasService.flushRenderFromProducers();
|
|
5090
5298
|
return;
|
|
5091
5299
|
}
|
|
5092
5300
|
const nextSpecs = this.buildDielineSpecs(sceneLayout);
|
|
5301
|
+
const nextEffects = this.buildImageClipEffects(sceneLayout);
|
|
5093
5302
|
if (seq !== this.renderSeq) return;
|
|
5094
5303
|
this.specs = nextSpecs;
|
|
5304
|
+
this.effects = nextEffects;
|
|
5095
5305
|
await this.canvasService.flushRenderFromProducers();
|
|
5096
5306
|
if (seq !== this.renderSeq) return;
|
|
5097
|
-
this.ensureLayerStacking();
|
|
5098
|
-
this.bringFeatureMarkersToFront();
|
|
5099
|
-
this.canvasService.bringLayerToFront("ruler-overlay");
|
|
5100
5307
|
this.canvasService.requestRenderAll();
|
|
5101
5308
|
}
|
|
5102
5309
|
getGeometry() {
|
|
@@ -5529,9 +5736,14 @@ var FeatureTool = class {
|
|
|
5529
5736
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
5530
5737
|
this.id,
|
|
5531
5738
|
() => ({
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5739
|
+
passes: [
|
|
5740
|
+
{
|
|
5741
|
+
id: FEATURE_OVERLAY_LAYER_ID,
|
|
5742
|
+
stack: 880,
|
|
5743
|
+
order: 0,
|
|
5744
|
+
objects: this.specs
|
|
5745
|
+
}
|
|
5746
|
+
]
|
|
5535
5747
|
}),
|
|
5536
5748
|
{ priority: 350 }
|
|
5537
5749
|
);
|
|
@@ -5674,10 +5886,10 @@ var FeatureTool = class {
|
|
|
5674
5886
|
await this.refreshGeometry();
|
|
5675
5887
|
this.setWorkingFeatures(original);
|
|
5676
5888
|
this.hasWorkingChanges = false;
|
|
5889
|
+
this.clearFeatureSessionState();
|
|
5677
5890
|
this.redraw();
|
|
5678
5891
|
this.emitWorkingChange();
|
|
5679
5892
|
this.updateCommittedFeatures(original);
|
|
5680
|
-
this.clearFeatureSessionState();
|
|
5681
5893
|
return { ok: true };
|
|
5682
5894
|
}
|
|
5683
5895
|
},
|
|
@@ -5993,6 +6205,7 @@ var FeatureTool = class {
|
|
|
5993
6205
|
}
|
|
5994
6206
|
getDraggableMarkerTarget(target) {
|
|
5995
6207
|
var _a, _b;
|
|
6208
|
+
if (!this.isFeatureSessionActive || !this.isToolActive) return null;
|
|
5996
6209
|
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "feature-marker") return null;
|
|
5997
6210
|
if (((_b = target.data) == null ? void 0 : _b.markerRole) !== "handle") return null;
|
|
5998
6211
|
return target;
|
|
@@ -6115,18 +6328,12 @@ var FeatureTool = class {
|
|
|
6115
6328
|
if (seq !== this.renderSeq) return;
|
|
6116
6329
|
await this.canvasService.flushRenderFromProducers();
|
|
6117
6330
|
if (seq !== this.renderSeq) return;
|
|
6118
|
-
this.syncOverlayOrder();
|
|
6119
6331
|
if (options.enforceConstraints) {
|
|
6120
6332
|
this.enforceConstraints();
|
|
6121
6333
|
}
|
|
6122
6334
|
}
|
|
6123
|
-
syncOverlayOrder() {
|
|
6124
|
-
if (!this.canvasService) return;
|
|
6125
|
-
this.canvasService.bringLayerToFront(FEATURE_OVERLAY_LAYER_ID);
|
|
6126
|
-
this.canvasService.bringLayerToFront("ruler-overlay");
|
|
6127
|
-
}
|
|
6128
6335
|
buildFeatureSpecs() {
|
|
6129
|
-
if (!this.currentGeometry || this.workingFeatures.length === 0) {
|
|
6336
|
+
if (!this.isFeatureSessionActive || !this.currentGeometry || this.workingFeatures.length === 0) {
|
|
6130
6337
|
return [];
|
|
6131
6338
|
}
|
|
6132
6339
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -6196,11 +6403,12 @@ var FeatureTool = class {
|
|
|
6196
6403
|
const color = feature.color || (feature.operation === "add" ? "#00FF00" : "#FF0000");
|
|
6197
6404
|
const strokeDash = feature.strokeDash || (feature.operation === "subtract" ? [4, 4] : void 0);
|
|
6198
6405
|
const interactive = options.markerRole === "handle";
|
|
6406
|
+
const sessionVisible = this.isToolActive && this.isFeatureSessionActive;
|
|
6199
6407
|
const baseData = this.buildMarkerData(marker, options);
|
|
6200
6408
|
const commonProps = {
|
|
6201
|
-
visible:
|
|
6202
|
-
selectable: interactive &&
|
|
6203
|
-
evented: interactive &&
|
|
6409
|
+
visible: sessionVisible,
|
|
6410
|
+
selectable: interactive && sessionVisible,
|
|
6411
|
+
evented: interactive && sessionVisible,
|
|
6204
6412
|
hasControls: false,
|
|
6205
6413
|
hasBorders: false,
|
|
6206
6414
|
hoverCursor: interactive ? "move" : "default",
|
|
@@ -6265,7 +6473,7 @@ var FeatureTool = class {
|
|
|
6265
6473
|
markerOffsetY: -visualHeight / 2
|
|
6266
6474
|
},
|
|
6267
6475
|
props: {
|
|
6268
|
-
visible:
|
|
6476
|
+
visible: sessionVisible,
|
|
6269
6477
|
selectable: false,
|
|
6270
6478
|
evented: false,
|
|
6271
6479
|
width: visualWidth,
|
|
@@ -6433,9 +6641,14 @@ var FilmTool = class {
|
|
|
6433
6641
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
6434
6642
|
this.id,
|
|
6435
6643
|
() => ({
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6644
|
+
passes: [
|
|
6645
|
+
{
|
|
6646
|
+
id: FILM_LAYER_ID,
|
|
6647
|
+
stack: 1e3,
|
|
6648
|
+
order: 0,
|
|
6649
|
+
objects: this.specs
|
|
6650
|
+
}
|
|
6651
|
+
]
|
|
6439
6652
|
}),
|
|
6440
6653
|
{ priority: 500 }
|
|
6441
6654
|
);
|
|
@@ -6609,7 +6822,6 @@ var FilmTool = class {
|
|
|
6609
6822
|
this.specs = this.buildFilmSpecs(this.renderImageUrl, this.opacity);
|
|
6610
6823
|
await this.canvasService.flushRenderFromProducers();
|
|
6611
6824
|
if (seq !== this.renderSeq) return;
|
|
6612
|
-
this.canvasService.bringLayerToFront(FILM_LAYER_ID);
|
|
6613
6825
|
this.canvasService.requestRenderAll();
|
|
6614
6826
|
}
|
|
6615
6827
|
};
|
|
@@ -6756,10 +6968,22 @@ var RulerTool = class {
|
|
|
6756
6968
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
6757
6969
|
this.id,
|
|
6758
6970
|
() => ({
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6971
|
+
passes: [
|
|
6972
|
+
{
|
|
6973
|
+
id: RULER_LAYER_ID,
|
|
6974
|
+
stack: 950,
|
|
6975
|
+
order: 0,
|
|
6976
|
+
replace: true,
|
|
6977
|
+
visibility: {
|
|
6978
|
+
op: "not",
|
|
6979
|
+
expr: {
|
|
6980
|
+
op: "activeToolIn",
|
|
6981
|
+
ids: ["pooder.kit.white-ink"]
|
|
6982
|
+
}
|
|
6983
|
+
},
|
|
6984
|
+
objects: this.specs
|
|
6985
|
+
}
|
|
6986
|
+
]
|
|
6763
6987
|
}),
|
|
6764
6988
|
{ priority: 400 }
|
|
6765
6989
|
);
|
|
@@ -7251,7 +7475,6 @@ var RulerTool = class {
|
|
|
7251
7475
|
this.specs = specs;
|
|
7252
7476
|
await this.canvasService.flushRenderFromProducers();
|
|
7253
7477
|
if (seq !== this.renderSeq) return;
|
|
7254
|
-
this.canvasService.bringLayerToFront(RULER_LAYER_ID);
|
|
7255
7478
|
this.canvasService.requestRenderAll();
|
|
7256
7479
|
this.log("render:done", { seq });
|
|
7257
7480
|
}
|
|
@@ -7263,7 +7486,6 @@ var WHITE_INK_OBJECT_LAYER_ID = "white-ink.user";
|
|
|
7263
7486
|
var WHITE_INK_COVER_LAYER_ID = "white-ink.cover";
|
|
7264
7487
|
var WHITE_INK_OVERLAY_LAYER_ID = "white-ink.overlay";
|
|
7265
7488
|
var IMAGE_OBJECT_LAYER_ID3 = "image.user";
|
|
7266
|
-
var IMAGE_OVERLAY_LAYER_ID2 = "image-overlay";
|
|
7267
7489
|
var WHITE_INK_DEBUG_KEY = "whiteInk.debug";
|
|
7268
7490
|
var WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY = "whiteInk.previewImageVisible";
|
|
7269
7491
|
var WHITE_INK_DEFAULT_OPACITY = 0.85;
|
|
@@ -7341,11 +7563,26 @@ var WhiteInkTool = class {
|
|
|
7341
7563
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
7342
7564
|
this.id,
|
|
7343
7565
|
() => ({
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7566
|
+
passes: [
|
|
7567
|
+
{
|
|
7568
|
+
id: WHITE_INK_COVER_LAYER_ID,
|
|
7569
|
+
stack: 220,
|
|
7570
|
+
order: 0,
|
|
7571
|
+
objects: this.coverSpecs
|
|
7572
|
+
},
|
|
7573
|
+
{
|
|
7574
|
+
id: WHITE_INK_OBJECT_LAYER_ID,
|
|
7575
|
+
stack: 221,
|
|
7576
|
+
order: 0,
|
|
7577
|
+
objects: this.whiteSpecs
|
|
7578
|
+
},
|
|
7579
|
+
{
|
|
7580
|
+
id: WHITE_INK_OVERLAY_LAYER_ID,
|
|
7581
|
+
stack: 790,
|
|
7582
|
+
order: 0,
|
|
7583
|
+
objects: this.overlaySpecs
|
|
7584
|
+
}
|
|
7585
|
+
]
|
|
7349
7586
|
}),
|
|
7350
7587
|
{ priority: 260 }
|
|
7351
7588
|
);
|
|
@@ -7424,7 +7661,6 @@ var WhiteInkTool = class {
|
|
|
7424
7661
|
(_a = this.dirtyTrackerDisposable) == null ? void 0 : _a.dispose();
|
|
7425
7662
|
this.dirtyTrackerDisposable = void 0;
|
|
7426
7663
|
this.clearRenderedWhiteInks();
|
|
7427
|
-
this.applyImageVisibilityForWhiteInk(false);
|
|
7428
7664
|
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
7429
7665
|
this.renderProducerDisposable = void 0;
|
|
7430
7666
|
if (this.canvasService) {
|
|
@@ -8264,25 +8500,9 @@ var WhiteInkTool = class {
|
|
|
8264
8500
|
selectable: false,
|
|
8265
8501
|
evented: false,
|
|
8266
8502
|
excludeFromExport: true
|
|
8267
|
-
}
|
|
8268
|
-
}
|
|
8269
|
-
];
|
|
8270
|
-
}
|
|
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
|
-
}
|
|
8503
|
+
}
|
|
8504
|
+
}
|
|
8505
|
+
];
|
|
8286
8506
|
}
|
|
8287
8507
|
resolveRenderItems() {
|
|
8288
8508
|
if (this.isToolActive) {
|
|
@@ -8308,52 +8528,6 @@ var WhiteInkTool = class {
|
|
|
8308
8528
|
whiteScaleAdjustY: scaleAdjust.y
|
|
8309
8529
|
};
|
|
8310
8530
|
}
|
|
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");
|
|
8356
|
-
}
|
|
8357
8531
|
clearRenderedWhiteInks() {
|
|
8358
8532
|
if (!this.canvasService) return;
|
|
8359
8533
|
this.whiteSpecs = [];
|
|
@@ -8385,7 +8559,6 @@ var WhiteInkTool = class {
|
|
|
8385
8559
|
this.syncToolActiveFromWorkbench();
|
|
8386
8560
|
const seq = ++this.renderSeq;
|
|
8387
8561
|
const previewActive = this.isPreviewActive();
|
|
8388
|
-
this.applyImageVisibilityForWhiteInk(previewActive);
|
|
8389
8562
|
const frame = this.getFrameRect();
|
|
8390
8563
|
const frameSpecs = this.buildFrameSpecs(frame);
|
|
8391
8564
|
let whiteSpecs = [];
|
|
@@ -8433,7 +8606,6 @@ var WhiteInkTool = class {
|
|
|
8433
8606
|
this.overlaySpecs = frameSpecs;
|
|
8434
8607
|
await this.canvasService.flushRenderFromProducers();
|
|
8435
8608
|
if (seq !== this.renderSeq) return;
|
|
8436
|
-
this.syncZOrder();
|
|
8437
8609
|
this.canvasService.requestRenderAll();
|
|
8438
8610
|
}
|
|
8439
8611
|
getMaskCacheKey(sourceUrl, tint) {
|
|
@@ -8615,62 +8787,9 @@ var SceneLayoutService = class {
|
|
|
8615
8787
|
}
|
|
8616
8788
|
};
|
|
8617
8789
|
|
|
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
8790
|
// src/services/CanvasService.ts
|
|
8673
8791
|
var import_fabric5 = require("fabric");
|
|
8792
|
+
var import_core11 = require("@pooder/core");
|
|
8674
8793
|
|
|
8675
8794
|
// src/services/ViewportSystem.ts
|
|
8676
8795
|
var ViewportSystem = class {
|
|
@@ -8745,6 +8864,53 @@ var ViewportSystem = class {
|
|
|
8745
8864
|
}
|
|
8746
8865
|
};
|
|
8747
8866
|
|
|
8867
|
+
// src/services/visibility.ts
|
|
8868
|
+
function compareLayerObjectCount(actual, cmp, expected) {
|
|
8869
|
+
if (cmp === ">") return actual > expected;
|
|
8870
|
+
if (cmp === ">=") return actual >= expected;
|
|
8871
|
+
if (cmp === "<") return actual < expected;
|
|
8872
|
+
if (cmp === "<=") return actual <= expected;
|
|
8873
|
+
return actual === expected;
|
|
8874
|
+
}
|
|
8875
|
+
function layerState(context, layerId) {
|
|
8876
|
+
return context.layers.get(layerId) || { exists: false, objectCount: 0 };
|
|
8877
|
+
}
|
|
8878
|
+
function evaluateVisibilityExpr(expr, context) {
|
|
8879
|
+
var _a;
|
|
8880
|
+
if (!expr) return true;
|
|
8881
|
+
if (expr.op === "const") {
|
|
8882
|
+
return Boolean(expr.value);
|
|
8883
|
+
}
|
|
8884
|
+
if (expr.op === "activeToolIn") {
|
|
8885
|
+
const activeToolId = (_a = context.activeToolId) != null ? _a : null;
|
|
8886
|
+
return !!activeToolId && expr.ids.includes(activeToolId);
|
|
8887
|
+
}
|
|
8888
|
+
if (expr.op === "sessionActive") {
|
|
8889
|
+
const toolId = String(expr.toolId || "").trim();
|
|
8890
|
+
if (!toolId) return false;
|
|
8891
|
+
return context.isSessionActive ? context.isSessionActive(toolId) : false;
|
|
8892
|
+
}
|
|
8893
|
+
if (expr.op === "layerExists") {
|
|
8894
|
+
return layerState(context, expr.layerId).exists === true;
|
|
8895
|
+
}
|
|
8896
|
+
if (expr.op === "layerObjectCount") {
|
|
8897
|
+
const expected = Number(expr.value);
|
|
8898
|
+
if (!Number.isFinite(expected)) return false;
|
|
8899
|
+
const count = layerState(context, expr.layerId).objectCount;
|
|
8900
|
+
return compareLayerObjectCount(count, expr.cmp, expected);
|
|
8901
|
+
}
|
|
8902
|
+
if (expr.op === "not") {
|
|
8903
|
+
return !evaluateVisibilityExpr(expr.expr, context);
|
|
8904
|
+
}
|
|
8905
|
+
if (expr.op === "all") {
|
|
8906
|
+
return expr.exprs.every((item) => evaluateVisibilityExpr(item, context));
|
|
8907
|
+
}
|
|
8908
|
+
if (expr.op === "any") {
|
|
8909
|
+
return expr.exprs.some((item) => evaluateVisibilityExpr(item, context));
|
|
8910
|
+
}
|
|
8911
|
+
return true;
|
|
8912
|
+
}
|
|
8913
|
+
|
|
8748
8914
|
// src/services/CanvasService.ts
|
|
8749
8915
|
var CanvasService = class {
|
|
8750
8916
|
constructor(el, options) {
|
|
@@ -8753,8 +8919,45 @@ var CanvasService = class {
|
|
|
8753
8919
|
this.producerFlushRequested = false;
|
|
8754
8920
|
this.producerLoopPending = false;
|
|
8755
8921
|
this.producerLoopPromise = null;
|
|
8756
|
-
this.
|
|
8757
|
-
this.
|
|
8922
|
+
this.producerApplyInProgress = false;
|
|
8923
|
+
this.visibilityRefreshScheduled = false;
|
|
8924
|
+
this.managedProducerPassIds = /* @__PURE__ */ new Set();
|
|
8925
|
+
this.managedPassMetas = /* @__PURE__ */ new Map();
|
|
8926
|
+
this.canvasForwardersBound = false;
|
|
8927
|
+
this.forwardSelectionCreated = (e) => {
|
|
8928
|
+
var _a;
|
|
8929
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("selection:created", e);
|
|
8930
|
+
};
|
|
8931
|
+
this.forwardSelectionUpdated = (e) => {
|
|
8932
|
+
var _a;
|
|
8933
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("selection:updated", e);
|
|
8934
|
+
};
|
|
8935
|
+
this.forwardSelectionCleared = (e) => {
|
|
8936
|
+
var _a;
|
|
8937
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("selection:cleared", e);
|
|
8938
|
+
};
|
|
8939
|
+
this.forwardObjectModified = (e) => {
|
|
8940
|
+
var _a;
|
|
8941
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("object:modified", e);
|
|
8942
|
+
};
|
|
8943
|
+
this.forwardObjectAdded = (e) => {
|
|
8944
|
+
var _a;
|
|
8945
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("object:added", e);
|
|
8946
|
+
};
|
|
8947
|
+
this.forwardObjectRemoved = (e) => {
|
|
8948
|
+
var _a;
|
|
8949
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("object:removed", e);
|
|
8950
|
+
};
|
|
8951
|
+
this.onToolActivated = () => {
|
|
8952
|
+
this.applyManagedPassVisibility();
|
|
8953
|
+
};
|
|
8954
|
+
this.onToolSessionChanged = () => {
|
|
8955
|
+
this.applyManagedPassVisibility();
|
|
8956
|
+
};
|
|
8957
|
+
this.onCanvasObjectChanged = () => {
|
|
8958
|
+
if (this.producerApplyInProgress) return;
|
|
8959
|
+
this.scheduleManagedPassVisibilityRefresh();
|
|
8960
|
+
};
|
|
8758
8961
|
if (el instanceof import_fabric5.Canvas) {
|
|
8759
8962
|
this.canvas = el;
|
|
8760
8963
|
} else {
|
|
@@ -8771,25 +8974,52 @@ var CanvasService = class {
|
|
|
8771
8974
|
this.setEventBus(options.eventBus);
|
|
8772
8975
|
}
|
|
8773
8976
|
}
|
|
8977
|
+
init(context) {
|
|
8978
|
+
if (this.context) {
|
|
8979
|
+
this.detachContextEvents(this.context.eventBus);
|
|
8980
|
+
}
|
|
8981
|
+
this.context = context;
|
|
8982
|
+
this.workbenchService = context.get(import_core11.WORKBENCH_SERVICE);
|
|
8983
|
+
this.toolSessionService = context.get(import_core11.TOOL_SESSION_SERVICE);
|
|
8984
|
+
this.setEventBus(context.eventBus);
|
|
8985
|
+
this.attachContextEvents(context.eventBus);
|
|
8986
|
+
}
|
|
8987
|
+
attachContextEvents(eventBus) {
|
|
8988
|
+
eventBus.on("tool:activated", this.onToolActivated);
|
|
8989
|
+
eventBus.on("tool:session:change", this.onToolSessionChanged);
|
|
8990
|
+
eventBus.on("object:added", this.onCanvasObjectChanged);
|
|
8991
|
+
eventBus.on("object:removed", this.onCanvasObjectChanged);
|
|
8992
|
+
}
|
|
8993
|
+
detachContextEvents(eventBus) {
|
|
8994
|
+
eventBus.off("tool:activated", this.onToolActivated);
|
|
8995
|
+
eventBus.off("tool:session:change", this.onToolSessionChanged);
|
|
8996
|
+
eventBus.off("object:added", this.onCanvasObjectChanged);
|
|
8997
|
+
eventBus.off("object:removed", this.onCanvasObjectChanged);
|
|
8998
|
+
}
|
|
8774
8999
|
setEventBus(eventBus) {
|
|
8775
9000
|
this.eventBus = eventBus;
|
|
8776
9001
|
this.setupEvents();
|
|
8777
9002
|
}
|
|
8778
9003
|
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"));
|
|
9004
|
+
if (this.canvasForwardersBound) return;
|
|
9005
|
+
this.canvas.on("selection:created", this.forwardSelectionCreated);
|
|
9006
|
+
this.canvas.on("selection:updated", this.forwardSelectionUpdated);
|
|
9007
|
+
this.canvas.on("selection:cleared", this.forwardSelectionCleared);
|
|
9008
|
+
this.canvas.on("object:modified", this.forwardObjectModified);
|
|
9009
|
+
this.canvas.on("object:added", this.forwardObjectAdded);
|
|
9010
|
+
this.canvas.on("object:removed", this.forwardObjectRemoved);
|
|
9011
|
+
this.canvasForwardersBound = true;
|
|
8788
9012
|
}
|
|
8789
9013
|
dispose() {
|
|
9014
|
+
if (this.context) {
|
|
9015
|
+
this.detachContextEvents(this.context.eventBus);
|
|
9016
|
+
}
|
|
8790
9017
|
this.renderProducers.clear();
|
|
8791
|
-
this.
|
|
8792
|
-
this.
|
|
9018
|
+
this.managedProducerPassIds.clear();
|
|
9019
|
+
this.managedPassMetas.clear();
|
|
9020
|
+
this.context = void 0;
|
|
9021
|
+
this.workbenchService = void 0;
|
|
9022
|
+
this.toolSessionService = void 0;
|
|
8793
9023
|
this.producerFlushRequested = false;
|
|
8794
9024
|
this.canvas.dispose();
|
|
8795
9025
|
}
|
|
@@ -8867,118 +9097,274 @@ var CanvasService = class {
|
|
|
8867
9097
|
return a.toolId.localeCompare(b.toolId);
|
|
8868
9098
|
});
|
|
8869
9099
|
}
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
9100
|
+
normalizePassSpecValue(spec) {
|
|
9101
|
+
const id = String(spec.id || "").trim();
|
|
9102
|
+
if (!id) return null;
|
|
9103
|
+
return {
|
|
9104
|
+
id,
|
|
9105
|
+
stack: Number.isFinite(spec.stack) ? Number(spec.stack) : 0,
|
|
9106
|
+
order: Number.isFinite(spec.order) ? Number(spec.order) : 0,
|
|
9107
|
+
replace: spec.replace !== false,
|
|
9108
|
+
visibility: spec.visibility,
|
|
9109
|
+
effects: Array.isArray(spec.effects) ? [...spec.effects] : [],
|
|
9110
|
+
objects: Array.isArray(spec.objects) ? [...spec.objects] : []
|
|
9111
|
+
};
|
|
9112
|
+
}
|
|
9113
|
+
normalizeClipPathEffectSpec(effect, passId, index) {
|
|
9114
|
+
if (!effect || effect.type !== "clipPath") return null;
|
|
9115
|
+
const source = effect.source;
|
|
9116
|
+
if (!source || typeof source !== "object") return null;
|
|
9117
|
+
const sourceId = String(source.id || "").trim();
|
|
9118
|
+
if (!sourceId) return null;
|
|
9119
|
+
const targetPassIds = Array.isArray(effect.targetPassIds) ? effect.targetPassIds.map((item) => String(item || "").trim()).filter((item) => item.length > 0) : [];
|
|
9120
|
+
if (!targetPassIds.length) return null;
|
|
9121
|
+
const customId = String(effect.id || "").trim();
|
|
9122
|
+
const key = customId || `${passId}.effect.clipPath.${index}`;
|
|
9123
|
+
return {
|
|
9124
|
+
type: "clipPath",
|
|
9125
|
+
key,
|
|
9126
|
+
source: {
|
|
9127
|
+
...source,
|
|
9128
|
+
id: sourceId
|
|
9129
|
+
},
|
|
9130
|
+
targetPassIds
|
|
9131
|
+
};
|
|
9132
|
+
}
|
|
9133
|
+
mergePassSpec(map, rawSpec, producerId) {
|
|
9134
|
+
const normalized = this.normalizePassSpecValue(rawSpec);
|
|
9135
|
+
if (!normalized) return;
|
|
9136
|
+
const existing = map.get(normalized.id);
|
|
9137
|
+
if (!existing) {
|
|
9138
|
+
map.set(normalized.id, normalized);
|
|
9139
|
+
return;
|
|
9140
|
+
}
|
|
9141
|
+
existing.objects.push(...normalized.objects);
|
|
9142
|
+
existing.replace = existing.replace || normalized.replace;
|
|
9143
|
+
existing.stack = normalized.stack;
|
|
9144
|
+
existing.order = normalized.order;
|
|
9145
|
+
if (normalized.visibility !== void 0) {
|
|
9146
|
+
existing.visibility = normalized.visibility;
|
|
9147
|
+
}
|
|
9148
|
+
existing.effects.push(...normalized.effects);
|
|
9149
|
+
if (normalized.objects.length === 0 && normalized.effects.length === 0) {
|
|
9150
|
+
console.debug(
|
|
9151
|
+
`[CanvasService] pass "${normalized.id}" from producer "${producerId}" updated ordering/visibility only.`
|
|
9152
|
+
);
|
|
9153
|
+
}
|
|
9154
|
+
}
|
|
9155
|
+
comparePassMeta(a, b) {
|
|
9156
|
+
if (a.stack !== b.stack) return a.stack - b.stack;
|
|
9157
|
+
if (a.order !== b.order) return a.order - b.order;
|
|
9158
|
+
return a.id.localeCompare(b.id);
|
|
9159
|
+
}
|
|
9160
|
+
getPassObjectOrder(obj) {
|
|
9161
|
+
var _a;
|
|
9162
|
+
const raw = Number((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passOrder);
|
|
9163
|
+
return Number.isFinite(raw) ? raw : Number.MAX_SAFE_INTEGER;
|
|
9164
|
+
}
|
|
9165
|
+
getPassCanvasObjects(passId) {
|
|
9166
|
+
const all = this.canvas.getObjects();
|
|
9167
|
+
return all.filter((obj) => {
|
|
9168
|
+
var _a;
|
|
9169
|
+
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passId) === passId;
|
|
9170
|
+
}).sort((a, b) => {
|
|
9171
|
+
const orderA = this.getPassObjectOrder(a);
|
|
9172
|
+
const orderB = this.getPassObjectOrder(b);
|
|
9173
|
+
if (orderA !== orderB) return orderA - orderB;
|
|
9174
|
+
return all.indexOf(a) - all.indexOf(b);
|
|
9175
|
+
});
|
|
9176
|
+
}
|
|
9177
|
+
getPassObjects(passId) {
|
|
9178
|
+
return this.getPassCanvasObjects(passId);
|
|
9179
|
+
}
|
|
9180
|
+
getRootLayerObjects(layerId) {
|
|
9181
|
+
return this.getPassCanvasObjects(layerId);
|
|
9182
|
+
}
|
|
9183
|
+
isManagedPassObject(obj) {
|
|
9184
|
+
var _a;
|
|
9185
|
+
const passId = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passId;
|
|
9186
|
+
return typeof passId === "string" && this.managedPassMetas.has(passId);
|
|
9187
|
+
}
|
|
9188
|
+
syncManagedPassStacking(passes) {
|
|
9189
|
+
const orderedPasses = [...passes].sort((a, b) => this.comparePassMeta(a, b));
|
|
9190
|
+
if (!orderedPasses.length) return;
|
|
9191
|
+
const canvasObjects = this.canvas.getObjects();
|
|
9192
|
+
const managedObjects = canvasObjects.filter(
|
|
9193
|
+
(obj) => this.isManagedPassObject(obj)
|
|
9194
|
+
);
|
|
9195
|
+
if (!managedObjects.length) return;
|
|
9196
|
+
const firstManagedIndex = managedObjects.map((obj) => canvasObjects.indexOf(obj)).filter((index) => index >= 0).reduce((min, value) => Math.min(min, value), Number.MAX_SAFE_INTEGER);
|
|
9197
|
+
let targetIndex = Number.isFinite(firstManagedIndex) ? firstManagedIndex : 0;
|
|
9198
|
+
orderedPasses.forEach((meta) => {
|
|
9199
|
+
const objects = this.getPassCanvasObjects(meta.id);
|
|
9200
|
+
objects.forEach((obj) => {
|
|
9201
|
+
this.moveObjectInCanvas(obj, targetIndex);
|
|
9202
|
+
targetIndex += 1;
|
|
9203
|
+
});
|
|
9204
|
+
});
|
|
9205
|
+
}
|
|
9206
|
+
getPassRuntimeState() {
|
|
9207
|
+
const state = /* @__PURE__ */ new Map();
|
|
9208
|
+
const ensure = (passId) => {
|
|
9209
|
+
const id = String(passId || "").trim();
|
|
9210
|
+
if (!id) return { exists: false, objectCount: 0 };
|
|
9211
|
+
let item = state.get(id);
|
|
9212
|
+
if (!item) {
|
|
9213
|
+
item = { exists: false, objectCount: 0 };
|
|
9214
|
+
state.set(id, item);
|
|
9215
|
+
}
|
|
9216
|
+
return item;
|
|
9217
|
+
};
|
|
9218
|
+
this.canvas.getObjects().forEach((obj) => {
|
|
9219
|
+
var _a;
|
|
9220
|
+
const passId = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passId;
|
|
9221
|
+
if (typeof passId === "string") {
|
|
9222
|
+
const item = ensure(passId);
|
|
9223
|
+
item.exists = true;
|
|
9224
|
+
item.objectCount += 1;
|
|
9225
|
+
}
|
|
9226
|
+
});
|
|
9227
|
+
this.managedPassMetas.forEach((meta) => {
|
|
9228
|
+
const item = ensure(meta.id);
|
|
9229
|
+
item.exists = true;
|
|
9230
|
+
});
|
|
9231
|
+
return state;
|
|
9232
|
+
}
|
|
9233
|
+
applyManagedPassVisibility(options = {}) {
|
|
9234
|
+
var _a, _b;
|
|
9235
|
+
if (!this.managedPassMetas.size) return false;
|
|
9236
|
+
const layers = this.getPassRuntimeState();
|
|
9237
|
+
const activeToolId = (_b = (_a = this.workbenchService) == null ? void 0 : _a.activeToolId) != null ? _b : null;
|
|
9238
|
+
const isSessionActive = (toolId) => {
|
|
9239
|
+
if (!this.toolSessionService) return false;
|
|
9240
|
+
return this.toolSessionService.getState(toolId).status === "active";
|
|
9241
|
+
};
|
|
9242
|
+
let changed = false;
|
|
9243
|
+
this.managedPassMetas.forEach((meta) => {
|
|
9244
|
+
const visible = evaluateVisibilityExpr(meta.visibility, {
|
|
9245
|
+
activeToolId,
|
|
9246
|
+
isSessionActive,
|
|
9247
|
+
layers
|
|
9248
|
+
});
|
|
9249
|
+
changed = this.setPassVisibility(meta.id, visible) || changed;
|
|
9250
|
+
});
|
|
9251
|
+
if (changed && options.render !== false) {
|
|
9252
|
+
this.requestRenderAll();
|
|
9253
|
+
}
|
|
9254
|
+
return changed;
|
|
9255
|
+
}
|
|
9256
|
+
scheduleManagedPassVisibilityRefresh() {
|
|
9257
|
+
if (this.visibilityRefreshScheduled) return;
|
|
9258
|
+
this.visibilityRefreshScheduled = true;
|
|
9259
|
+
void Promise.resolve().then(() => {
|
|
9260
|
+
this.visibilityRefreshScheduled = false;
|
|
9261
|
+
this.applyManagedPassVisibility();
|
|
8877
9262
|
});
|
|
8878
9263
|
}
|
|
8879
9264
|
async collectAndApplyProducerSpecs() {
|
|
8880
|
-
const
|
|
8881
|
-
const rootLayerSpecs = /* @__PURE__ */ new Map();
|
|
8882
|
-
const replaceLayerIds = /* @__PURE__ */ new Set();
|
|
8883
|
-
const replaceRootLayerIds = /* @__PURE__ */ new Set();
|
|
9265
|
+
const passes = /* @__PURE__ */ new Map();
|
|
8884
9266
|
const entries = this.sortedRenderProducerEntries();
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
if (layerId) replaceRootLayerIds.add(layerId);
|
|
8899
|
-
});
|
|
9267
|
+
this.producerApplyInProgress = true;
|
|
9268
|
+
try {
|
|
9269
|
+
for (const entry of entries) {
|
|
9270
|
+
try {
|
|
9271
|
+
const result = await entry.producer();
|
|
9272
|
+
if (!result) continue;
|
|
9273
|
+
const specs = Array.isArray(result.passes) ? result.passes : [];
|
|
9274
|
+
specs.forEach((spec) => this.mergePassSpec(passes, spec, entry.toolId));
|
|
9275
|
+
} catch (error) {
|
|
9276
|
+
console.error(
|
|
9277
|
+
`[CanvasService] render producer "${entry.toolId}" failed.`,
|
|
9278
|
+
error
|
|
9279
|
+
);
|
|
8900
9280
|
}
|
|
8901
|
-
} catch (error) {
|
|
8902
|
-
console.error(
|
|
8903
|
-
`[CanvasService] render producer "${entry.toolId}" failed.`,
|
|
8904
|
-
error
|
|
8905
|
-
);
|
|
8906
9281
|
}
|
|
8907
|
-
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
9282
|
+
const nextPassIds = /* @__PURE__ */ new Set();
|
|
9283
|
+
const nextManagedPassMetas = /* @__PURE__ */ new Map();
|
|
9284
|
+
const nextEffects = [];
|
|
9285
|
+
for (const pass of passes.values()) {
|
|
9286
|
+
nextPassIds.add(pass.id);
|
|
9287
|
+
nextManagedPassMetas.set(pass.id, {
|
|
9288
|
+
id: pass.id,
|
|
9289
|
+
stack: pass.stack,
|
|
9290
|
+
order: pass.order,
|
|
9291
|
+
visibility: pass.visibility
|
|
9292
|
+
});
|
|
9293
|
+
await this.applyObjectSpecsToPass(pass.id, pass.objects, {
|
|
9294
|
+
render: false,
|
|
9295
|
+
replace: pass.replace
|
|
9296
|
+
});
|
|
9297
|
+
pass.effects.forEach((effect, index) => {
|
|
9298
|
+
const normalized = this.normalizeClipPathEffectSpec(
|
|
9299
|
+
effect,
|
|
9300
|
+
pass.id,
|
|
9301
|
+
index
|
|
9302
|
+
);
|
|
9303
|
+
if (!normalized) return;
|
|
9304
|
+
nextEffects.push(normalized);
|
|
9305
|
+
});
|
|
8916
9306
|
}
|
|
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));
|
|
9307
|
+
for (const passId of this.managedProducerPassIds) {
|
|
9308
|
+
if (nextPassIds.has(passId)) continue;
|
|
9309
|
+
await this.applyObjectSpecsToPass(passId, [], {
|
|
9310
|
+
render: false,
|
|
9311
|
+
replace: true
|
|
9312
|
+
});
|
|
8929
9313
|
}
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
|
|
9314
|
+
this.managedProducerPassIds = nextPassIds;
|
|
9315
|
+
this.managedPassMetas = nextManagedPassMetas;
|
|
9316
|
+
this.syncManagedPassStacking(Array.from(nextManagedPassMetas.values()));
|
|
9317
|
+
await this.applyManagedPassEffects(nextEffects);
|
|
9318
|
+
this.applyManagedPassVisibility({ render: false });
|
|
9319
|
+
} finally {
|
|
9320
|
+
this.producerApplyInProgress = false;
|
|
8935
9321
|
}
|
|
8936
|
-
this.managedProducerLayerIds = nextLayerIds;
|
|
8937
|
-
this.managedProducerRootLayerIds = nextRootLayerIds;
|
|
8938
9322
|
this.requestRenderAll();
|
|
8939
9323
|
}
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
});
|
|
8949
|
-
}
|
|
8950
|
-
/**
|
|
8951
|
-
* Create a layer (Group) with the given ID if it doesn't exist.
|
|
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;
|
|
9324
|
+
async applyManagedPassEffects(effects) {
|
|
9325
|
+
const effectTargetMap = /* @__PURE__ */ new Map();
|
|
9326
|
+
for (const effect of effects) {
|
|
9327
|
+
if (effect.type !== "clipPath") continue;
|
|
9328
|
+
effect.targetPassIds.forEach((targetPassId) => {
|
|
9329
|
+
this.getPassCanvasObjects(targetPassId).forEach((obj) => {
|
|
9330
|
+
effectTargetMap.set(obj, effect);
|
|
9331
|
+
});
|
|
8977
9332
|
});
|
|
8978
9333
|
}
|
|
9334
|
+
const managedObjects = this.canvas.getObjects().filter(
|
|
9335
|
+
(obj) => this.isManagedPassObject(obj)
|
|
9336
|
+
);
|
|
9337
|
+
const effectTemplateCache = /* @__PURE__ */ new Map();
|
|
9338
|
+
for (const obj of managedObjects) {
|
|
9339
|
+
const targetEffect = effectTargetMap.get(obj);
|
|
9340
|
+
if (!targetEffect) {
|
|
9341
|
+
this.clearClipPathEffectFromObject(obj);
|
|
9342
|
+
continue;
|
|
9343
|
+
}
|
|
9344
|
+
let template = effectTemplateCache.get(targetEffect.key);
|
|
9345
|
+
if (template === void 0) {
|
|
9346
|
+
template = await this.createClipPathTemplate(targetEffect);
|
|
9347
|
+
effectTemplateCache.set(targetEffect.key, template);
|
|
9348
|
+
}
|
|
9349
|
+
if (!template) {
|
|
9350
|
+
this.clearClipPathEffectFromObject(obj);
|
|
9351
|
+
continue;
|
|
9352
|
+
}
|
|
9353
|
+
await this.applyClipPathEffectToObject(
|
|
9354
|
+
obj,
|
|
9355
|
+
template,
|
|
9356
|
+
targetEffect.key
|
|
9357
|
+
);
|
|
9358
|
+
}
|
|
9359
|
+
}
|
|
9360
|
+
getObject(id, passId) {
|
|
9361
|
+
const normalizedId = String(id || "").trim();
|
|
9362
|
+
if (!normalizedId) return void 0;
|
|
8979
9363
|
return this.canvas.getObjects().find((obj) => {
|
|
8980
|
-
var _a;
|
|
8981
|
-
|
|
9364
|
+
var _a, _b;
|
|
9365
|
+
if (((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id) !== normalizedId) return false;
|
|
9366
|
+
if (!passId) return true;
|
|
9367
|
+
return ((_b = obj == null ? void 0 : obj.data) == null ? void 0 : _b.passId) === passId;
|
|
8982
9368
|
});
|
|
8983
9369
|
}
|
|
8984
9370
|
requestRenderAll() {
|
|
@@ -9164,114 +9550,187 @@ var CanvasService = class {
|
|
|
9164
9550
|
next.top = objectTop + objectHeight * this.originFactor(originY);
|
|
9165
9551
|
return next;
|
|
9166
9552
|
}
|
|
9167
|
-
|
|
9168
|
-
const
|
|
9169
|
-
|
|
9170
|
-
layer.set({ visible });
|
|
9171
|
-
}
|
|
9172
|
-
const objects = this.getRootLayerObjects(layerId);
|
|
9553
|
+
setPassVisibility(passId, visible) {
|
|
9554
|
+
const objects = this.getPassCanvasObjects(passId);
|
|
9555
|
+
let changed = false;
|
|
9173
9556
|
objects.forEach((obj) => {
|
|
9174
9557
|
var _a, _b;
|
|
9558
|
+
if (obj.visible === visible) return;
|
|
9175
9559
|
(_a = obj.set) == null ? void 0 : _a.call(obj, { visible });
|
|
9176
9560
|
(_b = obj.setCoords) == null ? void 0 : _b.call(obj);
|
|
9561
|
+
changed = true;
|
|
9177
9562
|
});
|
|
9563
|
+
return changed;
|
|
9178
9564
|
}
|
|
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));
|
|
9565
|
+
setLayerVisibility(layerId, visible) {
|
|
9566
|
+
return this.setPassVisibility(layerId, visible);
|
|
9186
9567
|
}
|
|
9187
|
-
|
|
9188
|
-
const
|
|
9189
|
-
|
|
9568
|
+
bringPassToFront(passId) {
|
|
9569
|
+
const objects = this.getPassCanvasObjects(passId);
|
|
9570
|
+
objects.forEach((obj) => this.canvas.bringObjectToFront(obj));
|
|
9190
9571
|
}
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
await this.applyObjectSpecsToContainer(layer, objects, options);
|
|
9572
|
+
bringLayerToFront(layerId) {
|
|
9573
|
+
this.bringPassToFront(layerId);
|
|
9194
9574
|
}
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9575
|
+
async applyPassSpec(spec, options = {}) {
|
|
9576
|
+
await this.applyObjectSpecsToPass(spec.id, spec.objects, {
|
|
9577
|
+
render: options.render,
|
|
9578
|
+
replace: spec.replace !== false
|
|
9199
9579
|
});
|
|
9200
9580
|
}
|
|
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
|
-
}
|
|
9581
|
+
async applyObjectSpecsToRootLayer(passId, specs, options = {}) {
|
|
9582
|
+
await this.applyObjectSpecsToPass(passId, specs, {
|
|
9583
|
+
render: options.render,
|
|
9584
|
+
replace: true
|
|
9210
9585
|
});
|
|
9211
|
-
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9586
|
+
}
|
|
9587
|
+
normalizeObjectSpecs(specs) {
|
|
9588
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9589
|
+
const normalized = [];
|
|
9590
|
+
(specs || []).forEach((spec) => {
|
|
9591
|
+
const id = String((spec == null ? void 0 : spec.id) || "").trim();
|
|
9592
|
+
if (!id || seen.has(id)) return;
|
|
9593
|
+
seen.add(id);
|
|
9594
|
+
normalized.push({
|
|
9595
|
+
...spec,
|
|
9596
|
+
id
|
|
9597
|
+
});
|
|
9216
9598
|
});
|
|
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 });
|
|
9599
|
+
return normalized;
|
|
9600
|
+
}
|
|
9601
|
+
async cloneFabricObject(source) {
|
|
9602
|
+
const clone = source.clone;
|
|
9603
|
+
if (typeof clone !== "function") return void 0;
|
|
9604
|
+
const result = clone.call(source);
|
|
9605
|
+
if (!result || typeof result.then !== "function") {
|
|
9606
|
+
return void 0;
|
|
9234
9607
|
}
|
|
9235
|
-
|
|
9236
|
-
|
|
9608
|
+
try {
|
|
9609
|
+
const copied = await result;
|
|
9610
|
+
return copied;
|
|
9611
|
+
} catch (e) {
|
|
9612
|
+
return void 0;
|
|
9237
9613
|
}
|
|
9238
9614
|
}
|
|
9239
|
-
async
|
|
9240
|
-
|
|
9241
|
-
const
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9615
|
+
async createClipPathTemplate(effect) {
|
|
9616
|
+
var _a, _b;
|
|
9617
|
+
const source = effect.source;
|
|
9618
|
+
const sourceId = String(source.id || "").trim();
|
|
9619
|
+
if (!sourceId) return null;
|
|
9620
|
+
const template = await this.createFabricObject({
|
|
9621
|
+
...source,
|
|
9622
|
+
id: sourceId,
|
|
9623
|
+
data: {
|
|
9624
|
+
...source.data || {},
|
|
9625
|
+
id: sourceId,
|
|
9626
|
+
type: "clip-path-effect-template",
|
|
9627
|
+
effectKey: effect.key
|
|
9628
|
+
},
|
|
9629
|
+
props: {
|
|
9630
|
+
...source.props || {},
|
|
9631
|
+
selectable: false,
|
|
9632
|
+
evented: false,
|
|
9633
|
+
excludeFromExport: true
|
|
9247
9634
|
}
|
|
9248
9635
|
});
|
|
9636
|
+
if (!template) return null;
|
|
9637
|
+
(_a = template.set) == null ? void 0 : _a.call(template, {
|
|
9638
|
+
selectable: false,
|
|
9639
|
+
evented: false,
|
|
9640
|
+
excludeFromExport: true,
|
|
9641
|
+
absolutePositioned: true
|
|
9642
|
+
});
|
|
9643
|
+
(_b = template.setCoords) == null ? void 0 : _b.call(template);
|
|
9644
|
+
return template;
|
|
9645
|
+
}
|
|
9646
|
+
isClipPathEffectManaged(target) {
|
|
9647
|
+
return typeof (target == null ? void 0 : target.__pooderEffectClipKey) === "string";
|
|
9648
|
+
}
|
|
9649
|
+
clearClipPathEffectFromObject(target) {
|
|
9650
|
+
var _a, _b;
|
|
9651
|
+
if (!target) return;
|
|
9652
|
+
if (!this.isClipPathEffectManaged(target)) return;
|
|
9653
|
+
(_a = target.set) == null ? void 0 : _a.call(target, { clipPath: void 0 });
|
|
9654
|
+
(_b = target.setCoords) == null ? void 0 : _b.call(target);
|
|
9655
|
+
delete target.__pooderEffectClipKey;
|
|
9656
|
+
}
|
|
9657
|
+
async applyClipPathEffectToObject(target, clipTemplate, effectKey) {
|
|
9658
|
+
var _a, _b, _c, _d;
|
|
9659
|
+
if (!target) return;
|
|
9660
|
+
const clipPath = await this.cloneFabricObject(clipTemplate);
|
|
9661
|
+
if (!clipPath) {
|
|
9662
|
+
this.clearClipPathEffectFromObject(target);
|
|
9663
|
+
return;
|
|
9664
|
+
}
|
|
9665
|
+
(_a = clipPath.set) == null ? void 0 : _a.call(clipPath, {
|
|
9666
|
+
selectable: false,
|
|
9667
|
+
evented: false,
|
|
9668
|
+
excludeFromExport: true,
|
|
9669
|
+
absolutePositioned: true
|
|
9670
|
+
});
|
|
9671
|
+
(_b = clipPath.setCoords) == null ? void 0 : _b.call(clipPath);
|
|
9672
|
+
(_c = target.set) == null ? void 0 : _c.call(target, { clipPath });
|
|
9673
|
+
(_d = target.setCoords) == null ? void 0 : _d.call(target);
|
|
9674
|
+
target.__pooderEffectClipKey = effectKey;
|
|
9675
|
+
}
|
|
9676
|
+
async applyObjectSpecsToPass(passId, specs, options = {}) {
|
|
9677
|
+
const normalizedPassId = String(passId || "").trim();
|
|
9678
|
+
if (!normalizedPassId) return;
|
|
9679
|
+
const replace = options.replace !== false;
|
|
9680
|
+
const normalizedSpecs = this.normalizeObjectSpecs(specs);
|
|
9681
|
+
const desiredIds = new Set(normalizedSpecs.map((s) => s.id));
|
|
9682
|
+
const existing = this.getPassCanvasObjects(normalizedPassId);
|
|
9683
|
+
if (replace) {
|
|
9684
|
+
existing.forEach((obj) => {
|
|
9685
|
+
var _a;
|
|
9686
|
+
const id = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id;
|
|
9687
|
+
if (typeof id === "string" && !desiredIds.has(id)) {
|
|
9688
|
+
this.canvas.remove(obj);
|
|
9689
|
+
}
|
|
9690
|
+
});
|
|
9691
|
+
}
|
|
9249
9692
|
const byId = /* @__PURE__ */ new Map();
|
|
9250
|
-
|
|
9693
|
+
this.getPassCanvasObjects(normalizedPassId).forEach((obj) => {
|
|
9251
9694
|
var _a;
|
|
9252
9695
|
const id = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id;
|
|
9253
9696
|
if (typeof id === "string") byId.set(id, obj);
|
|
9254
9697
|
});
|
|
9255
|
-
for (let index = 0; index <
|
|
9256
|
-
const spec =
|
|
9698
|
+
for (let index = 0; index < normalizedSpecs.length; index += 1) {
|
|
9699
|
+
const spec = normalizedSpecs[index];
|
|
9257
9700
|
let current = byId.get(spec.id);
|
|
9258
|
-
if (
|
|
9259
|
-
|
|
9701
|
+
if (spec.type === "path") {
|
|
9702
|
+
const nextPathData = this.readPathDataFromSpec(spec);
|
|
9703
|
+
if (!nextPathData || !nextPathData.trim()) {
|
|
9704
|
+
if (current) {
|
|
9705
|
+
this.canvas.remove(current);
|
|
9706
|
+
byId.delete(spec.id);
|
|
9707
|
+
}
|
|
9708
|
+
continue;
|
|
9709
|
+
}
|
|
9710
|
+
}
|
|
9711
|
+
if (current && this.shouldRecreateObject(current, spec)) {
|
|
9712
|
+
this.canvas.remove(current);
|
|
9260
9713
|
byId.delete(spec.id);
|
|
9261
9714
|
current = void 0;
|
|
9262
9715
|
}
|
|
9263
9716
|
if (!current) {
|
|
9264
9717
|
const created = await this.createFabricObject(spec);
|
|
9265
9718
|
if (!created) continue;
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9719
|
+
this.patchFabricObject(created, spec, {
|
|
9720
|
+
passId: normalizedPassId,
|
|
9721
|
+
layerId: normalizedPassId,
|
|
9722
|
+
passOrder: index
|
|
9723
|
+
});
|
|
9724
|
+
this.canvas.add(created);
|
|
9725
|
+
byId.set(spec.id, created);
|
|
9726
|
+
continue;
|
|
9271
9727
|
}
|
|
9272
|
-
this.
|
|
9728
|
+
this.patchFabricObject(current, spec, {
|
|
9729
|
+
passId: normalizedPassId,
|
|
9730
|
+
layerId: normalizedPassId,
|
|
9731
|
+
passOrder: index
|
|
9732
|
+
});
|
|
9273
9733
|
}
|
|
9274
|
-
container.dirty = true;
|
|
9275
9734
|
if (options.render !== false) {
|
|
9276
9735
|
this.requestRenderAll();
|
|
9277
9736
|
}
|
|
@@ -9283,10 +9742,56 @@ var CanvasService = class {
|
|
|
9283
9742
|
...extraData || {},
|
|
9284
9743
|
id: spec.id
|
|
9285
9744
|
};
|
|
9745
|
+
nextData.__renderSourceKey = this.getSpecRenderSourceKey(spec);
|
|
9286
9746
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9287
9747
|
obj.set({ ...props, data: nextData });
|
|
9288
9748
|
obj.setCoords();
|
|
9289
9749
|
}
|
|
9750
|
+
readPathDataFromSpec(spec) {
|
|
9751
|
+
var _a, _b;
|
|
9752
|
+
if (spec.type !== "path") return void 0;
|
|
9753
|
+
const raw = ((_a = spec.props) == null ? void 0 : _a.path) || ((_b = spec.props) == null ? void 0 : _b.pathData);
|
|
9754
|
+
if (typeof raw !== "string") return void 0;
|
|
9755
|
+
return raw;
|
|
9756
|
+
}
|
|
9757
|
+
hashText(value) {
|
|
9758
|
+
let hash = 2166136261;
|
|
9759
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
9760
|
+
hash ^= value.charCodeAt(i);
|
|
9761
|
+
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
9762
|
+
}
|
|
9763
|
+
return (hash >>> 0).toString(16);
|
|
9764
|
+
}
|
|
9765
|
+
getSpecRenderSourceKey(spec) {
|
|
9766
|
+
var _a, _b;
|
|
9767
|
+
switch (spec.type) {
|
|
9768
|
+
case "path": {
|
|
9769
|
+
const pathData = this.readPathDataFromSpec(spec) || "";
|
|
9770
|
+
return `path:${this.hashText(pathData)}`;
|
|
9771
|
+
}
|
|
9772
|
+
case "image":
|
|
9773
|
+
return `image:${String(spec.src || "")}`;
|
|
9774
|
+
case "text":
|
|
9775
|
+
return `text:${String((_b = (_a = spec.props) == null ? void 0 : _a.text) != null ? _b : "")}`;
|
|
9776
|
+
case "rect":
|
|
9777
|
+
return "rect";
|
|
9778
|
+
default:
|
|
9779
|
+
return String(spec.type || "");
|
|
9780
|
+
}
|
|
9781
|
+
}
|
|
9782
|
+
shouldRecreateObject(current, spec) {
|
|
9783
|
+
var _a;
|
|
9784
|
+
if (!current) return true;
|
|
9785
|
+
const currentType = String((current == null ? void 0 : current.type) || "").toLowerCase();
|
|
9786
|
+
if (currentType !== spec.type) return true;
|
|
9787
|
+
const expectedKey = this.getSpecRenderSourceKey(spec);
|
|
9788
|
+
const currentKey = String(((_a = current == null ? void 0 : current.data) == null ? void 0 : _a.__renderSourceKey) || "");
|
|
9789
|
+
if (currentKey && expectedKey && currentKey !== expectedKey) return true;
|
|
9790
|
+
if (spec.type === "image" && spec.src && current.getSrc) {
|
|
9791
|
+
return current.getSrc() !== spec.src;
|
|
9792
|
+
}
|
|
9793
|
+
return false;
|
|
9794
|
+
}
|
|
9290
9795
|
resolveFabricProps(spec, props) {
|
|
9291
9796
|
const space = spec.space || "scene";
|
|
9292
9797
|
const next = this.resolveLayoutProps(spec, props);
|
|
@@ -9310,26 +9815,26 @@ var CanvasService = class {
|
|
|
9310
9815
|
next.scaleY = rawScaleY * sceneScale;
|
|
9311
9816
|
return next;
|
|
9312
9817
|
}
|
|
9313
|
-
|
|
9818
|
+
moveObjectInCanvas(obj, index) {
|
|
9314
9819
|
if (!obj) return;
|
|
9315
|
-
const moveObjectTo =
|
|
9820
|
+
const moveObjectTo = this.canvas.moveObjectTo;
|
|
9316
9821
|
if (typeof moveObjectTo === "function") {
|
|
9317
|
-
moveObjectTo.call(
|
|
9822
|
+
moveObjectTo.call(this.canvas, obj, index);
|
|
9318
9823
|
return;
|
|
9319
9824
|
}
|
|
9320
|
-
const list =
|
|
9825
|
+
const list = this.canvas._objects;
|
|
9321
9826
|
if (!Array.isArray(list)) return;
|
|
9322
9827
|
const from = list.indexOf(obj);
|
|
9323
9828
|
if (from < 0 || from === index) return;
|
|
9324
9829
|
list.splice(from, 1);
|
|
9325
9830
|
const target = Math.max(0, Math.min(index, list.length));
|
|
9326
9831
|
list.splice(target, 0, obj);
|
|
9327
|
-
if (typeof
|
|
9328
|
-
|
|
9832
|
+
if (typeof this.canvas._onStackOrderChanged === "function") {
|
|
9833
|
+
this.canvas._onStackOrderChanged();
|
|
9329
9834
|
}
|
|
9330
9835
|
}
|
|
9331
9836
|
async createFabricObject(spec) {
|
|
9332
|
-
var _a, _b
|
|
9837
|
+
var _a, _b;
|
|
9333
9838
|
if (spec.type === "rect") {
|
|
9334
9839
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9335
9840
|
const rect = new import_fabric5.Rect({
|
|
@@ -9340,7 +9845,7 @@ var CanvasService = class {
|
|
|
9340
9845
|
return rect;
|
|
9341
9846
|
}
|
|
9342
9847
|
if (spec.type === "path") {
|
|
9343
|
-
const pathData =
|
|
9848
|
+
const pathData = this.readPathDataFromSpec(spec);
|
|
9344
9849
|
if (!pathData) return void 0;
|
|
9345
9850
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9346
9851
|
const path = new import_fabric5.Path(pathData, {
|
|
@@ -9362,7 +9867,7 @@ var CanvasService = class {
|
|
|
9362
9867
|
return image;
|
|
9363
9868
|
}
|
|
9364
9869
|
if (spec.type === "text") {
|
|
9365
|
-
const content = String((
|
|
9870
|
+
const content = String((_b = (_a = spec.props) == null ? void 0 : _a.text) != null ? _b : "");
|
|
9366
9871
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9367
9872
|
const text = new import_fabric5.Text(content, {
|
|
9368
9873
|
...props,
|
|
@@ -9385,8 +9890,8 @@ var CanvasService = class {
|
|
|
9385
9890
|
MirrorTool,
|
|
9386
9891
|
RulerTool,
|
|
9387
9892
|
SceneLayoutService,
|
|
9388
|
-
SceneVisibilityService,
|
|
9389
9893
|
SizeTool,
|
|
9390
9894
|
ViewportSystem,
|
|
9391
|
-
WhiteInkTool
|
|
9895
|
+
WhiteInkTool,
|
|
9896
|
+
evaluateVisibilityExpr
|
|
9392
9897
|
});
|