@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.mjs
CHANGED
|
@@ -3,291 +3,94 @@ import {
|
|
|
3
3
|
ContributionPointIds
|
|
4
4
|
} from "@pooder/core";
|
|
5
5
|
import { FabricImage } from "fabric";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
this.color = "";
|
|
18
|
-
this.url = "";
|
|
19
|
-
this.specs = [];
|
|
20
|
-
this.renderSeq = 0;
|
|
21
|
-
this.renderImageUrl = "";
|
|
22
|
-
this.sourceSizeBySrc = /* @__PURE__ */ new Map();
|
|
23
|
-
this.pendingSizeBySrc = /* @__PURE__ */ new Map();
|
|
24
|
-
this.onCanvasResized = () => {
|
|
25
|
-
this.updateBackground();
|
|
26
|
-
};
|
|
27
|
-
if (options) {
|
|
28
|
-
Object.assign(this, options);
|
|
6
|
+
|
|
7
|
+
// src/coordinate.ts
|
|
8
|
+
var Coordinate = class {
|
|
9
|
+
/**
|
|
10
|
+
* Calculate layout to fit content within container while preserving aspect ratio.
|
|
11
|
+
*/
|
|
12
|
+
static calculateLayout(container, content, padding = 0) {
|
|
13
|
+
const availableWidth = Math.max(0, container.width - padding * 2);
|
|
14
|
+
const availableHeight = Math.max(0, container.height - padding * 2);
|
|
15
|
+
if (content.width === 0 || content.height === 0) {
|
|
16
|
+
return { scale: 1, offsetX: 0, offsetY: 0, width: 0, height: 0 };
|
|
29
17
|
}
|
|
18
|
+
const scaleX = availableWidth / content.width;
|
|
19
|
+
const scaleY = availableHeight / content.height;
|
|
20
|
+
const scale = Math.min(scaleX, scaleY);
|
|
21
|
+
const width = content.width * scale;
|
|
22
|
+
const height = content.height * scale;
|
|
23
|
+
const offsetX = (container.width - width) / 2;
|
|
24
|
+
const offsetY = (container.height - height) / 2;
|
|
25
|
+
return { scale, offsetX, offsetY, width, height };
|
|
30
26
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
(_a = this.renderProducerDisposable) == null ? void 0 : _a.dispose();
|
|
39
|
-
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
40
|
-
this.id,
|
|
41
|
-
() => ({
|
|
42
|
-
layerSpecs: {
|
|
43
|
-
[BACKGROUND_LAYER_ID]: this.specs
|
|
44
|
-
}
|
|
45
|
-
}),
|
|
46
|
-
{ priority: 0 }
|
|
47
|
-
);
|
|
48
|
-
const configService = context.services.get(
|
|
49
|
-
"ConfigurationService"
|
|
50
|
-
);
|
|
51
|
-
if (configService) {
|
|
52
|
-
this.color = configService.get("background.color", this.color);
|
|
53
|
-
this.url = configService.get("background.url", this.url);
|
|
54
|
-
configService.onAnyChange((e) => {
|
|
55
|
-
if (e.key.startsWith("background.")) {
|
|
56
|
-
const prop = e.key.split(".")[1];
|
|
57
|
-
console.log(
|
|
58
|
-
`[BackgroundTool] Config change detected: ${e.key} -> ${e.value}, prop: ${prop}`
|
|
59
|
-
);
|
|
60
|
-
if (prop && prop in this) {
|
|
61
|
-
console.log(
|
|
62
|
-
`[BackgroundTool] Updating option ${prop} to ${e.value}`
|
|
63
|
-
);
|
|
64
|
-
this[prop] = e.value;
|
|
65
|
-
this.updateBackground();
|
|
66
|
-
} else {
|
|
67
|
-
console.warn(
|
|
68
|
-
`[BackgroundTool] Property ${prop} not found in options`
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
context.eventBus.on("canvas:resized", this.onCanvasResized);
|
|
75
|
-
this.updateBackground();
|
|
27
|
+
/**
|
|
28
|
+
* Convert an absolute value to a normalized value (0-1).
|
|
29
|
+
* @param value Absolute value (e.g., pixels)
|
|
30
|
+
* @param total Total dimension size (e.g., canvas width)
|
|
31
|
+
*/
|
|
32
|
+
static toNormalized(value, total) {
|
|
33
|
+
return total === 0 ? 0 : value / total;
|
|
76
34
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
this.renderProducerDisposable = void 0;
|
|
85
|
-
if (!this.canvasService) return;
|
|
86
|
-
const layer = this.canvasService.getLayer(BACKGROUND_LAYER_ID);
|
|
87
|
-
if (layer) {
|
|
88
|
-
this.canvasService.canvas.remove(layer);
|
|
89
|
-
}
|
|
90
|
-
void this.canvasService.flushRenderFromProducers();
|
|
91
|
-
this.canvasService.requestRenderAll();
|
|
92
|
-
this.canvasService = void 0;
|
|
35
|
+
/**
|
|
36
|
+
* Convert a normalized value (0-1) to an absolute value.
|
|
37
|
+
* @param normalized Normalized value (0-1)
|
|
38
|
+
* @param total Total dimension size (e.g., canvas width)
|
|
39
|
+
*/
|
|
40
|
+
static toAbsolute(normalized, total) {
|
|
41
|
+
return normalized * total;
|
|
93
42
|
}
|
|
94
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Normalize a point's coordinates.
|
|
45
|
+
*/
|
|
46
|
+
static normalizePoint(point, size) {
|
|
95
47
|
return {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
id: "background.color",
|
|
99
|
-
type: "color",
|
|
100
|
-
label: "Background Color",
|
|
101
|
-
default: ""
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
id: "background.url",
|
|
105
|
-
type: "string",
|
|
106
|
-
label: "Image URL",
|
|
107
|
-
default: ""
|
|
108
|
-
}
|
|
109
|
-
],
|
|
110
|
-
[ContributionPointIds.COMMANDS]: [
|
|
111
|
-
{
|
|
112
|
-
command: "reset",
|
|
113
|
-
title: "Reset Background",
|
|
114
|
-
handler: () => {
|
|
115
|
-
this.updateBackground();
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
command: "clear",
|
|
121
|
-
title: "Clear Background",
|
|
122
|
-
handler: () => {
|
|
123
|
-
this.color = "transparent";
|
|
124
|
-
this.url = "";
|
|
125
|
-
this.updateBackground();
|
|
126
|
-
return true;
|
|
127
|
-
}
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
command: "setBackgroundColor",
|
|
131
|
-
title: "Set Background Color",
|
|
132
|
-
handler: (color) => {
|
|
133
|
-
if (this.color === color) return true;
|
|
134
|
-
this.color = color;
|
|
135
|
-
this.updateBackground();
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
command: "setBackgroundImage",
|
|
141
|
-
title: "Set Background Image",
|
|
142
|
-
handler: (url) => {
|
|
143
|
-
if (this.url === url) return true;
|
|
144
|
-
this.url = url;
|
|
145
|
-
this.updateBackground();
|
|
146
|
-
return true;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
]
|
|
48
|
+
x: this.toNormalized(point.x, size.width),
|
|
49
|
+
y: this.toNormalized(point.y, size.height)
|
|
150
50
|
};
|
|
151
51
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Denormalize a point's coordinates to absolute pixels.
|
|
54
|
+
*/
|
|
55
|
+
static denormalizePoint(point, size) {
|
|
156
56
|
return {
|
|
157
|
-
|
|
158
|
-
|
|
57
|
+
x: this.toAbsolute(point.x, size.width),
|
|
58
|
+
y: this.toAbsolute(point.y, size.height)
|
|
159
59
|
};
|
|
160
60
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
props: {
|
|
174
|
-
left: 0,
|
|
175
|
-
top: 0,
|
|
176
|
-
width,
|
|
177
|
-
height,
|
|
178
|
-
originX: "left",
|
|
179
|
-
originY: "top",
|
|
180
|
-
fill: color,
|
|
181
|
-
selectable: false,
|
|
182
|
-
evented: false,
|
|
183
|
-
excludeFromExport: true
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
];
|
|
187
|
-
if (!imageUrl) {
|
|
188
|
-
return specs;
|
|
189
|
-
}
|
|
190
|
-
const sourceSize = this.sourceSizeBySrc.get(imageUrl);
|
|
191
|
-
const sourceWidth = Math.max(1, Number((sourceSize == null ? void 0 : sourceSize.width) || width));
|
|
192
|
-
const sourceHeight = Math.max(1, Number((sourceSize == null ? void 0 : sourceSize.height) || height));
|
|
193
|
-
const coverScale = Math.max(width / sourceWidth, height / sourceHeight);
|
|
194
|
-
specs.push({
|
|
195
|
-
id: BACKGROUND_IMAGE_ID,
|
|
196
|
-
type: "image",
|
|
197
|
-
src: imageUrl,
|
|
198
|
-
space: "screen",
|
|
199
|
-
data: {
|
|
200
|
-
id: BACKGROUND_IMAGE_ID,
|
|
201
|
-
layerId: BACKGROUND_LAYER_ID,
|
|
202
|
-
type: "background-image"
|
|
203
|
-
},
|
|
204
|
-
props: {
|
|
205
|
-
left: 0,
|
|
206
|
-
top: 0,
|
|
207
|
-
originX: "left",
|
|
208
|
-
originY: "top",
|
|
209
|
-
scaleX: coverScale,
|
|
210
|
-
scaleY: coverScale,
|
|
211
|
-
selectable: false,
|
|
212
|
-
evented: false,
|
|
213
|
-
excludeFromExport: true
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
return specs;
|
|
217
|
-
}
|
|
218
|
-
async ensureImageSize(src) {
|
|
219
|
-
if (!src) return null;
|
|
220
|
-
const cached = this.sourceSizeBySrc.get(src);
|
|
221
|
-
if (cached) return cached;
|
|
222
|
-
const pending = this.pendingSizeBySrc.get(src);
|
|
223
|
-
if (pending) {
|
|
224
|
-
return pending;
|
|
225
|
-
}
|
|
226
|
-
const task = this.loadImageSize(src);
|
|
227
|
-
this.pendingSizeBySrc.set(src, task);
|
|
228
|
-
try {
|
|
229
|
-
return await task;
|
|
230
|
-
} finally {
|
|
231
|
-
if (this.pendingSizeBySrc.get(src) === task) {
|
|
232
|
-
this.pendingSizeBySrc.delete(src);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
async loadImageSize(src) {
|
|
237
|
-
try {
|
|
238
|
-
const image = await FabricImage.fromURL(src, {
|
|
239
|
-
crossOrigin: "anonymous"
|
|
240
|
-
});
|
|
241
|
-
const width = Number((image == null ? void 0 : image.width) || 0);
|
|
242
|
-
const height = Number((image == null ? void 0 : image.height) || 0);
|
|
243
|
-
if (width > 0 && height > 0) {
|
|
244
|
-
const size = { width, height };
|
|
245
|
-
this.sourceSizeBySrc.set(src, size);
|
|
246
|
-
return size;
|
|
247
|
-
}
|
|
248
|
-
} catch (error) {
|
|
249
|
-
console.error("[BackgroundTool] Failed to load image", src, error);
|
|
61
|
+
static convertUnit(value, from, to) {
|
|
62
|
+
if (from === to) return value;
|
|
63
|
+
const toMM = {
|
|
64
|
+
px: 0.264583,
|
|
65
|
+
// 1px = 0.264583mm (96 DPI)
|
|
66
|
+
mm: 1,
|
|
67
|
+
cm: 10,
|
|
68
|
+
in: 25.4
|
|
69
|
+
};
|
|
70
|
+
const mmValue = value * (from === "px" ? toMM.px : toMM[from] || 1);
|
|
71
|
+
if (to === "px") {
|
|
72
|
+
return mmValue / toMM.px;
|
|
250
73
|
}
|
|
251
|
-
return
|
|
74
|
+
return mmValue / (toMM[to] || 1);
|
|
252
75
|
}
|
|
253
|
-
|
|
254
|
-
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/units.ts
|
|
79
|
+
function parseLengthToMm(input, defaultUnit) {
|
|
80
|
+
var _a, _b;
|
|
81
|
+
if (typeof input === "number") {
|
|
82
|
+
if (!Number.isFinite(input)) return 0;
|
|
83
|
+
return Coordinate.convertUnit(input, defaultUnit, "mm");
|
|
255
84
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (seq !== this.renderSeq) return;
|
|
266
|
-
if (loaded) {
|
|
267
|
-
this.renderImageUrl = nextUrl;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
this.specs = this.buildBackgroundSpecs(color, this.renderImageUrl);
|
|
271
|
-
await this.canvasService.flushRenderFromProducers();
|
|
272
|
-
if (seq !== this.renderSeq) return;
|
|
273
|
-
const layer = this.canvasService.getLayer(BACKGROUND_LAYER_ID);
|
|
274
|
-
if (layer) {
|
|
275
|
-
this.canvasService.canvas.sendObjectToBack(layer);
|
|
276
|
-
}
|
|
277
|
-
this.canvasService.requestRenderAll();
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
// src/extensions/image.ts
|
|
282
|
-
import {
|
|
283
|
-
ContributionPointIds as ContributionPointIds2
|
|
284
|
-
} from "@pooder/core";
|
|
285
|
-
import {
|
|
286
|
-
Canvas as FabricCanvas,
|
|
287
|
-
Image as FabricImage2,
|
|
288
|
-
Pattern,
|
|
289
|
-
Point
|
|
290
|
-
} from "fabric";
|
|
85
|
+
const raw = input.trim();
|
|
86
|
+
if (!raw) return 0;
|
|
87
|
+
const match = raw.match(/^([+-]?\d+(?:\.\d+)?)\s*(px|mm|cm|in)?$/i);
|
|
88
|
+
if (!match) return 0;
|
|
89
|
+
const value = Number(match[1]);
|
|
90
|
+
if (!Number.isFinite(value)) return 0;
|
|
91
|
+
const unit = (_b = (_a = match[2]) == null ? void 0 : _a.toLowerCase()) != null ? _b : defaultUnit;
|
|
92
|
+
return Coordinate.convertUnit(value, unit, "mm");
|
|
93
|
+
}
|
|
291
94
|
|
|
292
95
|
// src/extensions/dielineShape.ts
|
|
293
96
|
var BUILTIN_DIELINE_SHAPES = [
|
|
@@ -304,7 +107,7 @@ var DEFAULT_HEART_SHAPE_PARAMS = {
|
|
|
304
107
|
tipSharpness: 0
|
|
305
108
|
};
|
|
306
109
|
var DEFAULT_DIELINE_SHAPE_STYLE = {
|
|
307
|
-
fitMode: "
|
|
110
|
+
fitMode: "stretch",
|
|
308
111
|
...DEFAULT_HEART_SHAPE_PARAMS
|
|
309
112
|
};
|
|
310
113
|
function isDielineShape(value) {
|
|
@@ -354,962 +157,1393 @@ function getHeartShapeParams(style) {
|
|
|
354
157
|
};
|
|
355
158
|
}
|
|
356
159
|
|
|
357
|
-
// src/extensions/
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
160
|
+
// src/extensions/sceneLayoutModel.ts
|
|
161
|
+
var DEFAULT_SIZE_STATE = {
|
|
162
|
+
unit: "mm",
|
|
163
|
+
actualWidthMm: 500,
|
|
164
|
+
actualHeightMm: 500,
|
|
165
|
+
constraintMode: "free",
|
|
166
|
+
aspectRatio: 1,
|
|
167
|
+
cutMode: "trim",
|
|
168
|
+
cutMarginMm: 0,
|
|
169
|
+
viewPadding: 140,
|
|
170
|
+
minMm: 10,
|
|
171
|
+
maxMm: 2e3,
|
|
172
|
+
stepMm: 0.1
|
|
173
|
+
};
|
|
174
|
+
function clamp(value, min, max) {
|
|
175
|
+
return Math.max(min, Math.min(max, value));
|
|
367
176
|
}
|
|
368
|
-
function
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if (s.outsideAbove) score++;
|
|
372
|
-
}
|
|
373
|
-
return score;
|
|
177
|
+
function roundToStep(value, step) {
|
|
178
|
+
if (!Number.isFinite(step) || step <= 0) return value;
|
|
179
|
+
return Math.round(value / step) * step;
|
|
374
180
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
if (!Number.isFinite(start) || !Number.isFinite(end)) return 0;
|
|
380
|
-
const s = (start % total + total) % total;
|
|
381
|
-
const e = (end % total + total) % total;
|
|
382
|
-
return e >= s ? e - s : total - s + e;
|
|
181
|
+
function sanitizeMmValue(valueMm, limits) {
|
|
182
|
+
if (!Number.isFinite(valueMm)) return limits.minMm;
|
|
183
|
+
const rounded = roundToStep(valueMm, limits.stepMm);
|
|
184
|
+
return clamp(rounded, limits.minMm, limits.maxMm);
|
|
383
185
|
}
|
|
384
|
-
function
|
|
385
|
-
if (
|
|
386
|
-
|
|
387
|
-
const n = Math.max(0, Math.floor(count));
|
|
388
|
-
if (n <= 0) return [];
|
|
389
|
-
const dist = wrappedDistance(total, start, end);
|
|
390
|
-
if (n === 1) return [(start % total + total) % total];
|
|
391
|
-
const step = dist / (n - 1);
|
|
392
|
-
const offsets = [];
|
|
393
|
-
for (let i = 0; i < n; i++) {
|
|
394
|
-
const raw = start + step * i;
|
|
395
|
-
const wrapped = (raw % total + total) % total;
|
|
396
|
-
offsets.push(wrapped);
|
|
397
|
-
}
|
|
398
|
-
return offsets;
|
|
186
|
+
function normalizeUnit(value) {
|
|
187
|
+
if (value === "cm" || value === "in") return value;
|
|
188
|
+
return "mm";
|
|
399
189
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
const { x, y, width, height } = geometry;
|
|
404
|
-
const left = x - width / 2;
|
|
405
|
-
const top = y - height / 2;
|
|
406
|
-
return {
|
|
407
|
-
x: left + feature.x * width,
|
|
408
|
-
y: top + feature.y * height
|
|
409
|
-
};
|
|
190
|
+
function normalizeConstraintMode(value) {
|
|
191
|
+
if (value === "lockAspect" || value === "equal") return value;
|
|
192
|
+
return "free";
|
|
410
193
|
}
|
|
411
|
-
function
|
|
412
|
-
if (
|
|
413
|
-
|
|
414
|
-
} else {
|
|
415
|
-
paper.view.viewSize = new paper.Size(width, height);
|
|
416
|
-
}
|
|
194
|
+
function normalizeCutMode(value) {
|
|
195
|
+
if (value === "outset" || value === "inset") return value;
|
|
196
|
+
return "trim";
|
|
417
197
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
let result = shape;
|
|
421
|
-
if (typeof result.resolveCrossings === "function") result = result.resolveCrossings();
|
|
422
|
-
if (typeof result.reduce === "function") result = result.reduce({});
|
|
423
|
-
if (typeof result.reorient === "function") result = result.reorient(true, true);
|
|
424
|
-
if (typeof result.reduce === "function") result = result.reduce({});
|
|
425
|
-
return result;
|
|
198
|
+
function toMm(value, fromUnit) {
|
|
199
|
+
return Coordinate.convertUnit(value, fromUnit, "mm");
|
|
426
200
|
}
|
|
427
|
-
function
|
|
428
|
-
return
|
|
201
|
+
function fromMm(valueMm, toUnit) {
|
|
202
|
+
return Coordinate.convertUnit(valueMm, "mm", toUnit);
|
|
429
203
|
}
|
|
430
|
-
function
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
if (validHits.length === 0) return null;
|
|
441
|
-
validHits.sort((a, b) => b.point.y - a.point.y);
|
|
442
|
-
const flags = validHits.map((h) => {
|
|
443
|
-
const above = h.point.add(new paper.Point(0, -delta));
|
|
444
|
-
const below = h.point.add(new paper.Point(0, delta));
|
|
445
|
-
return {
|
|
446
|
-
insideAbove: mainShape.contains(above),
|
|
447
|
-
insideBelow: mainShape.contains(below)
|
|
448
|
-
};
|
|
449
|
-
});
|
|
450
|
-
const idx = pickExitIndex(flags);
|
|
451
|
-
if (idx < 0) return null;
|
|
452
|
-
if (isBridgeDebugEnabled()) {
|
|
453
|
-
console.debug("Geometry: Bridge ray", {
|
|
454
|
-
x,
|
|
455
|
-
validHits: validHits.length,
|
|
456
|
-
idx,
|
|
457
|
-
delta,
|
|
458
|
-
overlap,
|
|
459
|
-
op
|
|
460
|
-
});
|
|
204
|
+
function resolvePaddingPx(raw, containerWidth, containerHeight) {
|
|
205
|
+
if (typeof raw === "number") return Math.max(0, raw);
|
|
206
|
+
if (typeof raw === "string") {
|
|
207
|
+
if (raw.endsWith("%")) {
|
|
208
|
+
const percent = parseFloat(raw) / 100;
|
|
209
|
+
if (!Number.isFinite(percent)) return 0;
|
|
210
|
+
return Math.max(0, Math.min(containerWidth, containerHeight) * percent);
|
|
211
|
+
}
|
|
212
|
+
const fixed = parseFloat(raw);
|
|
213
|
+
return Number.isFinite(fixed) ? Math.max(0, fixed) : 0;
|
|
461
214
|
}
|
|
462
|
-
|
|
463
|
-
return { point: hit.point, location: hit };
|
|
215
|
+
return 0;
|
|
464
216
|
}
|
|
465
|
-
function
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
pointsA.map((p) => ({
|
|
469
|
-
outsideAbove: !mainShape.contains(p.add(new paper.Point(0, -delta)))
|
|
470
|
-
}))
|
|
217
|
+
function readSizeState(configService) {
|
|
218
|
+
const unit = normalizeUnit(
|
|
219
|
+
configService.get("size.unit", DEFAULT_SIZE_STATE.unit)
|
|
471
220
|
);
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
}))
|
|
221
|
+
const minMm = Math.max(
|
|
222
|
+
0.1,
|
|
223
|
+
Number(configService.get("size.minMm", DEFAULT_SIZE_STATE.minMm))
|
|
476
224
|
);
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
console.debug("Geometry: Bridge chain", {
|
|
481
|
-
scoreA,
|
|
482
|
-
scoreB,
|
|
483
|
-
lenA: pointsA.length,
|
|
484
|
-
lenB: pointsB.length,
|
|
485
|
-
ratioA,
|
|
486
|
-
ratioB,
|
|
487
|
-
delta,
|
|
488
|
-
overlap,
|
|
489
|
-
op
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
const ratioEps = 1e-6;
|
|
493
|
-
if (Math.abs(ratioA - ratioB) > ratioEps) {
|
|
494
|
-
return ratioA > ratioB ? pointsA : pointsB;
|
|
495
|
-
}
|
|
496
|
-
if (scoreA !== scoreB) return scoreA > scoreB ? pointsA : pointsB;
|
|
497
|
-
return pointsA.length <= pointsB.length ? pointsA : pointsB;
|
|
498
|
-
}
|
|
499
|
-
function fitPathItemToRect(item, rect, fitMode) {
|
|
500
|
-
const { left, top, width, height } = rect;
|
|
501
|
-
const bounds = item.bounds;
|
|
502
|
-
if (width <= 0 || height <= 0 || !Number.isFinite(bounds.width) || !Number.isFinite(bounds.height) || bounds.width <= 0 || bounds.height <= 0) {
|
|
503
|
-
item.position = new paper.Point(left + width / 2, top + height / 2);
|
|
504
|
-
return item;
|
|
505
|
-
}
|
|
506
|
-
item.translate(new paper.Point(-bounds.left, -bounds.top));
|
|
507
|
-
if (fitMode === "stretch") {
|
|
508
|
-
item.scale(width / bounds.width, height / bounds.height, new paper.Point(0, 0));
|
|
509
|
-
item.translate(new paper.Point(left, top));
|
|
510
|
-
return item;
|
|
511
|
-
}
|
|
512
|
-
const uniformScale = Math.min(width / bounds.width, height / bounds.height);
|
|
513
|
-
item.scale(uniformScale, uniformScale, new paper.Point(0, 0));
|
|
514
|
-
const scaledWidth = bounds.width * uniformScale;
|
|
515
|
-
const scaledHeight = bounds.height * uniformScale;
|
|
516
|
-
item.translate(
|
|
517
|
-
new paper.Point(
|
|
518
|
-
left + (width - scaledWidth) / 2,
|
|
519
|
-
top + (height - scaledHeight) / 2
|
|
520
|
-
)
|
|
225
|
+
const maxMm = Math.max(
|
|
226
|
+
minMm,
|
|
227
|
+
Number(configService.get("size.maxMm", DEFAULT_SIZE_STATE.maxMm))
|
|
521
228
|
);
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
const { lobeSpread, notchDepth, tipSharpness } = params;
|
|
526
|
-
const halfSpread = 0.22 + lobeSpread * 0.18;
|
|
527
|
-
const notchY = 0.06 + notchDepth * 0.2;
|
|
528
|
-
const shoulderY = 0.24 + notchDepth * 0.2;
|
|
529
|
-
const topLift = 0.12 + (1 - notchDepth) * 0.06;
|
|
530
|
-
const topY = notchY - topLift;
|
|
531
|
-
const sideCtrlY = shoulderY - (0.18 - notchDepth * 0.08);
|
|
532
|
-
const lowerCtrlY = 0.58 + (1 - tipSharpness) * 0.16;
|
|
533
|
-
const tipCtrlX = 0.34 - tipSharpness * 0.2;
|
|
534
|
-
const notchCtrlX = 0.06 + lobeSpread * 0.06;
|
|
535
|
-
const lobeCtrlX = 0.1 + lobeSpread * 0.08;
|
|
536
|
-
const notchCtrlY = notchY - topLift * 0.45;
|
|
537
|
-
const xPeakL = 0.5 - halfSpread;
|
|
538
|
-
const xPeakR = 0.5 + halfSpread;
|
|
539
|
-
const heartPath = new paper.Path({ insert: false });
|
|
540
|
-
heartPath.moveTo(new paper.Point(0.5, notchY));
|
|
541
|
-
heartPath.cubicCurveTo(
|
|
542
|
-
new paper.Point(0.5 - notchCtrlX, notchCtrlY),
|
|
543
|
-
new paper.Point(xPeakL + lobeCtrlX, topY),
|
|
544
|
-
new paper.Point(xPeakL, topY)
|
|
229
|
+
const stepMm = Math.max(
|
|
230
|
+
1e-3,
|
|
231
|
+
Number(configService.get("size.stepMm", DEFAULT_SIZE_STATE.stepMm))
|
|
545
232
|
);
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
233
|
+
const actualWidthMm = sanitizeMmValue(
|
|
234
|
+
parseLengthToMm(
|
|
235
|
+
configService.get("size.actualWidthMm", DEFAULT_SIZE_STATE.actualWidthMm),
|
|
236
|
+
"mm"
|
|
237
|
+
),
|
|
238
|
+
{ minMm, maxMm, stepMm }
|
|
550
239
|
);
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
240
|
+
const actualHeightMm = sanitizeMmValue(
|
|
241
|
+
parseLengthToMm(
|
|
242
|
+
configService.get(
|
|
243
|
+
"size.actualHeightMm",
|
|
244
|
+
DEFAULT_SIZE_STATE.actualHeightMm
|
|
245
|
+
),
|
|
246
|
+
"mm"
|
|
247
|
+
),
|
|
248
|
+
{ minMm, maxMm, stepMm }
|
|
555
249
|
);
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
new paper.Point(1, lowerCtrlY),
|
|
559
|
-
new paper.Point(1, shoulderY)
|
|
250
|
+
const aspectRaw = Number(
|
|
251
|
+
configService.get("size.aspectRatio", DEFAULT_SIZE_STATE.aspectRatio)
|
|
560
252
|
);
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
253
|
+
const aspectRatio = Number.isFinite(aspectRaw) && aspectRaw > 0 ? aspectRaw : actualWidthMm / Math.max(1e-3, actualHeightMm);
|
|
254
|
+
const cutMarginMm = Math.max(
|
|
255
|
+
0,
|
|
256
|
+
parseLengthToMm(
|
|
257
|
+
configService.get("size.cutMarginMm", DEFAULT_SIZE_STATE.cutMarginMm),
|
|
258
|
+
"mm"
|
|
259
|
+
)
|
|
565
260
|
);
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
new paper.Point(0.5, notchY)
|
|
261
|
+
const viewPadding = configService.get(
|
|
262
|
+
"size.viewPadding",
|
|
263
|
+
DEFAULT_SIZE_STATE.viewPadding
|
|
570
264
|
);
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
265
|
+
return {
|
|
266
|
+
unit,
|
|
267
|
+
actualWidthMm,
|
|
268
|
+
actualHeightMm,
|
|
269
|
+
constraintMode: normalizeConstraintMode(
|
|
270
|
+
configService.get(
|
|
271
|
+
"size.constraintMode",
|
|
272
|
+
DEFAULT_SIZE_STATE.constraintMode
|
|
273
|
+
)
|
|
274
|
+
),
|
|
275
|
+
aspectRatio,
|
|
276
|
+
cutMode: normalizeCutMode(
|
|
277
|
+
configService.get("size.cutMode", DEFAULT_SIZE_STATE.cutMode)
|
|
278
|
+
),
|
|
279
|
+
cutMarginMm,
|
|
280
|
+
viewPadding,
|
|
281
|
+
minMm,
|
|
282
|
+
maxMm,
|
|
283
|
+
stepMm
|
|
284
|
+
};
|
|
584
285
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
point: [x - width / 2, y - height / 2],
|
|
590
|
-
size: [Math.max(0, width), Math.max(0, height)],
|
|
591
|
-
radius: Math.max(0, radius)
|
|
592
|
-
});
|
|
593
|
-
},
|
|
594
|
-
circle: (options) => {
|
|
595
|
-
const { x, y, width, height } = options;
|
|
596
|
-
const r = Math.min(width, height) / 2;
|
|
597
|
-
return new paper.Path.Circle({
|
|
598
|
-
center: new paper.Point(x, y),
|
|
599
|
-
radius: Math.max(0, r)
|
|
600
|
-
});
|
|
601
|
-
},
|
|
602
|
-
ellipse: (options) => {
|
|
603
|
-
const { x, y, width, height } = options;
|
|
604
|
-
return new paper.Path.Ellipse({
|
|
605
|
-
center: new paper.Point(x, y),
|
|
606
|
-
radius: [Math.max(0, width / 2), Math.max(0, height / 2)]
|
|
607
|
-
});
|
|
608
|
-
},
|
|
609
|
-
heart: createHeartBaseShape
|
|
610
|
-
};
|
|
611
|
-
function createCustomBaseShape(options) {
|
|
612
|
-
var _a;
|
|
613
|
-
const {
|
|
614
|
-
pathData,
|
|
615
|
-
customSourceWidthPx,
|
|
616
|
-
customSourceHeightPx,
|
|
617
|
-
x,
|
|
618
|
-
y,
|
|
286
|
+
function rectByCenter(centerX, centerY, width, height) {
|
|
287
|
+
return {
|
|
288
|
+
left: centerX - width / 2,
|
|
289
|
+
top: centerY - height / 2,
|
|
619
290
|
width,
|
|
620
|
-
height
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
}
|
|
625
|
-
const center = new paper.Point(x, y);
|
|
626
|
-
const hasMultipleSubPaths = ((_a = (pathData.match(/[Mm]/g) || []).length) != null ? _a : 0) > 1;
|
|
627
|
-
const path = hasMultipleSubPaths ? new paper.CompoundPath(pathData) : (() => {
|
|
628
|
-
const single = new paper.Path();
|
|
629
|
-
single.pathData = pathData;
|
|
630
|
-
return single;
|
|
631
|
-
})();
|
|
632
|
-
const sourceWidth = Number(customSourceWidthPx != null ? customSourceWidthPx : 0);
|
|
633
|
-
const sourceHeight = Number(customSourceHeightPx != null ? customSourceHeightPx : 0);
|
|
634
|
-
if (Number.isFinite(sourceWidth) && Number.isFinite(sourceHeight) && sourceWidth > 0 && sourceHeight > 0 && width > 0 && height > 0) {
|
|
635
|
-
const targetLeft = x - width / 2;
|
|
636
|
-
const targetTop = y - height / 2;
|
|
637
|
-
path.scale(width / sourceWidth, height / sourceHeight, new paper.Point(0, 0));
|
|
638
|
-
path.translate(new paper.Point(targetLeft, targetTop));
|
|
639
|
-
return path;
|
|
640
|
-
}
|
|
641
|
-
if (width > 0 && height > 0 && path.bounds.width > 0 && path.bounds.height > 0) {
|
|
642
|
-
path.position = center;
|
|
643
|
-
path.scale(width / path.bounds.width, height / path.bounds.height);
|
|
644
|
-
return path;
|
|
645
|
-
}
|
|
646
|
-
path.position = center;
|
|
647
|
-
return path;
|
|
648
|
-
}
|
|
649
|
-
function createBaseShape(options) {
|
|
650
|
-
const { shape } = options;
|
|
651
|
-
if (shape === "custom") {
|
|
652
|
-
const customShape = createCustomBaseShape(options);
|
|
653
|
-
if (customShape) return customShape;
|
|
654
|
-
return BUILTIN_SHAPE_BUILDERS[DEFAULT_DIELINE_SHAPE](options);
|
|
655
|
-
}
|
|
656
|
-
return BUILTIN_SHAPE_BUILDERS[shape](options);
|
|
291
|
+
height,
|
|
292
|
+
centerX,
|
|
293
|
+
centerY
|
|
294
|
+
};
|
|
657
295
|
}
|
|
658
|
-
function
|
|
659
|
-
if (
|
|
660
|
-
return
|
|
296
|
+
function getCutSizeMm(size) {
|
|
297
|
+
if (size.cutMode === "trim") {
|
|
298
|
+
return { widthMm: size.actualWidthMm, heightMm: size.actualHeightMm };
|
|
661
299
|
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
let bestDistance = Infinity;
|
|
669
|
-
for (const child of children) {
|
|
670
|
-
const location = child.getNearestLocation(anchor);
|
|
671
|
-
const point = location == null ? void 0 : location.point;
|
|
672
|
-
if (!point) continue;
|
|
673
|
-
const distance = point.getDistance(anchor);
|
|
674
|
-
if (distance < bestDistance) {
|
|
675
|
-
bestDistance = distance;
|
|
676
|
-
best = child;
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
return best;
|
|
300
|
+
const delta = size.cutMarginMm * 2;
|
|
301
|
+
if (size.cutMode === "outset") {
|
|
302
|
+
return {
|
|
303
|
+
widthMm: size.actualWidthMm + delta,
|
|
304
|
+
heightMm: size.actualHeightMm + delta
|
|
305
|
+
};
|
|
680
306
|
}
|
|
681
|
-
return
|
|
307
|
+
return {
|
|
308
|
+
widthMm: Math.max(size.minMm, size.actualWidthMm - delta),
|
|
309
|
+
heightMm: Math.max(size.minMm, size.actualHeightMm - delta)
|
|
310
|
+
};
|
|
682
311
|
}
|
|
683
|
-
function
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
radius: r
|
|
693
|
-
});
|
|
694
|
-
} else {
|
|
695
|
-
const r = feature.radius || 5;
|
|
696
|
-
item = new paper.Path.Circle({
|
|
697
|
-
center,
|
|
698
|
-
radius: r
|
|
699
|
-
});
|
|
312
|
+
function computeSceneLayout(canvasService, size) {
|
|
313
|
+
const canvasWidth = canvasService.canvas.width || 0;
|
|
314
|
+
const canvasHeight = canvasService.canvas.height || 0;
|
|
315
|
+
if (canvasWidth <= 0 || canvasHeight <= 0) return null;
|
|
316
|
+
const { widthMm: cutWidthMm, heightMm: cutHeightMm } = getCutSizeMm(size);
|
|
317
|
+
const viewWidthMm = Math.max(size.actualWidthMm, cutWidthMm);
|
|
318
|
+
const viewHeightMm = Math.max(size.actualHeightMm, cutHeightMm);
|
|
319
|
+
if (!Number.isFinite(viewWidthMm) || !Number.isFinite(viewHeightMm) || viewWidthMm <= 0 || viewHeightMm <= 0) {
|
|
320
|
+
return null;
|
|
700
321
|
}
|
|
701
|
-
|
|
702
|
-
|
|
322
|
+
const paddingPx = resolvePaddingPx(
|
|
323
|
+
size.viewPadding,
|
|
324
|
+
canvasWidth,
|
|
325
|
+
canvasHeight
|
|
326
|
+
);
|
|
327
|
+
canvasService.viewport.updateContainer(canvasWidth, canvasHeight);
|
|
328
|
+
canvasService.viewport.setPadding(paddingPx);
|
|
329
|
+
canvasService.viewport.updatePhysical(viewWidthMm, viewHeightMm);
|
|
330
|
+
const layout = canvasService.viewport.layout;
|
|
331
|
+
if (!Number.isFinite(layout.scale) || !Number.isFinite(layout.offsetX) || !Number.isFinite(layout.offsetY) || layout.scale <= 0) {
|
|
332
|
+
return null;
|
|
703
333
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
334
|
+
const centerX = layout.offsetX + layout.width / 2;
|
|
335
|
+
const centerY = layout.offsetY + layout.height / 2;
|
|
336
|
+
const trimWidthPx = size.actualWidthMm * layout.scale;
|
|
337
|
+
const trimHeightPx = size.actualHeightMm * layout.scale;
|
|
338
|
+
const cutWidthPx = cutWidthMm * layout.scale;
|
|
339
|
+
const cutHeightPx = cutHeightMm * layout.scale;
|
|
340
|
+
const trimRect = rectByCenter(centerX, centerY, trimWidthPx, trimHeightPx);
|
|
341
|
+
const cutRect = rectByCenter(centerX, centerY, cutWidthPx, cutHeightPx);
|
|
342
|
+
const bleedRect = rectByCenter(
|
|
343
|
+
centerX,
|
|
344
|
+
centerY,
|
|
345
|
+
Math.max(trimWidthPx, cutWidthPx),
|
|
346
|
+
Math.max(trimHeightPx, cutHeightPx)
|
|
347
|
+
);
|
|
348
|
+
return {
|
|
349
|
+
scale: layout.scale,
|
|
350
|
+
canvasWidth,
|
|
351
|
+
canvasHeight,
|
|
352
|
+
trimRect,
|
|
353
|
+
cutRect,
|
|
354
|
+
bleedRect,
|
|
355
|
+
trimWidthMm: size.actualWidthMm,
|
|
356
|
+
trimHeightMm: size.actualHeightMm,
|
|
357
|
+
cutWidthMm,
|
|
358
|
+
cutHeightMm,
|
|
359
|
+
cutMode: size.cutMode,
|
|
360
|
+
cutMarginMm: size.cutMarginMm
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function buildSceneGeometry(configService, layout) {
|
|
364
|
+
const radiusMm = parseLengthToMm(
|
|
365
|
+
configService.get("dieline.radius", 0),
|
|
366
|
+
"mm"
|
|
367
|
+
);
|
|
368
|
+
const offset = (layout.cutRect.width - layout.trimRect.width) / 2;
|
|
369
|
+
const sourceWidth = Number(configService.get("dieline.customSourceWidthPx", 0));
|
|
370
|
+
const sourceHeight = Number(
|
|
371
|
+
configService.get("dieline.customSourceHeightPx", 0)
|
|
372
|
+
);
|
|
373
|
+
const shapeStyle = normalizeShapeStyle(
|
|
374
|
+
configService.get("dieline.shapeStyle", DEFAULT_DIELINE_SHAPE_STYLE)
|
|
375
|
+
);
|
|
376
|
+
return {
|
|
377
|
+
shape: normalizeDielineShape(
|
|
378
|
+
configService.get("dieline.shape", DEFAULT_DIELINE_SHAPE)
|
|
379
|
+
),
|
|
380
|
+
shapeStyle,
|
|
381
|
+
unit: "px",
|
|
382
|
+
x: layout.trimRect.centerX,
|
|
383
|
+
y: layout.trimRect.centerY,
|
|
384
|
+
width: layout.trimRect.width,
|
|
385
|
+
height: layout.trimRect.height,
|
|
386
|
+
radius: radiusMm * layout.scale,
|
|
387
|
+
offset,
|
|
388
|
+
scale: layout.scale,
|
|
389
|
+
pathData: configService.get("dieline.pathData"),
|
|
390
|
+
customSourceWidthPx: Number.isFinite(sourceWidth) && sourceWidth > 0 ? sourceWidth : void 0,
|
|
391
|
+
customSourceHeightPx: Number.isFinite(sourceHeight) && sourceHeight > 0 ? sourceHeight : void 0
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/extensions/background.ts
|
|
396
|
+
var BACKGROUND_LAYER_ID = "background";
|
|
397
|
+
var BACKGROUND_CONFIG_KEY = "background.config";
|
|
398
|
+
var DEFAULT_WIDTH = 800;
|
|
399
|
+
var DEFAULT_HEIGHT = 600;
|
|
400
|
+
var DEFAULT_BACKGROUND_CONFIG = {
|
|
401
|
+
version: 1,
|
|
402
|
+
layers: [
|
|
403
|
+
{
|
|
404
|
+
id: "base-color",
|
|
405
|
+
kind: "color",
|
|
406
|
+
anchor: "viewport",
|
|
407
|
+
fit: "cover",
|
|
408
|
+
opacity: 1,
|
|
409
|
+
order: 0,
|
|
410
|
+
enabled: true,
|
|
411
|
+
exportable: false,
|
|
412
|
+
color: "#fff"
|
|
413
|
+
}
|
|
414
|
+
]
|
|
415
|
+
};
|
|
416
|
+
function clampOpacity(value, fallback) {
|
|
417
|
+
const numeric = Number(value);
|
|
418
|
+
if (!Number.isFinite(numeric)) {
|
|
419
|
+
return Math.max(0, Math.min(1, fallback));
|
|
420
|
+
}
|
|
421
|
+
return Math.max(0, Math.min(1, numeric));
|
|
422
|
+
}
|
|
423
|
+
function normalizeLayerKind(value, fallback) {
|
|
424
|
+
if (value === "color" || value === "image") {
|
|
425
|
+
return value;
|
|
426
|
+
}
|
|
427
|
+
return fallback;
|
|
428
|
+
}
|
|
429
|
+
function normalizeFitMode2(value, fallback) {
|
|
430
|
+
if (value === "contain" || value === "cover" || value === "stretch") {
|
|
431
|
+
return value;
|
|
432
|
+
}
|
|
433
|
+
return fallback;
|
|
434
|
+
}
|
|
435
|
+
function normalizeAnchor(value, fallback) {
|
|
436
|
+
if (typeof value !== "string") return fallback;
|
|
437
|
+
const trimmed = value.trim();
|
|
438
|
+
return trimmed || fallback;
|
|
439
|
+
}
|
|
440
|
+
function normalizeOrder(value, fallback) {
|
|
441
|
+
const numeric = Number(value);
|
|
442
|
+
if (!Number.isFinite(numeric)) return fallback;
|
|
443
|
+
return numeric;
|
|
444
|
+
}
|
|
445
|
+
function normalizeLayer(raw, index, fallback) {
|
|
446
|
+
const fallbackLayer = fallback || {
|
|
447
|
+
id: `layer-${index + 1}`,
|
|
448
|
+
kind: "image",
|
|
449
|
+
anchor: "viewport",
|
|
450
|
+
fit: "contain",
|
|
451
|
+
opacity: 1,
|
|
452
|
+
order: index,
|
|
453
|
+
enabled: true,
|
|
454
|
+
exportable: false,
|
|
455
|
+
src: ""
|
|
456
|
+
};
|
|
457
|
+
if (!raw || typeof raw !== "object") {
|
|
458
|
+
return { ...fallbackLayer };
|
|
459
|
+
}
|
|
460
|
+
const input = raw;
|
|
461
|
+
const kind = normalizeLayerKind(input.kind, fallbackLayer.kind);
|
|
462
|
+
return {
|
|
463
|
+
id: typeof input.id === "string" && input.id.trim().length > 0 ? input.id.trim() : fallbackLayer.id,
|
|
464
|
+
kind,
|
|
465
|
+
anchor: normalizeAnchor(input.anchor, fallbackLayer.anchor),
|
|
466
|
+
fit: normalizeFitMode2(input.fit, fallbackLayer.fit),
|
|
467
|
+
opacity: clampOpacity(input.opacity, fallbackLayer.opacity),
|
|
468
|
+
order: normalizeOrder(input.order, fallbackLayer.order),
|
|
469
|
+
enabled: typeof input.enabled === "boolean" ? input.enabled : fallbackLayer.enabled,
|
|
470
|
+
exportable: typeof input.exportable === "boolean" ? input.exportable : fallbackLayer.exportable,
|
|
471
|
+
color: kind === "color" ? typeof input.color === "string" ? input.color : typeof fallbackLayer.color === "string" ? fallbackLayer.color : "#ffffff" : void 0,
|
|
472
|
+
src: kind === "image" ? typeof input.src === "string" ? input.src.trim() : typeof fallbackLayer.src === "string" ? fallbackLayer.src : "" : void 0
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
function normalizeConfig(raw) {
|
|
476
|
+
if (!raw || typeof raw !== "object") {
|
|
477
|
+
return cloneConfig(DEFAULT_BACKGROUND_CONFIG);
|
|
478
|
+
}
|
|
479
|
+
const input = raw;
|
|
480
|
+
const version = Number.isFinite(Number(input.version)) ? Number(input.version) : DEFAULT_BACKGROUND_CONFIG.version;
|
|
481
|
+
const baseLayers = Array.isArray(input.layers) ? input.layers.map((layer, index) => normalizeLayer(layer, index)) : cloneConfig(DEFAULT_BACKGROUND_CONFIG).layers;
|
|
482
|
+
const uniqueLayers = [];
|
|
483
|
+
const seen = /* @__PURE__ */ new Set();
|
|
484
|
+
baseLayers.forEach((layer, index) => {
|
|
485
|
+
let nextId = layer.id || `layer-${index + 1}`;
|
|
486
|
+
let serial = 1;
|
|
487
|
+
while (seen.has(nextId)) {
|
|
488
|
+
serial += 1;
|
|
489
|
+
nextId = `${layer.id || `layer-${index + 1}`}-${serial}`;
|
|
490
|
+
}
|
|
491
|
+
seen.add(nextId);
|
|
492
|
+
uniqueLayers.push({ ...layer, id: nextId });
|
|
493
|
+
});
|
|
494
|
+
return {
|
|
495
|
+
version,
|
|
496
|
+
layers: uniqueLayers
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
function cloneConfig(config) {
|
|
500
|
+
return {
|
|
501
|
+
version: config.version,
|
|
502
|
+
layers: (config.layers || []).map((layer) => ({ ...layer }))
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
function mergeConfig(base, patch) {
|
|
506
|
+
const merged = {
|
|
507
|
+
version: patch.version === void 0 ? base.version : Number.isFinite(Number(patch.version)) ? Number(patch.version) : base.version,
|
|
508
|
+
layers: Array.isArray(patch.layers) ? patch.layers.map((layer, index) => normalizeLayer(layer, index)) : base.layers.map((layer) => ({ ...layer }))
|
|
509
|
+
};
|
|
510
|
+
return normalizeConfig(merged);
|
|
511
|
+
}
|
|
512
|
+
function configSignature(config) {
|
|
513
|
+
return JSON.stringify(config);
|
|
514
|
+
}
|
|
515
|
+
var BackgroundTool = class {
|
|
516
|
+
constructor(options) {
|
|
517
|
+
this.id = "pooder.kit.background";
|
|
518
|
+
this.metadata = {
|
|
519
|
+
name: "BackgroundTool"
|
|
520
|
+
};
|
|
521
|
+
this.config = cloneConfig(DEFAULT_BACKGROUND_CONFIG);
|
|
522
|
+
this.specs = [];
|
|
523
|
+
this.renderSeq = 0;
|
|
524
|
+
this.latestSceneLayout = null;
|
|
525
|
+
this.sourceSizeBySrc = /* @__PURE__ */ new Map();
|
|
526
|
+
this.pendingSizeBySrc = /* @__PURE__ */ new Map();
|
|
527
|
+
this.onCanvasResized = () => {
|
|
528
|
+
this.latestSceneLayout = null;
|
|
529
|
+
this.updateBackground();
|
|
530
|
+
};
|
|
531
|
+
this.onSceneLayoutChanged = (layout) => {
|
|
532
|
+
this.latestSceneLayout = layout;
|
|
533
|
+
this.updateBackground();
|
|
534
|
+
};
|
|
535
|
+
if (options && typeof options === "object") {
|
|
536
|
+
this.config = mergeConfig(this.config, options);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
activate(context) {
|
|
540
|
+
var _a, _b;
|
|
541
|
+
this.canvasService = context.services.get("CanvasService");
|
|
542
|
+
if (!this.canvasService) {
|
|
543
|
+
console.warn("CanvasService not found for BackgroundTool");
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
this.configService = context.services.get(
|
|
547
|
+
"ConfigurationService"
|
|
548
|
+
);
|
|
549
|
+
if (this.configService) {
|
|
550
|
+
this.config = normalizeConfig(
|
|
551
|
+
this.configService.get(
|
|
552
|
+
BACKGROUND_CONFIG_KEY,
|
|
553
|
+
DEFAULT_BACKGROUND_CONFIG
|
|
554
|
+
)
|
|
555
|
+
);
|
|
556
|
+
(_a = this.configChangeDisposable) == null ? void 0 : _a.dispose();
|
|
557
|
+
this.configChangeDisposable = this.configService.onAnyChange(
|
|
558
|
+
(e) => {
|
|
559
|
+
if (e.key === BACKGROUND_CONFIG_KEY) {
|
|
560
|
+
this.config = normalizeConfig(e.value);
|
|
561
|
+
this.updateBackground();
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
if (e.key.startsWith("size.")) {
|
|
565
|
+
this.latestSceneLayout = null;
|
|
566
|
+
this.updateBackground();
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
572
|
+
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
573
|
+
this.id,
|
|
574
|
+
() => ({
|
|
575
|
+
passes: [
|
|
576
|
+
{
|
|
577
|
+
id: BACKGROUND_LAYER_ID,
|
|
578
|
+
stack: 0,
|
|
579
|
+
order: 0,
|
|
580
|
+
objects: this.specs
|
|
581
|
+
}
|
|
582
|
+
]
|
|
583
|
+
}),
|
|
584
|
+
{ priority: 0 }
|
|
585
|
+
);
|
|
586
|
+
context.eventBus.on("canvas:resized", this.onCanvasResized);
|
|
587
|
+
context.eventBus.on("scene:layout:change", this.onSceneLayoutChanged);
|
|
588
|
+
this.updateBackground();
|
|
589
|
+
}
|
|
590
|
+
deactivate(context) {
|
|
591
|
+
var _a, _b;
|
|
592
|
+
context.eventBus.off("canvas:resized", this.onCanvasResized);
|
|
593
|
+
context.eventBus.off("scene:layout:change", this.onSceneLayoutChanged);
|
|
594
|
+
this.renderSeq += 1;
|
|
595
|
+
this.specs = [];
|
|
596
|
+
this.latestSceneLayout = null;
|
|
597
|
+
(_a = this.configChangeDisposable) == null ? void 0 : _a.dispose();
|
|
598
|
+
this.configChangeDisposable = void 0;
|
|
599
|
+
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
600
|
+
this.renderProducerDisposable = void 0;
|
|
601
|
+
if (!this.canvasService) return;
|
|
602
|
+
void this.canvasService.flushRenderFromProducers();
|
|
603
|
+
this.canvasService.requestRenderAll();
|
|
604
|
+
this.canvasService = void 0;
|
|
605
|
+
this.configService = void 0;
|
|
606
|
+
}
|
|
607
|
+
contribute() {
|
|
608
|
+
return {
|
|
609
|
+
[ContributionPointIds.CONFIGURATIONS]: [
|
|
610
|
+
{
|
|
611
|
+
id: BACKGROUND_CONFIG_KEY,
|
|
612
|
+
type: "json",
|
|
613
|
+
label: "Background Config",
|
|
614
|
+
default: cloneConfig(DEFAULT_BACKGROUND_CONFIG)
|
|
615
|
+
}
|
|
616
|
+
],
|
|
617
|
+
[ContributionPointIds.COMMANDS]: [
|
|
618
|
+
{
|
|
619
|
+
command: "background.getConfig",
|
|
620
|
+
title: "Get Background Config",
|
|
621
|
+
handler: () => cloneConfig(this.config)
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
command: "background.resetConfig",
|
|
625
|
+
title: "Reset Background Config",
|
|
626
|
+
handler: () => {
|
|
627
|
+
this.commitConfig(cloneConfig(DEFAULT_BACKGROUND_CONFIG));
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
command: "background.replaceConfig",
|
|
633
|
+
title: "Replace Background Config",
|
|
634
|
+
handler: (config) => {
|
|
635
|
+
this.commitConfig(normalizeConfig(config));
|
|
636
|
+
return true;
|
|
637
|
+
}
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
command: "background.patchConfig",
|
|
641
|
+
title: "Patch Background Config",
|
|
642
|
+
handler: (patch) => {
|
|
643
|
+
this.commitConfig(mergeConfig(this.config, patch || {}));
|
|
644
|
+
return true;
|
|
645
|
+
}
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
command: "background.upsertLayer",
|
|
649
|
+
title: "Upsert Background Layer",
|
|
650
|
+
handler: (layer) => {
|
|
651
|
+
const normalized = normalizeLayer(layer, 0);
|
|
652
|
+
const existingIndex = this.config.layers.findIndex(
|
|
653
|
+
(item) => item.id === normalized.id
|
|
654
|
+
);
|
|
655
|
+
const nextLayers = [...this.config.layers];
|
|
656
|
+
if (existingIndex >= 0) {
|
|
657
|
+
nextLayers[existingIndex] = normalizeLayer(
|
|
658
|
+
{ ...nextLayers[existingIndex], ...layer },
|
|
659
|
+
existingIndex,
|
|
660
|
+
nextLayers[existingIndex]
|
|
768
661
|
);
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
662
|
+
} else {
|
|
663
|
+
nextLayers.push(
|
|
664
|
+
normalizeLayer(
|
|
665
|
+
{
|
|
666
|
+
...normalized,
|
|
667
|
+
order: Number.isFinite(Number(layer.order)) ? Number(layer.order) : nextLayers.length
|
|
668
|
+
},
|
|
669
|
+
nextLayers.length
|
|
670
|
+
)
|
|
774
671
|
);
|
|
775
|
-
const pointsA = offsetsA.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
776
|
-
const pointsB = offsetsB.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
777
|
-
if (pointsA.length >= 2 && pointsB.length >= 2) {
|
|
778
|
-
let topBase = selectOuterChain({
|
|
779
|
-
mainShape: bridgeBasePath,
|
|
780
|
-
pointsA,
|
|
781
|
-
pointsB,
|
|
782
|
-
delta,
|
|
783
|
-
overlap,
|
|
784
|
-
op: f.operation
|
|
785
|
-
});
|
|
786
|
-
const dist2 = (a, b) => {
|
|
787
|
-
const dx = a.x - b.x;
|
|
788
|
-
const dy = a.y - b.y;
|
|
789
|
-
return dx * dx + dy * dy;
|
|
790
|
-
};
|
|
791
|
-
if (dist2(topBase[0], leftHit.point) > dist2(topBase[0], rightHit.point)) {
|
|
792
|
-
topBase = topBase.slice().reverse();
|
|
793
|
-
}
|
|
794
|
-
topBase = topBase.slice();
|
|
795
|
-
topBase[0] = leftHit.point;
|
|
796
|
-
topBase[topBase.length - 1] = rightHit.point;
|
|
797
|
-
const capShiftY = f.operation === "subtract" ? -Math.max(overlap * 2, delta) : overlap;
|
|
798
|
-
const topPoints = topBase.map(
|
|
799
|
-
(p) => p.add(new paper.Point(0, capShiftY))
|
|
800
|
-
);
|
|
801
|
-
const bridgeBottomY = bridgeBottom + overlap * 2;
|
|
802
|
-
const bridgePoly = new paper.Path({ insert: false });
|
|
803
|
-
for (const p of topPoints) bridgePoly.add(p);
|
|
804
|
-
bridgePoly.add(new paper.Point(xRight, bridgeBottomY));
|
|
805
|
-
bridgePoly.add(new paper.Point(xLeft, bridgeBottomY));
|
|
806
|
-
bridgePoly.closed = true;
|
|
807
|
-
const unitedItem = item.unite(bridgePoly);
|
|
808
|
-
item.remove();
|
|
809
|
-
bridgePoly.remove();
|
|
810
|
-
if (f.operation === "add") {
|
|
811
|
-
adds.push(unitedItem);
|
|
812
|
-
} else {
|
|
813
|
-
subtracts.push(unitedItem);
|
|
814
|
-
}
|
|
815
|
-
return;
|
|
816
|
-
}
|
|
817
672
|
}
|
|
673
|
+
this.commitConfig(
|
|
674
|
+
normalizeConfig({
|
|
675
|
+
...this.config,
|
|
676
|
+
layers: nextLayers
|
|
677
|
+
})
|
|
678
|
+
);
|
|
679
|
+
return true;
|
|
818
680
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
command: "background.removeLayer",
|
|
684
|
+
title: "Remove Background Layer",
|
|
685
|
+
handler: (id) => {
|
|
686
|
+
const nextLayers = this.config.layers.filter(
|
|
687
|
+
(layer) => layer.id !== id
|
|
688
|
+
);
|
|
689
|
+
this.commitConfig(
|
|
690
|
+
normalizeConfig({
|
|
691
|
+
...this.config,
|
|
692
|
+
layers: nextLayers
|
|
693
|
+
})
|
|
694
|
+
);
|
|
695
|
+
return true;
|
|
829
696
|
}
|
|
830
697
|
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
698
|
+
]
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
commitConfig(next) {
|
|
702
|
+
const normalized = normalizeConfig(next);
|
|
703
|
+
if (configSignature(normalized) === configSignature(this.config)) {
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
if (this.configService) {
|
|
707
|
+
this.configService.update(BACKGROUND_CONFIG_KEY, cloneConfig(normalized));
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
this.config = normalized;
|
|
711
|
+
this.updateBackground();
|
|
712
|
+
}
|
|
713
|
+
getViewportRect() {
|
|
714
|
+
var _a, _b;
|
|
715
|
+
const width = Number(((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 0);
|
|
716
|
+
const height = Number(((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 0);
|
|
717
|
+
return {
|
|
718
|
+
left: 0,
|
|
719
|
+
top: 0,
|
|
720
|
+
width: width > 0 ? width : DEFAULT_WIDTH,
|
|
721
|
+
height: height > 0 ? height : DEFAULT_HEIGHT
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
resolveSceneLayout() {
|
|
725
|
+
if (this.latestSceneLayout) return this.latestSceneLayout;
|
|
726
|
+
if (!this.canvasService || !this.configService) return null;
|
|
727
|
+
const layout = computeSceneLayout(
|
|
728
|
+
this.canvasService,
|
|
729
|
+
readSizeState(this.configService)
|
|
730
|
+
);
|
|
731
|
+
this.latestSceneLayout = layout;
|
|
732
|
+
return layout;
|
|
733
|
+
}
|
|
734
|
+
resolveFocusRect() {
|
|
735
|
+
const layout = this.resolveSceneLayout();
|
|
736
|
+
if (!layout) return null;
|
|
737
|
+
return {
|
|
738
|
+
left: layout.trimRect.left,
|
|
739
|
+
top: layout.trimRect.top,
|
|
740
|
+
width: layout.trimRect.width,
|
|
741
|
+
height: layout.trimRect.height
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
resolveAnchorRect(anchor) {
|
|
745
|
+
if (anchor === "focus") {
|
|
746
|
+
return this.resolveFocusRect() || this.getViewportRect();
|
|
747
|
+
}
|
|
748
|
+
if (anchor !== "viewport") {
|
|
749
|
+
return this.getViewportRect();
|
|
750
|
+
}
|
|
751
|
+
return this.getViewportRect();
|
|
752
|
+
}
|
|
753
|
+
resolveImagePlacement(target, sourceSize, fit) {
|
|
754
|
+
const targetWidth = Math.max(1, Number(target.width || 0));
|
|
755
|
+
const targetHeight = Math.max(1, Number(target.height || 0));
|
|
756
|
+
const sourceWidth = Math.max(1, Number(sourceSize.width || 0));
|
|
757
|
+
const sourceHeight = Math.max(1, Number(sourceSize.height || 0));
|
|
758
|
+
if (fit === "stretch") {
|
|
759
|
+
return {
|
|
760
|
+
left: target.left,
|
|
761
|
+
top: target.top,
|
|
762
|
+
scaleX: targetWidth / sourceWidth,
|
|
763
|
+
scaleY: targetHeight / sourceHeight
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
const scale = fit === "contain" ? Math.min(targetWidth / sourceWidth, targetHeight / sourceHeight) : Math.max(targetWidth / sourceWidth, targetHeight / sourceHeight);
|
|
767
|
+
const renderWidth = sourceWidth * scale;
|
|
768
|
+
const renderHeight = sourceHeight * scale;
|
|
769
|
+
return {
|
|
770
|
+
left: target.left + (targetWidth - renderWidth) / 2,
|
|
771
|
+
top: target.top + (targetHeight - renderHeight) / 2,
|
|
772
|
+
scaleX: scale,
|
|
773
|
+
scaleY: scale
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
buildColorLayerSpec(layer) {
|
|
777
|
+
const rect = this.resolveAnchorRect(layer.anchor);
|
|
778
|
+
return {
|
|
779
|
+
id: `background.layer.${layer.id}.color`,
|
|
780
|
+
type: "rect",
|
|
781
|
+
space: "screen",
|
|
782
|
+
data: {
|
|
783
|
+
id: `background.layer.${layer.id}.color`,
|
|
784
|
+
layerId: BACKGROUND_LAYER_ID,
|
|
785
|
+
type: "background-layer",
|
|
786
|
+
layerRef: layer.id,
|
|
787
|
+
layerKind: layer.kind
|
|
788
|
+
},
|
|
789
|
+
props: {
|
|
790
|
+
left: rect.left,
|
|
791
|
+
top: rect.top,
|
|
792
|
+
width: rect.width,
|
|
793
|
+
height: rect.height,
|
|
794
|
+
originX: "left",
|
|
795
|
+
originY: "top",
|
|
796
|
+
fill: layer.color || "transparent",
|
|
797
|
+
opacity: layer.opacity,
|
|
798
|
+
selectable: false,
|
|
799
|
+
evented: false,
|
|
800
|
+
excludeFromExport: !layer.exportable
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
buildImageLayerSpec(layer) {
|
|
805
|
+
const src = String(layer.src || "").trim();
|
|
806
|
+
if (!src) return [];
|
|
807
|
+
const sourceSize = this.sourceSizeBySrc.get(src);
|
|
808
|
+
if (!sourceSize) return [];
|
|
809
|
+
const rect = this.resolveAnchorRect(layer.anchor);
|
|
810
|
+
const placement = this.resolveImagePlacement(rect, sourceSize, layer.fit);
|
|
811
|
+
return [
|
|
812
|
+
{
|
|
813
|
+
id: `background.layer.${layer.id}.image`,
|
|
814
|
+
type: "image",
|
|
815
|
+
src,
|
|
816
|
+
space: "screen",
|
|
817
|
+
data: {
|
|
818
|
+
id: `background.layer.${layer.id}.image`,
|
|
819
|
+
layerId: BACKGROUND_LAYER_ID,
|
|
820
|
+
type: "background-layer",
|
|
821
|
+
layerRef: layer.id,
|
|
822
|
+
layerKind: layer.kind
|
|
823
|
+
},
|
|
824
|
+
props: {
|
|
825
|
+
left: placement.left,
|
|
826
|
+
top: placement.top,
|
|
827
|
+
originX: "left",
|
|
828
|
+
originY: "top",
|
|
829
|
+
scaleX: placement.scaleX,
|
|
830
|
+
scaleY: placement.scaleY,
|
|
831
|
+
opacity: layer.opacity,
|
|
832
|
+
selectable: false,
|
|
833
|
+
evented: false,
|
|
834
|
+
excludeFromExport: !layer.exportable
|
|
836
835
|
}
|
|
837
836
|
}
|
|
837
|
+
];
|
|
838
|
+
}
|
|
839
|
+
buildBackgroundSpecs(config) {
|
|
840
|
+
const activeLayers = (config.layers || []).filter((layer) => layer.enabled).map((layer, index) => ({ layer, index })).sort((a, b) => {
|
|
841
|
+
if (a.layer.order !== b.layer.order) {
|
|
842
|
+
return a.layer.order - b.layer.order;
|
|
843
|
+
}
|
|
844
|
+
return a.index - b.index;
|
|
838
845
|
});
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
item.remove();
|
|
845
|
-
mainShape = normalizePathItem(temp);
|
|
846
|
-
} catch (e) {
|
|
847
|
-
console.error("Geometry: Failed to unite feature", e);
|
|
848
|
-
item.remove();
|
|
849
|
-
}
|
|
846
|
+
const specs = [];
|
|
847
|
+
activeLayers.forEach(({ layer }) => {
|
|
848
|
+
if (layer.kind === "color") {
|
|
849
|
+
specs.push(this.buildColorLayerSpec(layer));
|
|
850
|
+
return;
|
|
850
851
|
}
|
|
852
|
+
specs.push(...this.buildImageLayerSpec(layer));
|
|
853
|
+
});
|
|
854
|
+
return specs;
|
|
855
|
+
}
|
|
856
|
+
collectActiveImageUrls(config) {
|
|
857
|
+
const urls = /* @__PURE__ */ new Set();
|
|
858
|
+
(config.layers || []).forEach((layer) => {
|
|
859
|
+
if (!layer.enabled || layer.kind !== "image") return;
|
|
860
|
+
const src = String(layer.src || "").trim();
|
|
861
|
+
if (!src) return;
|
|
862
|
+
urls.add(src);
|
|
863
|
+
});
|
|
864
|
+
return Array.from(urls);
|
|
865
|
+
}
|
|
866
|
+
async ensureImageSize(src) {
|
|
867
|
+
if (!src) return null;
|
|
868
|
+
const cached = this.sourceSizeBySrc.get(src);
|
|
869
|
+
if (cached) return cached;
|
|
870
|
+
const pending = this.pendingSizeBySrc.get(src);
|
|
871
|
+
if (pending) {
|
|
872
|
+
return pending;
|
|
851
873
|
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
} catch (e) {
|
|
860
|
-
console.error("Geometry: Failed to subtract feature", e);
|
|
861
|
-
item.remove();
|
|
862
|
-
}
|
|
874
|
+
const task = this.loadImageSize(src);
|
|
875
|
+
this.pendingSizeBySrc.set(src, task);
|
|
876
|
+
try {
|
|
877
|
+
return await task;
|
|
878
|
+
} finally {
|
|
879
|
+
if (this.pendingSizeBySrc.get(src) === task) {
|
|
880
|
+
this.pendingSizeBySrc.delete(src);
|
|
863
881
|
}
|
|
864
882
|
}
|
|
865
883
|
}
|
|
866
|
-
|
|
867
|
-
}
|
|
868
|
-
function applySurfaceFeatures(shape, features, options) {
|
|
869
|
-
const surfaceFeatures = features.filter(
|
|
870
|
-
(f) => f.renderBehavior === "surface"
|
|
871
|
-
);
|
|
872
|
-
if (surfaceFeatures.length === 0) return shape;
|
|
873
|
-
let result = shape;
|
|
874
|
-
for (const f of surfaceFeatures) {
|
|
875
|
-
const pos = resolveFeaturePosition(f, options);
|
|
876
|
-
const center = new paper.Point(pos.x, pos.y);
|
|
877
|
-
const item = createFeatureItem(f, center);
|
|
884
|
+
async loadImageSize(src) {
|
|
878
885
|
try {
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
const
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
result = normalizePathItem(temp);
|
|
886
|
+
const image = await FabricImage.fromURL(src, {
|
|
887
|
+
crossOrigin: "anonymous"
|
|
888
|
+
});
|
|
889
|
+
const width = Number((image == null ? void 0 : image.width) || 0);
|
|
890
|
+
const height = Number((image == null ? void 0 : image.height) || 0);
|
|
891
|
+
if (width > 0 && height > 0) {
|
|
892
|
+
const size = { width, height };
|
|
893
|
+
this.sourceSizeBySrc.set(src, size);
|
|
894
|
+
return size;
|
|
889
895
|
}
|
|
890
|
-
} catch (
|
|
891
|
-
console.error("
|
|
892
|
-
item.remove();
|
|
896
|
+
} catch (error) {
|
|
897
|
+
console.error("[BackgroundTool] Failed to load image", src, error);
|
|
893
898
|
}
|
|
899
|
+
return null;
|
|
894
900
|
}
|
|
895
|
-
|
|
901
|
+
updateBackground() {
|
|
902
|
+
void this.updateBackgroundAsync();
|
|
903
|
+
}
|
|
904
|
+
async updateBackgroundAsync() {
|
|
905
|
+
if (!this.canvasService) return;
|
|
906
|
+
const seq = ++this.renderSeq;
|
|
907
|
+
const currentConfig = cloneConfig(this.config);
|
|
908
|
+
const activeUrls = this.collectActiveImageUrls(currentConfig);
|
|
909
|
+
if (activeUrls.length > 0) {
|
|
910
|
+
await Promise.all(activeUrls.map((url) => this.ensureImageSize(url)));
|
|
911
|
+
if (seq !== this.renderSeq) return;
|
|
912
|
+
}
|
|
913
|
+
this.specs = this.buildBackgroundSpecs(currentConfig);
|
|
914
|
+
await this.canvasService.flushRenderFromProducers();
|
|
915
|
+
if (seq !== this.renderSeq) return;
|
|
916
|
+
this.canvasService.requestRenderAll();
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
|
|
920
|
+
// src/extensions/image.ts
|
|
921
|
+
import {
|
|
922
|
+
ContributionPointIds as ContributionPointIds2
|
|
923
|
+
} from "@pooder/core";
|
|
924
|
+
import {
|
|
925
|
+
Canvas as FabricCanvas,
|
|
926
|
+
Image as FabricImage2,
|
|
927
|
+
Pattern,
|
|
928
|
+
Point
|
|
929
|
+
} from "fabric";
|
|
930
|
+
|
|
931
|
+
// src/extensions/geometry.ts
|
|
932
|
+
import paper from "paper";
|
|
933
|
+
|
|
934
|
+
// src/extensions/bridgeSelection.ts
|
|
935
|
+
function pickExitIndex(hits) {
|
|
936
|
+
for (let i = 0; i < hits.length; i++) {
|
|
937
|
+
const h = hits[i];
|
|
938
|
+
if (h.insideBelow && !h.insideAbove) return i;
|
|
939
|
+
}
|
|
940
|
+
return -1;
|
|
896
941
|
}
|
|
897
|
-
function
|
|
898
|
-
|
|
899
|
-
const
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
const finalShape = applySurfaceFeatures(perimeter, options.features, options);
|
|
904
|
-
const pathData = finalShape.pathData;
|
|
905
|
-
finalShape.remove();
|
|
906
|
-
return pathData;
|
|
942
|
+
function scoreOutsideAbove(samples) {
|
|
943
|
+
let score = 0;
|
|
944
|
+
for (const s of samples) {
|
|
945
|
+
if (s.outsideAbove) score++;
|
|
946
|
+
}
|
|
947
|
+
return score;
|
|
907
948
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
const perimeter = getPerimeterShape(options);
|
|
917
|
-
const mainShape = applySurfaceFeatures(perimeter, options.features, options);
|
|
918
|
-
const finalMask = maskRect.subtract(mainShape);
|
|
919
|
-
maskRect.remove();
|
|
920
|
-
mainShape.remove();
|
|
921
|
-
const pathData = finalMask.pathData;
|
|
922
|
-
finalMask.remove();
|
|
923
|
-
return pathData;
|
|
949
|
+
|
|
950
|
+
// src/extensions/wrappedOffsets.ts
|
|
951
|
+
function wrappedDistance(total, start, end) {
|
|
952
|
+
if (!Number.isFinite(total) || total <= 0) return 0;
|
|
953
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return 0;
|
|
954
|
+
const s = (start % total + total) % total;
|
|
955
|
+
const e = (end % total + total) % total;
|
|
956
|
+
return e >= s ? e - s : total - s + e;
|
|
924
957
|
}
|
|
925
|
-
function
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
const
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
pOffset,
|
|
939
|
-
offsetOptions.features,
|
|
940
|
-
offsetOptions
|
|
941
|
-
);
|
|
942
|
-
let bleedZone;
|
|
943
|
-
if (offset > 0) {
|
|
944
|
-
bleedZone = shapeOffset.subtract(shapeOriginal);
|
|
945
|
-
} else {
|
|
946
|
-
bleedZone = shapeOriginal.subtract(shapeOffset);
|
|
958
|
+
function sampleWrappedOffsets(total, start, end, count) {
|
|
959
|
+
if (!Number.isFinite(total) || total <= 0) return [];
|
|
960
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return [];
|
|
961
|
+
const n = Math.max(0, Math.floor(count));
|
|
962
|
+
if (n <= 0) return [];
|
|
963
|
+
const dist = wrappedDistance(total, start, end);
|
|
964
|
+
if (n === 1) return [(start % total + total) % total];
|
|
965
|
+
const step = dist / (n - 1);
|
|
966
|
+
const offsets = [];
|
|
967
|
+
for (let i = 0; i < n; i++) {
|
|
968
|
+
const raw = start + step * i;
|
|
969
|
+
const wrapped = (raw % total + total) % total;
|
|
970
|
+
offsets.push(wrapped);
|
|
947
971
|
}
|
|
948
|
-
|
|
949
|
-
shapeOriginal.remove();
|
|
950
|
-
shapeOffset.remove();
|
|
951
|
-
bleedZone.remove();
|
|
952
|
-
return pathData;
|
|
972
|
+
return offsets;
|
|
953
973
|
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
const
|
|
958
|
-
const
|
|
959
|
-
const
|
|
960
|
-
|
|
961
|
-
|
|
974
|
+
|
|
975
|
+
// src/extensions/geometry.ts
|
|
976
|
+
function resolveFeaturePosition(feature, geometry) {
|
|
977
|
+
const { x, y, width, height } = geometry;
|
|
978
|
+
const left = x - width / 2;
|
|
979
|
+
const top = y - height / 2;
|
|
980
|
+
return {
|
|
981
|
+
x: left + feature.x * width,
|
|
982
|
+
y: top + feature.y * height
|
|
962
983
|
};
|
|
963
|
-
shape.remove();
|
|
964
|
-
return result;
|
|
965
984
|
}
|
|
966
|
-
function
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
985
|
+
function ensurePaper(width, height) {
|
|
986
|
+
if (!paper.project) {
|
|
987
|
+
paper.setup(new paper.Size(width, height));
|
|
988
|
+
} else {
|
|
989
|
+
paper.view.viewSize = new paper.Size(width, height);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
var isBridgeDebugEnabled = () => Boolean(globalThis.__POODER_BRIDGE_DEBUG__);
|
|
993
|
+
function normalizePathItem(shape) {
|
|
994
|
+
let result = shape;
|
|
995
|
+
if (typeof result.resolveCrossings === "function") result = result.resolveCrossings();
|
|
996
|
+
if (typeof result.reduce === "function") result = result.reduce({});
|
|
997
|
+
if (typeof result.reorient === "function") result = result.reorient(true, true);
|
|
998
|
+
if (typeof result.reduce === "function") result = result.reduce({});
|
|
978
999
|
return result;
|
|
979
1000
|
}
|
|
980
|
-
function
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1001
|
+
function getBridgeDelta(itemBounds, overlap) {
|
|
1002
|
+
return Math.max(overlap, Math.min(5, Math.max(1, itemBounds.height * 0.02)));
|
|
1003
|
+
}
|
|
1004
|
+
function getExitHit(args) {
|
|
1005
|
+
const { mainShape, x, bridgeBottom, toY, eps, delta, overlap, op } = args;
|
|
1006
|
+
const ray = new paper.Path.Line({
|
|
1007
|
+
from: [x, bridgeBottom],
|
|
1008
|
+
to: [x, toY],
|
|
1009
|
+
insert: false
|
|
1010
|
+
});
|
|
1011
|
+
const intersections = mainShape.getIntersections(ray) || [];
|
|
1012
|
+
ray.remove();
|
|
1013
|
+
const validHits = intersections.filter((i) => i.point.y < bridgeBottom - eps);
|
|
1014
|
+
if (validHits.length === 0) return null;
|
|
1015
|
+
validHits.sort((a, b) => b.point.y - a.point.y);
|
|
1016
|
+
const flags = validHits.map((h) => {
|
|
1017
|
+
const above = h.point.add(new paper.Point(0, -delta));
|
|
1018
|
+
const below = h.point.add(new paper.Point(0, delta));
|
|
1019
|
+
return {
|
|
1020
|
+
insideAbove: mainShape.contains(above),
|
|
1021
|
+
insideBelow: mainShape.contains(below)
|
|
1022
|
+
};
|
|
1023
|
+
});
|
|
1024
|
+
const idx = pickExitIndex(flags);
|
|
1025
|
+
if (idx < 0) return null;
|
|
1026
|
+
if (isBridgeDebugEnabled()) {
|
|
1027
|
+
console.debug("Geometry: Bridge ray", {
|
|
1028
|
+
x,
|
|
1029
|
+
validHits: validHits.length,
|
|
1030
|
+
idx,
|
|
1031
|
+
delta,
|
|
1032
|
+
overlap,
|
|
1033
|
+
op
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
const hit = validHits[idx];
|
|
1037
|
+
return { point: hit.point, location: hit };
|
|
991
1038
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1039
|
+
function selectOuterChain(args) {
|
|
1040
|
+
const { mainShape, pointsA, pointsB, delta, overlap, op } = args;
|
|
1041
|
+
const scoreA = scoreOutsideAbove(
|
|
1042
|
+
pointsA.map((p) => ({
|
|
1043
|
+
outsideAbove: !mainShape.contains(p.add(new paper.Point(0, -delta)))
|
|
1044
|
+
}))
|
|
1045
|
+
);
|
|
1046
|
+
const scoreB = scoreOutsideAbove(
|
|
1047
|
+
pointsB.map((p) => ({
|
|
1048
|
+
outsideAbove: !mainShape.contains(p.add(new paper.Point(0, -delta)))
|
|
1049
|
+
}))
|
|
1050
|
+
);
|
|
1051
|
+
const ratioA = scoreA / pointsA.length;
|
|
1052
|
+
const ratioB = scoreB / pointsB.length;
|
|
1053
|
+
if (isBridgeDebugEnabled()) {
|
|
1054
|
+
console.debug("Geometry: Bridge chain", {
|
|
1055
|
+
scoreA,
|
|
1056
|
+
scoreB,
|
|
1057
|
+
lenA: pointsA.length,
|
|
1058
|
+
lenB: pointsB.length,
|
|
1059
|
+
ratioA,
|
|
1060
|
+
ratioB,
|
|
1061
|
+
delta,
|
|
1062
|
+
overlap,
|
|
1063
|
+
op
|
|
1064
|
+
});
|
|
1012
1065
|
}
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
* @param total Total dimension size (e.g., canvas width)
|
|
1017
|
-
*/
|
|
1018
|
-
static toNormalized(value, total) {
|
|
1019
|
-
return total === 0 ? 0 : value / total;
|
|
1066
|
+
const ratioEps = 1e-6;
|
|
1067
|
+
if (Math.abs(ratioA - ratioB) > ratioEps) {
|
|
1068
|
+
return ratioA > ratioB ? pointsA : pointsB;
|
|
1020
1069
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1070
|
+
if (scoreA !== scoreB) return scoreA > scoreB ? pointsA : pointsB;
|
|
1071
|
+
return pointsA.length <= pointsB.length ? pointsA : pointsB;
|
|
1072
|
+
}
|
|
1073
|
+
function fitPathItemToRect(item, rect, fitMode) {
|
|
1074
|
+
const { left, top, width, height } = rect;
|
|
1075
|
+
const bounds = item.bounds;
|
|
1076
|
+
if (width <= 0 || height <= 0 || !Number.isFinite(bounds.width) || !Number.isFinite(bounds.height) || bounds.width <= 0 || bounds.height <= 0) {
|
|
1077
|
+
item.position = new paper.Point(left + width / 2, top + height / 2);
|
|
1078
|
+
return item;
|
|
1028
1079
|
}
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
return
|
|
1034
|
-
x: this.toNormalized(point.x, size.width),
|
|
1035
|
-
y: this.toNormalized(point.y, size.height)
|
|
1036
|
-
};
|
|
1080
|
+
item.translate(new paper.Point(-bounds.left, -bounds.top));
|
|
1081
|
+
if (fitMode === "stretch") {
|
|
1082
|
+
item.scale(width / bounds.width, height / bounds.height, new paper.Point(0, 0));
|
|
1083
|
+
item.translate(new paper.Point(left, top));
|
|
1084
|
+
return item;
|
|
1037
1085
|
}
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1086
|
+
const uniformScale = Math.min(width / bounds.width, height / bounds.height);
|
|
1087
|
+
item.scale(uniformScale, uniformScale, new paper.Point(0, 0));
|
|
1088
|
+
const scaledWidth = bounds.width * uniformScale;
|
|
1089
|
+
const scaledHeight = bounds.height * uniformScale;
|
|
1090
|
+
item.translate(
|
|
1091
|
+
new paper.Point(
|
|
1092
|
+
left + (width - scaledWidth) / 2,
|
|
1093
|
+
top + (height - scaledHeight) / 2
|
|
1094
|
+
)
|
|
1095
|
+
);
|
|
1096
|
+
return item;
|
|
1097
|
+
}
|
|
1098
|
+
function createNormalizedHeartPath(params) {
|
|
1099
|
+
const { lobeSpread, notchDepth, tipSharpness } = params;
|
|
1100
|
+
const halfSpread = 0.22 + lobeSpread * 0.18;
|
|
1101
|
+
const notchY = 0.06 + notchDepth * 0.2;
|
|
1102
|
+
const shoulderY = 0.24 + notchDepth * 0.2;
|
|
1103
|
+
const topLift = 0.12 + (1 - notchDepth) * 0.06;
|
|
1104
|
+
const topY = notchY - topLift;
|
|
1105
|
+
const sideCtrlY = shoulderY - (0.18 - notchDepth * 0.08);
|
|
1106
|
+
const lowerCtrlY = 0.58 + (1 - tipSharpness) * 0.16;
|
|
1107
|
+
const tipCtrlX = 0.34 - tipSharpness * 0.2;
|
|
1108
|
+
const notchCtrlX = 0.06 + lobeSpread * 0.06;
|
|
1109
|
+
const lobeCtrlX = 0.1 + lobeSpread * 0.08;
|
|
1110
|
+
const notchCtrlY = notchY - topLift * 0.45;
|
|
1111
|
+
const xPeakL = 0.5 - halfSpread;
|
|
1112
|
+
const xPeakR = 0.5 + halfSpread;
|
|
1113
|
+
const heartPath = new paper.Path({ insert: false });
|
|
1114
|
+
heartPath.moveTo(new paper.Point(0.5, notchY));
|
|
1115
|
+
heartPath.cubicCurveTo(
|
|
1116
|
+
new paper.Point(0.5 - notchCtrlX, notchCtrlY),
|
|
1117
|
+
new paper.Point(xPeakL + lobeCtrlX, topY),
|
|
1118
|
+
new paper.Point(xPeakL, topY)
|
|
1119
|
+
);
|
|
1120
|
+
heartPath.cubicCurveTo(
|
|
1121
|
+
new paper.Point(xPeakL - lobeCtrlX, topY),
|
|
1122
|
+
new paper.Point(0, sideCtrlY),
|
|
1123
|
+
new paper.Point(0, shoulderY)
|
|
1124
|
+
);
|
|
1125
|
+
heartPath.cubicCurveTo(
|
|
1126
|
+
new paper.Point(0, lowerCtrlY),
|
|
1127
|
+
new paper.Point(tipCtrlX, 1),
|
|
1128
|
+
new paper.Point(0.5, 1)
|
|
1129
|
+
);
|
|
1130
|
+
heartPath.cubicCurveTo(
|
|
1131
|
+
new paper.Point(1 - tipCtrlX, 1),
|
|
1132
|
+
new paper.Point(1, lowerCtrlY),
|
|
1133
|
+
new paper.Point(1, shoulderY)
|
|
1134
|
+
);
|
|
1135
|
+
heartPath.cubicCurveTo(
|
|
1136
|
+
new paper.Point(1, sideCtrlY),
|
|
1137
|
+
new paper.Point(xPeakR + lobeCtrlX, topY),
|
|
1138
|
+
new paper.Point(xPeakR, topY)
|
|
1139
|
+
);
|
|
1140
|
+
heartPath.cubicCurveTo(
|
|
1141
|
+
new paper.Point(xPeakR - lobeCtrlX, topY),
|
|
1142
|
+
new paper.Point(0.5 + notchCtrlX, notchCtrlY),
|
|
1143
|
+
new paper.Point(0.5, notchY)
|
|
1144
|
+
);
|
|
1145
|
+
heartPath.closed = true;
|
|
1146
|
+
return heartPath;
|
|
1147
|
+
}
|
|
1148
|
+
function createHeartBaseShape(options) {
|
|
1149
|
+
const { x, y, width, height } = options;
|
|
1150
|
+
const w = Math.max(0, width);
|
|
1151
|
+
const h = Math.max(0, height);
|
|
1152
|
+
const left = x - w / 2;
|
|
1153
|
+
const top = y - h / 2;
|
|
1154
|
+
const fitMode = getShapeFitMode(options.shapeStyle);
|
|
1155
|
+
const heartParams = getHeartShapeParams(options.shapeStyle);
|
|
1156
|
+
const rawHeart = createNormalizedHeartPath(heartParams);
|
|
1157
|
+
return fitPathItemToRect(rawHeart, { left, top, width: w, height: h }, fitMode);
|
|
1158
|
+
}
|
|
1159
|
+
var BUILTIN_SHAPE_BUILDERS = {
|
|
1160
|
+
rect: (options) => {
|
|
1161
|
+
const { x, y, width, height, radius } = options;
|
|
1162
|
+
return new paper.Path.Rectangle({
|
|
1163
|
+
point: [x - width / 2, y - height / 2],
|
|
1164
|
+
size: [Math.max(0, width), Math.max(0, height)],
|
|
1165
|
+
radius: Math.max(0, radius)
|
|
1166
|
+
});
|
|
1167
|
+
},
|
|
1168
|
+
circle: (options) => {
|
|
1169
|
+
const { x, y, width, height } = options;
|
|
1170
|
+
const r = Math.min(width, height) / 2;
|
|
1171
|
+
return new paper.Path.Circle({
|
|
1172
|
+
center: new paper.Point(x, y),
|
|
1173
|
+
radius: Math.max(0, r)
|
|
1174
|
+
});
|
|
1175
|
+
},
|
|
1176
|
+
ellipse: (options) => {
|
|
1177
|
+
const { x, y, width, height } = options;
|
|
1178
|
+
return new paper.Path.Ellipse({
|
|
1179
|
+
center: new paper.Point(x, y),
|
|
1180
|
+
radius: [Math.max(0, width / 2), Math.max(0, height / 2)]
|
|
1181
|
+
});
|
|
1182
|
+
},
|
|
1183
|
+
heart: createHeartBaseShape
|
|
1184
|
+
};
|
|
1185
|
+
function createCustomBaseShape(options) {
|
|
1186
|
+
var _a;
|
|
1187
|
+
const {
|
|
1188
|
+
pathData,
|
|
1189
|
+
customSourceWidthPx,
|
|
1190
|
+
customSourceHeightPx,
|
|
1191
|
+
x,
|
|
1192
|
+
y,
|
|
1193
|
+
width,
|
|
1194
|
+
height
|
|
1195
|
+
} = options;
|
|
1196
|
+
if (typeof pathData !== "string" || pathData.trim().length === 0) {
|
|
1197
|
+
return null;
|
|
1046
1198
|
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1199
|
+
const center = new paper.Point(x, y);
|
|
1200
|
+
const hasMultipleSubPaths = ((_a = (pathData.match(/[Mm]/g) || []).length) != null ? _a : 0) > 1;
|
|
1201
|
+
const path = hasMultipleSubPaths ? new paper.CompoundPath(pathData) : (() => {
|
|
1202
|
+
const single = new paper.Path();
|
|
1203
|
+
single.pathData = pathData;
|
|
1204
|
+
return single;
|
|
1205
|
+
})();
|
|
1206
|
+
const sourceWidth = Number(customSourceWidthPx != null ? customSourceWidthPx : 0);
|
|
1207
|
+
const sourceHeight = Number(customSourceHeightPx != null ? customSourceHeightPx : 0);
|
|
1208
|
+
if (Number.isFinite(sourceWidth) && Number.isFinite(sourceHeight) && sourceWidth > 0 && sourceHeight > 0 && width > 0 && height > 0) {
|
|
1209
|
+
const targetLeft = x - width / 2;
|
|
1210
|
+
const targetTop = y - height / 2;
|
|
1211
|
+
path.scale(width / sourceWidth, height / sourceHeight, new paper.Point(0, 0));
|
|
1212
|
+
path.translate(new paper.Point(targetLeft, targetTop));
|
|
1213
|
+
return path;
|
|
1061
1214
|
}
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
var _a, _b;
|
|
1067
|
-
if (typeof input === "number") {
|
|
1068
|
-
if (!Number.isFinite(input)) return 0;
|
|
1069
|
-
return Coordinate.convertUnit(input, defaultUnit, "mm");
|
|
1215
|
+
if (width > 0 && height > 0 && path.bounds.width > 0 && path.bounds.height > 0) {
|
|
1216
|
+
path.position = center;
|
|
1217
|
+
path.scale(width / path.bounds.width, height / path.bounds.height);
|
|
1218
|
+
return path;
|
|
1070
1219
|
}
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
const match = raw.match(/^([+-]?\d+(?:\.\d+)?)\s*(px|mm|cm|in)?$/i);
|
|
1074
|
-
if (!match) return 0;
|
|
1075
|
-
const value = Number(match[1]);
|
|
1076
|
-
if (!Number.isFinite(value)) return 0;
|
|
1077
|
-
const unit = (_b = (_a = match[2]) == null ? void 0 : _a.toLowerCase()) != null ? _b : defaultUnit;
|
|
1078
|
-
return Coordinate.convertUnit(value, unit, "mm");
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
// src/extensions/sceneLayoutModel.ts
|
|
1082
|
-
var DEFAULT_SIZE_STATE = {
|
|
1083
|
-
unit: "mm",
|
|
1084
|
-
actualWidthMm: 500,
|
|
1085
|
-
actualHeightMm: 500,
|
|
1086
|
-
constraintMode: "free",
|
|
1087
|
-
aspectRatio: 1,
|
|
1088
|
-
cutMode: "trim",
|
|
1089
|
-
cutMarginMm: 0,
|
|
1090
|
-
viewPadding: 140,
|
|
1091
|
-
minMm: 10,
|
|
1092
|
-
maxMm: 2e3,
|
|
1093
|
-
stepMm: 0.1
|
|
1094
|
-
};
|
|
1095
|
-
function clamp(value, min, max) {
|
|
1096
|
-
return Math.max(min, Math.min(max, value));
|
|
1097
|
-
}
|
|
1098
|
-
function roundToStep(value, step) {
|
|
1099
|
-
if (!Number.isFinite(step) || step <= 0) return value;
|
|
1100
|
-
return Math.round(value / step) * step;
|
|
1101
|
-
}
|
|
1102
|
-
function sanitizeMmValue(valueMm, limits) {
|
|
1103
|
-
if (!Number.isFinite(valueMm)) return limits.minMm;
|
|
1104
|
-
const rounded = roundToStep(valueMm, limits.stepMm);
|
|
1105
|
-
return clamp(rounded, limits.minMm, limits.maxMm);
|
|
1106
|
-
}
|
|
1107
|
-
function normalizeUnit(value) {
|
|
1108
|
-
if (value === "cm" || value === "in") return value;
|
|
1109
|
-
return "mm";
|
|
1110
|
-
}
|
|
1111
|
-
function normalizeConstraintMode(value) {
|
|
1112
|
-
if (value === "lockAspect" || value === "equal") return value;
|
|
1113
|
-
return "free";
|
|
1220
|
+
path.position = center;
|
|
1221
|
+
return path;
|
|
1114
1222
|
}
|
|
1115
|
-
function
|
|
1116
|
-
|
|
1117
|
-
|
|
1223
|
+
function createBaseShape(options) {
|
|
1224
|
+
const { shape } = options;
|
|
1225
|
+
if (shape === "custom") {
|
|
1226
|
+
const customShape = createCustomBaseShape(options);
|
|
1227
|
+
if (customShape) return customShape;
|
|
1228
|
+
return BUILTIN_SHAPE_BUILDERS[DEFAULT_DIELINE_SHAPE](options);
|
|
1229
|
+
}
|
|
1230
|
+
return BUILTIN_SHAPE_BUILDERS[shape](options);
|
|
1118
1231
|
}
|
|
1119
|
-
function
|
|
1120
|
-
|
|
1232
|
+
function resolveBridgeBasePath(shape, anchor) {
|
|
1233
|
+
if (shape instanceof paper.Path) {
|
|
1234
|
+
return shape;
|
|
1235
|
+
}
|
|
1236
|
+
if (shape instanceof paper.CompoundPath) {
|
|
1237
|
+
const children = (shape.children || []).filter(
|
|
1238
|
+
(child) => child instanceof paper.Path
|
|
1239
|
+
);
|
|
1240
|
+
if (!children.length) return null;
|
|
1241
|
+
let best = children[0];
|
|
1242
|
+
let bestDistance = Infinity;
|
|
1243
|
+
for (const child of children) {
|
|
1244
|
+
const location = child.getNearestLocation(anchor);
|
|
1245
|
+
const point = location == null ? void 0 : location.point;
|
|
1246
|
+
if (!point) continue;
|
|
1247
|
+
const distance = point.getDistance(anchor);
|
|
1248
|
+
if (distance < bestDistance) {
|
|
1249
|
+
bestDistance = distance;
|
|
1250
|
+
best = child;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
return best;
|
|
1254
|
+
}
|
|
1255
|
+
return null;
|
|
1121
1256
|
}
|
|
1122
|
-
function
|
|
1123
|
-
|
|
1257
|
+
function createFeatureItem(feature, center) {
|
|
1258
|
+
let item;
|
|
1259
|
+
if (feature.shape === "rect") {
|
|
1260
|
+
const w = feature.width || 10;
|
|
1261
|
+
const h = feature.height || 10;
|
|
1262
|
+
const r = feature.radius || 0;
|
|
1263
|
+
item = new paper.Path.Rectangle({
|
|
1264
|
+
point: [center.x - w / 2, center.y - h / 2],
|
|
1265
|
+
size: [w, h],
|
|
1266
|
+
radius: r
|
|
1267
|
+
});
|
|
1268
|
+
} else {
|
|
1269
|
+
const r = feature.radius || 5;
|
|
1270
|
+
item = new paper.Path.Circle({
|
|
1271
|
+
center,
|
|
1272
|
+
radius: r
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
if (feature.rotation) {
|
|
1276
|
+
item.rotate(feature.rotation, center);
|
|
1277
|
+
}
|
|
1278
|
+
return item;
|
|
1124
1279
|
}
|
|
1125
|
-
function
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1280
|
+
function getPerimeterShape(options) {
|
|
1281
|
+
let mainShape = createBaseShape(options);
|
|
1282
|
+
const { features } = options;
|
|
1283
|
+
if (features && features.length > 0) {
|
|
1284
|
+
const edgeFeatures = features.filter(
|
|
1285
|
+
(f) => !f.renderBehavior || f.renderBehavior === "edge"
|
|
1286
|
+
);
|
|
1287
|
+
const adds = [];
|
|
1288
|
+
const subtracts = [];
|
|
1289
|
+
edgeFeatures.forEach((f) => {
|
|
1290
|
+
const pos = resolveFeaturePosition(f, options);
|
|
1291
|
+
const center = new paper.Point(pos.x, pos.y);
|
|
1292
|
+
const item = createFeatureItem(f, center);
|
|
1293
|
+
if (f.bridge && f.bridge.type === "vertical") {
|
|
1294
|
+
const itemBounds = item.bounds;
|
|
1295
|
+
const mainBounds = mainShape.bounds;
|
|
1296
|
+
const bridgeTop = mainBounds.top;
|
|
1297
|
+
const bridgeBottom = itemBounds.top;
|
|
1298
|
+
if (bridgeBottom > bridgeTop) {
|
|
1299
|
+
const overlap = 2;
|
|
1300
|
+
const rayPadding = 10;
|
|
1301
|
+
const eps = 0.1;
|
|
1302
|
+
const delta = getBridgeDelta(itemBounds, overlap);
|
|
1303
|
+
const toY = bridgeTop - rayPadding;
|
|
1304
|
+
const inset = Math.min(1, Math.max(0, itemBounds.width * 0.01));
|
|
1305
|
+
const xLeft = itemBounds.left + inset;
|
|
1306
|
+
const xRight = itemBounds.right - inset;
|
|
1307
|
+
const bridgeBasePath = resolveBridgeBasePath(mainShape, center);
|
|
1308
|
+
const canBridge = !!bridgeBasePath && xRight - xLeft > eps;
|
|
1309
|
+
if (canBridge && bridgeBasePath) {
|
|
1310
|
+
const leftHit = getExitHit({
|
|
1311
|
+
mainShape: bridgeBasePath,
|
|
1312
|
+
x: xLeft,
|
|
1313
|
+
bridgeBottom,
|
|
1314
|
+
toY,
|
|
1315
|
+
eps,
|
|
1316
|
+
delta,
|
|
1317
|
+
overlap,
|
|
1318
|
+
op: f.operation
|
|
1319
|
+
});
|
|
1320
|
+
const rightHit = getExitHit({
|
|
1321
|
+
mainShape: bridgeBasePath,
|
|
1322
|
+
x: xRight,
|
|
1323
|
+
bridgeBottom,
|
|
1324
|
+
toY,
|
|
1325
|
+
eps,
|
|
1326
|
+
delta,
|
|
1327
|
+
overlap,
|
|
1328
|
+
op: f.operation
|
|
1329
|
+
});
|
|
1330
|
+
if (leftHit && rightHit) {
|
|
1331
|
+
const pathLength = bridgeBasePath.length;
|
|
1332
|
+
const leftOffset = leftHit.location.offset;
|
|
1333
|
+
const rightOffset = rightHit.location.offset;
|
|
1334
|
+
const distanceA = wrappedDistance(pathLength, leftOffset, rightOffset);
|
|
1335
|
+
const distanceB = wrappedDistance(pathLength, rightOffset, leftOffset);
|
|
1336
|
+
const countFor = (d) => Math.max(8, Math.min(80, Math.ceil(d / 6)));
|
|
1337
|
+
const offsetsA = sampleWrappedOffsets(
|
|
1338
|
+
pathLength,
|
|
1339
|
+
leftOffset,
|
|
1340
|
+
rightOffset,
|
|
1341
|
+
countFor(distanceA)
|
|
1342
|
+
);
|
|
1343
|
+
const offsetsB = sampleWrappedOffsets(
|
|
1344
|
+
pathLength,
|
|
1345
|
+
rightOffset,
|
|
1346
|
+
leftOffset,
|
|
1347
|
+
countFor(distanceB)
|
|
1348
|
+
);
|
|
1349
|
+
const pointsA = offsetsA.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
1350
|
+
const pointsB = offsetsB.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
1351
|
+
if (pointsA.length >= 2 && pointsB.length >= 2) {
|
|
1352
|
+
let topBase = selectOuterChain({
|
|
1353
|
+
mainShape: bridgeBasePath,
|
|
1354
|
+
pointsA,
|
|
1355
|
+
pointsB,
|
|
1356
|
+
delta,
|
|
1357
|
+
overlap,
|
|
1358
|
+
op: f.operation
|
|
1359
|
+
});
|
|
1360
|
+
const dist2 = (a, b) => {
|
|
1361
|
+
const dx = a.x - b.x;
|
|
1362
|
+
const dy = a.y - b.y;
|
|
1363
|
+
return dx * dx + dy * dy;
|
|
1364
|
+
};
|
|
1365
|
+
if (dist2(topBase[0], leftHit.point) > dist2(topBase[0], rightHit.point)) {
|
|
1366
|
+
topBase = topBase.slice().reverse();
|
|
1367
|
+
}
|
|
1368
|
+
topBase = topBase.slice();
|
|
1369
|
+
topBase[0] = leftHit.point;
|
|
1370
|
+
topBase[topBase.length - 1] = rightHit.point;
|
|
1371
|
+
const capShiftY = f.operation === "subtract" ? -Math.max(overlap * 2, delta) : overlap;
|
|
1372
|
+
const topPoints = topBase.map(
|
|
1373
|
+
(p) => p.add(new paper.Point(0, capShiftY))
|
|
1374
|
+
);
|
|
1375
|
+
const bridgeBottomY = bridgeBottom + overlap * 2;
|
|
1376
|
+
const bridgePoly = new paper.Path({ insert: false });
|
|
1377
|
+
for (const p of topPoints) bridgePoly.add(p);
|
|
1378
|
+
bridgePoly.add(new paper.Point(xRight, bridgeBottomY));
|
|
1379
|
+
bridgePoly.add(new paper.Point(xLeft, bridgeBottomY));
|
|
1380
|
+
bridgePoly.closed = true;
|
|
1381
|
+
const unitedItem = item.unite(bridgePoly);
|
|
1382
|
+
item.remove();
|
|
1383
|
+
bridgePoly.remove();
|
|
1384
|
+
if (f.operation === "add") {
|
|
1385
|
+
adds.push(unitedItem);
|
|
1386
|
+
} else {
|
|
1387
|
+
subtracts.push(unitedItem);
|
|
1388
|
+
}
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
if (f.operation === "add") {
|
|
1394
|
+
adds.push(item);
|
|
1395
|
+
} else {
|
|
1396
|
+
subtracts.push(item);
|
|
1397
|
+
}
|
|
1398
|
+
} else {
|
|
1399
|
+
if (f.operation === "add") {
|
|
1400
|
+
adds.push(item);
|
|
1401
|
+
} else {
|
|
1402
|
+
subtracts.push(item);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
} else {
|
|
1406
|
+
if (f.operation === "add") {
|
|
1407
|
+
adds.push(item);
|
|
1408
|
+
} else {
|
|
1409
|
+
subtracts.push(item);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
});
|
|
1413
|
+
if (adds.length > 0) {
|
|
1414
|
+
for (const item of adds) {
|
|
1415
|
+
try {
|
|
1416
|
+
const temp = mainShape.unite(item);
|
|
1417
|
+
mainShape.remove();
|
|
1418
|
+
item.remove();
|
|
1419
|
+
mainShape = normalizePathItem(temp);
|
|
1420
|
+
} catch (e) {
|
|
1421
|
+
console.error("Geometry: Failed to unite feature", e);
|
|
1422
|
+
item.remove();
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
if (subtracts.length > 0) {
|
|
1427
|
+
for (const item of subtracts) {
|
|
1428
|
+
try {
|
|
1429
|
+
const temp = mainShape.subtract(item);
|
|
1430
|
+
mainShape.remove();
|
|
1431
|
+
item.remove();
|
|
1432
|
+
mainShape = normalizePathItem(temp);
|
|
1433
|
+
} catch (e) {
|
|
1434
|
+
console.error("Geometry: Failed to subtract feature", e);
|
|
1435
|
+
item.remove();
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1132
1438
|
}
|
|
1133
|
-
const fixed = parseFloat(raw);
|
|
1134
|
-
return Number.isFinite(fixed) ? Math.max(0, fixed) : 0;
|
|
1135
1439
|
}
|
|
1136
|
-
return
|
|
1440
|
+
return mainShape;
|
|
1137
1441
|
}
|
|
1138
|
-
function
|
|
1139
|
-
const
|
|
1140
|
-
|
|
1141
|
-
);
|
|
1142
|
-
const minMm = Math.max(
|
|
1143
|
-
0.1,
|
|
1144
|
-
Number(configService.get("size.minMm", DEFAULT_SIZE_STATE.minMm))
|
|
1145
|
-
);
|
|
1146
|
-
const maxMm = Math.max(
|
|
1147
|
-
minMm,
|
|
1148
|
-
Number(configService.get("size.maxMm", DEFAULT_SIZE_STATE.maxMm))
|
|
1149
|
-
);
|
|
1150
|
-
const stepMm = Math.max(
|
|
1151
|
-
1e-3,
|
|
1152
|
-
Number(configService.get("size.stepMm", DEFAULT_SIZE_STATE.stepMm))
|
|
1153
|
-
);
|
|
1154
|
-
const actualWidthMm = sanitizeMmValue(
|
|
1155
|
-
parseLengthToMm(
|
|
1156
|
-
configService.get("size.actualWidthMm", DEFAULT_SIZE_STATE.actualWidthMm),
|
|
1157
|
-
"mm"
|
|
1158
|
-
),
|
|
1159
|
-
{ minMm, maxMm, stepMm }
|
|
1160
|
-
);
|
|
1161
|
-
const actualHeightMm = sanitizeMmValue(
|
|
1162
|
-
parseLengthToMm(
|
|
1163
|
-
configService.get(
|
|
1164
|
-
"size.actualHeightMm",
|
|
1165
|
-
DEFAULT_SIZE_STATE.actualHeightMm
|
|
1166
|
-
),
|
|
1167
|
-
"mm"
|
|
1168
|
-
),
|
|
1169
|
-
{ minMm, maxMm, stepMm }
|
|
1170
|
-
);
|
|
1171
|
-
const aspectRaw = Number(
|
|
1172
|
-
configService.get("size.aspectRatio", DEFAULT_SIZE_STATE.aspectRatio)
|
|
1173
|
-
);
|
|
1174
|
-
const aspectRatio = Number.isFinite(aspectRaw) && aspectRaw > 0 ? aspectRaw : actualWidthMm / Math.max(1e-3, actualHeightMm);
|
|
1175
|
-
const cutMarginMm = Math.max(
|
|
1176
|
-
0,
|
|
1177
|
-
parseLengthToMm(
|
|
1178
|
-
configService.get("size.cutMarginMm", DEFAULT_SIZE_STATE.cutMarginMm),
|
|
1179
|
-
"mm"
|
|
1180
|
-
)
|
|
1181
|
-
);
|
|
1182
|
-
const viewPadding = configService.get(
|
|
1183
|
-
"size.viewPadding",
|
|
1184
|
-
DEFAULT_SIZE_STATE.viewPadding
|
|
1442
|
+
function applySurfaceFeatures(shape, features, options) {
|
|
1443
|
+
const surfaceFeatures = features.filter(
|
|
1444
|
+
(f) => f.renderBehavior === "surface"
|
|
1185
1445
|
);
|
|
1186
|
-
return
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1446
|
+
if (surfaceFeatures.length === 0) return shape;
|
|
1447
|
+
let result = shape;
|
|
1448
|
+
for (const f of surfaceFeatures) {
|
|
1449
|
+
const pos = resolveFeaturePosition(f, options);
|
|
1450
|
+
const center = new paper.Point(pos.x, pos.y);
|
|
1451
|
+
const item = createFeatureItem(f, center);
|
|
1452
|
+
try {
|
|
1453
|
+
if (f.operation === "add") {
|
|
1454
|
+
const temp = result.unite(item);
|
|
1455
|
+
result.remove();
|
|
1456
|
+
item.remove();
|
|
1457
|
+
result = normalizePathItem(temp);
|
|
1458
|
+
} else {
|
|
1459
|
+
const temp = result.subtract(item);
|
|
1460
|
+
result.remove();
|
|
1461
|
+
item.remove();
|
|
1462
|
+
result = normalizePathItem(temp);
|
|
1463
|
+
}
|
|
1464
|
+
} catch (e) {
|
|
1465
|
+
console.error("Geometry: Failed to apply surface feature", e);
|
|
1466
|
+
item.remove();
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return result;
|
|
1206
1470
|
}
|
|
1207
|
-
function
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1471
|
+
function generateDielinePath(options) {
|
|
1472
|
+
const paperWidth = options.canvasWidth || options.width * 2 || 2e3;
|
|
1473
|
+
const paperHeight = options.canvasHeight || options.height * 2 || 2e3;
|
|
1474
|
+
ensurePaper(paperWidth, paperHeight);
|
|
1475
|
+
paper.project.activeLayer.removeChildren();
|
|
1476
|
+
const perimeter = getPerimeterShape(options);
|
|
1477
|
+
const finalShape = applySurfaceFeatures(perimeter, options.features, options);
|
|
1478
|
+
const pathData = finalShape.pathData;
|
|
1479
|
+
finalShape.remove();
|
|
1480
|
+
return pathData;
|
|
1216
1481
|
}
|
|
1217
|
-
function
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1482
|
+
function generateBleedZonePath(originalOptions, offsetOptions, offset) {
|
|
1483
|
+
const paperWidth = originalOptions.canvasWidth || originalOptions.width * 2 || 2e3;
|
|
1484
|
+
const paperHeight = originalOptions.canvasHeight || originalOptions.height * 2 || 2e3;
|
|
1485
|
+
ensurePaper(paperWidth, paperHeight);
|
|
1486
|
+
paper.project.activeLayer.removeChildren();
|
|
1487
|
+
const pOriginal = getPerimeterShape(originalOptions);
|
|
1488
|
+
const shapeOriginal = applySurfaceFeatures(
|
|
1489
|
+
pOriginal,
|
|
1490
|
+
originalOptions.features,
|
|
1491
|
+
originalOptions
|
|
1492
|
+
);
|
|
1493
|
+
const pOffset = getPerimeterShape(offsetOptions);
|
|
1494
|
+
const shapeOffset = applySurfaceFeatures(
|
|
1495
|
+
pOffset,
|
|
1496
|
+
offsetOptions.features,
|
|
1497
|
+
offsetOptions
|
|
1498
|
+
);
|
|
1499
|
+
let bleedZone;
|
|
1500
|
+
if (offset > 0) {
|
|
1501
|
+
bleedZone = shapeOffset.subtract(shapeOriginal);
|
|
1502
|
+
} else {
|
|
1503
|
+
bleedZone = shapeOriginal.subtract(shapeOffset);
|
|
1227
1504
|
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1505
|
+
const pathData = bleedZone.pathData;
|
|
1506
|
+
shapeOriginal.remove();
|
|
1507
|
+
shapeOffset.remove();
|
|
1508
|
+
bleedZone.remove();
|
|
1509
|
+
return pathData;
|
|
1510
|
+
}
|
|
1511
|
+
function getLowestPointOnDieline(options) {
|
|
1512
|
+
ensurePaper(options.width * 2, options.height * 2);
|
|
1513
|
+
paper.project.activeLayer.removeChildren();
|
|
1514
|
+
const shape = createBaseShape(options);
|
|
1515
|
+
const bounds = shape.bounds;
|
|
1516
|
+
const result = {
|
|
1517
|
+
x: bounds.center.x,
|
|
1518
|
+
y: bounds.bottom
|
|
1231
1519
|
};
|
|
1520
|
+
shape.remove();
|
|
1521
|
+
return result;
|
|
1232
1522
|
}
|
|
1233
|
-
function
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
const
|
|
1238
|
-
const
|
|
1239
|
-
const
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
const paddingPx = resolvePaddingPx(
|
|
1244
|
-
size.viewPadding,
|
|
1245
|
-
canvasWidth,
|
|
1246
|
-
canvasHeight
|
|
1247
|
-
);
|
|
1248
|
-
canvasService.viewport.updateContainer(canvasWidth, canvasHeight);
|
|
1249
|
-
canvasService.viewport.setPadding(paddingPx);
|
|
1250
|
-
canvasService.viewport.updatePhysical(viewWidthMm, viewHeightMm);
|
|
1251
|
-
const layout = canvasService.viewport.layout;
|
|
1252
|
-
if (!Number.isFinite(layout.scale) || !Number.isFinite(layout.offsetX) || !Number.isFinite(layout.offsetY) || layout.scale <= 0) {
|
|
1253
|
-
return null;
|
|
1254
|
-
}
|
|
1255
|
-
const centerX = layout.offsetX + layout.width / 2;
|
|
1256
|
-
const centerY = layout.offsetY + layout.height / 2;
|
|
1257
|
-
const trimWidthPx = size.actualWidthMm * layout.scale;
|
|
1258
|
-
const trimHeightPx = size.actualHeightMm * layout.scale;
|
|
1259
|
-
const cutWidthPx = cutWidthMm * layout.scale;
|
|
1260
|
-
const cutHeightPx = cutHeightMm * layout.scale;
|
|
1261
|
-
const trimRect = rectByCenter(centerX, centerY, trimWidthPx, trimHeightPx);
|
|
1262
|
-
const cutRect = rectByCenter(centerX, centerY, cutWidthPx, cutHeightPx);
|
|
1263
|
-
const bleedRect = rectByCenter(
|
|
1264
|
-
centerX,
|
|
1265
|
-
centerY,
|
|
1266
|
-
Math.max(trimWidthPx, cutWidthPx),
|
|
1267
|
-
Math.max(trimHeightPx, cutHeightPx)
|
|
1268
|
-
);
|
|
1269
|
-
return {
|
|
1270
|
-
scale: layout.scale,
|
|
1271
|
-
canvasWidth,
|
|
1272
|
-
canvasHeight,
|
|
1273
|
-
trimRect,
|
|
1274
|
-
cutRect,
|
|
1275
|
-
bleedRect,
|
|
1276
|
-
trimWidthMm: size.actualWidthMm,
|
|
1277
|
-
trimHeightMm: size.actualHeightMm,
|
|
1278
|
-
cutWidthMm,
|
|
1279
|
-
cutHeightMm,
|
|
1280
|
-
cutMode: size.cutMode,
|
|
1281
|
-
cutMarginMm: size.cutMarginMm
|
|
1523
|
+
function getNearestPointOnDieline(point, options) {
|
|
1524
|
+
ensurePaper(options.width * 2, options.height * 2);
|
|
1525
|
+
paper.project.activeLayer.removeChildren();
|
|
1526
|
+
const shape = createBaseShape(options);
|
|
1527
|
+
const p = new paper.Point(point.x, point.y);
|
|
1528
|
+
const location = shape.getNearestLocation(p);
|
|
1529
|
+
const result = {
|
|
1530
|
+
x: location.point.x,
|
|
1531
|
+
y: location.point.y,
|
|
1532
|
+
normal: location.normal ? { x: location.normal.x, y: location.normal.y } : void 0
|
|
1282
1533
|
};
|
|
1534
|
+
shape.remove();
|
|
1535
|
+
return result;
|
|
1283
1536
|
}
|
|
1284
|
-
function
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
);
|
|
1289
|
-
const offset = (layout.cutRect.width - layout.trimRect.width) / 2;
|
|
1290
|
-
const sourceWidth = Number(configService.get("dieline.customSourceWidthPx", 0));
|
|
1291
|
-
const sourceHeight = Number(
|
|
1292
|
-
configService.get("dieline.customSourceHeightPx", 0)
|
|
1293
|
-
);
|
|
1294
|
-
const shapeStyle = normalizeShapeStyle(
|
|
1295
|
-
configService.get("dieline.shapeStyle", DEFAULT_DIELINE_SHAPE_STYLE)
|
|
1296
|
-
);
|
|
1537
|
+
function getPathBounds(pathData) {
|
|
1538
|
+
const path = new paper.Path();
|
|
1539
|
+
path.pathData = pathData;
|
|
1540
|
+
const bounds = path.bounds;
|
|
1541
|
+
path.remove();
|
|
1297
1542
|
return {
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
unit: "px",
|
|
1303
|
-
x: layout.trimRect.centerX,
|
|
1304
|
-
y: layout.trimRect.centerY,
|
|
1305
|
-
width: layout.trimRect.width,
|
|
1306
|
-
height: layout.trimRect.height,
|
|
1307
|
-
radius: radiusMm * layout.scale,
|
|
1308
|
-
offset,
|
|
1309
|
-
scale: layout.scale,
|
|
1310
|
-
pathData: configService.get("dieline.pathData"),
|
|
1311
|
-
customSourceWidthPx: Number.isFinite(sourceWidth) && sourceWidth > 0 ? sourceWidth : void 0,
|
|
1312
|
-
customSourceHeightPx: Number.isFinite(sourceHeight) && sourceHeight > 0 ? sourceHeight : void 0
|
|
1543
|
+
x: bounds.x,
|
|
1544
|
+
y: bounds.y,
|
|
1545
|
+
width: bounds.width,
|
|
1546
|
+
height: bounds.height
|
|
1313
1547
|
};
|
|
1314
1548
|
}
|
|
1315
1549
|
|
|
@@ -1332,6 +1566,7 @@ var ImageTool = class {
|
|
|
1332
1566
|
this.isImageSelectionActive = false;
|
|
1333
1567
|
this.focusedImageId = null;
|
|
1334
1568
|
this.renderSeq = 0;
|
|
1569
|
+
this.imageSpecs = [];
|
|
1335
1570
|
this.overlaySpecs = [];
|
|
1336
1571
|
this.onToolActivated = (event) => {
|
|
1337
1572
|
const before = this.isToolActive;
|
|
@@ -1439,9 +1674,34 @@ var ImageTool = class {
|
|
|
1439
1674
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
1440
1675
|
this.id,
|
|
1441
1676
|
() => ({
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1677
|
+
passes: [
|
|
1678
|
+
{
|
|
1679
|
+
id: IMAGE_OBJECT_LAYER_ID,
|
|
1680
|
+
stack: 500,
|
|
1681
|
+
order: 0,
|
|
1682
|
+
visibility: {
|
|
1683
|
+
op: "not",
|
|
1684
|
+
expr: {
|
|
1685
|
+
op: "sessionActive",
|
|
1686
|
+
toolId: "pooder.kit.white-ink"
|
|
1687
|
+
}
|
|
1688
|
+
},
|
|
1689
|
+
objects: this.imageSpecs
|
|
1690
|
+
},
|
|
1691
|
+
{
|
|
1692
|
+
id: IMAGE_OVERLAY_LAYER_ID,
|
|
1693
|
+
stack: 800,
|
|
1694
|
+
order: 0,
|
|
1695
|
+
visibility: {
|
|
1696
|
+
op: "not",
|
|
1697
|
+
expr: {
|
|
1698
|
+
op: "sessionActive",
|
|
1699
|
+
toolId: "pooder.kit.white-ink"
|
|
1700
|
+
}
|
|
1701
|
+
},
|
|
1702
|
+
objects: this.overlaySpecs
|
|
1703
|
+
}
|
|
1704
|
+
]
|
|
1445
1705
|
}),
|
|
1446
1706
|
{ priority: 300 }
|
|
1447
1707
|
);
|
|
@@ -1498,6 +1758,7 @@ var ImageTool = class {
|
|
|
1498
1758
|
this.cropShapeHatchPattern = void 0;
|
|
1499
1759
|
this.cropShapeHatchPatternColor = void 0;
|
|
1500
1760
|
this.cropShapeHatchPatternKey = void 0;
|
|
1761
|
+
this.imageSpecs = [];
|
|
1501
1762
|
this.overlaySpecs = [];
|
|
1502
1763
|
this.clearRenderedImages();
|
|
1503
1764
|
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
@@ -1973,9 +2234,7 @@ var ImageTool = class {
|
|
|
1973
2234
|
}
|
|
1974
2235
|
getOverlayObjects() {
|
|
1975
2236
|
if (!this.canvasService) return [];
|
|
1976
|
-
return this.canvasService.
|
|
1977
|
-
IMAGE_OVERLAY_LAYER_ID
|
|
1978
|
-
);
|
|
2237
|
+
return this.canvasService.getPassObjects(IMAGE_OVERLAY_LAYER_ID);
|
|
1979
2238
|
}
|
|
1980
2239
|
getImageObject(id) {
|
|
1981
2240
|
return this.getImageObjects().find((obj) => {
|
|
@@ -1985,9 +2244,9 @@ var ImageTool = class {
|
|
|
1985
2244
|
}
|
|
1986
2245
|
clearRenderedImages() {
|
|
1987
2246
|
if (!this.canvasService) return;
|
|
1988
|
-
|
|
1989
|
-
this.
|
|
1990
|
-
this.canvasService.
|
|
2247
|
+
this.imageSpecs = [];
|
|
2248
|
+
this.overlaySpecs = [];
|
|
2249
|
+
this.canvasService.requestRenderFromProducers();
|
|
1991
2250
|
}
|
|
1992
2251
|
purgeSourceSizeCacheForItem(item) {
|
|
1993
2252
|
if (!item) return;
|
|
@@ -2015,6 +2274,29 @@ var ImageTool = class {
|
|
|
2015
2274
|
}
|
|
2016
2275
|
return { width: 1, height: 1 };
|
|
2017
2276
|
}
|
|
2277
|
+
async ensureSourceSize(src) {
|
|
2278
|
+
if (!src) return null;
|
|
2279
|
+
const cached = this.sourceSizeBySrc.get(src);
|
|
2280
|
+
if (cached) return cached;
|
|
2281
|
+
try {
|
|
2282
|
+
const image = await FabricImage2.fromURL(src, {
|
|
2283
|
+
crossOrigin: "anonymous"
|
|
2284
|
+
});
|
|
2285
|
+
const width = Number((image == null ? void 0 : image.width) || 0);
|
|
2286
|
+
const height = Number((image == null ? void 0 : image.height) || 0);
|
|
2287
|
+
if (width > 0 && height > 0) {
|
|
2288
|
+
const size = { width, height };
|
|
2289
|
+
this.sourceSizeBySrc.set(src, size);
|
|
2290
|
+
return size;
|
|
2291
|
+
}
|
|
2292
|
+
} catch (error) {
|
|
2293
|
+
this.debug("image:size:load-failed", {
|
|
2294
|
+
src,
|
|
2295
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2296
|
+
});
|
|
2297
|
+
}
|
|
2298
|
+
return null;
|
|
2299
|
+
}
|
|
2018
2300
|
getCoverScale(frame, size) {
|
|
2019
2301
|
const sw = Math.max(1, size.width);
|
|
2020
2302
|
const sh = Math.max(1, size.height);
|
|
@@ -2349,24 +2631,6 @@ var ImageTool = class {
|
|
|
2349
2631
|
opacity: render.opacity
|
|
2350
2632
|
};
|
|
2351
2633
|
}
|
|
2352
|
-
toScreenObjectProps(props) {
|
|
2353
|
-
if (!this.canvasService) return props;
|
|
2354
|
-
const next = { ...props };
|
|
2355
|
-
if (Number.isFinite(next.left) || Number.isFinite(next.top)) {
|
|
2356
|
-
const mapped = this.canvasService.toScreenPoint({
|
|
2357
|
-
x: Number.isFinite(next.left) ? Number(next.left) : 0,
|
|
2358
|
-
y: Number.isFinite(next.top) ? Number(next.top) : 0
|
|
2359
|
-
});
|
|
2360
|
-
if (Number.isFinite(next.left)) next.left = mapped.x;
|
|
2361
|
-
if (Number.isFinite(next.top)) next.top = mapped.y;
|
|
2362
|
-
}
|
|
2363
|
-
const sceneScale = this.canvasService.getSceneScale();
|
|
2364
|
-
const sx = Number.isFinite(next.scaleX) ? Number(next.scaleX) : 1;
|
|
2365
|
-
const sy = Number.isFinite(next.scaleY) ? Number(next.scaleY) : 1;
|
|
2366
|
-
next.scaleX = sx * sceneScale;
|
|
2367
|
-
next.scaleY = sy * sceneScale;
|
|
2368
|
-
return next;
|
|
2369
|
-
}
|
|
2370
2634
|
toSceneObjectScale(value) {
|
|
2371
2635
|
if (!this.canvasService) return value;
|
|
2372
2636
|
return value / this.canvasService.getSceneScale();
|
|
@@ -2377,104 +2641,27 @@ var ImageTool = class {
|
|
|
2377
2641
|
if (typeof obj.getSrc === "function") return obj.getSrc();
|
|
2378
2642
|
return (_a = obj == null ? void 0 : obj._originalElement) == null ? void 0 : _a.src;
|
|
2379
2643
|
}
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
});
|
|
2393
|
-
}
|
|
2394
|
-
async upsertImageObject(item, frame, seq) {
|
|
2395
|
-
if (!this.canvasService) return;
|
|
2396
|
-
const canvas = this.canvasService.canvas;
|
|
2397
|
-
const render = this.resolveRenderImageState(item);
|
|
2398
|
-
if (!render.src) return;
|
|
2399
|
-
let obj = this.getImageObject(item.id);
|
|
2400
|
-
const currentSrc = this.getCurrentSrc(obj);
|
|
2401
|
-
if (obj && currentSrc && currentSrc !== render.src) {
|
|
2402
|
-
canvas.remove(obj);
|
|
2403
|
-
obj = void 0;
|
|
2404
|
-
}
|
|
2405
|
-
if (!obj) {
|
|
2406
|
-
const created = await FabricImage2.fromURL(render.src, {
|
|
2407
|
-
crossOrigin: "anonymous"
|
|
2408
|
-
});
|
|
2409
|
-
if (seq !== this.renderSeq) return;
|
|
2410
|
-
created.set({
|
|
2644
|
+
async buildImageSpecs(items, frame) {
|
|
2645
|
+
const specs = [];
|
|
2646
|
+
for (const item of items) {
|
|
2647
|
+
const render = this.resolveRenderImageState(item);
|
|
2648
|
+
if (!render.src) continue;
|
|
2649
|
+
const ensured = await this.ensureSourceSize(render.src);
|
|
2650
|
+
const sourceSize = ensured || this.getSourceSize(render.src);
|
|
2651
|
+
const props = this.computeCanvasProps(render, sourceSize, frame);
|
|
2652
|
+
specs.push({
|
|
2653
|
+
id: item.id,
|
|
2654
|
+
type: "image",
|
|
2655
|
+
src: render.src,
|
|
2411
2656
|
data: {
|
|
2412
2657
|
id: item.id,
|
|
2413
2658
|
layerId: IMAGE_OBJECT_LAYER_ID,
|
|
2414
2659
|
type: "image-item"
|
|
2415
|
-
}
|
|
2660
|
+
},
|
|
2661
|
+
props
|
|
2416
2662
|
});
|
|
2417
|
-
canvas.add(created);
|
|
2418
|
-
obj = created;
|
|
2419
|
-
}
|
|
2420
|
-
this.rememberSourceSize(render.src, obj);
|
|
2421
|
-
const sourceSize = this.getSourceSize(render.src, obj);
|
|
2422
|
-
const props = this.computeCanvasProps(render, sourceSize, frame);
|
|
2423
|
-
const screenProps = this.toScreenObjectProps(props);
|
|
2424
|
-
obj.set({
|
|
2425
|
-
...screenProps,
|
|
2426
|
-
data: {
|
|
2427
|
-
...obj.data || {},
|
|
2428
|
-
id: item.id,
|
|
2429
|
-
layerId: IMAGE_OBJECT_LAYER_ID,
|
|
2430
|
-
type: "image-item"
|
|
2431
|
-
}
|
|
2432
|
-
});
|
|
2433
|
-
this.applyImageControlVisibility(obj);
|
|
2434
|
-
obj.setCoords();
|
|
2435
|
-
const resolver = this.loadResolvers.get(item.id);
|
|
2436
|
-
if (resolver) {
|
|
2437
|
-
resolver();
|
|
2438
|
-
this.loadResolvers.delete(item.id);
|
|
2439
|
-
}
|
|
2440
|
-
}
|
|
2441
|
-
syncImageZOrder(items) {
|
|
2442
|
-
if (!this.canvasService) return;
|
|
2443
|
-
const canvas = this.canvasService.canvas;
|
|
2444
|
-
const objects = canvas.getObjects();
|
|
2445
|
-
let insertIndex = 0;
|
|
2446
|
-
const backgroundLayer = this.canvasService.getLayer("background");
|
|
2447
|
-
if (backgroundLayer) {
|
|
2448
|
-
const bgIndex = objects.indexOf(backgroundLayer);
|
|
2449
|
-
if (bgIndex >= 0) insertIndex = bgIndex + 1;
|
|
2450
|
-
}
|
|
2451
|
-
items.forEach((item) => {
|
|
2452
|
-
const obj = this.getImageObject(item.id);
|
|
2453
|
-
if (!obj) return;
|
|
2454
|
-
canvas.moveObjectTo(obj, insertIndex);
|
|
2455
|
-
insertIndex += 1;
|
|
2456
|
-
});
|
|
2457
|
-
const overlayObjects = this.getOverlayObjects().sort((a, b) => {
|
|
2458
|
-
var _a, _b, _c, _d;
|
|
2459
|
-
const az = Number((_b = (_a = a == null ? void 0 : a.data) == null ? void 0 : _a.zIndex) != null ? _b : 0);
|
|
2460
|
-
const bz = Number((_d = (_c = b == null ? void 0 : b.data) == null ? void 0 : _c.zIndex) != null ? _d : 0);
|
|
2461
|
-
return az - bz;
|
|
2462
|
-
});
|
|
2463
|
-
overlayObjects.forEach((obj) => {
|
|
2464
|
-
canvas.bringObjectToFront(obj);
|
|
2465
|
-
});
|
|
2466
|
-
if (this.isDebugEnabled()) {
|
|
2467
|
-
const stack = canvas.getObjects().map((obj, index) => {
|
|
2468
|
-
var _a, _b, _c;
|
|
2469
|
-
return {
|
|
2470
|
-
index,
|
|
2471
|
-
id: (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id,
|
|
2472
|
-
layerId: (_b = obj == null ? void 0 : obj.data) == null ? void 0 : _b.layerId,
|
|
2473
|
-
zIndex: (_c = obj == null ? void 0 : obj.data) == null ? void 0 : _c.zIndex
|
|
2474
|
-
};
|
|
2475
|
-
}).filter((item) => item.layerId === IMAGE_OVERLAY_LAYER_ID);
|
|
2476
|
-
this.debug("overlay:stack", stack);
|
|
2477
2663
|
}
|
|
2664
|
+
return specs;
|
|
2478
2665
|
}
|
|
2479
2666
|
buildOverlaySpecs(frame, sceneGeometry) {
|
|
2480
2667
|
const visible = this.isImageEditingVisible();
|
|
@@ -2638,7 +2825,7 @@ var ImageTool = class {
|
|
|
2638
2825
|
evented: false
|
|
2639
2826
|
}
|
|
2640
2827
|
};
|
|
2641
|
-
const specs = [...mask, ...shapeOverlay, frameSpec];
|
|
2828
|
+
const specs = shapeOverlay.length > 0 ? [...mask, ...shapeOverlay] : [...mask, ...shapeOverlay, frameSpec];
|
|
2642
2829
|
this.debug("overlay:built", {
|
|
2643
2830
|
frame,
|
|
2644
2831
|
shape: sceneGeometry == null ? void 0 : sceneGeometry.shape,
|
|
@@ -2668,30 +2855,32 @@ var ImageTool = class {
|
|
|
2668
2855
|
skipRender: true
|
|
2669
2856
|
});
|
|
2670
2857
|
}
|
|
2671
|
-
this.
|
|
2672
|
-
var _a, _b;
|
|
2673
|
-
const id = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id;
|
|
2674
|
-
if (typeof id === "string" && !desiredIds.has(id)) {
|
|
2675
|
-
(_b = this.canvasService) == null ? void 0 : _b.canvas.remove(obj);
|
|
2676
|
-
}
|
|
2677
|
-
});
|
|
2678
|
-
for (const item of renderItems) {
|
|
2679
|
-
if (seq !== this.renderSeq) return;
|
|
2680
|
-
await this.upsertImageObject(item, frame, seq);
|
|
2681
|
-
}
|
|
2858
|
+
const imageSpecs = await this.buildImageSpecs(renderItems, frame);
|
|
2682
2859
|
if (seq !== this.renderSeq) return;
|
|
2683
|
-
this.syncImageZOrder(renderItems);
|
|
2684
2860
|
const sceneGeometry = await this.resolveSceneGeometryForOverlay();
|
|
2685
2861
|
if (seq !== this.renderSeq) return;
|
|
2686
|
-
|
|
2687
|
-
this.overlaySpecs =
|
|
2862
|
+
this.imageSpecs = imageSpecs;
|
|
2863
|
+
this.overlaySpecs = this.buildOverlaySpecs(frame, sceneGeometry);
|
|
2688
2864
|
await this.canvasService.flushRenderFromProducers();
|
|
2689
|
-
this.
|
|
2865
|
+
if (seq !== this.renderSeq) return;
|
|
2866
|
+
renderItems.forEach((item) => {
|
|
2867
|
+
if (!this.getImageObject(item.id)) return;
|
|
2868
|
+
const resolver = this.loadResolvers.get(item.id);
|
|
2869
|
+
if (!resolver) return;
|
|
2870
|
+
resolver();
|
|
2871
|
+
this.loadResolvers.delete(item.id);
|
|
2872
|
+
});
|
|
2873
|
+
if (this.focusedImageId && this.isToolActive) {
|
|
2874
|
+
this.setImageFocus(this.focusedImageId, {
|
|
2875
|
+
syncCanvasSelection: true,
|
|
2876
|
+
skipRender: true
|
|
2877
|
+
});
|
|
2878
|
+
}
|
|
2690
2879
|
const overlayCanvasCount = this.getOverlayObjects().length;
|
|
2691
2880
|
this.debug("render:done", {
|
|
2692
2881
|
seq,
|
|
2693
2882
|
renderCount: renderItems.length,
|
|
2694
|
-
overlayCount: overlaySpecs.length,
|
|
2883
|
+
overlayCount: this.overlaySpecs.length,
|
|
2695
2884
|
overlayCanvasCount,
|
|
2696
2885
|
isToolActive: this.isToolActive,
|
|
2697
2886
|
isImageSelectionActive: this.isImageSelectionActive,
|
|
@@ -4341,11 +4530,11 @@ var DielineTool = class {
|
|
|
4341
4530
|
style: "solid"
|
|
4342
4531
|
},
|
|
4343
4532
|
insideColor: "rgba(0,0,0,0)",
|
|
4344
|
-
outsideColor: "#ffffff",
|
|
4345
4533
|
showBleedLines: true,
|
|
4346
4534
|
features: []
|
|
4347
4535
|
};
|
|
4348
4536
|
this.specs = [];
|
|
4537
|
+
this.effects = [];
|
|
4349
4538
|
this.renderSeq = 0;
|
|
4350
4539
|
this.onCanvasResized = () => {
|
|
4351
4540
|
this.updateDieline();
|
|
@@ -4382,10 +4571,23 @@ var DielineTool = class {
|
|
|
4382
4571
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
4383
4572
|
this.id,
|
|
4384
4573
|
() => ({
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4574
|
+
passes: [
|
|
4575
|
+
{
|
|
4576
|
+
id: DIELINE_LAYER_ID,
|
|
4577
|
+
stack: 700,
|
|
4578
|
+
order: 0,
|
|
4579
|
+
replace: true,
|
|
4580
|
+
visibility: {
|
|
4581
|
+
op: "not",
|
|
4582
|
+
expr: {
|
|
4583
|
+
op: "activeToolIn",
|
|
4584
|
+
ids: ["pooder.kit.image", "pooder.kit.white-ink"]
|
|
4585
|
+
}
|
|
4586
|
+
},
|
|
4587
|
+
effects: this.effects,
|
|
4588
|
+
objects: this.specs
|
|
4589
|
+
}
|
|
4590
|
+
]
|
|
4389
4591
|
}),
|
|
4390
4592
|
{ priority: 250 }
|
|
4391
4593
|
);
|
|
@@ -4441,10 +4643,6 @@ var DielineTool = class {
|
|
|
4441
4643
|
s.offsetLine.style
|
|
4442
4644
|
);
|
|
4443
4645
|
s.insideColor = configService.get("dieline.insideColor", s.insideColor);
|
|
4444
|
-
s.outsideColor = configService.get(
|
|
4445
|
-
"dieline.outsideColor",
|
|
4446
|
-
s.outsideColor
|
|
4447
|
-
);
|
|
4448
4646
|
s.showBleedLines = configService.get(
|
|
4449
4647
|
"dieline.showBleedLines",
|
|
4450
4648
|
s.showBleedLines
|
|
@@ -4507,9 +4705,6 @@ var DielineTool = class {
|
|
|
4507
4705
|
case "dieline.insideColor":
|
|
4508
4706
|
s.insideColor = e.value;
|
|
4509
4707
|
break;
|
|
4510
|
-
case "dieline.outsideColor":
|
|
4511
|
-
s.outsideColor = e.value;
|
|
4512
|
-
break;
|
|
4513
4708
|
case "dieline.showBleedLines":
|
|
4514
4709
|
s.showBleedLines = e.value;
|
|
4515
4710
|
break;
|
|
@@ -4538,6 +4733,7 @@ var DielineTool = class {
|
|
|
4538
4733
|
context.eventBus.off("canvas:resized", this.onCanvasResized);
|
|
4539
4734
|
this.renderSeq += 1;
|
|
4540
4735
|
this.specs = [];
|
|
4736
|
+
this.effects = [];
|
|
4541
4737
|
(_a = this.renderProducerDisposable) == null ? void 0 : _a.dispose();
|
|
4542
4738
|
this.renderProducerDisposable = void 0;
|
|
4543
4739
|
if (this.canvasService) {
|
|
@@ -4654,12 +4850,6 @@ var DielineTool = class {
|
|
|
4654
4850
|
label: "Inside Color",
|
|
4655
4851
|
default: s.insideColor
|
|
4656
4852
|
},
|
|
4657
|
-
{
|
|
4658
|
-
id: "dieline.outsideColor",
|
|
4659
|
-
type: "color",
|
|
4660
|
-
label: "Outside Color",
|
|
4661
|
-
default: s.outsideColor
|
|
4662
|
-
},
|
|
4663
4853
|
{
|
|
4664
4854
|
id: "dieline.features",
|
|
4665
4855
|
type: "json",
|
|
@@ -4783,6 +4973,12 @@ var DielineTool = class {
|
|
|
4783
4973
|
"ConfigurationService"
|
|
4784
4974
|
);
|
|
4785
4975
|
}
|
|
4976
|
+
hasImageItems() {
|
|
4977
|
+
const configService = this.getConfigService();
|
|
4978
|
+
if (!configService) return false;
|
|
4979
|
+
const items = configService.get("image.items", []);
|
|
4980
|
+
return Array.isArray(items) && items.length > 0;
|
|
4981
|
+
}
|
|
4786
4982
|
syncSizeState(configService) {
|
|
4787
4983
|
const sizeState = readSizeState(configService);
|
|
4788
4984
|
this.state.width = sizeState.actualWidthMm;
|
|
@@ -4790,29 +4986,6 @@ var DielineTool = class {
|
|
|
4790
4986
|
this.state.padding = sizeState.viewPadding;
|
|
4791
4987
|
this.state.offset = sizeState.cutMode === "outset" ? sizeState.cutMarginMm : sizeState.cutMode === "inset" ? -sizeState.cutMarginMm : 0;
|
|
4792
4988
|
}
|
|
4793
|
-
bringFeatureMarkersToFront() {
|
|
4794
|
-
if (!this.canvasService) return;
|
|
4795
|
-
const canvas = this.canvasService.canvas;
|
|
4796
|
-
canvas.getObjects().filter((obj) => {
|
|
4797
|
-
var _a;
|
|
4798
|
-
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.type) === "feature-marker";
|
|
4799
|
-
}).forEach((obj) => canvas.bringObjectToFront(obj));
|
|
4800
|
-
}
|
|
4801
|
-
ensureLayerStacking() {
|
|
4802
|
-
if (!this.canvasService) return;
|
|
4803
|
-
const layer = this.canvasService.getLayer(DIELINE_LAYER_ID);
|
|
4804
|
-
if (!layer) return;
|
|
4805
|
-
const userLayer = this.canvasService.getLayer("user");
|
|
4806
|
-
if (userLayer) {
|
|
4807
|
-
const layerIndex = this.canvasService.canvas.getObjects().indexOf(layer);
|
|
4808
|
-
const userIndex = this.canvasService.canvas.getObjects().indexOf(userLayer);
|
|
4809
|
-
if (layerIndex < userIndex) {
|
|
4810
|
-
this.canvasService.canvas.moveObjectTo(layer, userIndex + 1);
|
|
4811
|
-
}
|
|
4812
|
-
return;
|
|
4813
|
-
}
|
|
4814
|
-
this.canvasService.canvas.bringObjectToFront(layer);
|
|
4815
|
-
}
|
|
4816
4989
|
buildDielineSpecs(sceneLayout) {
|
|
4817
4990
|
var _a, _b;
|
|
4818
4991
|
const {
|
|
@@ -4822,10 +4995,10 @@ var DielineTool = class {
|
|
|
4822
4995
|
mainLine,
|
|
4823
4996
|
offsetLine,
|
|
4824
4997
|
insideColor,
|
|
4825
|
-
outsideColor,
|
|
4826
4998
|
showBleedLines,
|
|
4827
4999
|
features
|
|
4828
5000
|
} = this.state;
|
|
5001
|
+
const hasImages = this.hasImageItems();
|
|
4829
5002
|
const canvasW = sceneLayout.canvasWidth || ((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 800;
|
|
4830
5003
|
const canvasH = sceneLayout.canvasHeight || ((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 600;
|
|
4831
5004
|
const scale = sceneLayout.scale;
|
|
@@ -4847,41 +5020,8 @@ var DielineTool = class {
|
|
|
4847
5020
|
radius: (f.radius || 0) * scale
|
|
4848
5021
|
}));
|
|
4849
5022
|
const cutFeatures = absoluteFeatures.filter((f) => !f.skipCut);
|
|
4850
|
-
const
|
|
4851
|
-
|
|
4852
|
-
canvasHeight: canvasH,
|
|
4853
|
-
shape,
|
|
4854
|
-
width: cutW,
|
|
4855
|
-
height: cutH,
|
|
4856
|
-
radius: cutR,
|
|
4857
|
-
x: cx,
|
|
4858
|
-
y: cy,
|
|
4859
|
-
features: cutFeatures,
|
|
4860
|
-
shapeStyle,
|
|
4861
|
-
pathData: this.state.pathData,
|
|
4862
|
-
customSourceWidthPx: this.state.customSourceWidthPx,
|
|
4863
|
-
customSourceHeightPx: this.state.customSourceHeightPx
|
|
4864
|
-
});
|
|
4865
|
-
const specs = [
|
|
4866
|
-
{
|
|
4867
|
-
id: "dieline.mask",
|
|
4868
|
-
type: "path",
|
|
4869
|
-
space: "screen",
|
|
4870
|
-
data: { id: "dieline.mask", type: "dieline" },
|
|
4871
|
-
props: {
|
|
4872
|
-
pathData: maskPathData,
|
|
4873
|
-
fill: outsideColor,
|
|
4874
|
-
stroke: null,
|
|
4875
|
-
selectable: false,
|
|
4876
|
-
evented: false,
|
|
4877
|
-
originX: "left",
|
|
4878
|
-
originY: "top",
|
|
4879
|
-
left: 0,
|
|
4880
|
-
top: 0
|
|
4881
|
-
}
|
|
4882
|
-
}
|
|
4883
|
-
];
|
|
4884
|
-
if (insideColor && insideColor !== "transparent" && insideColor !== "rgba(0,0,0,0)") {
|
|
5023
|
+
const specs = [];
|
|
5024
|
+
if (insideColor && insideColor !== "transparent" && insideColor !== "rgba(0,0,0,0)" && !hasImages) {
|
|
4885
5025
|
const productPathData = generateDielinePath({
|
|
4886
5026
|
shape,
|
|
4887
5027
|
width: cutW,
|
|
@@ -5035,6 +5175,73 @@ var DielineTool = class {
|
|
|
5035
5175
|
});
|
|
5036
5176
|
return specs;
|
|
5037
5177
|
}
|
|
5178
|
+
buildImageClipEffects(sceneLayout) {
|
|
5179
|
+
var _a, _b;
|
|
5180
|
+
const { shape, shapeStyle, radius, features } = this.state;
|
|
5181
|
+
const canvasW = sceneLayout.canvasWidth || ((_a = this.canvasService) == null ? void 0 : _a.canvas.width) || 800;
|
|
5182
|
+
const canvasH = sceneLayout.canvasHeight || ((_b = this.canvasService) == null ? void 0 : _b.canvas.height) || 600;
|
|
5183
|
+
const scale = sceneLayout.scale;
|
|
5184
|
+
const cx = sceneLayout.trimRect.centerX;
|
|
5185
|
+
const cy = sceneLayout.trimRect.centerY;
|
|
5186
|
+
const visualWidth = sceneLayout.trimRect.width;
|
|
5187
|
+
const visualRadius = radius * scale;
|
|
5188
|
+
const cutW = sceneLayout.cutRect.width;
|
|
5189
|
+
const cutH = sceneLayout.cutRect.height;
|
|
5190
|
+
const visualOffset = (cutW - visualWidth) / 2;
|
|
5191
|
+
const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
5192
|
+
const absoluteFeatures = (features || []).map((f) => ({
|
|
5193
|
+
...f,
|
|
5194
|
+
x: f.x,
|
|
5195
|
+
y: f.y,
|
|
5196
|
+
width: (f.width || 0) * scale,
|
|
5197
|
+
height: (f.height || 0) * scale,
|
|
5198
|
+
radius: (f.radius || 0) * scale
|
|
5199
|
+
}));
|
|
5200
|
+
const cutFeatures = absoluteFeatures.filter((f) => !f.skipCut);
|
|
5201
|
+
const clipPathData = generateDielinePath({
|
|
5202
|
+
shape,
|
|
5203
|
+
width: cutW,
|
|
5204
|
+
height: cutH,
|
|
5205
|
+
radius: cutR,
|
|
5206
|
+
x: cx,
|
|
5207
|
+
y: cy,
|
|
5208
|
+
features: cutFeatures,
|
|
5209
|
+
shapeStyle,
|
|
5210
|
+
pathData: this.state.pathData,
|
|
5211
|
+
customSourceWidthPx: this.state.customSourceWidthPx,
|
|
5212
|
+
customSourceHeightPx: this.state.customSourceHeightPx,
|
|
5213
|
+
canvasWidth: canvasW,
|
|
5214
|
+
canvasHeight: canvasH
|
|
5215
|
+
});
|
|
5216
|
+
if (!clipPathData) return [];
|
|
5217
|
+
return [
|
|
5218
|
+
{
|
|
5219
|
+
type: "clipPath",
|
|
5220
|
+
id: "dieline.clip.image",
|
|
5221
|
+
targetPassIds: [IMAGE_OBJECT_LAYER_ID2],
|
|
5222
|
+
source: {
|
|
5223
|
+
id: "dieline.effect.clip-path",
|
|
5224
|
+
type: "path",
|
|
5225
|
+
space: "screen",
|
|
5226
|
+
data: {
|
|
5227
|
+
id: "dieline.effect.clip-path",
|
|
5228
|
+
type: "dieline-effect",
|
|
5229
|
+
effect: "clipPath"
|
|
5230
|
+
},
|
|
5231
|
+
props: {
|
|
5232
|
+
pathData: clipPathData,
|
|
5233
|
+
fill: "#000000",
|
|
5234
|
+
stroke: null,
|
|
5235
|
+
originX: "left",
|
|
5236
|
+
originY: "top",
|
|
5237
|
+
selectable: false,
|
|
5238
|
+
evented: false,
|
|
5239
|
+
excludeFromExport: true
|
|
5240
|
+
}
|
|
5241
|
+
}
|
|
5242
|
+
}
|
|
5243
|
+
];
|
|
5244
|
+
}
|
|
5038
5245
|
updateDieline(_emitEvent = true) {
|
|
5039
5246
|
void this.updateDielineAsync();
|
|
5040
5247
|
}
|
|
@@ -5051,17 +5258,17 @@ var DielineTool = class {
|
|
|
5051
5258
|
if (!sceneLayout) {
|
|
5052
5259
|
if (seq !== this.renderSeq) return;
|
|
5053
5260
|
this.specs = [];
|
|
5261
|
+
this.effects = [];
|
|
5054
5262
|
await this.canvasService.flushRenderFromProducers();
|
|
5055
5263
|
return;
|
|
5056
5264
|
}
|
|
5057
5265
|
const nextSpecs = this.buildDielineSpecs(sceneLayout);
|
|
5266
|
+
const nextEffects = this.buildImageClipEffects(sceneLayout);
|
|
5058
5267
|
if (seq !== this.renderSeq) return;
|
|
5059
5268
|
this.specs = nextSpecs;
|
|
5269
|
+
this.effects = nextEffects;
|
|
5060
5270
|
await this.canvasService.flushRenderFromProducers();
|
|
5061
5271
|
if (seq !== this.renderSeq) return;
|
|
5062
|
-
this.ensureLayerStacking();
|
|
5063
|
-
this.bringFeatureMarkersToFront();
|
|
5064
|
-
this.canvasService.bringLayerToFront("ruler-overlay");
|
|
5065
5272
|
this.canvasService.requestRenderAll();
|
|
5066
5273
|
}
|
|
5067
5274
|
getGeometry() {
|
|
@@ -5496,9 +5703,14 @@ var FeatureTool = class {
|
|
|
5496
5703
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
5497
5704
|
this.id,
|
|
5498
5705
|
() => ({
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5706
|
+
passes: [
|
|
5707
|
+
{
|
|
5708
|
+
id: FEATURE_OVERLAY_LAYER_ID,
|
|
5709
|
+
stack: 880,
|
|
5710
|
+
order: 0,
|
|
5711
|
+
objects: this.specs
|
|
5712
|
+
}
|
|
5713
|
+
]
|
|
5502
5714
|
}),
|
|
5503
5715
|
{ priority: 350 }
|
|
5504
5716
|
);
|
|
@@ -5641,10 +5853,10 @@ var FeatureTool = class {
|
|
|
5641
5853
|
await this.refreshGeometry();
|
|
5642
5854
|
this.setWorkingFeatures(original);
|
|
5643
5855
|
this.hasWorkingChanges = false;
|
|
5856
|
+
this.clearFeatureSessionState();
|
|
5644
5857
|
this.redraw();
|
|
5645
5858
|
this.emitWorkingChange();
|
|
5646
5859
|
this.updateCommittedFeatures(original);
|
|
5647
|
-
this.clearFeatureSessionState();
|
|
5648
5860
|
return { ok: true };
|
|
5649
5861
|
}
|
|
5650
5862
|
},
|
|
@@ -5960,6 +6172,7 @@ var FeatureTool = class {
|
|
|
5960
6172
|
}
|
|
5961
6173
|
getDraggableMarkerTarget(target) {
|
|
5962
6174
|
var _a, _b;
|
|
6175
|
+
if (!this.isFeatureSessionActive || !this.isToolActive) return null;
|
|
5963
6176
|
if (!target || ((_a = target.data) == null ? void 0 : _a.type) !== "feature-marker") return null;
|
|
5964
6177
|
if (((_b = target.data) == null ? void 0 : _b.markerRole) !== "handle") return null;
|
|
5965
6178
|
return target;
|
|
@@ -6082,18 +6295,12 @@ var FeatureTool = class {
|
|
|
6082
6295
|
if (seq !== this.renderSeq) return;
|
|
6083
6296
|
await this.canvasService.flushRenderFromProducers();
|
|
6084
6297
|
if (seq !== this.renderSeq) return;
|
|
6085
|
-
this.syncOverlayOrder();
|
|
6086
6298
|
if (options.enforceConstraints) {
|
|
6087
6299
|
this.enforceConstraints();
|
|
6088
6300
|
}
|
|
6089
6301
|
}
|
|
6090
|
-
syncOverlayOrder() {
|
|
6091
|
-
if (!this.canvasService) return;
|
|
6092
|
-
this.canvasService.bringLayerToFront(FEATURE_OVERLAY_LAYER_ID);
|
|
6093
|
-
this.canvasService.bringLayerToFront("ruler-overlay");
|
|
6094
|
-
}
|
|
6095
6302
|
buildFeatureSpecs() {
|
|
6096
|
-
if (!this.currentGeometry || this.workingFeatures.length === 0) {
|
|
6303
|
+
if (!this.isFeatureSessionActive || !this.currentGeometry || this.workingFeatures.length === 0) {
|
|
6097
6304
|
return [];
|
|
6098
6305
|
}
|
|
6099
6306
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -6163,11 +6370,12 @@ var FeatureTool = class {
|
|
|
6163
6370
|
const color = feature.color || (feature.operation === "add" ? "#00FF00" : "#FF0000");
|
|
6164
6371
|
const strokeDash = feature.strokeDash || (feature.operation === "subtract" ? [4, 4] : void 0);
|
|
6165
6372
|
const interactive = options.markerRole === "handle";
|
|
6373
|
+
const sessionVisible = this.isToolActive && this.isFeatureSessionActive;
|
|
6166
6374
|
const baseData = this.buildMarkerData(marker, options);
|
|
6167
6375
|
const commonProps = {
|
|
6168
|
-
visible:
|
|
6169
|
-
selectable: interactive &&
|
|
6170
|
-
evented: interactive &&
|
|
6376
|
+
visible: sessionVisible,
|
|
6377
|
+
selectable: interactive && sessionVisible,
|
|
6378
|
+
evented: interactive && sessionVisible,
|
|
6171
6379
|
hasControls: false,
|
|
6172
6380
|
hasBorders: false,
|
|
6173
6381
|
hoverCursor: interactive ? "move" : "default",
|
|
@@ -6232,7 +6440,7 @@ var FeatureTool = class {
|
|
|
6232
6440
|
markerOffsetY: -visualHeight / 2
|
|
6233
6441
|
},
|
|
6234
6442
|
props: {
|
|
6235
|
-
visible:
|
|
6443
|
+
visible: sessionVisible,
|
|
6236
6444
|
selectable: false,
|
|
6237
6445
|
evented: false,
|
|
6238
6446
|
width: visualWidth,
|
|
@@ -6402,9 +6610,14 @@ var FilmTool = class {
|
|
|
6402
6610
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
6403
6611
|
this.id,
|
|
6404
6612
|
() => ({
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6613
|
+
passes: [
|
|
6614
|
+
{
|
|
6615
|
+
id: FILM_LAYER_ID,
|
|
6616
|
+
stack: 1e3,
|
|
6617
|
+
order: 0,
|
|
6618
|
+
objects: this.specs
|
|
6619
|
+
}
|
|
6620
|
+
]
|
|
6408
6621
|
}),
|
|
6409
6622
|
{ priority: 500 }
|
|
6410
6623
|
);
|
|
@@ -6578,7 +6791,6 @@ var FilmTool = class {
|
|
|
6578
6791
|
this.specs = this.buildFilmSpecs(this.renderImageUrl, this.opacity);
|
|
6579
6792
|
await this.canvasService.flushRenderFromProducers();
|
|
6580
6793
|
if (seq !== this.renderSeq) return;
|
|
6581
|
-
this.canvasService.bringLayerToFront(FILM_LAYER_ID);
|
|
6582
6794
|
this.canvasService.requestRenderAll();
|
|
6583
6795
|
}
|
|
6584
6796
|
};
|
|
@@ -6729,10 +6941,22 @@ var RulerTool = class {
|
|
|
6729
6941
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
6730
6942
|
this.id,
|
|
6731
6943
|
() => ({
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6944
|
+
passes: [
|
|
6945
|
+
{
|
|
6946
|
+
id: RULER_LAYER_ID,
|
|
6947
|
+
stack: 950,
|
|
6948
|
+
order: 0,
|
|
6949
|
+
replace: true,
|
|
6950
|
+
visibility: {
|
|
6951
|
+
op: "not",
|
|
6952
|
+
expr: {
|
|
6953
|
+
op: "activeToolIn",
|
|
6954
|
+
ids: ["pooder.kit.white-ink"]
|
|
6955
|
+
}
|
|
6956
|
+
},
|
|
6957
|
+
objects: this.specs
|
|
6958
|
+
}
|
|
6959
|
+
]
|
|
6736
6960
|
}),
|
|
6737
6961
|
{ priority: 400 }
|
|
6738
6962
|
);
|
|
@@ -7224,7 +7448,6 @@ var RulerTool = class {
|
|
|
7224
7448
|
this.specs = specs;
|
|
7225
7449
|
await this.canvasService.flushRenderFromProducers();
|
|
7226
7450
|
if (seq !== this.renderSeq) return;
|
|
7227
|
-
this.canvasService.bringLayerToFront(RULER_LAYER_ID);
|
|
7228
7451
|
this.canvasService.requestRenderAll();
|
|
7229
7452
|
this.log("render:done", { seq });
|
|
7230
7453
|
}
|
|
@@ -7238,7 +7461,6 @@ var WHITE_INK_OBJECT_LAYER_ID = "white-ink.user";
|
|
|
7238
7461
|
var WHITE_INK_COVER_LAYER_ID = "white-ink.cover";
|
|
7239
7462
|
var WHITE_INK_OVERLAY_LAYER_ID = "white-ink.overlay";
|
|
7240
7463
|
var IMAGE_OBJECT_LAYER_ID3 = "image.user";
|
|
7241
|
-
var IMAGE_OVERLAY_LAYER_ID2 = "image-overlay";
|
|
7242
7464
|
var WHITE_INK_DEBUG_KEY = "whiteInk.debug";
|
|
7243
7465
|
var WHITE_INK_PREVIEW_IMAGE_VISIBLE_KEY = "whiteInk.previewImageVisible";
|
|
7244
7466
|
var WHITE_INK_DEFAULT_OPACITY = 0.85;
|
|
@@ -7316,11 +7538,26 @@ var WhiteInkTool = class {
|
|
|
7316
7538
|
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
7317
7539
|
this.id,
|
|
7318
7540
|
() => ({
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7541
|
+
passes: [
|
|
7542
|
+
{
|
|
7543
|
+
id: WHITE_INK_COVER_LAYER_ID,
|
|
7544
|
+
stack: 220,
|
|
7545
|
+
order: 0,
|
|
7546
|
+
objects: this.coverSpecs
|
|
7547
|
+
},
|
|
7548
|
+
{
|
|
7549
|
+
id: WHITE_INK_OBJECT_LAYER_ID,
|
|
7550
|
+
stack: 221,
|
|
7551
|
+
order: 0,
|
|
7552
|
+
objects: this.whiteSpecs
|
|
7553
|
+
},
|
|
7554
|
+
{
|
|
7555
|
+
id: WHITE_INK_OVERLAY_LAYER_ID,
|
|
7556
|
+
stack: 790,
|
|
7557
|
+
order: 0,
|
|
7558
|
+
objects: this.overlaySpecs
|
|
7559
|
+
}
|
|
7560
|
+
]
|
|
7324
7561
|
}),
|
|
7325
7562
|
{ priority: 260 }
|
|
7326
7563
|
);
|
|
@@ -7399,7 +7636,6 @@ var WhiteInkTool = class {
|
|
|
7399
7636
|
(_a = this.dirtyTrackerDisposable) == null ? void 0 : _a.dispose();
|
|
7400
7637
|
this.dirtyTrackerDisposable = void 0;
|
|
7401
7638
|
this.clearRenderedWhiteInks();
|
|
7402
|
-
this.applyImageVisibilityForWhiteInk(false);
|
|
7403
7639
|
(_b = this.renderProducerDisposable) == null ? void 0 : _b.dispose();
|
|
7404
7640
|
this.renderProducerDisposable = void 0;
|
|
7405
7641
|
if (this.canvasService) {
|
|
@@ -8239,25 +8475,9 @@ var WhiteInkTool = class {
|
|
|
8239
8475
|
selectable: false,
|
|
8240
8476
|
evented: false,
|
|
8241
8477
|
excludeFromExport: true
|
|
8242
|
-
}
|
|
8243
|
-
}
|
|
8244
|
-
];
|
|
8245
|
-
}
|
|
8246
|
-
applyImageVisibilityForWhiteInk(previewActive) {
|
|
8247
|
-
if (!this.canvasService) return;
|
|
8248
|
-
const visible = !previewActive;
|
|
8249
|
-
let changed = false;
|
|
8250
|
-
this.canvasService.canvas.getObjects().forEach((obj) => {
|
|
8251
|
-
var _a, _b;
|
|
8252
|
-
if (((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.layerId) !== IMAGE_OBJECT_LAYER_ID3) return;
|
|
8253
|
-
if (obj.visible === visible) return;
|
|
8254
|
-
obj.set({ visible });
|
|
8255
|
-
(_b = obj.setCoords) == null ? void 0 : _b.call(obj);
|
|
8256
|
-
changed = true;
|
|
8257
|
-
});
|
|
8258
|
-
if (changed) {
|
|
8259
|
-
this.canvasService.requestRenderAll();
|
|
8260
|
-
}
|
|
8478
|
+
}
|
|
8479
|
+
}
|
|
8480
|
+
];
|
|
8261
8481
|
}
|
|
8262
8482
|
resolveRenderItems() {
|
|
8263
8483
|
if (this.isToolActive) {
|
|
@@ -8283,52 +8503,6 @@ var WhiteInkTool = class {
|
|
|
8283
8503
|
whiteScaleAdjustY: scaleAdjust.y
|
|
8284
8504
|
};
|
|
8285
8505
|
}
|
|
8286
|
-
resolveDefaultInsertIndex(objects) {
|
|
8287
|
-
if (!this.canvasService) return 0;
|
|
8288
|
-
const backgroundLayer = this.canvasService.getLayer("background");
|
|
8289
|
-
if (!backgroundLayer) return 0;
|
|
8290
|
-
const bgIndex = objects.indexOf(backgroundLayer);
|
|
8291
|
-
if (bgIndex < 0) return 0;
|
|
8292
|
-
return bgIndex + 1;
|
|
8293
|
-
}
|
|
8294
|
-
syncZOrder() {
|
|
8295
|
-
if (!this.canvasService) return;
|
|
8296
|
-
const canvas = this.canvasService.canvas;
|
|
8297
|
-
const whiteObjects = this.canvasService.getRootLayerObjects(
|
|
8298
|
-
WHITE_INK_OBJECT_LAYER_ID
|
|
8299
|
-
);
|
|
8300
|
-
const coverObjects = this.canvasService.getRootLayerObjects(
|
|
8301
|
-
WHITE_INK_COVER_LAYER_ID
|
|
8302
|
-
);
|
|
8303
|
-
const frameObjects = this.canvasService.getRootLayerObjects(
|
|
8304
|
-
WHITE_INK_OVERLAY_LAYER_ID
|
|
8305
|
-
);
|
|
8306
|
-
const currentObjects = canvas.getObjects();
|
|
8307
|
-
const imageIndexes = currentObjects.map(
|
|
8308
|
-
(obj, index) => {
|
|
8309
|
-
var _a;
|
|
8310
|
-
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.layerId) === IMAGE_OBJECT_LAYER_ID3 ? index : -1;
|
|
8311
|
-
}
|
|
8312
|
-
).filter((index) => index >= 0);
|
|
8313
|
-
let whiteInsertIndex = imageIndexes.length ? Math.min(...imageIndexes) : this.resolveDefaultInsertIndex(currentObjects);
|
|
8314
|
-
let coverInsertIndex = whiteInsertIndex;
|
|
8315
|
-
coverObjects.forEach((obj) => {
|
|
8316
|
-
canvas.moveObjectTo(obj, coverInsertIndex);
|
|
8317
|
-
coverInsertIndex += 1;
|
|
8318
|
-
});
|
|
8319
|
-
whiteInsertIndex = coverInsertIndex;
|
|
8320
|
-
whiteObjects.forEach((obj) => {
|
|
8321
|
-
canvas.moveObjectTo(obj, whiteInsertIndex);
|
|
8322
|
-
whiteInsertIndex += 1;
|
|
8323
|
-
});
|
|
8324
|
-
frameObjects.forEach((obj) => canvas.bringObjectToFront(obj));
|
|
8325
|
-
canvas.getObjects().filter((obj) => {
|
|
8326
|
-
var _a;
|
|
8327
|
-
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.layerId) === IMAGE_OVERLAY_LAYER_ID2;
|
|
8328
|
-
}).forEach((obj) => canvas.bringObjectToFront(obj));
|
|
8329
|
-
this.canvasService.bringLayerToFront("dieline-overlay");
|
|
8330
|
-
this.canvasService.bringLayerToFront("ruler-overlay");
|
|
8331
|
-
}
|
|
8332
8506
|
clearRenderedWhiteInks() {
|
|
8333
8507
|
if (!this.canvasService) return;
|
|
8334
8508
|
this.whiteSpecs = [];
|
|
@@ -8360,7 +8534,6 @@ var WhiteInkTool = class {
|
|
|
8360
8534
|
this.syncToolActiveFromWorkbench();
|
|
8361
8535
|
const seq = ++this.renderSeq;
|
|
8362
8536
|
const previewActive = this.isPreviewActive();
|
|
8363
|
-
this.applyImageVisibilityForWhiteInk(previewActive);
|
|
8364
8537
|
const frame = this.getFrameRect();
|
|
8365
8538
|
const frameSpecs = this.buildFrameSpecs(frame);
|
|
8366
8539
|
let whiteSpecs = [];
|
|
@@ -8408,7 +8581,6 @@ var WhiteInkTool = class {
|
|
|
8408
8581
|
this.overlaySpecs = frameSpecs;
|
|
8409
8582
|
await this.canvasService.flushRenderFromProducers();
|
|
8410
8583
|
if (seq !== this.renderSeq) return;
|
|
8411
|
-
this.syncZOrder();
|
|
8412
8584
|
this.canvasService.requestRenderAll();
|
|
8413
8585
|
}
|
|
8414
8586
|
getMaskCacheKey(sourceUrl, tint) {
|
|
@@ -8593,62 +8765,12 @@ var SceneLayoutService = class {
|
|
|
8593
8765
|
}
|
|
8594
8766
|
};
|
|
8595
8767
|
|
|
8596
|
-
// src/extensions/sceneVisibility.ts
|
|
8597
|
-
import { WORKBENCH_SERVICE } from "@pooder/core";
|
|
8598
|
-
var CANVAS_SERVICE_ID2 = "CanvasService";
|
|
8599
|
-
var HIDDEN_DIELINE_TOOLS = /* @__PURE__ */ new Set(["pooder.kit.image", "pooder.kit.white-ink"]);
|
|
8600
|
-
var HIDDEN_RULER_TOOLS = /* @__PURE__ */ new Set(["pooder.kit.white-ink"]);
|
|
8601
|
-
var SceneVisibilityService = class {
|
|
8602
|
-
constructor() {
|
|
8603
|
-
this.activeToolId = null;
|
|
8604
|
-
this.onToolActivated = (e) => {
|
|
8605
|
-
this.activeToolId = e.id;
|
|
8606
|
-
this.apply();
|
|
8607
|
-
};
|
|
8608
|
-
this.onObjectAdded = () => {
|
|
8609
|
-
this.apply();
|
|
8610
|
-
};
|
|
8611
|
-
}
|
|
8612
|
-
init(context) {
|
|
8613
|
-
var _a, _b;
|
|
8614
|
-
if (this.context) {
|
|
8615
|
-
this.dispose(this.context);
|
|
8616
|
-
}
|
|
8617
|
-
const canvasService = context.get(CANVAS_SERVICE_ID2);
|
|
8618
|
-
if (!canvasService) {
|
|
8619
|
-
throw new Error("[SceneVisibilityService] CanvasService is required.");
|
|
8620
|
-
}
|
|
8621
|
-
this.context = context;
|
|
8622
|
-
this.canvasService = canvasService;
|
|
8623
|
-
this.activeToolId = (_b = (_a = context.get(WORKBENCH_SERVICE)) == null ? void 0 : _a.activeToolId) != null ? _b : null;
|
|
8624
|
-
context.eventBus.on("tool:activated", this.onToolActivated);
|
|
8625
|
-
context.eventBus.on("object:added", this.onObjectAdded);
|
|
8626
|
-
this.apply();
|
|
8627
|
-
}
|
|
8628
|
-
dispose(context) {
|
|
8629
|
-
var _a;
|
|
8630
|
-
const activeContext = (_a = this.context) != null ? _a : context;
|
|
8631
|
-
activeContext.eventBus.off("tool:activated", this.onToolActivated);
|
|
8632
|
-
activeContext.eventBus.off("object:added", this.onObjectAdded);
|
|
8633
|
-
this.context = void 0;
|
|
8634
|
-
this.activeToolId = null;
|
|
8635
|
-
this.canvasService = void 0;
|
|
8636
|
-
}
|
|
8637
|
-
apply() {
|
|
8638
|
-
if (!this.canvasService) return;
|
|
8639
|
-
const dielineLayer = this.canvasService.getLayer("dieline-overlay");
|
|
8640
|
-
if (dielineLayer) {
|
|
8641
|
-
const visible = !HIDDEN_DIELINE_TOOLS.has(this.activeToolId || "");
|
|
8642
|
-
this.canvasService.setLayerVisibility("dieline-overlay", visible);
|
|
8643
|
-
}
|
|
8644
|
-
const rulerVisible = !HIDDEN_RULER_TOOLS.has(this.activeToolId || "");
|
|
8645
|
-
this.canvasService.setLayerVisibility("ruler-overlay", rulerVisible);
|
|
8646
|
-
this.canvasService.requestRenderAll();
|
|
8647
|
-
}
|
|
8648
|
-
};
|
|
8649
|
-
|
|
8650
8768
|
// src/services/CanvasService.ts
|
|
8651
|
-
import { Canvas,
|
|
8769
|
+
import { Canvas, Rect, Path as Path2, Image as Image2, Text } from "fabric";
|
|
8770
|
+
import {
|
|
8771
|
+
TOOL_SESSION_SERVICE,
|
|
8772
|
+
WORKBENCH_SERVICE
|
|
8773
|
+
} from "@pooder/core";
|
|
8652
8774
|
|
|
8653
8775
|
// src/services/ViewportSystem.ts
|
|
8654
8776
|
var ViewportSystem = class {
|
|
@@ -8723,6 +8845,53 @@ var ViewportSystem = class {
|
|
|
8723
8845
|
}
|
|
8724
8846
|
};
|
|
8725
8847
|
|
|
8848
|
+
// src/services/visibility.ts
|
|
8849
|
+
function compareLayerObjectCount(actual, cmp, expected) {
|
|
8850
|
+
if (cmp === ">") return actual > expected;
|
|
8851
|
+
if (cmp === ">=") return actual >= expected;
|
|
8852
|
+
if (cmp === "<") return actual < expected;
|
|
8853
|
+
if (cmp === "<=") return actual <= expected;
|
|
8854
|
+
return actual === expected;
|
|
8855
|
+
}
|
|
8856
|
+
function layerState(context, layerId) {
|
|
8857
|
+
return context.layers.get(layerId) || { exists: false, objectCount: 0 };
|
|
8858
|
+
}
|
|
8859
|
+
function evaluateVisibilityExpr(expr, context) {
|
|
8860
|
+
var _a;
|
|
8861
|
+
if (!expr) return true;
|
|
8862
|
+
if (expr.op === "const") {
|
|
8863
|
+
return Boolean(expr.value);
|
|
8864
|
+
}
|
|
8865
|
+
if (expr.op === "activeToolIn") {
|
|
8866
|
+
const activeToolId = (_a = context.activeToolId) != null ? _a : null;
|
|
8867
|
+
return !!activeToolId && expr.ids.includes(activeToolId);
|
|
8868
|
+
}
|
|
8869
|
+
if (expr.op === "sessionActive") {
|
|
8870
|
+
const toolId = String(expr.toolId || "").trim();
|
|
8871
|
+
if (!toolId) return false;
|
|
8872
|
+
return context.isSessionActive ? context.isSessionActive(toolId) : false;
|
|
8873
|
+
}
|
|
8874
|
+
if (expr.op === "layerExists") {
|
|
8875
|
+
return layerState(context, expr.layerId).exists === true;
|
|
8876
|
+
}
|
|
8877
|
+
if (expr.op === "layerObjectCount") {
|
|
8878
|
+
const expected = Number(expr.value);
|
|
8879
|
+
if (!Number.isFinite(expected)) return false;
|
|
8880
|
+
const count = layerState(context, expr.layerId).objectCount;
|
|
8881
|
+
return compareLayerObjectCount(count, expr.cmp, expected);
|
|
8882
|
+
}
|
|
8883
|
+
if (expr.op === "not") {
|
|
8884
|
+
return !evaluateVisibilityExpr(expr.expr, context);
|
|
8885
|
+
}
|
|
8886
|
+
if (expr.op === "all") {
|
|
8887
|
+
return expr.exprs.every((item) => evaluateVisibilityExpr(item, context));
|
|
8888
|
+
}
|
|
8889
|
+
if (expr.op === "any") {
|
|
8890
|
+
return expr.exprs.some((item) => evaluateVisibilityExpr(item, context));
|
|
8891
|
+
}
|
|
8892
|
+
return true;
|
|
8893
|
+
}
|
|
8894
|
+
|
|
8726
8895
|
// src/services/CanvasService.ts
|
|
8727
8896
|
var CanvasService = class {
|
|
8728
8897
|
constructor(el, options) {
|
|
@@ -8731,8 +8900,45 @@ var CanvasService = class {
|
|
|
8731
8900
|
this.producerFlushRequested = false;
|
|
8732
8901
|
this.producerLoopPending = false;
|
|
8733
8902
|
this.producerLoopPromise = null;
|
|
8734
|
-
this.
|
|
8735
|
-
this.
|
|
8903
|
+
this.producerApplyInProgress = false;
|
|
8904
|
+
this.visibilityRefreshScheduled = false;
|
|
8905
|
+
this.managedProducerPassIds = /* @__PURE__ */ new Set();
|
|
8906
|
+
this.managedPassMetas = /* @__PURE__ */ new Map();
|
|
8907
|
+
this.canvasForwardersBound = false;
|
|
8908
|
+
this.forwardSelectionCreated = (e) => {
|
|
8909
|
+
var _a;
|
|
8910
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("selection:created", e);
|
|
8911
|
+
};
|
|
8912
|
+
this.forwardSelectionUpdated = (e) => {
|
|
8913
|
+
var _a;
|
|
8914
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("selection:updated", e);
|
|
8915
|
+
};
|
|
8916
|
+
this.forwardSelectionCleared = (e) => {
|
|
8917
|
+
var _a;
|
|
8918
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("selection:cleared", e);
|
|
8919
|
+
};
|
|
8920
|
+
this.forwardObjectModified = (e) => {
|
|
8921
|
+
var _a;
|
|
8922
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("object:modified", e);
|
|
8923
|
+
};
|
|
8924
|
+
this.forwardObjectAdded = (e) => {
|
|
8925
|
+
var _a;
|
|
8926
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("object:added", e);
|
|
8927
|
+
};
|
|
8928
|
+
this.forwardObjectRemoved = (e) => {
|
|
8929
|
+
var _a;
|
|
8930
|
+
(_a = this.eventBus) == null ? void 0 : _a.emit("object:removed", e);
|
|
8931
|
+
};
|
|
8932
|
+
this.onToolActivated = () => {
|
|
8933
|
+
this.applyManagedPassVisibility();
|
|
8934
|
+
};
|
|
8935
|
+
this.onToolSessionChanged = () => {
|
|
8936
|
+
this.applyManagedPassVisibility();
|
|
8937
|
+
};
|
|
8938
|
+
this.onCanvasObjectChanged = () => {
|
|
8939
|
+
if (this.producerApplyInProgress) return;
|
|
8940
|
+
this.scheduleManagedPassVisibilityRefresh();
|
|
8941
|
+
};
|
|
8736
8942
|
if (el instanceof Canvas) {
|
|
8737
8943
|
this.canvas = el;
|
|
8738
8944
|
} else {
|
|
@@ -8749,25 +8955,52 @@ var CanvasService = class {
|
|
|
8749
8955
|
this.setEventBus(options.eventBus);
|
|
8750
8956
|
}
|
|
8751
8957
|
}
|
|
8958
|
+
init(context) {
|
|
8959
|
+
if (this.context) {
|
|
8960
|
+
this.detachContextEvents(this.context.eventBus);
|
|
8961
|
+
}
|
|
8962
|
+
this.context = context;
|
|
8963
|
+
this.workbenchService = context.get(WORKBENCH_SERVICE);
|
|
8964
|
+
this.toolSessionService = context.get(TOOL_SESSION_SERVICE);
|
|
8965
|
+
this.setEventBus(context.eventBus);
|
|
8966
|
+
this.attachContextEvents(context.eventBus);
|
|
8967
|
+
}
|
|
8968
|
+
attachContextEvents(eventBus) {
|
|
8969
|
+
eventBus.on("tool:activated", this.onToolActivated);
|
|
8970
|
+
eventBus.on("tool:session:change", this.onToolSessionChanged);
|
|
8971
|
+
eventBus.on("object:added", this.onCanvasObjectChanged);
|
|
8972
|
+
eventBus.on("object:removed", this.onCanvasObjectChanged);
|
|
8973
|
+
}
|
|
8974
|
+
detachContextEvents(eventBus) {
|
|
8975
|
+
eventBus.off("tool:activated", this.onToolActivated);
|
|
8976
|
+
eventBus.off("tool:session:change", this.onToolSessionChanged);
|
|
8977
|
+
eventBus.off("object:added", this.onCanvasObjectChanged);
|
|
8978
|
+
eventBus.off("object:removed", this.onCanvasObjectChanged);
|
|
8979
|
+
}
|
|
8752
8980
|
setEventBus(eventBus) {
|
|
8753
8981
|
this.eventBus = eventBus;
|
|
8754
8982
|
this.setupEvents();
|
|
8755
8983
|
}
|
|
8756
8984
|
setupEvents() {
|
|
8757
|
-
if (
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
this.canvas.on("selection:
|
|
8761
|
-
this.canvas.on("
|
|
8762
|
-
this.canvas.on("
|
|
8763
|
-
this.canvas.on("object:
|
|
8764
|
-
this.
|
|
8765
|
-
this.canvas.on("object:removed", forward("object:removed"));
|
|
8985
|
+
if (this.canvasForwardersBound) return;
|
|
8986
|
+
this.canvas.on("selection:created", this.forwardSelectionCreated);
|
|
8987
|
+
this.canvas.on("selection:updated", this.forwardSelectionUpdated);
|
|
8988
|
+
this.canvas.on("selection:cleared", this.forwardSelectionCleared);
|
|
8989
|
+
this.canvas.on("object:modified", this.forwardObjectModified);
|
|
8990
|
+
this.canvas.on("object:added", this.forwardObjectAdded);
|
|
8991
|
+
this.canvas.on("object:removed", this.forwardObjectRemoved);
|
|
8992
|
+
this.canvasForwardersBound = true;
|
|
8766
8993
|
}
|
|
8767
8994
|
dispose() {
|
|
8995
|
+
if (this.context) {
|
|
8996
|
+
this.detachContextEvents(this.context.eventBus);
|
|
8997
|
+
}
|
|
8768
8998
|
this.renderProducers.clear();
|
|
8769
|
-
this.
|
|
8770
|
-
this.
|
|
8999
|
+
this.managedProducerPassIds.clear();
|
|
9000
|
+
this.managedPassMetas.clear();
|
|
9001
|
+
this.context = void 0;
|
|
9002
|
+
this.workbenchService = void 0;
|
|
9003
|
+
this.toolSessionService = void 0;
|
|
8771
9004
|
this.producerFlushRequested = false;
|
|
8772
9005
|
this.canvas.dispose();
|
|
8773
9006
|
}
|
|
@@ -8845,118 +9078,274 @@ var CanvasService = class {
|
|
|
8845
9078
|
return a.toolId.localeCompare(b.toolId);
|
|
8846
9079
|
});
|
|
8847
9080
|
}
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
9081
|
+
normalizePassSpecValue(spec) {
|
|
9082
|
+
const id = String(spec.id || "").trim();
|
|
9083
|
+
if (!id) return null;
|
|
9084
|
+
return {
|
|
9085
|
+
id,
|
|
9086
|
+
stack: Number.isFinite(spec.stack) ? Number(spec.stack) : 0,
|
|
9087
|
+
order: Number.isFinite(spec.order) ? Number(spec.order) : 0,
|
|
9088
|
+
replace: spec.replace !== false,
|
|
9089
|
+
visibility: spec.visibility,
|
|
9090
|
+
effects: Array.isArray(spec.effects) ? [...spec.effects] : [],
|
|
9091
|
+
objects: Array.isArray(spec.objects) ? [...spec.objects] : []
|
|
9092
|
+
};
|
|
9093
|
+
}
|
|
9094
|
+
normalizeClipPathEffectSpec(effect, passId, index) {
|
|
9095
|
+
if (!effect || effect.type !== "clipPath") return null;
|
|
9096
|
+
const source = effect.source;
|
|
9097
|
+
if (!source || typeof source !== "object") return null;
|
|
9098
|
+
const sourceId = String(source.id || "").trim();
|
|
9099
|
+
if (!sourceId) return null;
|
|
9100
|
+
const targetPassIds = Array.isArray(effect.targetPassIds) ? effect.targetPassIds.map((item) => String(item || "").trim()).filter((item) => item.length > 0) : [];
|
|
9101
|
+
if (!targetPassIds.length) return null;
|
|
9102
|
+
const customId = String(effect.id || "").trim();
|
|
9103
|
+
const key = customId || `${passId}.effect.clipPath.${index}`;
|
|
9104
|
+
return {
|
|
9105
|
+
type: "clipPath",
|
|
9106
|
+
key,
|
|
9107
|
+
source: {
|
|
9108
|
+
...source,
|
|
9109
|
+
id: sourceId
|
|
9110
|
+
},
|
|
9111
|
+
targetPassIds
|
|
9112
|
+
};
|
|
9113
|
+
}
|
|
9114
|
+
mergePassSpec(map, rawSpec, producerId) {
|
|
9115
|
+
const normalized = this.normalizePassSpecValue(rawSpec);
|
|
9116
|
+
if (!normalized) return;
|
|
9117
|
+
const existing = map.get(normalized.id);
|
|
9118
|
+
if (!existing) {
|
|
9119
|
+
map.set(normalized.id, normalized);
|
|
9120
|
+
return;
|
|
9121
|
+
}
|
|
9122
|
+
existing.objects.push(...normalized.objects);
|
|
9123
|
+
existing.replace = existing.replace || normalized.replace;
|
|
9124
|
+
existing.stack = normalized.stack;
|
|
9125
|
+
existing.order = normalized.order;
|
|
9126
|
+
if (normalized.visibility !== void 0) {
|
|
9127
|
+
existing.visibility = normalized.visibility;
|
|
9128
|
+
}
|
|
9129
|
+
existing.effects.push(...normalized.effects);
|
|
9130
|
+
if (normalized.objects.length === 0 && normalized.effects.length === 0) {
|
|
9131
|
+
console.debug(
|
|
9132
|
+
`[CanvasService] pass "${normalized.id}" from producer "${producerId}" updated ordering/visibility only.`
|
|
9133
|
+
);
|
|
9134
|
+
}
|
|
9135
|
+
}
|
|
9136
|
+
comparePassMeta(a, b) {
|
|
9137
|
+
if (a.stack !== b.stack) return a.stack - b.stack;
|
|
9138
|
+
if (a.order !== b.order) return a.order - b.order;
|
|
9139
|
+
return a.id.localeCompare(b.id);
|
|
9140
|
+
}
|
|
9141
|
+
getPassObjectOrder(obj) {
|
|
9142
|
+
var _a;
|
|
9143
|
+
const raw = Number((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passOrder);
|
|
9144
|
+
return Number.isFinite(raw) ? raw : Number.MAX_SAFE_INTEGER;
|
|
9145
|
+
}
|
|
9146
|
+
getPassCanvasObjects(passId) {
|
|
9147
|
+
const all = this.canvas.getObjects();
|
|
9148
|
+
return all.filter((obj) => {
|
|
9149
|
+
var _a;
|
|
9150
|
+
return ((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passId) === passId;
|
|
9151
|
+
}).sort((a, b) => {
|
|
9152
|
+
const orderA = this.getPassObjectOrder(a);
|
|
9153
|
+
const orderB = this.getPassObjectOrder(b);
|
|
9154
|
+
if (orderA !== orderB) return orderA - orderB;
|
|
9155
|
+
return all.indexOf(a) - all.indexOf(b);
|
|
9156
|
+
});
|
|
9157
|
+
}
|
|
9158
|
+
getPassObjects(passId) {
|
|
9159
|
+
return this.getPassCanvasObjects(passId);
|
|
9160
|
+
}
|
|
9161
|
+
getRootLayerObjects(layerId) {
|
|
9162
|
+
return this.getPassCanvasObjects(layerId);
|
|
9163
|
+
}
|
|
9164
|
+
isManagedPassObject(obj) {
|
|
9165
|
+
var _a;
|
|
9166
|
+
const passId = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passId;
|
|
9167
|
+
return typeof passId === "string" && this.managedPassMetas.has(passId);
|
|
9168
|
+
}
|
|
9169
|
+
syncManagedPassStacking(passes) {
|
|
9170
|
+
const orderedPasses = [...passes].sort((a, b) => this.comparePassMeta(a, b));
|
|
9171
|
+
if (!orderedPasses.length) return;
|
|
9172
|
+
const canvasObjects = this.canvas.getObjects();
|
|
9173
|
+
const managedObjects = canvasObjects.filter(
|
|
9174
|
+
(obj) => this.isManagedPassObject(obj)
|
|
9175
|
+
);
|
|
9176
|
+
if (!managedObjects.length) return;
|
|
9177
|
+
const firstManagedIndex = managedObjects.map((obj) => canvasObjects.indexOf(obj)).filter((index) => index >= 0).reduce((min, value) => Math.min(min, value), Number.MAX_SAFE_INTEGER);
|
|
9178
|
+
let targetIndex = Number.isFinite(firstManagedIndex) ? firstManagedIndex : 0;
|
|
9179
|
+
orderedPasses.forEach((meta) => {
|
|
9180
|
+
const objects = this.getPassCanvasObjects(meta.id);
|
|
9181
|
+
objects.forEach((obj) => {
|
|
9182
|
+
this.moveObjectInCanvas(obj, targetIndex);
|
|
9183
|
+
targetIndex += 1;
|
|
9184
|
+
});
|
|
9185
|
+
});
|
|
9186
|
+
}
|
|
9187
|
+
getPassRuntimeState() {
|
|
9188
|
+
const state = /* @__PURE__ */ new Map();
|
|
9189
|
+
const ensure = (passId) => {
|
|
9190
|
+
const id = String(passId || "").trim();
|
|
9191
|
+
if (!id) return { exists: false, objectCount: 0 };
|
|
9192
|
+
let item = state.get(id);
|
|
9193
|
+
if (!item) {
|
|
9194
|
+
item = { exists: false, objectCount: 0 };
|
|
9195
|
+
state.set(id, item);
|
|
9196
|
+
}
|
|
9197
|
+
return item;
|
|
9198
|
+
};
|
|
9199
|
+
this.canvas.getObjects().forEach((obj) => {
|
|
9200
|
+
var _a;
|
|
9201
|
+
const passId = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.passId;
|
|
9202
|
+
if (typeof passId === "string") {
|
|
9203
|
+
const item = ensure(passId);
|
|
9204
|
+
item.exists = true;
|
|
9205
|
+
item.objectCount += 1;
|
|
9206
|
+
}
|
|
9207
|
+
});
|
|
9208
|
+
this.managedPassMetas.forEach((meta) => {
|
|
9209
|
+
const item = ensure(meta.id);
|
|
9210
|
+
item.exists = true;
|
|
9211
|
+
});
|
|
9212
|
+
return state;
|
|
9213
|
+
}
|
|
9214
|
+
applyManagedPassVisibility(options = {}) {
|
|
9215
|
+
var _a, _b;
|
|
9216
|
+
if (!this.managedPassMetas.size) return false;
|
|
9217
|
+
const layers = this.getPassRuntimeState();
|
|
9218
|
+
const activeToolId = (_b = (_a = this.workbenchService) == null ? void 0 : _a.activeToolId) != null ? _b : null;
|
|
9219
|
+
const isSessionActive = (toolId) => {
|
|
9220
|
+
if (!this.toolSessionService) return false;
|
|
9221
|
+
return this.toolSessionService.getState(toolId).status === "active";
|
|
9222
|
+
};
|
|
9223
|
+
let changed = false;
|
|
9224
|
+
this.managedPassMetas.forEach((meta) => {
|
|
9225
|
+
const visible = evaluateVisibilityExpr(meta.visibility, {
|
|
9226
|
+
activeToolId,
|
|
9227
|
+
isSessionActive,
|
|
9228
|
+
layers
|
|
9229
|
+
});
|
|
9230
|
+
changed = this.setPassVisibility(meta.id, visible) || changed;
|
|
9231
|
+
});
|
|
9232
|
+
if (changed && options.render !== false) {
|
|
9233
|
+
this.requestRenderAll();
|
|
9234
|
+
}
|
|
9235
|
+
return changed;
|
|
9236
|
+
}
|
|
9237
|
+
scheduleManagedPassVisibilityRefresh() {
|
|
9238
|
+
if (this.visibilityRefreshScheduled) return;
|
|
9239
|
+
this.visibilityRefreshScheduled = true;
|
|
9240
|
+
void Promise.resolve().then(() => {
|
|
9241
|
+
this.visibilityRefreshScheduled = false;
|
|
9242
|
+
this.applyManagedPassVisibility();
|
|
8855
9243
|
});
|
|
8856
9244
|
}
|
|
8857
9245
|
async collectAndApplyProducerSpecs() {
|
|
8858
|
-
const
|
|
8859
|
-
const rootLayerSpecs = /* @__PURE__ */ new Map();
|
|
8860
|
-
const replaceLayerIds = /* @__PURE__ */ new Set();
|
|
8861
|
-
const replaceRootLayerIds = /* @__PURE__ */ new Set();
|
|
9246
|
+
const passes = /* @__PURE__ */ new Map();
|
|
8862
9247
|
const entries = this.sortedRenderProducerEntries();
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8866
|
-
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
if (layerId) replaceRootLayerIds.add(layerId);
|
|
8877
|
-
});
|
|
9248
|
+
this.producerApplyInProgress = true;
|
|
9249
|
+
try {
|
|
9250
|
+
for (const entry of entries) {
|
|
9251
|
+
try {
|
|
9252
|
+
const result = await entry.producer();
|
|
9253
|
+
if (!result) continue;
|
|
9254
|
+
const specs = Array.isArray(result.passes) ? result.passes : [];
|
|
9255
|
+
specs.forEach((spec) => this.mergePassSpec(passes, spec, entry.toolId));
|
|
9256
|
+
} catch (error) {
|
|
9257
|
+
console.error(
|
|
9258
|
+
`[CanvasService] render producer "${entry.toolId}" failed.`,
|
|
9259
|
+
error
|
|
9260
|
+
);
|
|
8878
9261
|
}
|
|
8879
|
-
} catch (error) {
|
|
8880
|
-
console.error(
|
|
8881
|
-
`[CanvasService] render producer "${entry.toolId}" failed.`,
|
|
8882
|
-
error
|
|
8883
|
-
);
|
|
8884
9262
|
}
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
9263
|
+
const nextPassIds = /* @__PURE__ */ new Set();
|
|
9264
|
+
const nextManagedPassMetas = /* @__PURE__ */ new Map();
|
|
9265
|
+
const nextEffects = [];
|
|
9266
|
+
for (const pass of passes.values()) {
|
|
9267
|
+
nextPassIds.add(pass.id);
|
|
9268
|
+
nextManagedPassMetas.set(pass.id, {
|
|
9269
|
+
id: pass.id,
|
|
9270
|
+
stack: pass.stack,
|
|
9271
|
+
order: pass.order,
|
|
9272
|
+
visibility: pass.visibility
|
|
9273
|
+
});
|
|
9274
|
+
await this.applyObjectSpecsToPass(pass.id, pass.objects, {
|
|
9275
|
+
render: false,
|
|
9276
|
+
replace: pass.replace
|
|
9277
|
+
});
|
|
9278
|
+
pass.effects.forEach((effect, index) => {
|
|
9279
|
+
const normalized = this.normalizeClipPathEffectSpec(
|
|
9280
|
+
effect,
|
|
9281
|
+
pass.id,
|
|
9282
|
+
index
|
|
9283
|
+
);
|
|
9284
|
+
if (!normalized) return;
|
|
9285
|
+
nextEffects.push(normalized);
|
|
9286
|
+
});
|
|
8894
9287
|
}
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
await this.applyObjectSpecsToContainer(layer, [], { render: false });
|
|
8902
|
-
}
|
|
8903
|
-
for (const [layerId, specs] of rootLayerSpecs.entries()) {
|
|
8904
|
-
if (replaceRootLayerIds.has(layerId)) {
|
|
8905
|
-
const existing = this.getRootLayerObjects(layerId);
|
|
8906
|
-
existing.forEach((obj) => this.canvas.remove(obj));
|
|
9288
|
+
for (const passId of this.managedProducerPassIds) {
|
|
9289
|
+
if (nextPassIds.has(passId)) continue;
|
|
9290
|
+
await this.applyObjectSpecsToPass(passId, [], {
|
|
9291
|
+
render: false,
|
|
9292
|
+
replace: true
|
|
9293
|
+
});
|
|
8907
9294
|
}
|
|
8908
|
-
|
|
8909
|
-
|
|
8910
|
-
|
|
8911
|
-
|
|
8912
|
-
|
|
9295
|
+
this.managedProducerPassIds = nextPassIds;
|
|
9296
|
+
this.managedPassMetas = nextManagedPassMetas;
|
|
9297
|
+
this.syncManagedPassStacking(Array.from(nextManagedPassMetas.values()));
|
|
9298
|
+
await this.applyManagedPassEffects(nextEffects);
|
|
9299
|
+
this.applyManagedPassVisibility({ render: false });
|
|
9300
|
+
} finally {
|
|
9301
|
+
this.producerApplyInProgress = false;
|
|
8913
9302
|
}
|
|
8914
|
-
this.managedProducerLayerIds = nextLayerIds;
|
|
8915
|
-
this.managedProducerRootLayerIds = nextRootLayerIds;
|
|
8916
9303
|
this.requestRenderAll();
|
|
8917
9304
|
}
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
});
|
|
8927
|
-
}
|
|
8928
|
-
/**
|
|
8929
|
-
* Create a layer (Group) with the given ID if it doesn't exist.
|
|
8930
|
-
*/
|
|
8931
|
-
createLayer(id, options = {}) {
|
|
8932
|
-
let layer = this.getLayer(id);
|
|
8933
|
-
if (!layer) {
|
|
8934
|
-
const defaultOptions = {
|
|
8935
|
-
selectable: false,
|
|
8936
|
-
evented: false,
|
|
8937
|
-
...options,
|
|
8938
|
-
data: { ...options.data, id }
|
|
8939
|
-
};
|
|
8940
|
-
layer = new Group([], defaultOptions);
|
|
8941
|
-
this.canvas.add(layer);
|
|
8942
|
-
}
|
|
8943
|
-
return layer;
|
|
8944
|
-
}
|
|
8945
|
-
/**
|
|
8946
|
-
* Find an object by ID, optionally within a specific layer.
|
|
8947
|
-
*/
|
|
8948
|
-
getObject(id, layerId) {
|
|
8949
|
-
if (layerId) {
|
|
8950
|
-
const layer = this.getLayer(layerId);
|
|
8951
|
-
if (!layer) return void 0;
|
|
8952
|
-
return layer.getObjects().find((obj) => {
|
|
8953
|
-
var _a;
|
|
8954
|
-
return ((_a = obj.data) == null ? void 0 : _a.id) === id;
|
|
9305
|
+
async applyManagedPassEffects(effects) {
|
|
9306
|
+
const effectTargetMap = /* @__PURE__ */ new Map();
|
|
9307
|
+
for (const effect of effects) {
|
|
9308
|
+
if (effect.type !== "clipPath") continue;
|
|
9309
|
+
effect.targetPassIds.forEach((targetPassId) => {
|
|
9310
|
+
this.getPassCanvasObjects(targetPassId).forEach((obj) => {
|
|
9311
|
+
effectTargetMap.set(obj, effect);
|
|
9312
|
+
});
|
|
8955
9313
|
});
|
|
8956
9314
|
}
|
|
9315
|
+
const managedObjects = this.canvas.getObjects().filter(
|
|
9316
|
+
(obj) => this.isManagedPassObject(obj)
|
|
9317
|
+
);
|
|
9318
|
+
const effectTemplateCache = /* @__PURE__ */ new Map();
|
|
9319
|
+
for (const obj of managedObjects) {
|
|
9320
|
+
const targetEffect = effectTargetMap.get(obj);
|
|
9321
|
+
if (!targetEffect) {
|
|
9322
|
+
this.clearClipPathEffectFromObject(obj);
|
|
9323
|
+
continue;
|
|
9324
|
+
}
|
|
9325
|
+
let template = effectTemplateCache.get(targetEffect.key);
|
|
9326
|
+
if (template === void 0) {
|
|
9327
|
+
template = await this.createClipPathTemplate(targetEffect);
|
|
9328
|
+
effectTemplateCache.set(targetEffect.key, template);
|
|
9329
|
+
}
|
|
9330
|
+
if (!template) {
|
|
9331
|
+
this.clearClipPathEffectFromObject(obj);
|
|
9332
|
+
continue;
|
|
9333
|
+
}
|
|
9334
|
+
await this.applyClipPathEffectToObject(
|
|
9335
|
+
obj,
|
|
9336
|
+
template,
|
|
9337
|
+
targetEffect.key
|
|
9338
|
+
);
|
|
9339
|
+
}
|
|
9340
|
+
}
|
|
9341
|
+
getObject(id, passId) {
|
|
9342
|
+
const normalizedId = String(id || "").trim();
|
|
9343
|
+
if (!normalizedId) return void 0;
|
|
8957
9344
|
return this.canvas.getObjects().find((obj) => {
|
|
8958
|
-
var _a;
|
|
8959
|
-
|
|
9345
|
+
var _a, _b;
|
|
9346
|
+
if (((_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id) !== normalizedId) return false;
|
|
9347
|
+
if (!passId) return true;
|
|
9348
|
+
return ((_b = obj == null ? void 0 : obj.data) == null ? void 0 : _b.passId) === passId;
|
|
8960
9349
|
});
|
|
8961
9350
|
}
|
|
8962
9351
|
requestRenderAll() {
|
|
@@ -9142,114 +9531,187 @@ var CanvasService = class {
|
|
|
9142
9531
|
next.top = objectTop + objectHeight * this.originFactor(originY);
|
|
9143
9532
|
return next;
|
|
9144
9533
|
}
|
|
9145
|
-
|
|
9146
|
-
const
|
|
9147
|
-
|
|
9148
|
-
layer.set({ visible });
|
|
9149
|
-
}
|
|
9150
|
-
const objects = this.getRootLayerObjects(layerId);
|
|
9534
|
+
setPassVisibility(passId, visible) {
|
|
9535
|
+
const objects = this.getPassCanvasObjects(passId);
|
|
9536
|
+
let changed = false;
|
|
9151
9537
|
objects.forEach((obj) => {
|
|
9152
9538
|
var _a, _b;
|
|
9539
|
+
if (obj.visible === visible) return;
|
|
9153
9540
|
(_a = obj.set) == null ? void 0 : _a.call(obj, { visible });
|
|
9154
9541
|
(_b = obj.setCoords) == null ? void 0 : _b.call(obj);
|
|
9542
|
+
changed = true;
|
|
9155
9543
|
});
|
|
9544
|
+
return changed;
|
|
9156
9545
|
}
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
if (layer) {
|
|
9160
|
-
this.canvas.bringObjectToFront(layer);
|
|
9161
|
-
}
|
|
9162
|
-
const objects = this.getRootLayerObjects(layerId);
|
|
9163
|
-
objects.forEach((obj) => this.canvas.bringObjectToFront(obj));
|
|
9546
|
+
setLayerVisibility(layerId, visible) {
|
|
9547
|
+
return this.setPassVisibility(layerId, visible);
|
|
9164
9548
|
}
|
|
9165
|
-
|
|
9166
|
-
const
|
|
9167
|
-
|
|
9549
|
+
bringPassToFront(passId) {
|
|
9550
|
+
const objects = this.getPassCanvasObjects(passId);
|
|
9551
|
+
objects.forEach((obj) => this.canvas.bringObjectToFront(obj));
|
|
9168
9552
|
}
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
await this.applyObjectSpecsToContainer(layer, objects, options);
|
|
9553
|
+
bringLayerToFront(layerId) {
|
|
9554
|
+
this.bringPassToFront(layerId);
|
|
9172
9555
|
}
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9556
|
+
async applyPassSpec(spec, options = {}) {
|
|
9557
|
+
await this.applyObjectSpecsToPass(spec.id, spec.objects, {
|
|
9558
|
+
render: options.render,
|
|
9559
|
+
replace: spec.replace !== false
|
|
9177
9560
|
});
|
|
9178
9561
|
}
|
|
9179
|
-
async applyObjectSpecsToRootLayer(
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
var _a;
|
|
9184
|
-
const id = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id;
|
|
9185
|
-
if (typeof id === "string" && !desiredIds.has(id)) {
|
|
9186
|
-
this.canvas.remove(obj);
|
|
9187
|
-
}
|
|
9562
|
+
async applyObjectSpecsToRootLayer(passId, specs, options = {}) {
|
|
9563
|
+
await this.applyObjectSpecsToPass(passId, specs, {
|
|
9564
|
+
render: options.render,
|
|
9565
|
+
replace: true
|
|
9188
9566
|
});
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9567
|
+
}
|
|
9568
|
+
normalizeObjectSpecs(specs) {
|
|
9569
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9570
|
+
const normalized = [];
|
|
9571
|
+
(specs || []).forEach((spec) => {
|
|
9572
|
+
const id = String((spec == null ? void 0 : spec.id) || "").trim();
|
|
9573
|
+
if (!id || seen.has(id)) return;
|
|
9574
|
+
seen.add(id);
|
|
9575
|
+
normalized.push({
|
|
9576
|
+
...spec,
|
|
9577
|
+
id
|
|
9578
|
+
});
|
|
9194
9579
|
});
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
if (!current) {
|
|
9204
|
-
const created = await this.createFabricObject(spec);
|
|
9205
|
-
if (!created) continue;
|
|
9206
|
-
this.patchFabricObject(created, spec, { layerId });
|
|
9207
|
-
this.canvas.add(created);
|
|
9208
|
-
byId.set(spec.id, created);
|
|
9209
|
-
continue;
|
|
9210
|
-
}
|
|
9211
|
-
this.patchFabricObject(current, spec, { layerId });
|
|
9580
|
+
return normalized;
|
|
9581
|
+
}
|
|
9582
|
+
async cloneFabricObject(source) {
|
|
9583
|
+
const clone = source.clone;
|
|
9584
|
+
if (typeof clone !== "function") return void 0;
|
|
9585
|
+
const result = clone.call(source);
|
|
9586
|
+
if (!result || typeof result.then !== "function") {
|
|
9587
|
+
return void 0;
|
|
9212
9588
|
}
|
|
9213
|
-
|
|
9214
|
-
|
|
9589
|
+
try {
|
|
9590
|
+
const copied = await result;
|
|
9591
|
+
return copied;
|
|
9592
|
+
} catch (e) {
|
|
9593
|
+
return void 0;
|
|
9215
9594
|
}
|
|
9216
9595
|
}
|
|
9217
|
-
async
|
|
9218
|
-
|
|
9219
|
-
const
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9596
|
+
async createClipPathTemplate(effect) {
|
|
9597
|
+
var _a, _b;
|
|
9598
|
+
const source = effect.source;
|
|
9599
|
+
const sourceId = String(source.id || "").trim();
|
|
9600
|
+
if (!sourceId) return null;
|
|
9601
|
+
const template = await this.createFabricObject({
|
|
9602
|
+
...source,
|
|
9603
|
+
id: sourceId,
|
|
9604
|
+
data: {
|
|
9605
|
+
...source.data || {},
|
|
9606
|
+
id: sourceId,
|
|
9607
|
+
type: "clip-path-effect-template",
|
|
9608
|
+
effectKey: effect.key
|
|
9609
|
+
},
|
|
9610
|
+
props: {
|
|
9611
|
+
...source.props || {},
|
|
9612
|
+
selectable: false,
|
|
9613
|
+
evented: false,
|
|
9614
|
+
excludeFromExport: true
|
|
9225
9615
|
}
|
|
9226
9616
|
});
|
|
9617
|
+
if (!template) return null;
|
|
9618
|
+
(_a = template.set) == null ? void 0 : _a.call(template, {
|
|
9619
|
+
selectable: false,
|
|
9620
|
+
evented: false,
|
|
9621
|
+
excludeFromExport: true,
|
|
9622
|
+
absolutePositioned: true
|
|
9623
|
+
});
|
|
9624
|
+
(_b = template.setCoords) == null ? void 0 : _b.call(template);
|
|
9625
|
+
return template;
|
|
9626
|
+
}
|
|
9627
|
+
isClipPathEffectManaged(target) {
|
|
9628
|
+
return typeof (target == null ? void 0 : target.__pooderEffectClipKey) === "string";
|
|
9629
|
+
}
|
|
9630
|
+
clearClipPathEffectFromObject(target) {
|
|
9631
|
+
var _a, _b;
|
|
9632
|
+
if (!target) return;
|
|
9633
|
+
if (!this.isClipPathEffectManaged(target)) return;
|
|
9634
|
+
(_a = target.set) == null ? void 0 : _a.call(target, { clipPath: void 0 });
|
|
9635
|
+
(_b = target.setCoords) == null ? void 0 : _b.call(target);
|
|
9636
|
+
delete target.__pooderEffectClipKey;
|
|
9637
|
+
}
|
|
9638
|
+
async applyClipPathEffectToObject(target, clipTemplate, effectKey) {
|
|
9639
|
+
var _a, _b, _c, _d;
|
|
9640
|
+
if (!target) return;
|
|
9641
|
+
const clipPath = await this.cloneFabricObject(clipTemplate);
|
|
9642
|
+
if (!clipPath) {
|
|
9643
|
+
this.clearClipPathEffectFromObject(target);
|
|
9644
|
+
return;
|
|
9645
|
+
}
|
|
9646
|
+
(_a = clipPath.set) == null ? void 0 : _a.call(clipPath, {
|
|
9647
|
+
selectable: false,
|
|
9648
|
+
evented: false,
|
|
9649
|
+
excludeFromExport: true,
|
|
9650
|
+
absolutePositioned: true
|
|
9651
|
+
});
|
|
9652
|
+
(_b = clipPath.setCoords) == null ? void 0 : _b.call(clipPath);
|
|
9653
|
+
(_c = target.set) == null ? void 0 : _c.call(target, { clipPath });
|
|
9654
|
+
(_d = target.setCoords) == null ? void 0 : _d.call(target);
|
|
9655
|
+
target.__pooderEffectClipKey = effectKey;
|
|
9656
|
+
}
|
|
9657
|
+
async applyObjectSpecsToPass(passId, specs, options = {}) {
|
|
9658
|
+
const normalizedPassId = String(passId || "").trim();
|
|
9659
|
+
if (!normalizedPassId) return;
|
|
9660
|
+
const replace = options.replace !== false;
|
|
9661
|
+
const normalizedSpecs = this.normalizeObjectSpecs(specs);
|
|
9662
|
+
const desiredIds = new Set(normalizedSpecs.map((s) => s.id));
|
|
9663
|
+
const existing = this.getPassCanvasObjects(normalizedPassId);
|
|
9664
|
+
if (replace) {
|
|
9665
|
+
existing.forEach((obj) => {
|
|
9666
|
+
var _a;
|
|
9667
|
+
const id = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id;
|
|
9668
|
+
if (typeof id === "string" && !desiredIds.has(id)) {
|
|
9669
|
+
this.canvas.remove(obj);
|
|
9670
|
+
}
|
|
9671
|
+
});
|
|
9672
|
+
}
|
|
9227
9673
|
const byId = /* @__PURE__ */ new Map();
|
|
9228
|
-
|
|
9674
|
+
this.getPassCanvasObjects(normalizedPassId).forEach((obj) => {
|
|
9229
9675
|
var _a;
|
|
9230
9676
|
const id = (_a = obj == null ? void 0 : obj.data) == null ? void 0 : _a.id;
|
|
9231
9677
|
if (typeof id === "string") byId.set(id, obj);
|
|
9232
9678
|
});
|
|
9233
|
-
for (let index = 0; index <
|
|
9234
|
-
const spec =
|
|
9679
|
+
for (let index = 0; index < normalizedSpecs.length; index += 1) {
|
|
9680
|
+
const spec = normalizedSpecs[index];
|
|
9235
9681
|
let current = byId.get(spec.id);
|
|
9236
|
-
if (
|
|
9237
|
-
|
|
9682
|
+
if (spec.type === "path") {
|
|
9683
|
+
const nextPathData = this.readPathDataFromSpec(spec);
|
|
9684
|
+
if (!nextPathData || !nextPathData.trim()) {
|
|
9685
|
+
if (current) {
|
|
9686
|
+
this.canvas.remove(current);
|
|
9687
|
+
byId.delete(spec.id);
|
|
9688
|
+
}
|
|
9689
|
+
continue;
|
|
9690
|
+
}
|
|
9691
|
+
}
|
|
9692
|
+
if (current && this.shouldRecreateObject(current, spec)) {
|
|
9693
|
+
this.canvas.remove(current);
|
|
9238
9694
|
byId.delete(spec.id);
|
|
9239
9695
|
current = void 0;
|
|
9240
9696
|
}
|
|
9241
9697
|
if (!current) {
|
|
9242
9698
|
const created = await this.createFabricObject(spec);
|
|
9243
9699
|
if (!created) continue;
|
|
9244
|
-
|
|
9245
|
-
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9700
|
+
this.patchFabricObject(created, spec, {
|
|
9701
|
+
passId: normalizedPassId,
|
|
9702
|
+
layerId: normalizedPassId,
|
|
9703
|
+
passOrder: index
|
|
9704
|
+
});
|
|
9705
|
+
this.canvas.add(created);
|
|
9706
|
+
byId.set(spec.id, created);
|
|
9707
|
+
continue;
|
|
9249
9708
|
}
|
|
9250
|
-
this.
|
|
9709
|
+
this.patchFabricObject(current, spec, {
|
|
9710
|
+
passId: normalizedPassId,
|
|
9711
|
+
layerId: normalizedPassId,
|
|
9712
|
+
passOrder: index
|
|
9713
|
+
});
|
|
9251
9714
|
}
|
|
9252
|
-
container.dirty = true;
|
|
9253
9715
|
if (options.render !== false) {
|
|
9254
9716
|
this.requestRenderAll();
|
|
9255
9717
|
}
|
|
@@ -9261,10 +9723,56 @@ var CanvasService = class {
|
|
|
9261
9723
|
...extraData || {},
|
|
9262
9724
|
id: spec.id
|
|
9263
9725
|
};
|
|
9726
|
+
nextData.__renderSourceKey = this.getSpecRenderSourceKey(spec);
|
|
9264
9727
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9265
9728
|
obj.set({ ...props, data: nextData });
|
|
9266
9729
|
obj.setCoords();
|
|
9267
9730
|
}
|
|
9731
|
+
readPathDataFromSpec(spec) {
|
|
9732
|
+
var _a, _b;
|
|
9733
|
+
if (spec.type !== "path") return void 0;
|
|
9734
|
+
const raw = ((_a = spec.props) == null ? void 0 : _a.path) || ((_b = spec.props) == null ? void 0 : _b.pathData);
|
|
9735
|
+
if (typeof raw !== "string") return void 0;
|
|
9736
|
+
return raw;
|
|
9737
|
+
}
|
|
9738
|
+
hashText(value) {
|
|
9739
|
+
let hash = 2166136261;
|
|
9740
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
9741
|
+
hash ^= value.charCodeAt(i);
|
|
9742
|
+
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
9743
|
+
}
|
|
9744
|
+
return (hash >>> 0).toString(16);
|
|
9745
|
+
}
|
|
9746
|
+
getSpecRenderSourceKey(spec) {
|
|
9747
|
+
var _a, _b;
|
|
9748
|
+
switch (spec.type) {
|
|
9749
|
+
case "path": {
|
|
9750
|
+
const pathData = this.readPathDataFromSpec(spec) || "";
|
|
9751
|
+
return `path:${this.hashText(pathData)}`;
|
|
9752
|
+
}
|
|
9753
|
+
case "image":
|
|
9754
|
+
return `image:${String(spec.src || "")}`;
|
|
9755
|
+
case "text":
|
|
9756
|
+
return `text:${String((_b = (_a = spec.props) == null ? void 0 : _a.text) != null ? _b : "")}`;
|
|
9757
|
+
case "rect":
|
|
9758
|
+
return "rect";
|
|
9759
|
+
default:
|
|
9760
|
+
return String(spec.type || "");
|
|
9761
|
+
}
|
|
9762
|
+
}
|
|
9763
|
+
shouldRecreateObject(current, spec) {
|
|
9764
|
+
var _a;
|
|
9765
|
+
if (!current) return true;
|
|
9766
|
+
const currentType = String((current == null ? void 0 : current.type) || "").toLowerCase();
|
|
9767
|
+
if (currentType !== spec.type) return true;
|
|
9768
|
+
const expectedKey = this.getSpecRenderSourceKey(spec);
|
|
9769
|
+
const currentKey = String(((_a = current == null ? void 0 : current.data) == null ? void 0 : _a.__renderSourceKey) || "");
|
|
9770
|
+
if (currentKey && expectedKey && currentKey !== expectedKey) return true;
|
|
9771
|
+
if (spec.type === "image" && spec.src && current.getSrc) {
|
|
9772
|
+
return current.getSrc() !== spec.src;
|
|
9773
|
+
}
|
|
9774
|
+
return false;
|
|
9775
|
+
}
|
|
9268
9776
|
resolveFabricProps(spec, props) {
|
|
9269
9777
|
const space = spec.space || "scene";
|
|
9270
9778
|
const next = this.resolveLayoutProps(spec, props);
|
|
@@ -9288,26 +9796,26 @@ var CanvasService = class {
|
|
|
9288
9796
|
next.scaleY = rawScaleY * sceneScale;
|
|
9289
9797
|
return next;
|
|
9290
9798
|
}
|
|
9291
|
-
|
|
9799
|
+
moveObjectInCanvas(obj, index) {
|
|
9292
9800
|
if (!obj) return;
|
|
9293
|
-
const moveObjectTo =
|
|
9801
|
+
const moveObjectTo = this.canvas.moveObjectTo;
|
|
9294
9802
|
if (typeof moveObjectTo === "function") {
|
|
9295
|
-
moveObjectTo.call(
|
|
9803
|
+
moveObjectTo.call(this.canvas, obj, index);
|
|
9296
9804
|
return;
|
|
9297
9805
|
}
|
|
9298
|
-
const list =
|
|
9806
|
+
const list = this.canvas._objects;
|
|
9299
9807
|
if (!Array.isArray(list)) return;
|
|
9300
9808
|
const from = list.indexOf(obj);
|
|
9301
9809
|
if (from < 0 || from === index) return;
|
|
9302
9810
|
list.splice(from, 1);
|
|
9303
9811
|
const target = Math.max(0, Math.min(index, list.length));
|
|
9304
9812
|
list.splice(target, 0, obj);
|
|
9305
|
-
if (typeof
|
|
9306
|
-
|
|
9813
|
+
if (typeof this.canvas._onStackOrderChanged === "function") {
|
|
9814
|
+
this.canvas._onStackOrderChanged();
|
|
9307
9815
|
}
|
|
9308
9816
|
}
|
|
9309
9817
|
async createFabricObject(spec) {
|
|
9310
|
-
var _a, _b
|
|
9818
|
+
var _a, _b;
|
|
9311
9819
|
if (spec.type === "rect") {
|
|
9312
9820
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9313
9821
|
const rect = new Rect({
|
|
@@ -9318,7 +9826,7 @@ var CanvasService = class {
|
|
|
9318
9826
|
return rect;
|
|
9319
9827
|
}
|
|
9320
9828
|
if (spec.type === "path") {
|
|
9321
|
-
const pathData =
|
|
9829
|
+
const pathData = this.readPathDataFromSpec(spec);
|
|
9322
9830
|
if (!pathData) return void 0;
|
|
9323
9831
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9324
9832
|
const path = new Path2(pathData, {
|
|
@@ -9340,7 +9848,7 @@ var CanvasService = class {
|
|
|
9340
9848
|
return image;
|
|
9341
9849
|
}
|
|
9342
9850
|
if (spec.type === "text") {
|
|
9343
|
-
const content = String((
|
|
9851
|
+
const content = String((_b = (_a = spec.props) == null ? void 0 : _a.text) != null ? _b : "");
|
|
9344
9852
|
const props = this.resolveFabricProps(spec, spec.props || {});
|
|
9345
9853
|
const text = new Text(content, {
|
|
9346
9854
|
...props,
|
|
@@ -9362,8 +9870,8 @@ export {
|
|
|
9362
9870
|
MirrorTool,
|
|
9363
9871
|
RulerTool,
|
|
9364
9872
|
SceneLayoutService,
|
|
9365
|
-
SceneVisibilityService,
|
|
9366
9873
|
SizeTool,
|
|
9367
9874
|
ViewportSystem,
|
|
9368
|
-
WhiteInkTool
|
|
9875
|
+
WhiteInkTool,
|
|
9876
|
+
evaluateVisibilityExpr
|
|
9369
9877
|
};
|