@pooder/kit 6.2.0 → 6.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.test-dist/src/extensions/dieline/DielineTool.js +22 -9
- package/.test-dist/src/extensions/dieline/renderBuilder.js +47 -14
- package/.test-dist/src/extensions/feature/FeatureTool.js +20 -3
- package/.test-dist/src/extensions/featureCoordinates.js +21 -0
- package/.test-dist/src/extensions/featurePlacement.js +46 -0
- package/.test-dist/src/extensions/image/ImageTool.js +46 -348
- package/.test-dist/src/extensions/image/sessionOverlay.js +148 -0
- package/.test-dist/src/extensions/ruler/RulerTool.js +25 -2
- package/.test-dist/tests/run.js +25 -0
- package/CHANGELOG.md +12 -0
- package/dist/index.d.mts +4 -5
- package/dist/index.d.ts +4 -5
- package/dist/index.js +1506 -1494
- package/dist/index.mjs +1506 -1494
- package/package.json +1 -1
- package/src/extensions/dieline/DielineTool.ts +29 -9
- package/src/extensions/dieline/renderBuilder.ts +65 -17
- package/src/extensions/feature/FeatureTool.ts +23 -3
- package/src/extensions/featureCoordinates.ts +35 -0
- package/src/extensions/featurePlacement.ts +118 -0
- package/src/extensions/image/ImageTool.ts +57 -412
- package/src/extensions/image/sessionOverlay.ts +206 -0
- package/src/extensions/ruler/RulerTool.ts +24 -2
- package/tests/run.ts +37 -0
package/dist/index.js
CHANGED
|
@@ -2270,967 +2270,1097 @@ var BackgroundTool = class {
|
|
|
2270
2270
|
var import_core2 = require("@pooder/core");
|
|
2271
2271
|
var import_fabric2 = require("fabric");
|
|
2272
2272
|
|
|
2273
|
-
// src/
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
// src/extensions/bridgeSelection.ts
|
|
2277
|
-
function pickExitIndex(hits) {
|
|
2278
|
-
for (let i = 0; i < hits.length; i++) {
|
|
2279
|
-
const h = hits[i];
|
|
2280
|
-
if (h.insideBelow && !h.insideAbove) return i;
|
|
2281
|
-
}
|
|
2282
|
-
return -1;
|
|
2273
|
+
// src/shared/scene/frame.ts
|
|
2274
|
+
function emptyFrameRect() {
|
|
2275
|
+
return { left: 0, top: 0, width: 0, height: 0 };
|
|
2283
2276
|
}
|
|
2284
|
-
function
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
if (s.outsideAbove) score++;
|
|
2277
|
+
function resolveCutFrameRect(canvasService, configService) {
|
|
2278
|
+
if (!canvasService || !configService) {
|
|
2279
|
+
return emptyFrameRect();
|
|
2288
2280
|
}
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
function wrappedDistance(total, start, end) {
|
|
2294
|
-
if (!Number.isFinite(total) || total <= 0) return 0;
|
|
2295
|
-
if (!Number.isFinite(start) || !Number.isFinite(end)) return 0;
|
|
2296
|
-
const s = (start % total + total) % total;
|
|
2297
|
-
const e = (end % total + total) % total;
|
|
2298
|
-
return e >= s ? e - s : total - s + e;
|
|
2299
|
-
}
|
|
2300
|
-
function sampleWrappedOffsets(total, start, end, count) {
|
|
2301
|
-
if (!Number.isFinite(total) || total <= 0) return [];
|
|
2302
|
-
if (!Number.isFinite(start) || !Number.isFinite(end)) return [];
|
|
2303
|
-
const n = Math.max(0, Math.floor(count));
|
|
2304
|
-
if (n <= 0) return [];
|
|
2305
|
-
const dist = wrappedDistance(total, start, end);
|
|
2306
|
-
if (n === 1) return [(start % total + total) % total];
|
|
2307
|
-
const step = dist / (n - 1);
|
|
2308
|
-
const offsets = [];
|
|
2309
|
-
for (let i = 0; i < n; i++) {
|
|
2310
|
-
const raw = start + step * i;
|
|
2311
|
-
const wrapped = (raw % total + total) % total;
|
|
2312
|
-
offsets.push(wrapped);
|
|
2281
|
+
const sizeState = readSizeState(configService);
|
|
2282
|
+
const layout = computeSceneLayout(canvasService, sizeState);
|
|
2283
|
+
if (!layout) {
|
|
2284
|
+
return emptyFrameRect();
|
|
2313
2285
|
}
|
|
2314
|
-
return
|
|
2286
|
+
return canvasService.toSceneRect({
|
|
2287
|
+
left: layout.cutRect.left,
|
|
2288
|
+
top: layout.cutRect.top,
|
|
2289
|
+
width: layout.cutRect.width,
|
|
2290
|
+
height: layout.cutRect.height
|
|
2291
|
+
});
|
|
2315
2292
|
}
|
|
2316
|
-
|
|
2317
|
-
// src/extensions/geometry.ts
|
|
2318
|
-
function resolveFeaturePosition(feature, geometry) {
|
|
2319
|
-
const { x, y, width, height } = geometry;
|
|
2320
|
-
const left = x - width / 2;
|
|
2321
|
-
const top = y - height / 2;
|
|
2293
|
+
function toLayoutSceneRect(rect) {
|
|
2322
2294
|
return {
|
|
2323
|
-
|
|
2324
|
-
|
|
2295
|
+
left: rect.left,
|
|
2296
|
+
top: rect.top,
|
|
2297
|
+
width: rect.width,
|
|
2298
|
+
height: rect.height,
|
|
2299
|
+
space: "scene"
|
|
2325
2300
|
};
|
|
2326
2301
|
}
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
import_paper.default.view.viewSize = new import_paper.default.Size(width, height);
|
|
2332
|
-
}
|
|
2333
|
-
}
|
|
2334
|
-
var isBridgeDebugEnabled = () => Boolean(globalThis.__POODER_BRIDGE_DEBUG__);
|
|
2335
|
-
function normalizePathItem(shape) {
|
|
2336
|
-
let result = shape;
|
|
2337
|
-
if (typeof result.resolveCrossings === "function") result = result.resolveCrossings();
|
|
2338
|
-
if (typeof result.reduce === "function") result = result.reduce({});
|
|
2339
|
-
if (typeof result.reorient === "function") result = result.reorient(true, true);
|
|
2340
|
-
if (typeof result.reduce === "function") result = result.reduce({});
|
|
2341
|
-
return result;
|
|
2342
|
-
}
|
|
2343
|
-
function getBridgeDelta(itemBounds, overlap) {
|
|
2344
|
-
return Math.max(overlap, Math.min(5, Math.max(1, itemBounds.height * 0.02)));
|
|
2345
|
-
}
|
|
2346
|
-
function getExitHit(args) {
|
|
2347
|
-
const { mainShape, x, bridgeBottom, toY, eps, delta, overlap, op } = args;
|
|
2348
|
-
const ray = new import_paper.default.Path.Line({
|
|
2349
|
-
from: [x, bridgeBottom],
|
|
2350
|
-
to: [x, toY],
|
|
2351
|
-
insert: false
|
|
2352
|
-
});
|
|
2353
|
-
const intersections = mainShape.getIntersections(ray) || [];
|
|
2354
|
-
ray.remove();
|
|
2355
|
-
const validHits = intersections.filter((i) => i.point.y < bridgeBottom - eps);
|
|
2356
|
-
if (validHits.length === 0) return null;
|
|
2357
|
-
validHits.sort((a, b) => b.point.y - a.point.y);
|
|
2358
|
-
const flags = validHits.map((h) => {
|
|
2359
|
-
const above = h.point.add(new import_paper.default.Point(0, -delta));
|
|
2360
|
-
const below = h.point.add(new import_paper.default.Point(0, delta));
|
|
2361
|
-
return {
|
|
2362
|
-
insideAbove: mainShape.contains(above),
|
|
2363
|
-
insideBelow: mainShape.contains(below)
|
|
2364
|
-
};
|
|
2365
|
-
});
|
|
2366
|
-
const idx = pickExitIndex(flags);
|
|
2367
|
-
if (idx < 0) return null;
|
|
2368
|
-
if (isBridgeDebugEnabled()) {
|
|
2369
|
-
console.debug("Geometry: Bridge ray", {
|
|
2370
|
-
x,
|
|
2371
|
-
validHits: validHits.length,
|
|
2372
|
-
idx,
|
|
2373
|
-
delta,
|
|
2374
|
-
overlap,
|
|
2375
|
-
op
|
|
2376
|
-
});
|
|
2377
|
-
}
|
|
2378
|
-
const hit = validHits[idx];
|
|
2379
|
-
return { point: hit.point, location: hit };
|
|
2302
|
+
|
|
2303
|
+
// src/shared/runtime/sessionState.ts
|
|
2304
|
+
function cloneWithJson(value) {
|
|
2305
|
+
return JSON.parse(JSON.stringify(value));
|
|
2380
2306
|
}
|
|
2381
|
-
function
|
|
2382
|
-
const
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
const scoreB = scoreOutsideAbove(
|
|
2389
|
-
pointsB.map((p) => ({
|
|
2390
|
-
outsideAbove: !mainShape.contains(p.add(new import_paper.default.Point(0, -delta)))
|
|
2391
|
-
}))
|
|
2392
|
-
);
|
|
2393
|
-
const ratioA = scoreA / pointsA.length;
|
|
2394
|
-
const ratioB = scoreB / pointsB.length;
|
|
2395
|
-
if (isBridgeDebugEnabled()) {
|
|
2396
|
-
console.debug("Geometry: Bridge chain", {
|
|
2397
|
-
scoreA,
|
|
2398
|
-
scoreB,
|
|
2399
|
-
lenA: pointsA.length,
|
|
2400
|
-
lenB: pointsB.length,
|
|
2401
|
-
ratioA,
|
|
2402
|
-
ratioB,
|
|
2403
|
-
delta,
|
|
2404
|
-
overlap,
|
|
2405
|
-
op
|
|
2406
|
-
});
|
|
2407
|
-
}
|
|
2408
|
-
const ratioEps = 1e-6;
|
|
2409
|
-
if (Math.abs(ratioA - ratioB) > ratioEps) {
|
|
2410
|
-
return ratioA > ratioB ? pointsA : pointsB;
|
|
2307
|
+
function applyCommittedSnapshot(session, nextCommitted, options) {
|
|
2308
|
+
const clone = options.clone;
|
|
2309
|
+
session.committed = clone(nextCommitted);
|
|
2310
|
+
const shouldPreserveDirtyWorking = options.toolActive && options.preserveDirtyWorking !== false && session.hasWorkingChanges;
|
|
2311
|
+
if (!shouldPreserveDirtyWorking) {
|
|
2312
|
+
session.working = clone(session.committed);
|
|
2313
|
+
session.hasWorkingChanges = false;
|
|
2411
2314
|
}
|
|
2412
|
-
if (scoreA !== scoreB) return scoreA > scoreB ? pointsA : pointsB;
|
|
2413
|
-
return pointsA.length <= pointsB.length ? pointsA : pointsB;
|
|
2414
2315
|
}
|
|
2415
|
-
function
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
if (
|
|
2419
|
-
|
|
2420
|
-
return
|
|
2421
|
-
}
|
|
2422
|
-
item.translate(new import_paper.default.Point(-bounds.left, -bounds.top));
|
|
2423
|
-
if (fitMode === "stretch") {
|
|
2424
|
-
item.scale(width / bounds.width, height / bounds.height, new import_paper.default.Point(0, 0));
|
|
2425
|
-
item.translate(new import_paper.default.Point(left, top));
|
|
2426
|
-
return item;
|
|
2316
|
+
function runDeferredConfigUpdate(state, action, cooldownMs = 0) {
|
|
2317
|
+
state.isUpdatingConfig = true;
|
|
2318
|
+
action();
|
|
2319
|
+
if (cooldownMs <= 0) {
|
|
2320
|
+
state.isUpdatingConfig = false;
|
|
2321
|
+
return;
|
|
2427
2322
|
}
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
const scaledHeight = bounds.height * uniformScale;
|
|
2432
|
-
item.translate(
|
|
2433
|
-
new import_paper.default.Point(
|
|
2434
|
-
left + (width - scaledWidth) / 2,
|
|
2435
|
-
top + (height - scaledHeight) / 2
|
|
2436
|
-
)
|
|
2437
|
-
);
|
|
2438
|
-
return item;
|
|
2439
|
-
}
|
|
2440
|
-
function createNormalizedHeartPath(params) {
|
|
2441
|
-
const { lobeSpread, notchDepth, tipSharpness } = params;
|
|
2442
|
-
const halfSpread = 0.22 + lobeSpread * 0.18;
|
|
2443
|
-
const notchY = 0.06 + notchDepth * 0.2;
|
|
2444
|
-
const shoulderY = 0.24 + notchDepth * 0.2;
|
|
2445
|
-
const topLift = 0.12 + (1 - notchDepth) * 0.06;
|
|
2446
|
-
const topY = notchY - topLift;
|
|
2447
|
-
const sideCtrlY = shoulderY - (0.18 - notchDepth * 0.08);
|
|
2448
|
-
const lowerCtrlY = 0.58 + (1 - tipSharpness) * 0.16;
|
|
2449
|
-
const tipCtrlX = 0.34 - tipSharpness * 0.2;
|
|
2450
|
-
const notchCtrlX = 0.06 + lobeSpread * 0.06;
|
|
2451
|
-
const lobeCtrlX = 0.1 + lobeSpread * 0.08;
|
|
2452
|
-
const notchCtrlY = notchY - topLift * 0.45;
|
|
2453
|
-
const xPeakL = 0.5 - halfSpread;
|
|
2454
|
-
const xPeakR = 0.5 + halfSpread;
|
|
2455
|
-
const heartPath = new import_paper.default.Path({ insert: false });
|
|
2456
|
-
heartPath.moveTo(new import_paper.default.Point(0.5, notchY));
|
|
2457
|
-
heartPath.cubicCurveTo(
|
|
2458
|
-
new import_paper.default.Point(0.5 - notchCtrlX, notchCtrlY),
|
|
2459
|
-
new import_paper.default.Point(xPeakL + lobeCtrlX, topY),
|
|
2460
|
-
new import_paper.default.Point(xPeakL, topY)
|
|
2461
|
-
);
|
|
2462
|
-
heartPath.cubicCurveTo(
|
|
2463
|
-
new import_paper.default.Point(xPeakL - lobeCtrlX, topY),
|
|
2464
|
-
new import_paper.default.Point(0, sideCtrlY),
|
|
2465
|
-
new import_paper.default.Point(0, shoulderY)
|
|
2466
|
-
);
|
|
2467
|
-
heartPath.cubicCurveTo(
|
|
2468
|
-
new import_paper.default.Point(0, lowerCtrlY),
|
|
2469
|
-
new import_paper.default.Point(tipCtrlX, 1),
|
|
2470
|
-
new import_paper.default.Point(0.5, 1)
|
|
2471
|
-
);
|
|
2472
|
-
heartPath.cubicCurveTo(
|
|
2473
|
-
new import_paper.default.Point(1 - tipCtrlX, 1),
|
|
2474
|
-
new import_paper.default.Point(1, lowerCtrlY),
|
|
2475
|
-
new import_paper.default.Point(1, shoulderY)
|
|
2476
|
-
);
|
|
2477
|
-
heartPath.cubicCurveTo(
|
|
2478
|
-
new import_paper.default.Point(1, sideCtrlY),
|
|
2479
|
-
new import_paper.default.Point(xPeakR + lobeCtrlX, topY),
|
|
2480
|
-
new import_paper.default.Point(xPeakR, topY)
|
|
2481
|
-
);
|
|
2482
|
-
heartPath.cubicCurveTo(
|
|
2483
|
-
new import_paper.default.Point(xPeakR - lobeCtrlX, topY),
|
|
2484
|
-
new import_paper.default.Point(0.5 + notchCtrlX, notchCtrlY),
|
|
2485
|
-
new import_paper.default.Point(0.5, notchY)
|
|
2486
|
-
);
|
|
2487
|
-
heartPath.closed = true;
|
|
2488
|
-
return heartPath;
|
|
2323
|
+
setTimeout(() => {
|
|
2324
|
+
state.isUpdatingConfig = false;
|
|
2325
|
+
}, cooldownMs);
|
|
2489
2326
|
}
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
return new import_paper.default.Path.Rectangle({
|
|
2505
|
-
point: [x - width / 2, y - height / 2],
|
|
2506
|
-
size: [Math.max(0, width), Math.max(0, height)],
|
|
2507
|
-
radius: Math.max(0, radius)
|
|
2508
|
-
});
|
|
2509
|
-
},
|
|
2510
|
-
circle: (options) => {
|
|
2511
|
-
const { x, y, width, height } = options;
|
|
2512
|
-
const r = Math.min(width, height) / 2;
|
|
2513
|
-
return new import_paper.default.Path.Circle({
|
|
2514
|
-
center: new import_paper.default.Point(x, y),
|
|
2515
|
-
radius: Math.max(0, r)
|
|
2516
|
-
});
|
|
2517
|
-
},
|
|
2518
|
-
ellipse: (options) => {
|
|
2519
|
-
const { x, y, width, height } = options;
|
|
2520
|
-
return new import_paper.default.Path.Ellipse({
|
|
2521
|
-
center: new import_paper.default.Point(x, y),
|
|
2522
|
-
radius: [Math.max(0, width / 2), Math.max(0, height / 2)]
|
|
2523
|
-
});
|
|
2524
|
-
},
|
|
2525
|
-
heart: createHeartBaseShape
|
|
2526
|
-
};
|
|
2527
|
-
function createCustomBaseShape(options) {
|
|
2528
|
-
var _a;
|
|
2529
|
-
const {
|
|
2530
|
-
pathData,
|
|
2531
|
-
customSourceWidthPx,
|
|
2532
|
-
customSourceHeightPx,
|
|
2533
|
-
x,
|
|
2534
|
-
y,
|
|
2535
|
-
width,
|
|
2536
|
-
height
|
|
2537
|
-
} = options;
|
|
2538
|
-
if (typeof pathData !== "string" || pathData.trim().length === 0) {
|
|
2539
|
-
return null;
|
|
2540
|
-
}
|
|
2541
|
-
const center = new import_paper.default.Point(x, y);
|
|
2542
|
-
const hasMultipleSubPaths = ((_a = (pathData.match(/[Mm]/g) || []).length) != null ? _a : 0) > 1;
|
|
2543
|
-
const path = hasMultipleSubPaths ? new import_paper.default.CompoundPath(pathData) : (() => {
|
|
2544
|
-
const single = new import_paper.default.Path();
|
|
2545
|
-
single.pathData = pathData;
|
|
2546
|
-
return single;
|
|
2547
|
-
})();
|
|
2548
|
-
const sourceWidth = Number(customSourceWidthPx != null ? customSourceWidthPx : 0);
|
|
2549
|
-
const sourceHeight = Number(customSourceHeightPx != null ? customSourceHeightPx : 0);
|
|
2550
|
-
if (Number.isFinite(sourceWidth) && Number.isFinite(sourceHeight) && sourceWidth > 0 && sourceHeight > 0 && width > 0 && height > 0) {
|
|
2551
|
-
const targetLeft = x - width / 2;
|
|
2552
|
-
const targetTop = y - height / 2;
|
|
2553
|
-
path.scale(width / sourceWidth, height / sourceHeight, new import_paper.default.Point(0, 0));
|
|
2554
|
-
path.translate(new import_paper.default.Point(targetLeft, targetTop));
|
|
2555
|
-
return path;
|
|
2556
|
-
}
|
|
2557
|
-
if (width > 0 && height > 0 && path.bounds.width > 0 && path.bounds.height > 0) {
|
|
2558
|
-
path.position = center;
|
|
2559
|
-
path.scale(width / path.bounds.width, height / path.bounds.height);
|
|
2560
|
-
return path;
|
|
2561
|
-
}
|
|
2562
|
-
path.position = center;
|
|
2563
|
-
return path;
|
|
2564
|
-
}
|
|
2565
|
-
function createBaseShape(options) {
|
|
2566
|
-
const { shape } = options;
|
|
2567
|
-
if (shape === "custom") {
|
|
2568
|
-
const customShape = createCustomBaseShape(options);
|
|
2569
|
-
if (customShape) return customShape;
|
|
2570
|
-
return BUILTIN_SHAPE_BUILDERS[DEFAULT_DIELINE_SHAPE](options);
|
|
2571
|
-
}
|
|
2572
|
-
return BUILTIN_SHAPE_BUILDERS[shape](options);
|
|
2573
|
-
}
|
|
2574
|
-
function resolveBridgeBasePath(shape, anchor) {
|
|
2575
|
-
if (shape instanceof import_paper.default.Path) {
|
|
2576
|
-
return shape;
|
|
2577
|
-
}
|
|
2578
|
-
if (shape instanceof import_paper.default.CompoundPath) {
|
|
2579
|
-
const children = (shape.children || []).filter(
|
|
2580
|
-
(child) => child instanceof import_paper.default.Path
|
|
2581
|
-
);
|
|
2582
|
-
if (!children.length) return null;
|
|
2583
|
-
let best = children[0];
|
|
2584
|
-
let bestDistance = Infinity;
|
|
2585
|
-
for (const child of children) {
|
|
2586
|
-
const location = child.getNearestLocation(anchor);
|
|
2587
|
-
const point = location == null ? void 0 : location.point;
|
|
2588
|
-
if (!point) continue;
|
|
2589
|
-
const distance = point.getDistance(anchor);
|
|
2590
|
-
if (distance < bestDistance) {
|
|
2591
|
-
bestDistance = distance;
|
|
2592
|
-
best = child;
|
|
2327
|
+
|
|
2328
|
+
// src/extensions/image/commands.ts
|
|
2329
|
+
function createImageCommands(tool) {
|
|
2330
|
+
return [
|
|
2331
|
+
{
|
|
2332
|
+
command: "addImage",
|
|
2333
|
+
id: "addImage",
|
|
2334
|
+
title: "Add Image",
|
|
2335
|
+
handler: async (url, options) => {
|
|
2336
|
+
const result = await tool.upsertImageEntry(url, {
|
|
2337
|
+
mode: "add",
|
|
2338
|
+
addOptions: options
|
|
2339
|
+
});
|
|
2340
|
+
return result.id;
|
|
2593
2341
|
}
|
|
2594
|
-
}
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
);
|
|
2685
|
-
const offsetsB = sampleWrappedOffsets(
|
|
2686
|
-
pathLength,
|
|
2687
|
-
rightOffset,
|
|
2688
|
-
leftOffset,
|
|
2689
|
-
countFor(distanceB)
|
|
2690
|
-
);
|
|
2691
|
-
const pointsA = offsetsA.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
2692
|
-
const pointsB = offsetsB.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
2693
|
-
if (pointsA.length >= 2 && pointsB.length >= 2) {
|
|
2694
|
-
let topBase = selectOuterChain({
|
|
2695
|
-
mainShape: bridgeBasePath,
|
|
2696
|
-
pointsA,
|
|
2697
|
-
pointsB,
|
|
2698
|
-
delta,
|
|
2699
|
-
overlap,
|
|
2700
|
-
op: f.operation
|
|
2701
|
-
});
|
|
2702
|
-
const dist2 = (a, b) => {
|
|
2703
|
-
const dx = a.x - b.x;
|
|
2704
|
-
const dy = a.y - b.y;
|
|
2705
|
-
return dx * dx + dy * dy;
|
|
2706
|
-
};
|
|
2707
|
-
if (dist2(topBase[0], leftHit.point) > dist2(topBase[0], rightHit.point)) {
|
|
2708
|
-
topBase = topBase.slice().reverse();
|
|
2709
|
-
}
|
|
2710
|
-
topBase = topBase.slice();
|
|
2711
|
-
topBase[0] = leftHit.point;
|
|
2712
|
-
topBase[topBase.length - 1] = rightHit.point;
|
|
2713
|
-
const capShiftY = f.operation === "subtract" ? -Math.max(overlap * 2, delta) : overlap;
|
|
2714
|
-
const topPoints = topBase.map(
|
|
2715
|
-
(p) => p.add(new import_paper.default.Point(0, capShiftY))
|
|
2716
|
-
);
|
|
2717
|
-
const bridgeBottomY = bridgeBottom + overlap * 2;
|
|
2718
|
-
const bridgePoly = new import_paper.default.Path({ insert: false });
|
|
2719
|
-
for (const p of topPoints) bridgePoly.add(p);
|
|
2720
|
-
bridgePoly.add(new import_paper.default.Point(xRight, bridgeBottomY));
|
|
2721
|
-
bridgePoly.add(new import_paper.default.Point(xLeft, bridgeBottomY));
|
|
2722
|
-
bridgePoly.closed = true;
|
|
2723
|
-
const unitedItem = item.unite(bridgePoly);
|
|
2724
|
-
item.remove();
|
|
2725
|
-
bridgePoly.remove();
|
|
2726
|
-
if (f.operation === "add") {
|
|
2727
|
-
adds.push(unitedItem);
|
|
2728
|
-
} else {
|
|
2729
|
-
subtracts.push(unitedItem);
|
|
2730
|
-
}
|
|
2731
|
-
return;
|
|
2732
|
-
}
|
|
2733
|
-
}
|
|
2734
|
-
}
|
|
2735
|
-
if (f.operation === "add") {
|
|
2736
|
-
adds.push(item);
|
|
2737
|
-
} else {
|
|
2738
|
-
subtracts.push(item);
|
|
2739
|
-
}
|
|
2740
|
-
} else {
|
|
2741
|
-
if (f.operation === "add") {
|
|
2742
|
-
adds.push(item);
|
|
2743
|
-
} else {
|
|
2744
|
-
subtracts.push(item);
|
|
2342
|
+
},
|
|
2343
|
+
{
|
|
2344
|
+
command: "upsertImage",
|
|
2345
|
+
id: "upsertImage",
|
|
2346
|
+
title: "Upsert Image",
|
|
2347
|
+
handler: async (url, options = {}) => {
|
|
2348
|
+
return await tool.upsertImageEntry(url, options);
|
|
2349
|
+
}
|
|
2350
|
+
},
|
|
2351
|
+
{
|
|
2352
|
+
command: "getWorkingImages",
|
|
2353
|
+
id: "getWorkingImages",
|
|
2354
|
+
title: "Get Working Images",
|
|
2355
|
+
handler: () => {
|
|
2356
|
+
return tool.cloneItems(tool.workingItems);
|
|
2357
|
+
}
|
|
2358
|
+
},
|
|
2359
|
+
{
|
|
2360
|
+
command: "setWorkingImage",
|
|
2361
|
+
id: "setWorkingImage",
|
|
2362
|
+
title: "Set Working Image",
|
|
2363
|
+
handler: (id, updates) => {
|
|
2364
|
+
tool.updateImageInWorking(id, updates);
|
|
2365
|
+
}
|
|
2366
|
+
},
|
|
2367
|
+
{
|
|
2368
|
+
command: "resetWorkingImages",
|
|
2369
|
+
id: "resetWorkingImages",
|
|
2370
|
+
title: "Reset Working Images",
|
|
2371
|
+
handler: () => {
|
|
2372
|
+
tool.workingItems = tool.cloneItems(tool.items);
|
|
2373
|
+
tool.hasWorkingChanges = false;
|
|
2374
|
+
tool.updateImages();
|
|
2375
|
+
tool.emitWorkingChange();
|
|
2376
|
+
}
|
|
2377
|
+
},
|
|
2378
|
+
{
|
|
2379
|
+
command: "completeImages",
|
|
2380
|
+
id: "completeImages",
|
|
2381
|
+
title: "Complete Images",
|
|
2382
|
+
handler: async () => {
|
|
2383
|
+
return await tool.commitWorkingImagesAsCropped();
|
|
2384
|
+
}
|
|
2385
|
+
},
|
|
2386
|
+
{
|
|
2387
|
+
command: "exportUserCroppedImage",
|
|
2388
|
+
id: "exportUserCroppedImage",
|
|
2389
|
+
title: "Export User Cropped Image",
|
|
2390
|
+
handler: async (options = {}) => {
|
|
2391
|
+
return await tool.exportUserCroppedImage(options);
|
|
2392
|
+
}
|
|
2393
|
+
},
|
|
2394
|
+
{
|
|
2395
|
+
command: "fitImageToArea",
|
|
2396
|
+
id: "fitImageToArea",
|
|
2397
|
+
title: "Fit Image to Area",
|
|
2398
|
+
handler: async (id, area) => {
|
|
2399
|
+
await tool.fitImageToArea(id, area);
|
|
2400
|
+
}
|
|
2401
|
+
},
|
|
2402
|
+
{
|
|
2403
|
+
command: "fitImageToDefaultArea",
|
|
2404
|
+
id: "fitImageToDefaultArea",
|
|
2405
|
+
title: "Fit Image to Default Area",
|
|
2406
|
+
handler: async (id) => {
|
|
2407
|
+
await tool.fitImageToDefaultArea(id);
|
|
2408
|
+
}
|
|
2409
|
+
},
|
|
2410
|
+
{
|
|
2411
|
+
command: "focusImage",
|
|
2412
|
+
id: "focusImage",
|
|
2413
|
+
title: "Focus Image",
|
|
2414
|
+
handler: (id, options = {}) => {
|
|
2415
|
+
return tool.setImageFocus(id, options);
|
|
2416
|
+
}
|
|
2417
|
+
},
|
|
2418
|
+
{
|
|
2419
|
+
command: "removeImage",
|
|
2420
|
+
id: "removeImage",
|
|
2421
|
+
title: "Remove Image",
|
|
2422
|
+
handler: (id) => {
|
|
2423
|
+
const removed = tool.items.find((item) => item.id === id);
|
|
2424
|
+
const next = tool.items.filter((item) => item.id !== id);
|
|
2425
|
+
if (next.length !== tool.items.length) {
|
|
2426
|
+
tool.purgeSourceSizeCacheForItem(removed);
|
|
2427
|
+
if (tool.focusedImageId === id) {
|
|
2428
|
+
tool.setImageFocus(null, {
|
|
2429
|
+
syncCanvasSelection: true,
|
|
2430
|
+
skipRender: true
|
|
2431
|
+
});
|
|
2745
2432
|
}
|
|
2746
|
-
|
|
2747
|
-
} else {
|
|
2748
|
-
if (f.operation === "add") {
|
|
2749
|
-
adds.push(item);
|
|
2750
|
-
} else {
|
|
2751
|
-
subtracts.push(item);
|
|
2433
|
+
tool.updateConfig(next);
|
|
2752
2434
|
}
|
|
2753
2435
|
}
|
|
2754
|
-
}
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2436
|
+
},
|
|
2437
|
+
{
|
|
2438
|
+
command: "updateImage",
|
|
2439
|
+
id: "updateImage",
|
|
2440
|
+
title: "Update Image",
|
|
2441
|
+
handler: async (id, updates, options = {}) => {
|
|
2442
|
+
await tool.updateImage(id, updates, options);
|
|
2443
|
+
}
|
|
2444
|
+
},
|
|
2445
|
+
{
|
|
2446
|
+
command: "clearImages",
|
|
2447
|
+
id: "clearImages",
|
|
2448
|
+
title: "Clear Images",
|
|
2449
|
+
handler: () => {
|
|
2450
|
+
tool.sourceSizeCache.clear();
|
|
2451
|
+
tool.setImageFocus(null, {
|
|
2452
|
+
syncCanvasSelection: true,
|
|
2453
|
+
skipRender: true
|
|
2454
|
+
});
|
|
2455
|
+
tool.updateConfig([]);
|
|
2456
|
+
}
|
|
2457
|
+
},
|
|
2458
|
+
{
|
|
2459
|
+
command: "bringToFront",
|
|
2460
|
+
id: "bringToFront",
|
|
2461
|
+
title: "Bring Image to Front",
|
|
2462
|
+
handler: (id) => {
|
|
2463
|
+
const index = tool.items.findIndex((item) => item.id === id);
|
|
2464
|
+
if (index !== -1 && index < tool.items.length - 1) {
|
|
2465
|
+
const next = [...tool.items];
|
|
2466
|
+
const [item] = next.splice(index, 1);
|
|
2467
|
+
next.push(item);
|
|
2468
|
+
tool.updateConfig(next);
|
|
2765
2469
|
}
|
|
2766
2470
|
}
|
|
2767
|
-
}
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2471
|
+
},
|
|
2472
|
+
{
|
|
2473
|
+
command: "sendToBack",
|
|
2474
|
+
id: "sendToBack",
|
|
2475
|
+
title: "Send Image to Back",
|
|
2476
|
+
handler: (id) => {
|
|
2477
|
+
const index = tool.items.findIndex((item) => item.id === id);
|
|
2478
|
+
if (index > 0) {
|
|
2479
|
+
const next = [...tool.items];
|
|
2480
|
+
const [item] = next.splice(index, 1);
|
|
2481
|
+
next.unshift(item);
|
|
2482
|
+
tool.updateConfig(next);
|
|
2778
2483
|
}
|
|
2779
2484
|
}
|
|
2780
2485
|
}
|
|
2486
|
+
];
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2489
|
+
// src/extensions/image/config.ts
|
|
2490
|
+
function createImageConfigurations() {
|
|
2491
|
+
return [
|
|
2492
|
+
{
|
|
2493
|
+
id: "image.items",
|
|
2494
|
+
type: "array",
|
|
2495
|
+
label: "Images",
|
|
2496
|
+
default: []
|
|
2497
|
+
},
|
|
2498
|
+
{
|
|
2499
|
+
id: "image.debug",
|
|
2500
|
+
type: "boolean",
|
|
2501
|
+
label: "Image Debug Log",
|
|
2502
|
+
default: false
|
|
2503
|
+
},
|
|
2504
|
+
{
|
|
2505
|
+
id: "image.control.cornerSize",
|
|
2506
|
+
type: "number",
|
|
2507
|
+
label: "Image Control Corner Size",
|
|
2508
|
+
min: 4,
|
|
2509
|
+
max: 64,
|
|
2510
|
+
step: 1,
|
|
2511
|
+
default: 14
|
|
2512
|
+
},
|
|
2513
|
+
{
|
|
2514
|
+
id: "image.control.touchCornerSize",
|
|
2515
|
+
type: "number",
|
|
2516
|
+
label: "Image Control Touch Corner Size",
|
|
2517
|
+
min: 8,
|
|
2518
|
+
max: 96,
|
|
2519
|
+
step: 1,
|
|
2520
|
+
default: 24
|
|
2521
|
+
},
|
|
2522
|
+
{
|
|
2523
|
+
id: "image.control.cornerStyle",
|
|
2524
|
+
type: "select",
|
|
2525
|
+
label: "Image Control Corner Style",
|
|
2526
|
+
options: ["circle", "rect"],
|
|
2527
|
+
default: "circle"
|
|
2528
|
+
},
|
|
2529
|
+
{
|
|
2530
|
+
id: "image.control.cornerColor",
|
|
2531
|
+
type: "color",
|
|
2532
|
+
label: "Image Control Corner Color",
|
|
2533
|
+
default: "#ffffff"
|
|
2534
|
+
},
|
|
2535
|
+
{
|
|
2536
|
+
id: "image.control.cornerStrokeColor",
|
|
2537
|
+
type: "color",
|
|
2538
|
+
label: "Image Control Corner Stroke Color",
|
|
2539
|
+
default: "#1677ff"
|
|
2540
|
+
},
|
|
2541
|
+
{
|
|
2542
|
+
id: "image.control.transparentCorners",
|
|
2543
|
+
type: "boolean",
|
|
2544
|
+
label: "Image Control Transparent Corners",
|
|
2545
|
+
default: false
|
|
2546
|
+
},
|
|
2547
|
+
{
|
|
2548
|
+
id: "image.control.borderColor",
|
|
2549
|
+
type: "color",
|
|
2550
|
+
label: "Image Control Border Color",
|
|
2551
|
+
default: "#1677ff"
|
|
2552
|
+
},
|
|
2553
|
+
{
|
|
2554
|
+
id: "image.control.borderScaleFactor",
|
|
2555
|
+
type: "number",
|
|
2556
|
+
label: "Image Control Border Width",
|
|
2557
|
+
min: 0.5,
|
|
2558
|
+
max: 8,
|
|
2559
|
+
step: 0.1,
|
|
2560
|
+
default: 1.5
|
|
2561
|
+
},
|
|
2562
|
+
{
|
|
2563
|
+
id: "image.control.padding",
|
|
2564
|
+
type: "number",
|
|
2565
|
+
label: "Image Control Padding",
|
|
2566
|
+
min: 0,
|
|
2567
|
+
max: 64,
|
|
2568
|
+
step: 1,
|
|
2569
|
+
default: 0
|
|
2570
|
+
},
|
|
2571
|
+
{
|
|
2572
|
+
id: "image.frame.strokeColor",
|
|
2573
|
+
type: "color",
|
|
2574
|
+
label: "Image Frame Stroke Color",
|
|
2575
|
+
default: "#808080"
|
|
2576
|
+
},
|
|
2577
|
+
{
|
|
2578
|
+
id: "image.frame.strokeWidth",
|
|
2579
|
+
type: "number",
|
|
2580
|
+
label: "Image Frame Stroke Width",
|
|
2581
|
+
min: 0,
|
|
2582
|
+
max: 20,
|
|
2583
|
+
step: 0.5,
|
|
2584
|
+
default: 2
|
|
2585
|
+
},
|
|
2586
|
+
{
|
|
2587
|
+
id: "image.frame.strokeStyle",
|
|
2588
|
+
type: "select",
|
|
2589
|
+
label: "Image Frame Stroke Style",
|
|
2590
|
+
options: ["solid", "dashed", "hidden"],
|
|
2591
|
+
default: "dashed"
|
|
2592
|
+
},
|
|
2593
|
+
{
|
|
2594
|
+
id: "image.frame.dashLength",
|
|
2595
|
+
type: "number",
|
|
2596
|
+
label: "Image Frame Dash Length",
|
|
2597
|
+
min: 1,
|
|
2598
|
+
max: 40,
|
|
2599
|
+
step: 1,
|
|
2600
|
+
default: 8
|
|
2601
|
+
},
|
|
2602
|
+
{
|
|
2603
|
+
id: "image.frame.innerBackground",
|
|
2604
|
+
type: "color",
|
|
2605
|
+
label: "Image Frame Inner Background",
|
|
2606
|
+
default: "rgba(0,0,0,0)"
|
|
2607
|
+
},
|
|
2608
|
+
{
|
|
2609
|
+
id: "image.frame.outerBackground",
|
|
2610
|
+
type: "color",
|
|
2611
|
+
label: "Image Frame Outer Background",
|
|
2612
|
+
default: "#f5f5f5"
|
|
2613
|
+
}
|
|
2614
|
+
];
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
// src/extensions/geometry.ts
|
|
2618
|
+
var import_paper = __toESM(require("paper"));
|
|
2619
|
+
|
|
2620
|
+
// src/extensions/bridgeSelection.ts
|
|
2621
|
+
function pickExitIndex(hits) {
|
|
2622
|
+
for (let i = 0; i < hits.length; i++) {
|
|
2623
|
+
const h = hits[i];
|
|
2624
|
+
if (h.insideBelow && !h.insideAbove) return i;
|
|
2781
2625
|
}
|
|
2782
|
-
return
|
|
2626
|
+
return -1;
|
|
2783
2627
|
}
|
|
2784
|
-
function
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
if (surfaceFeatures.length === 0) return shape;
|
|
2789
|
-
let result = shape;
|
|
2790
|
-
for (const f of surfaceFeatures) {
|
|
2791
|
-
const pos = resolveFeaturePosition(f, options);
|
|
2792
|
-
const center = new import_paper.default.Point(pos.x, pos.y);
|
|
2793
|
-
const item = createFeatureItem(f, center);
|
|
2794
|
-
try {
|
|
2795
|
-
if (f.operation === "add") {
|
|
2796
|
-
const temp = result.unite(item);
|
|
2797
|
-
result.remove();
|
|
2798
|
-
item.remove();
|
|
2799
|
-
result = normalizePathItem(temp);
|
|
2800
|
-
} else {
|
|
2801
|
-
const temp = result.subtract(item);
|
|
2802
|
-
result.remove();
|
|
2803
|
-
item.remove();
|
|
2804
|
-
result = normalizePathItem(temp);
|
|
2805
|
-
}
|
|
2806
|
-
} catch (e) {
|
|
2807
|
-
console.error("Geometry: Failed to apply surface feature", e);
|
|
2808
|
-
item.remove();
|
|
2809
|
-
}
|
|
2628
|
+
function scoreOutsideAbove(samples) {
|
|
2629
|
+
let score = 0;
|
|
2630
|
+
for (const s of samples) {
|
|
2631
|
+
if (s.outsideAbove) score++;
|
|
2810
2632
|
}
|
|
2811
|
-
return
|
|
2633
|
+
return score;
|
|
2812
2634
|
}
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
const
|
|
2819
|
-
const
|
|
2820
|
-
|
|
2821
|
-
finalShape.remove();
|
|
2822
|
-
return pathData;
|
|
2635
|
+
|
|
2636
|
+
// src/extensions/wrappedOffsets.ts
|
|
2637
|
+
function wrappedDistance(total, start, end) {
|
|
2638
|
+
if (!Number.isFinite(total) || total <= 0) return 0;
|
|
2639
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return 0;
|
|
2640
|
+
const s = (start % total + total) % total;
|
|
2641
|
+
const e = (end % total + total) % total;
|
|
2642
|
+
return e >= s ? e - s : total - s + e;
|
|
2823
2643
|
}
|
|
2824
|
-
function
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
const
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
pOffset,
|
|
2838
|
-
offsetOptions.features,
|
|
2839
|
-
offsetOptions
|
|
2840
|
-
);
|
|
2841
|
-
let bleedZone;
|
|
2842
|
-
if (offset > 0) {
|
|
2843
|
-
bleedZone = shapeOffset.subtract(shapeOriginal);
|
|
2844
|
-
} else {
|
|
2845
|
-
bleedZone = shapeOriginal.subtract(shapeOffset);
|
|
2644
|
+
function sampleWrappedOffsets(total, start, end, count) {
|
|
2645
|
+
if (!Number.isFinite(total) || total <= 0) return [];
|
|
2646
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) return [];
|
|
2647
|
+
const n = Math.max(0, Math.floor(count));
|
|
2648
|
+
if (n <= 0) return [];
|
|
2649
|
+
const dist = wrappedDistance(total, start, end);
|
|
2650
|
+
if (n === 1) return [(start % total + total) % total];
|
|
2651
|
+
const step = dist / (n - 1);
|
|
2652
|
+
const offsets = [];
|
|
2653
|
+
for (let i = 0; i < n; i++) {
|
|
2654
|
+
const raw = start + step * i;
|
|
2655
|
+
const wrapped = (raw % total + total) % total;
|
|
2656
|
+
offsets.push(wrapped);
|
|
2846
2657
|
}
|
|
2847
|
-
|
|
2848
|
-
shapeOriginal.remove();
|
|
2849
|
-
shapeOffset.remove();
|
|
2850
|
-
bleedZone.remove();
|
|
2851
|
-
return pathData;
|
|
2658
|
+
return offsets;
|
|
2852
2659
|
}
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
const
|
|
2857
|
-
const
|
|
2858
|
-
const
|
|
2859
|
-
|
|
2860
|
-
|
|
2660
|
+
|
|
2661
|
+
// src/extensions/geometry.ts
|
|
2662
|
+
function resolveFeaturePosition(feature, geometry) {
|
|
2663
|
+
const { x, y, width, height } = geometry;
|
|
2664
|
+
const left = x - width / 2;
|
|
2665
|
+
const top = y - height / 2;
|
|
2666
|
+
return {
|
|
2667
|
+
x: left + feature.x * width,
|
|
2668
|
+
y: top + feature.y * height
|
|
2861
2669
|
};
|
|
2862
|
-
shape.remove();
|
|
2863
|
-
return result;
|
|
2864
2670
|
}
|
|
2865
|
-
function
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2671
|
+
function ensurePaper(width, height) {
|
|
2672
|
+
if (!import_paper.default.project) {
|
|
2673
|
+
import_paper.default.setup(new import_paper.default.Size(width, height));
|
|
2674
|
+
} else {
|
|
2675
|
+
import_paper.default.view.viewSize = new import_paper.default.Size(width, height);
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
var isBridgeDebugEnabled = () => Boolean(globalThis.__POODER_BRIDGE_DEBUG__);
|
|
2679
|
+
function normalizePathItem(shape) {
|
|
2680
|
+
let result = shape;
|
|
2681
|
+
if (typeof result.resolveCrossings === "function") result = result.resolveCrossings();
|
|
2682
|
+
if (typeof result.reduce === "function") result = result.reduce({});
|
|
2683
|
+
if (typeof result.reorient === "function") result = result.reorient(true, true);
|
|
2684
|
+
if (typeof result.reduce === "function") result = result.reduce({});
|
|
2877
2685
|
return result;
|
|
2878
2686
|
}
|
|
2879
|
-
function
|
|
2880
|
-
|
|
2881
|
-
path.pathData = pathData;
|
|
2882
|
-
const bounds = path.bounds;
|
|
2883
|
-
path.remove();
|
|
2884
|
-
return {
|
|
2885
|
-
x: bounds.x,
|
|
2886
|
-
y: bounds.y,
|
|
2887
|
-
width: bounds.width,
|
|
2888
|
-
height: bounds.height
|
|
2889
|
-
};
|
|
2687
|
+
function getBridgeDelta(itemBounds, overlap) {
|
|
2688
|
+
return Math.max(overlap, Math.min(5, Math.max(1, itemBounds.height * 0.02)));
|
|
2890
2689
|
}
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2690
|
+
function getExitHit(args) {
|
|
2691
|
+
const { mainShape, x, bridgeBottom, toY, eps, delta, overlap, op } = args;
|
|
2692
|
+
const ray = new import_paper.default.Path.Line({
|
|
2693
|
+
from: [x, bridgeBottom],
|
|
2694
|
+
to: [x, toY],
|
|
2695
|
+
insert: false
|
|
2696
|
+
});
|
|
2697
|
+
const intersections = mainShape.getIntersections(ray) || [];
|
|
2698
|
+
ray.remove();
|
|
2699
|
+
const validHits = intersections.filter((i) => i.point.y < bridgeBottom - eps);
|
|
2700
|
+
if (validHits.length === 0) return null;
|
|
2701
|
+
validHits.sort((a, b) => b.point.y - a.point.y);
|
|
2702
|
+
const flags = validHits.map((h) => {
|
|
2703
|
+
const above = h.point.add(new import_paper.default.Point(0, -delta));
|
|
2704
|
+
const below = h.point.add(new import_paper.default.Point(0, delta));
|
|
2705
|
+
return {
|
|
2706
|
+
insideAbove: mainShape.contains(above),
|
|
2707
|
+
insideBelow: mainShape.contains(below)
|
|
2708
|
+
};
|
|
2709
|
+
});
|
|
2710
|
+
const idx = pickExitIndex(flags);
|
|
2711
|
+
if (idx < 0) return null;
|
|
2712
|
+
if (isBridgeDebugEnabled()) {
|
|
2713
|
+
console.debug("Geometry: Bridge ray", {
|
|
2714
|
+
x,
|
|
2715
|
+
validHits: validHits.length,
|
|
2716
|
+
idx,
|
|
2717
|
+
delta,
|
|
2718
|
+
overlap,
|
|
2719
|
+
op
|
|
2720
|
+
});
|
|
2721
|
+
}
|
|
2722
|
+
const hit = validHits[idx];
|
|
2723
|
+
return { point: hit.point, location: hit };
|
|
2895
2724
|
}
|
|
2896
|
-
function
|
|
2897
|
-
|
|
2898
|
-
|
|
2725
|
+
function selectOuterChain(args) {
|
|
2726
|
+
const { mainShape, pointsA, pointsB, delta, overlap, op } = args;
|
|
2727
|
+
const scoreA = scoreOutsideAbove(
|
|
2728
|
+
pointsA.map((p) => ({
|
|
2729
|
+
outsideAbove: !mainShape.contains(p.add(new import_paper.default.Point(0, -delta)))
|
|
2730
|
+
}))
|
|
2731
|
+
);
|
|
2732
|
+
const scoreB = scoreOutsideAbove(
|
|
2733
|
+
pointsB.map((p) => ({
|
|
2734
|
+
outsideAbove: !mainShape.contains(p.add(new import_paper.default.Point(0, -delta)))
|
|
2735
|
+
}))
|
|
2736
|
+
);
|
|
2737
|
+
const ratioA = scoreA / pointsA.length;
|
|
2738
|
+
const ratioB = scoreB / pointsB.length;
|
|
2739
|
+
if (isBridgeDebugEnabled()) {
|
|
2740
|
+
console.debug("Geometry: Bridge chain", {
|
|
2741
|
+
scoreA,
|
|
2742
|
+
scoreB,
|
|
2743
|
+
lenA: pointsA.length,
|
|
2744
|
+
lenB: pointsB.length,
|
|
2745
|
+
ratioA,
|
|
2746
|
+
ratioB,
|
|
2747
|
+
delta,
|
|
2748
|
+
overlap,
|
|
2749
|
+
op
|
|
2750
|
+
});
|
|
2899
2751
|
}
|
|
2900
|
-
const
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2752
|
+
const ratioEps = 1e-6;
|
|
2753
|
+
if (Math.abs(ratioA - ratioB) > ratioEps) {
|
|
2754
|
+
return ratioA > ratioB ? pointsA : pointsB;
|
|
2755
|
+
}
|
|
2756
|
+
if (scoreA !== scoreB) return scoreA > scoreB ? pointsA : pointsB;
|
|
2757
|
+
return pointsA.length <= pointsB.length ? pointsA : pointsB;
|
|
2758
|
+
}
|
|
2759
|
+
function fitPathItemToRect(item, rect, fitMode) {
|
|
2760
|
+
const { left, top, width, height } = rect;
|
|
2761
|
+
const bounds = item.bounds;
|
|
2762
|
+
if (width <= 0 || height <= 0 || !Number.isFinite(bounds.width) || !Number.isFinite(bounds.height) || bounds.width <= 0 || bounds.height <= 0) {
|
|
2763
|
+
item.position = new import_paper.default.Point(left + width / 2, top + height / 2);
|
|
2764
|
+
return item;
|
|
2765
|
+
}
|
|
2766
|
+
item.translate(new import_paper.default.Point(-bounds.left, -bounds.top));
|
|
2767
|
+
if (fitMode === "stretch") {
|
|
2768
|
+
item.scale(width / bounds.width, height / bounds.height, new import_paper.default.Point(0, 0));
|
|
2769
|
+
item.translate(new import_paper.default.Point(left, top));
|
|
2770
|
+
return item;
|
|
2904
2771
|
}
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2772
|
+
const uniformScale = Math.min(width / bounds.width, height / bounds.height);
|
|
2773
|
+
item.scale(uniformScale, uniformScale, new import_paper.default.Point(0, 0));
|
|
2774
|
+
const scaledWidth = bounds.width * uniformScale;
|
|
2775
|
+
const scaledHeight = bounds.height * uniformScale;
|
|
2776
|
+
item.translate(
|
|
2777
|
+
new import_paper.default.Point(
|
|
2778
|
+
left + (width - scaledWidth) / 2,
|
|
2779
|
+
top + (height - scaledHeight) / 2
|
|
2780
|
+
)
|
|
2781
|
+
);
|
|
2782
|
+
return item;
|
|
2911
2783
|
}
|
|
2912
|
-
function
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2784
|
+
function createNormalizedHeartPath(params) {
|
|
2785
|
+
const { lobeSpread, notchDepth, tipSharpness } = params;
|
|
2786
|
+
const halfSpread = 0.22 + lobeSpread * 0.18;
|
|
2787
|
+
const notchY = 0.06 + notchDepth * 0.2;
|
|
2788
|
+
const shoulderY = 0.24 + notchDepth * 0.2;
|
|
2789
|
+
const topLift = 0.12 + (1 - notchDepth) * 0.06;
|
|
2790
|
+
const topY = notchY - topLift;
|
|
2791
|
+
const sideCtrlY = shoulderY - (0.18 - notchDepth * 0.08);
|
|
2792
|
+
const lowerCtrlY = 0.58 + (1 - tipSharpness) * 0.16;
|
|
2793
|
+
const tipCtrlX = 0.34 - tipSharpness * 0.2;
|
|
2794
|
+
const notchCtrlX = 0.06 + lobeSpread * 0.06;
|
|
2795
|
+
const lobeCtrlX = 0.1 + lobeSpread * 0.08;
|
|
2796
|
+
const notchCtrlY = notchY - topLift * 0.45;
|
|
2797
|
+
const xPeakL = 0.5 - halfSpread;
|
|
2798
|
+
const xPeakR = 0.5 + halfSpread;
|
|
2799
|
+
const heartPath = new import_paper.default.Path({ insert: false });
|
|
2800
|
+
heartPath.moveTo(new import_paper.default.Point(0.5, notchY));
|
|
2801
|
+
heartPath.cubicCurveTo(
|
|
2802
|
+
new import_paper.default.Point(0.5 - notchCtrlX, notchCtrlY),
|
|
2803
|
+
new import_paper.default.Point(xPeakL + lobeCtrlX, topY),
|
|
2804
|
+
new import_paper.default.Point(xPeakL, topY)
|
|
2805
|
+
);
|
|
2806
|
+
heartPath.cubicCurveTo(
|
|
2807
|
+
new import_paper.default.Point(xPeakL - lobeCtrlX, topY),
|
|
2808
|
+
new import_paper.default.Point(0, sideCtrlY),
|
|
2809
|
+
new import_paper.default.Point(0, shoulderY)
|
|
2810
|
+
);
|
|
2811
|
+
heartPath.cubicCurveTo(
|
|
2812
|
+
new import_paper.default.Point(0, lowerCtrlY),
|
|
2813
|
+
new import_paper.default.Point(tipCtrlX, 1),
|
|
2814
|
+
new import_paper.default.Point(0.5, 1)
|
|
2815
|
+
);
|
|
2816
|
+
heartPath.cubicCurveTo(
|
|
2817
|
+
new import_paper.default.Point(1 - tipCtrlX, 1),
|
|
2818
|
+
new import_paper.default.Point(1, lowerCtrlY),
|
|
2819
|
+
new import_paper.default.Point(1, shoulderY)
|
|
2820
|
+
);
|
|
2821
|
+
heartPath.cubicCurveTo(
|
|
2822
|
+
new import_paper.default.Point(1, sideCtrlY),
|
|
2823
|
+
new import_paper.default.Point(xPeakR + lobeCtrlX, topY),
|
|
2824
|
+
new import_paper.default.Point(xPeakR, topY)
|
|
2825
|
+
);
|
|
2826
|
+
heartPath.cubicCurveTo(
|
|
2827
|
+
new import_paper.default.Point(xPeakR - lobeCtrlX, topY),
|
|
2828
|
+
new import_paper.default.Point(0.5 + notchCtrlX, notchCtrlY),
|
|
2829
|
+
new import_paper.default.Point(0.5, notchY)
|
|
2830
|
+
);
|
|
2831
|
+
heartPath.closed = true;
|
|
2832
|
+
return heartPath;
|
|
2920
2833
|
}
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2834
|
+
function createHeartBaseShape(options) {
|
|
2835
|
+
const { x, y, width, height } = options;
|
|
2836
|
+
const w = Math.max(0, width);
|
|
2837
|
+
const h = Math.max(0, height);
|
|
2838
|
+
const left = x - w / 2;
|
|
2839
|
+
const top = y - h / 2;
|
|
2840
|
+
const fitMode = getShapeFitMode(options.shapeStyle);
|
|
2841
|
+
const heartParams = getHeartShapeParams(options.shapeStyle);
|
|
2842
|
+
const rawHeart = createNormalizedHeartPath(heartParams);
|
|
2843
|
+
return fitPathItemToRect(rawHeart, { left, top, width: w, height: h }, fitMode);
|
|
2925
2844
|
}
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2845
|
+
var BUILTIN_SHAPE_BUILDERS = {
|
|
2846
|
+
rect: (options) => {
|
|
2847
|
+
const { x, y, width, height, radius } = options;
|
|
2848
|
+
return new import_paper.default.Path.Rectangle({
|
|
2849
|
+
point: [x - width / 2, y - height / 2],
|
|
2850
|
+
size: [Math.max(0, width), Math.max(0, height)],
|
|
2851
|
+
radius: Math.max(0, radius)
|
|
2852
|
+
});
|
|
2853
|
+
},
|
|
2854
|
+
circle: (options) => {
|
|
2855
|
+
const { x, y, width, height } = options;
|
|
2856
|
+
const r = Math.min(width, height) / 2;
|
|
2857
|
+
return new import_paper.default.Path.Circle({
|
|
2858
|
+
center: new import_paper.default.Point(x, y),
|
|
2859
|
+
radius: Math.max(0, r)
|
|
2860
|
+
});
|
|
2861
|
+
},
|
|
2862
|
+
ellipse: (options) => {
|
|
2863
|
+
const { x, y, width, height } = options;
|
|
2864
|
+
return new import_paper.default.Path.Ellipse({
|
|
2865
|
+
center: new import_paper.default.Point(x, y),
|
|
2866
|
+
radius: [Math.max(0, width / 2), Math.max(0, height / 2)]
|
|
2867
|
+
});
|
|
2868
|
+
},
|
|
2869
|
+
heart: createHeartBaseShape
|
|
2870
|
+
};
|
|
2871
|
+
function createCustomBaseShape(options) {
|
|
2872
|
+
var _a;
|
|
2873
|
+
const {
|
|
2874
|
+
pathData,
|
|
2875
|
+
customSourceWidthPx,
|
|
2876
|
+
customSourceHeightPx,
|
|
2877
|
+
x,
|
|
2878
|
+
y,
|
|
2879
|
+
width,
|
|
2880
|
+
height
|
|
2881
|
+
} = options;
|
|
2882
|
+
if (typeof pathData !== "string" || pathData.trim().length === 0) {
|
|
2883
|
+
return null;
|
|
2933
2884
|
}
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2885
|
+
const center = new import_paper.default.Point(x, y);
|
|
2886
|
+
const hasMultipleSubPaths = ((_a = (pathData.match(/[Mm]/g) || []).length) != null ? _a : 0) > 1;
|
|
2887
|
+
const path = hasMultipleSubPaths ? new import_paper.default.CompoundPath(pathData) : (() => {
|
|
2888
|
+
const single = new import_paper.default.Path();
|
|
2889
|
+
single.pathData = pathData;
|
|
2890
|
+
return single;
|
|
2891
|
+
})();
|
|
2892
|
+
const sourceWidth = Number(customSourceWidthPx != null ? customSourceWidthPx : 0);
|
|
2893
|
+
const sourceHeight = Number(customSourceHeightPx != null ? customSourceHeightPx : 0);
|
|
2894
|
+
if (Number.isFinite(sourceWidth) && Number.isFinite(sourceHeight) && sourceWidth > 0 && sourceHeight > 0 && width > 0 && height > 0) {
|
|
2895
|
+
const targetLeft = x - width / 2;
|
|
2896
|
+
const targetTop = y - height / 2;
|
|
2897
|
+
path.scale(width / sourceWidth, height / sourceHeight, new import_paper.default.Point(0, 0));
|
|
2898
|
+
path.translate(new import_paper.default.Point(targetLeft, targetTop));
|
|
2899
|
+
return path;
|
|
2941
2900
|
}
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
{
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
{
|
|
2979
|
-
command: "setWorkingImage",
|
|
2980
|
-
id: "setWorkingImage",
|
|
2981
|
-
title: "Set Working Image",
|
|
2982
|
-
handler: (id, updates) => {
|
|
2983
|
-
tool.updateImageInWorking(id, updates);
|
|
2984
|
-
}
|
|
2985
|
-
},
|
|
2986
|
-
{
|
|
2987
|
-
command: "resetWorkingImages",
|
|
2988
|
-
id: "resetWorkingImages",
|
|
2989
|
-
title: "Reset Working Images",
|
|
2990
|
-
handler: () => {
|
|
2991
|
-
tool.workingItems = tool.cloneItems(tool.items);
|
|
2992
|
-
tool.hasWorkingChanges = false;
|
|
2993
|
-
tool.updateImages();
|
|
2994
|
-
tool.emitWorkingChange();
|
|
2995
|
-
}
|
|
2996
|
-
},
|
|
2997
|
-
{
|
|
2998
|
-
command: "completeImages",
|
|
2999
|
-
id: "completeImages",
|
|
3000
|
-
title: "Complete Images",
|
|
3001
|
-
handler: async () => {
|
|
3002
|
-
return await tool.commitWorkingImagesAsCropped();
|
|
3003
|
-
}
|
|
3004
|
-
},
|
|
3005
|
-
{
|
|
3006
|
-
command: "exportUserCroppedImage",
|
|
3007
|
-
id: "exportUserCroppedImage",
|
|
3008
|
-
title: "Export User Cropped Image",
|
|
3009
|
-
handler: async (options = {}) => {
|
|
3010
|
-
return await tool.exportUserCroppedImage(options);
|
|
3011
|
-
}
|
|
3012
|
-
},
|
|
3013
|
-
{
|
|
3014
|
-
command: "fitImageToArea",
|
|
3015
|
-
id: "fitImageToArea",
|
|
3016
|
-
title: "Fit Image to Area",
|
|
3017
|
-
handler: async (id, area) => {
|
|
3018
|
-
await tool.fitImageToArea(id, area);
|
|
3019
|
-
}
|
|
3020
|
-
},
|
|
3021
|
-
{
|
|
3022
|
-
command: "fitImageToDefaultArea",
|
|
3023
|
-
id: "fitImageToDefaultArea",
|
|
3024
|
-
title: "Fit Image to Default Area",
|
|
3025
|
-
handler: async (id) => {
|
|
3026
|
-
await tool.fitImageToDefaultArea(id);
|
|
3027
|
-
}
|
|
3028
|
-
},
|
|
3029
|
-
{
|
|
3030
|
-
command: "focusImage",
|
|
3031
|
-
id: "focusImage",
|
|
3032
|
-
title: "Focus Image",
|
|
3033
|
-
handler: (id, options = {}) => {
|
|
3034
|
-
return tool.setImageFocus(id, options);
|
|
2901
|
+
if (width > 0 && height > 0 && path.bounds.width > 0 && path.bounds.height > 0) {
|
|
2902
|
+
path.position = center;
|
|
2903
|
+
path.scale(width / path.bounds.width, height / path.bounds.height);
|
|
2904
|
+
return path;
|
|
2905
|
+
}
|
|
2906
|
+
path.position = center;
|
|
2907
|
+
return path;
|
|
2908
|
+
}
|
|
2909
|
+
function createBaseShape(options) {
|
|
2910
|
+
const { shape } = options;
|
|
2911
|
+
if (shape === "custom") {
|
|
2912
|
+
const customShape = createCustomBaseShape(options);
|
|
2913
|
+
if (customShape) return customShape;
|
|
2914
|
+
return BUILTIN_SHAPE_BUILDERS[DEFAULT_DIELINE_SHAPE](options);
|
|
2915
|
+
}
|
|
2916
|
+
return BUILTIN_SHAPE_BUILDERS[shape](options);
|
|
2917
|
+
}
|
|
2918
|
+
function resolveBridgeBasePath(shape, anchor) {
|
|
2919
|
+
if (shape instanceof import_paper.default.Path) {
|
|
2920
|
+
return shape;
|
|
2921
|
+
}
|
|
2922
|
+
if (shape instanceof import_paper.default.CompoundPath) {
|
|
2923
|
+
const children = (shape.children || []).filter(
|
|
2924
|
+
(child) => child instanceof import_paper.default.Path
|
|
2925
|
+
);
|
|
2926
|
+
if (!children.length) return null;
|
|
2927
|
+
let best = children[0];
|
|
2928
|
+
let bestDistance = Infinity;
|
|
2929
|
+
for (const child of children) {
|
|
2930
|
+
const location = child.getNearestLocation(anchor);
|
|
2931
|
+
const point = location == null ? void 0 : location.point;
|
|
2932
|
+
if (!point) continue;
|
|
2933
|
+
const distance = point.getDistance(anchor);
|
|
2934
|
+
if (distance < bestDistance) {
|
|
2935
|
+
bestDistance = distance;
|
|
2936
|
+
best = child;
|
|
3035
2937
|
}
|
|
3036
|
-
}
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
2938
|
+
}
|
|
2939
|
+
return best;
|
|
2940
|
+
}
|
|
2941
|
+
return null;
|
|
2942
|
+
}
|
|
2943
|
+
function createFeatureItem(feature, center) {
|
|
2944
|
+
let item;
|
|
2945
|
+
if (feature.shape === "rect") {
|
|
2946
|
+
const w = feature.width || 10;
|
|
2947
|
+
const h = feature.height || 10;
|
|
2948
|
+
const r = feature.radius || 0;
|
|
2949
|
+
item = new import_paper.default.Path.Rectangle({
|
|
2950
|
+
point: [center.x - w / 2, center.y - h / 2],
|
|
2951
|
+
size: [w, h],
|
|
2952
|
+
radius: r
|
|
2953
|
+
});
|
|
2954
|
+
} else {
|
|
2955
|
+
const r = feature.radius || 5;
|
|
2956
|
+
item = new import_paper.default.Path.Circle({
|
|
2957
|
+
center,
|
|
2958
|
+
radius: r
|
|
2959
|
+
});
|
|
2960
|
+
}
|
|
2961
|
+
if (feature.rotation) {
|
|
2962
|
+
item.rotate(feature.rotation, center);
|
|
2963
|
+
}
|
|
2964
|
+
return item;
|
|
2965
|
+
}
|
|
2966
|
+
function getPerimeterShape(options) {
|
|
2967
|
+
let mainShape = createBaseShape(options);
|
|
2968
|
+
const { features } = options;
|
|
2969
|
+
if (features && features.length > 0) {
|
|
2970
|
+
const edgeFeatures = features.filter(
|
|
2971
|
+
(f) => !f.renderBehavior || f.renderBehavior === "edge"
|
|
2972
|
+
);
|
|
2973
|
+
const adds = [];
|
|
2974
|
+
const subtracts = [];
|
|
2975
|
+
edgeFeatures.forEach((f) => {
|
|
2976
|
+
const pos = resolveFeaturePosition(f, options);
|
|
2977
|
+
const center = new import_paper.default.Point(pos.x, pos.y);
|
|
2978
|
+
const item = createFeatureItem(f, center);
|
|
2979
|
+
if (f.bridge && f.bridge.type === "vertical") {
|
|
2980
|
+
const itemBounds = item.bounds;
|
|
2981
|
+
const mainBounds = mainShape.bounds;
|
|
2982
|
+
const bridgeTop = mainBounds.top;
|
|
2983
|
+
const bridgeBottom = itemBounds.top;
|
|
2984
|
+
if (bridgeBottom > bridgeTop) {
|
|
2985
|
+
const overlap = 2;
|
|
2986
|
+
const rayPadding = 10;
|
|
2987
|
+
const eps = 0.1;
|
|
2988
|
+
const delta = getBridgeDelta(itemBounds, overlap);
|
|
2989
|
+
const toY = bridgeTop - rayPadding;
|
|
2990
|
+
const inset = Math.min(1, Math.max(0, itemBounds.width * 0.01));
|
|
2991
|
+
const xLeft = itemBounds.left + inset;
|
|
2992
|
+
const xRight = itemBounds.right - inset;
|
|
2993
|
+
const bridgeBasePath = resolveBridgeBasePath(mainShape, center);
|
|
2994
|
+
const canBridge = !!bridgeBasePath && xRight - xLeft > eps;
|
|
2995
|
+
if (canBridge && bridgeBasePath) {
|
|
2996
|
+
const leftHit = getExitHit({
|
|
2997
|
+
mainShape: bridgeBasePath,
|
|
2998
|
+
x: xLeft,
|
|
2999
|
+
bridgeBottom,
|
|
3000
|
+
toY,
|
|
3001
|
+
eps,
|
|
3002
|
+
delta,
|
|
3003
|
+
overlap,
|
|
3004
|
+
op: f.operation
|
|
3005
|
+
});
|
|
3006
|
+
const rightHit = getExitHit({
|
|
3007
|
+
mainShape: bridgeBasePath,
|
|
3008
|
+
x: xRight,
|
|
3009
|
+
bridgeBottom,
|
|
3010
|
+
toY,
|
|
3011
|
+
eps,
|
|
3012
|
+
delta,
|
|
3013
|
+
overlap,
|
|
3014
|
+
op: f.operation
|
|
3050
3015
|
});
|
|
3016
|
+
if (leftHit && rightHit) {
|
|
3017
|
+
const pathLength = bridgeBasePath.length;
|
|
3018
|
+
const leftOffset = leftHit.location.offset;
|
|
3019
|
+
const rightOffset = rightHit.location.offset;
|
|
3020
|
+
const distanceA = wrappedDistance(pathLength, leftOffset, rightOffset);
|
|
3021
|
+
const distanceB = wrappedDistance(pathLength, rightOffset, leftOffset);
|
|
3022
|
+
const countFor = (d) => Math.max(8, Math.min(80, Math.ceil(d / 6)));
|
|
3023
|
+
const offsetsA = sampleWrappedOffsets(
|
|
3024
|
+
pathLength,
|
|
3025
|
+
leftOffset,
|
|
3026
|
+
rightOffset,
|
|
3027
|
+
countFor(distanceA)
|
|
3028
|
+
);
|
|
3029
|
+
const offsetsB = sampleWrappedOffsets(
|
|
3030
|
+
pathLength,
|
|
3031
|
+
rightOffset,
|
|
3032
|
+
leftOffset,
|
|
3033
|
+
countFor(distanceB)
|
|
3034
|
+
);
|
|
3035
|
+
const pointsA = offsetsA.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
3036
|
+
const pointsB = offsetsB.map((o) => bridgeBasePath.getPointAt(o)).filter((p) => Boolean(p));
|
|
3037
|
+
if (pointsA.length >= 2 && pointsB.length >= 2) {
|
|
3038
|
+
let topBase = selectOuterChain({
|
|
3039
|
+
mainShape: bridgeBasePath,
|
|
3040
|
+
pointsA,
|
|
3041
|
+
pointsB,
|
|
3042
|
+
delta,
|
|
3043
|
+
overlap,
|
|
3044
|
+
op: f.operation
|
|
3045
|
+
});
|
|
3046
|
+
const dist2 = (a, b) => {
|
|
3047
|
+
const dx = a.x - b.x;
|
|
3048
|
+
const dy = a.y - b.y;
|
|
3049
|
+
return dx * dx + dy * dy;
|
|
3050
|
+
};
|
|
3051
|
+
if (dist2(topBase[0], leftHit.point) > dist2(topBase[0], rightHit.point)) {
|
|
3052
|
+
topBase = topBase.slice().reverse();
|
|
3053
|
+
}
|
|
3054
|
+
topBase = topBase.slice();
|
|
3055
|
+
topBase[0] = leftHit.point;
|
|
3056
|
+
topBase[topBase.length - 1] = rightHit.point;
|
|
3057
|
+
const capShiftY = f.operation === "subtract" ? -Math.max(overlap * 2, delta) : overlap;
|
|
3058
|
+
const topPoints = topBase.map(
|
|
3059
|
+
(p) => p.add(new import_paper.default.Point(0, capShiftY))
|
|
3060
|
+
);
|
|
3061
|
+
const bridgeBottomY = bridgeBottom + overlap * 2;
|
|
3062
|
+
const bridgePoly = new import_paper.default.Path({ insert: false });
|
|
3063
|
+
for (const p of topPoints) bridgePoly.add(p);
|
|
3064
|
+
bridgePoly.add(new import_paper.default.Point(xRight, bridgeBottomY));
|
|
3065
|
+
bridgePoly.add(new import_paper.default.Point(xLeft, bridgeBottomY));
|
|
3066
|
+
bridgePoly.closed = true;
|
|
3067
|
+
const unitedItem = item.unite(bridgePoly);
|
|
3068
|
+
item.remove();
|
|
3069
|
+
bridgePoly.remove();
|
|
3070
|
+
if (f.operation === "add") {
|
|
3071
|
+
adds.push(unitedItem);
|
|
3072
|
+
} else {
|
|
3073
|
+
subtracts.push(unitedItem);
|
|
3074
|
+
}
|
|
3075
|
+
return;
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
if (f.operation === "add") {
|
|
3080
|
+
adds.push(item);
|
|
3081
|
+
} else {
|
|
3082
|
+
subtracts.push(item);
|
|
3083
|
+
}
|
|
3084
|
+
} else {
|
|
3085
|
+
if (f.operation === "add") {
|
|
3086
|
+
adds.push(item);
|
|
3087
|
+
} else {
|
|
3088
|
+
subtracts.push(item);
|
|
3051
3089
|
}
|
|
3052
|
-
|
|
3090
|
+
}
|
|
3091
|
+
} else {
|
|
3092
|
+
if (f.operation === "add") {
|
|
3093
|
+
adds.push(item);
|
|
3094
|
+
} else {
|
|
3095
|
+
subtracts.push(item);
|
|
3053
3096
|
}
|
|
3054
3097
|
}
|
|
3055
|
-
}
|
|
3056
|
-
{
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
title: "Clear Images",
|
|
3068
|
-
handler: () => {
|
|
3069
|
-
tool.sourceSizeCache.clear();
|
|
3070
|
-
tool.setImageFocus(null, {
|
|
3071
|
-
syncCanvasSelection: true,
|
|
3072
|
-
skipRender: true
|
|
3073
|
-
});
|
|
3074
|
-
tool.updateConfig([]);
|
|
3098
|
+
});
|
|
3099
|
+
if (adds.length > 0) {
|
|
3100
|
+
for (const item of adds) {
|
|
3101
|
+
try {
|
|
3102
|
+
const temp = mainShape.unite(item);
|
|
3103
|
+
mainShape.remove();
|
|
3104
|
+
item.remove();
|
|
3105
|
+
mainShape = normalizePathItem(temp);
|
|
3106
|
+
} catch (e) {
|
|
3107
|
+
console.error("Geometry: Failed to unite feature", e);
|
|
3108
|
+
item.remove();
|
|
3109
|
+
}
|
|
3075
3110
|
}
|
|
3076
|
-
}
|
|
3077
|
-
{
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
tool.updateConfig(next);
|
|
3111
|
+
}
|
|
3112
|
+
if (subtracts.length > 0) {
|
|
3113
|
+
for (const item of subtracts) {
|
|
3114
|
+
try {
|
|
3115
|
+
const temp = mainShape.subtract(item);
|
|
3116
|
+
mainShape.remove();
|
|
3117
|
+
item.remove();
|
|
3118
|
+
mainShape = normalizePathItem(temp);
|
|
3119
|
+
} catch (e) {
|
|
3120
|
+
console.error("Geometry: Failed to subtract feature", e);
|
|
3121
|
+
item.remove();
|
|
3088
3122
|
}
|
|
3089
3123
|
}
|
|
3090
|
-
}
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
return mainShape;
|
|
3127
|
+
}
|
|
3128
|
+
function applySurfaceFeatures(shape, features, options) {
|
|
3129
|
+
const surfaceFeatures = features.filter(
|
|
3130
|
+
(f) => f.renderBehavior === "surface"
|
|
3131
|
+
);
|
|
3132
|
+
if (surfaceFeatures.length === 0) return shape;
|
|
3133
|
+
let result = shape;
|
|
3134
|
+
for (const f of surfaceFeatures) {
|
|
3135
|
+
const pos = resolveFeaturePosition(f, options);
|
|
3136
|
+
const center = new import_paper.default.Point(pos.x, pos.y);
|
|
3137
|
+
const item = createFeatureItem(f, center);
|
|
3138
|
+
try {
|
|
3139
|
+
if (f.operation === "add") {
|
|
3140
|
+
const temp = result.unite(item);
|
|
3141
|
+
result.remove();
|
|
3142
|
+
item.remove();
|
|
3143
|
+
result = normalizePathItem(temp);
|
|
3144
|
+
} else {
|
|
3145
|
+
const temp = result.subtract(item);
|
|
3146
|
+
result.remove();
|
|
3147
|
+
item.remove();
|
|
3148
|
+
result = normalizePathItem(temp);
|
|
3103
3149
|
}
|
|
3150
|
+
} catch (e) {
|
|
3151
|
+
console.error("Geometry: Failed to apply surface feature", e);
|
|
3152
|
+
item.remove();
|
|
3104
3153
|
}
|
|
3105
|
-
|
|
3154
|
+
}
|
|
3155
|
+
return result;
|
|
3156
|
+
}
|
|
3157
|
+
function generateDielinePath(options) {
|
|
3158
|
+
const paperWidth = options.canvasWidth || options.width * 2 || 2e3;
|
|
3159
|
+
const paperHeight = options.canvasHeight || options.height * 2 || 2e3;
|
|
3160
|
+
ensurePaper(paperWidth, paperHeight);
|
|
3161
|
+
import_paper.default.project.activeLayer.removeChildren();
|
|
3162
|
+
const perimeter = getPerimeterShape(options);
|
|
3163
|
+
const finalShape = applySurfaceFeatures(perimeter, options.features, options);
|
|
3164
|
+
const pathData = finalShape.pathData;
|
|
3165
|
+
finalShape.remove();
|
|
3166
|
+
return pathData;
|
|
3167
|
+
}
|
|
3168
|
+
function generateBleedZonePath(originalOptions, offsetOptions, offset) {
|
|
3169
|
+
const paperWidth = originalOptions.canvasWidth || originalOptions.width * 2 || 2e3;
|
|
3170
|
+
const paperHeight = originalOptions.canvasHeight || originalOptions.height * 2 || 2e3;
|
|
3171
|
+
ensurePaper(paperWidth, paperHeight);
|
|
3172
|
+
import_paper.default.project.activeLayer.removeChildren();
|
|
3173
|
+
const pOriginal = getPerimeterShape(originalOptions);
|
|
3174
|
+
const shapeOriginal = applySurfaceFeatures(
|
|
3175
|
+
pOriginal,
|
|
3176
|
+
originalOptions.features,
|
|
3177
|
+
originalOptions
|
|
3178
|
+
);
|
|
3179
|
+
const pOffset = getPerimeterShape(offsetOptions);
|
|
3180
|
+
const shapeOffset = applySurfaceFeatures(
|
|
3181
|
+
pOffset,
|
|
3182
|
+
offsetOptions.features,
|
|
3183
|
+
offsetOptions
|
|
3184
|
+
);
|
|
3185
|
+
let bleedZone;
|
|
3186
|
+
if (offset > 0) {
|
|
3187
|
+
bleedZone = shapeOffset.subtract(shapeOriginal);
|
|
3188
|
+
} else {
|
|
3189
|
+
bleedZone = shapeOriginal.subtract(shapeOffset);
|
|
3190
|
+
}
|
|
3191
|
+
const pathData = bleedZone.pathData;
|
|
3192
|
+
shapeOriginal.remove();
|
|
3193
|
+
shapeOffset.remove();
|
|
3194
|
+
bleedZone.remove();
|
|
3195
|
+
return pathData;
|
|
3196
|
+
}
|
|
3197
|
+
function getLowestPointOnDieline(options) {
|
|
3198
|
+
ensurePaper(options.width * 2, options.height * 2);
|
|
3199
|
+
import_paper.default.project.activeLayer.removeChildren();
|
|
3200
|
+
const shape = createBaseShape(options);
|
|
3201
|
+
const bounds = shape.bounds;
|
|
3202
|
+
const result = {
|
|
3203
|
+
x: bounds.center.x,
|
|
3204
|
+
y: bounds.bottom
|
|
3205
|
+
};
|
|
3206
|
+
shape.remove();
|
|
3207
|
+
return result;
|
|
3208
|
+
}
|
|
3209
|
+
function getNearestPointOnDieline(point, options) {
|
|
3210
|
+
ensurePaper(options.width * 2, options.height * 2);
|
|
3211
|
+
import_paper.default.project.activeLayer.removeChildren();
|
|
3212
|
+
const shape = createBaseShape(options);
|
|
3213
|
+
const p = new import_paper.default.Point(point.x, point.y);
|
|
3214
|
+
const location = shape.getNearestLocation(p);
|
|
3215
|
+
const result = {
|
|
3216
|
+
x: location.point.x,
|
|
3217
|
+
y: location.point.y,
|
|
3218
|
+
normal: location.normal ? { x: location.normal.x, y: location.normal.y } : void 0
|
|
3219
|
+
};
|
|
3220
|
+
shape.remove();
|
|
3221
|
+
return result;
|
|
3106
3222
|
}
|
|
3107
3223
|
|
|
3108
|
-
// src/extensions/image/
|
|
3109
|
-
|
|
3224
|
+
// src/extensions/image/sessionOverlay.ts
|
|
3225
|
+
var EPSILON = 1e-4;
|
|
3226
|
+
var SHAPE_OUTLINE_COLOR = "rgba(255, 0, 0, 0.9)";
|
|
3227
|
+
var DEFAULT_HATCH_FILL = "rgba(255, 0, 0, 0.22)";
|
|
3228
|
+
function buildRectPath(width, height) {
|
|
3229
|
+
return `M 0 0 L ${width} 0 L ${width} ${height} L 0 ${height} Z`;
|
|
3230
|
+
}
|
|
3231
|
+
function buildViewportMaskPath(viewport, cutRect) {
|
|
3232
|
+
const cutLeft = cutRect.left - viewport.left;
|
|
3233
|
+
const cutTop = cutRect.top - viewport.top;
|
|
3110
3234
|
return [
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
}
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3235
|
+
buildRectPath(viewport.width, viewport.height),
|
|
3236
|
+
`M ${cutLeft} ${cutTop} L ${cutLeft + cutRect.width} ${cutTop} L ${cutLeft + cutRect.width} ${cutTop + cutRect.height} L ${cutLeft} ${cutTop + cutRect.height} Z`
|
|
3237
|
+
].join(" ");
|
|
3238
|
+
}
|
|
3239
|
+
function resolveCutShapeRadiusPx(geometry, cutRect) {
|
|
3240
|
+
const visualRadius = Number.isFinite(geometry.radius) ? Math.max(0, geometry.radius) : 0;
|
|
3241
|
+
const visualOffset = Number.isFinite(geometry.offset) ? geometry.offset : 0;
|
|
3242
|
+
const rawCutRadius = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
3243
|
+
const maxRadius = Math.max(0, Math.min(cutRect.width, cutRect.height) / 2);
|
|
3244
|
+
return Math.max(0, Math.min(maxRadius, rawCutRadius));
|
|
3245
|
+
}
|
|
3246
|
+
function buildBuiltinShapeOverlayPaths(cutRect, geometry) {
|
|
3247
|
+
if (!geometry || geometry.shape === "custom") {
|
|
3248
|
+
return null;
|
|
3249
|
+
}
|
|
3250
|
+
const radius = resolveCutShapeRadiusPx(geometry, cutRect);
|
|
3251
|
+
if (geometry.shape === "rect" && radius <= EPSILON) {
|
|
3252
|
+
return null;
|
|
3253
|
+
}
|
|
3254
|
+
const shapePathData = generateDielinePath({
|
|
3255
|
+
shape: geometry.shape,
|
|
3256
|
+
shapeStyle: geometry.shapeStyle,
|
|
3257
|
+
width: Math.max(1, cutRect.width),
|
|
3258
|
+
height: Math.max(1, cutRect.height),
|
|
3259
|
+
radius,
|
|
3260
|
+
x: cutRect.width / 2,
|
|
3261
|
+
y: cutRect.height / 2,
|
|
3262
|
+
features: [],
|
|
3263
|
+
canvasWidth: Math.max(1, cutRect.width),
|
|
3264
|
+
canvasHeight: Math.max(1, cutRect.height)
|
|
3265
|
+
});
|
|
3266
|
+
if (!shapePathData) {
|
|
3267
|
+
return null;
|
|
3268
|
+
}
|
|
3269
|
+
return {
|
|
3270
|
+
shapePathData,
|
|
3271
|
+
hatchPathData: `${buildRectPath(cutRect.width, cutRect.height)} ${shapePathData}`
|
|
3272
|
+
};
|
|
3273
|
+
}
|
|
3274
|
+
function buildImageSessionOverlaySpecs(args) {
|
|
3275
|
+
const { viewport, layout, geometry, visual, hatchPattern } = args;
|
|
3276
|
+
const cutRect = layout.cutRect;
|
|
3277
|
+
const specs = [];
|
|
3278
|
+
specs.push({
|
|
3279
|
+
id: "image.cropMask.rect",
|
|
3280
|
+
type: "path",
|
|
3281
|
+
space: "screen",
|
|
3282
|
+
data: { id: "image.cropMask.rect", zIndex: 1 },
|
|
3283
|
+
props: {
|
|
3284
|
+
pathData: buildViewportMaskPath(viewport, cutRect),
|
|
3285
|
+
left: viewport.left,
|
|
3286
|
+
top: viewport.top,
|
|
3287
|
+
originX: "left",
|
|
3288
|
+
originY: "top",
|
|
3289
|
+
fill: visual.outerBackground,
|
|
3290
|
+
stroke: null,
|
|
3291
|
+
fillRule: "evenodd",
|
|
3292
|
+
selectable: false,
|
|
3293
|
+
evented: false,
|
|
3294
|
+
excludeFromExport: true,
|
|
3295
|
+
objectCaching: false
|
|
3296
|
+
}
|
|
3297
|
+
});
|
|
3298
|
+
const shapeOverlay = buildBuiltinShapeOverlayPaths(cutRect, geometry);
|
|
3299
|
+
if (shapeOverlay) {
|
|
3300
|
+
specs.push({
|
|
3301
|
+
id: "image.cropShapeHatch",
|
|
3302
|
+
type: "path",
|
|
3303
|
+
space: "screen",
|
|
3304
|
+
data: { id: "image.cropShapeHatch", zIndex: 5 },
|
|
3305
|
+
props: {
|
|
3306
|
+
pathData: shapeOverlay.hatchPathData,
|
|
3307
|
+
left: cutRect.left,
|
|
3308
|
+
top: cutRect.top,
|
|
3309
|
+
originX: "left",
|
|
3310
|
+
originY: "top",
|
|
3311
|
+
fill: hatchPattern || DEFAULT_HATCH_FILL,
|
|
3312
|
+
opacity: hatchPattern ? 1 : 0.8,
|
|
3313
|
+
stroke: null,
|
|
3314
|
+
fillRule: "evenodd",
|
|
3315
|
+
selectable: false,
|
|
3316
|
+
evented: false,
|
|
3317
|
+
excludeFromExport: true,
|
|
3318
|
+
objectCaching: false
|
|
3319
|
+
}
|
|
3320
|
+
});
|
|
3321
|
+
specs.push({
|
|
3322
|
+
id: "image.cropShapeOutline",
|
|
3323
|
+
type: "path",
|
|
3324
|
+
space: "screen",
|
|
3325
|
+
data: { id: "image.cropShapeOutline", zIndex: 6 },
|
|
3326
|
+
props: {
|
|
3327
|
+
pathData: shapeOverlay.shapePathData,
|
|
3328
|
+
left: cutRect.left,
|
|
3329
|
+
top: cutRect.top,
|
|
3330
|
+
originX: "left",
|
|
3331
|
+
originY: "top",
|
|
3332
|
+
fill: "transparent",
|
|
3333
|
+
stroke: SHAPE_OUTLINE_COLOR,
|
|
3334
|
+
strokeWidth: 1,
|
|
3335
|
+
selectable: false,
|
|
3336
|
+
evented: false,
|
|
3337
|
+
excludeFromExport: true,
|
|
3338
|
+
objectCaching: false
|
|
3339
|
+
}
|
|
3340
|
+
});
|
|
3341
|
+
}
|
|
3342
|
+
specs.push({
|
|
3343
|
+
id: "image.cropFrame",
|
|
3344
|
+
type: "rect",
|
|
3345
|
+
space: "screen",
|
|
3346
|
+
data: { id: "image.cropFrame", zIndex: 7 },
|
|
3347
|
+
props: {
|
|
3348
|
+
left: cutRect.left,
|
|
3349
|
+
top: cutRect.top,
|
|
3350
|
+
width: cutRect.width,
|
|
3351
|
+
height: cutRect.height,
|
|
3352
|
+
originX: "left",
|
|
3353
|
+
originY: "top",
|
|
3354
|
+
fill: visual.innerBackground,
|
|
3355
|
+
stroke: visual.strokeStyle === "hidden" ? "rgba(0,0,0,0)" : visual.strokeColor,
|
|
3356
|
+
strokeWidth: visual.strokeStyle === "hidden" ? 0 : visual.strokeWidth,
|
|
3357
|
+
strokeDashArray: visual.strokeStyle === "dashed" ? [visual.dashLength, visual.dashLength] : void 0,
|
|
3358
|
+
selectable: false,
|
|
3359
|
+
evented: false,
|
|
3360
|
+
excludeFromExport: true
|
|
3232
3361
|
}
|
|
3233
|
-
|
|
3362
|
+
});
|
|
3363
|
+
return specs;
|
|
3234
3364
|
}
|
|
3235
3365
|
|
|
3236
3366
|
// src/extensions/image/ImageTool.ts
|
|
@@ -3692,11 +3822,21 @@ var ImageTool = class {
|
|
|
3692
3822
|
(_a = this.canvasService) == null ? void 0 : _a.requestRenderAll();
|
|
3693
3823
|
}
|
|
3694
3824
|
}
|
|
3825
|
+
clearSnapGuideContext() {
|
|
3826
|
+
var _a;
|
|
3827
|
+
const topContext = (_a = this.canvasService) == null ? void 0 : _a.canvas.contextTop;
|
|
3828
|
+
if (!this.canvasService || !topContext) return;
|
|
3829
|
+
this.canvasService.canvas.clearContext(topContext);
|
|
3830
|
+
}
|
|
3695
3831
|
clearSnapPreview() {
|
|
3696
3832
|
var _a;
|
|
3833
|
+
const shouldClearCanvas = this.hasRenderedSnapGuides || !!this.activeSnapX || !!this.activeSnapY;
|
|
3697
3834
|
this.activeSnapX = null;
|
|
3698
3835
|
this.activeSnapY = null;
|
|
3699
3836
|
this.hasRenderedSnapGuides = false;
|
|
3837
|
+
if (shouldClearCanvas) {
|
|
3838
|
+
this.clearSnapGuideContext();
|
|
3839
|
+
}
|
|
3700
3840
|
(_a = this.canvasService) == null ? void 0 : _a.requestRenderAll();
|
|
3701
3841
|
}
|
|
3702
3842
|
endMoveSnapInteraction() {
|
|
@@ -4100,9 +4240,6 @@ var ImageTool = class {
|
|
|
4100
4240
|
}
|
|
4101
4241
|
return this.canvasService.toScreenRect(frame || this.getFrameRect());
|
|
4102
4242
|
}
|
|
4103
|
-
toLayoutSceneRect(rect) {
|
|
4104
|
-
return toLayoutSceneRect(rect);
|
|
4105
|
-
}
|
|
4106
4243
|
async resolveDefaultFitArea() {
|
|
4107
4244
|
if (!this.canvasService) return null;
|
|
4108
4245
|
const frame = this.getFrameRect();
|
|
@@ -4229,74 +4366,37 @@ var ImageTool = class {
|
|
|
4229
4366
|
outerBackground: this.getConfig("image.frame.outerBackground", "#f5f5f5") || "#f5f5f5"
|
|
4230
4367
|
};
|
|
4231
4368
|
}
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
if (!isDielineShape(shape)) {
|
|
4369
|
+
resolveSessionOverlayState() {
|
|
4370
|
+
if (!this.canvasService || !this.context) {
|
|
4235
4371
|
return null;
|
|
4236
4372
|
}
|
|
4237
|
-
const radiusRaw = Number(raw == null ? void 0 : raw.radius);
|
|
4238
|
-
const offsetRaw = Number(raw == null ? void 0 : raw.offset);
|
|
4239
|
-
const unit = typeof (raw == null ? void 0 : raw.unit) === "string" ? raw.unit : "px";
|
|
4240
|
-
const radius = unit === "scene" || !this.canvasService ? radiusRaw : this.canvasService.toSceneLength(radiusRaw);
|
|
4241
|
-
const offset = unit === "scene" || !this.canvasService ? offsetRaw : this.canvasService.toSceneLength(offsetRaw);
|
|
4242
|
-
return {
|
|
4243
|
-
shape,
|
|
4244
|
-
shapeStyle: normalizeShapeStyle(raw == null ? void 0 : raw.shapeStyle),
|
|
4245
|
-
radius: Number.isFinite(radius) ? radius : 0,
|
|
4246
|
-
offset: Number.isFinite(offset) ? offset : 0
|
|
4247
|
-
};
|
|
4248
|
-
}
|
|
4249
|
-
async resolveSceneGeometryForOverlay() {
|
|
4250
|
-
if (!this.context) return null;
|
|
4251
|
-
const commandService = this.context.services.get("CommandService");
|
|
4252
|
-
if (commandService) {
|
|
4253
|
-
try {
|
|
4254
|
-
const raw = await Promise.resolve(
|
|
4255
|
-
commandService.executeCommand("getSceneGeometry")
|
|
4256
|
-
);
|
|
4257
|
-
const geometry2 = this.toSceneGeometryLike(raw);
|
|
4258
|
-
if (geometry2) {
|
|
4259
|
-
this.debug("overlay:sceneGeometry:command", geometry2);
|
|
4260
|
-
return geometry2;
|
|
4261
|
-
}
|
|
4262
|
-
this.debug("overlay:sceneGeometry:command:invalid", { raw });
|
|
4263
|
-
} catch (error) {
|
|
4264
|
-
this.debug("overlay:sceneGeometry:command:error", {
|
|
4265
|
-
error: error instanceof Error ? error.message : String(error)
|
|
4266
|
-
});
|
|
4267
|
-
}
|
|
4268
|
-
}
|
|
4269
|
-
if (!this.canvasService) return null;
|
|
4270
4373
|
const configService = this.context.services.get(
|
|
4271
4374
|
"ConfigurationService"
|
|
4272
4375
|
);
|
|
4273
|
-
if (!configService)
|
|
4274
|
-
const sizeState = readSizeState(configService);
|
|
4275
|
-
const layout = computeSceneLayout(this.canvasService, sizeState);
|
|
4276
|
-
if (!layout) {
|
|
4277
|
-
this.debug("overlay:sceneGeometry:fallback:missing-layout");
|
|
4376
|
+
if (!configService) {
|
|
4278
4377
|
return null;
|
|
4279
4378
|
}
|
|
4280
|
-
const
|
|
4281
|
-
|
|
4379
|
+
const layout = computeSceneLayout(
|
|
4380
|
+
this.canvasService,
|
|
4381
|
+
readSizeState(configService)
|
|
4282
4382
|
);
|
|
4283
|
-
if (
|
|
4284
|
-
this.debug("overlay:
|
|
4383
|
+
if (!layout) {
|
|
4384
|
+
this.debug("overlay:layout:missing");
|
|
4385
|
+
return null;
|
|
4285
4386
|
}
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4387
|
+
const geometry = buildSceneGeometry(configService, layout);
|
|
4388
|
+
this.debug("overlay:state:resolved", {
|
|
4389
|
+
cutRect: layout.cutRect,
|
|
4390
|
+
shape: geometry.shape,
|
|
4391
|
+
shapeStyle: geometry.shapeStyle,
|
|
4392
|
+
radius: geometry.radius,
|
|
4393
|
+
offset: geometry.offset
|
|
4394
|
+
});
|
|
4395
|
+
return { layout, geometry };
|
|
4294
4396
|
}
|
|
4295
4397
|
getCropShapeHatchPattern(color = "rgba(255, 0, 0, 0.6)") {
|
|
4296
|
-
var _a;
|
|
4297
4398
|
if (typeof document === "undefined") return void 0;
|
|
4298
|
-
const
|
|
4299
|
-
const cacheKey = `${color}::${sceneScale.toFixed(6)}`;
|
|
4399
|
+
const cacheKey = color;
|
|
4300
4400
|
if (this.cropShapeHatchPattern && this.cropShapeHatchPatternColor === color && this.cropShapeHatchPatternKey === cacheKey) {
|
|
4301
4401
|
return this.cropShapeHatchPattern;
|
|
4302
4402
|
}
|
|
@@ -4326,138 +4426,11 @@ var ImageTool = class {
|
|
|
4326
4426
|
// @ts-ignore: Fabric Pattern accepts canvas source here.
|
|
4327
4427
|
repetition: "repeat"
|
|
4328
4428
|
});
|
|
4329
|
-
pattern.patternTransform = [
|
|
4330
|
-
1 / sceneScale,
|
|
4331
|
-
0,
|
|
4332
|
-
0,
|
|
4333
|
-
1 / sceneScale,
|
|
4334
|
-
0,
|
|
4335
|
-
0
|
|
4336
|
-
];
|
|
4337
4429
|
this.cropShapeHatchPattern = pattern;
|
|
4338
4430
|
this.cropShapeHatchPatternColor = color;
|
|
4339
4431
|
this.cropShapeHatchPatternKey = cacheKey;
|
|
4340
4432
|
return pattern;
|
|
4341
4433
|
}
|
|
4342
|
-
buildCropShapeOverlaySpecs(frame, sceneGeometry) {
|
|
4343
|
-
var _a, _b;
|
|
4344
|
-
if (!sceneGeometry) {
|
|
4345
|
-
this.debug("overlay:shape:skip", { reason: "scene-geometry-missing" });
|
|
4346
|
-
return [];
|
|
4347
|
-
}
|
|
4348
|
-
if (sceneGeometry.shape === "custom") {
|
|
4349
|
-
this.debug("overlay:shape:skip", { reason: "shape-custom" });
|
|
4350
|
-
return [];
|
|
4351
|
-
}
|
|
4352
|
-
const shape = sceneGeometry.shape;
|
|
4353
|
-
const shapeStyle = sceneGeometry.shapeStyle;
|
|
4354
|
-
const inset = 0;
|
|
4355
|
-
const shapeWidth = Math.max(1, frame.width);
|
|
4356
|
-
const shapeHeight = Math.max(1, frame.height);
|
|
4357
|
-
const radius = this.resolveCutShapeRadius(sceneGeometry, frame);
|
|
4358
|
-
this.debug("overlay:shape:geometry", {
|
|
4359
|
-
shape,
|
|
4360
|
-
frameWidth: frame.width,
|
|
4361
|
-
frameHeight: frame.height,
|
|
4362
|
-
offset: sceneGeometry.offset,
|
|
4363
|
-
shapeStyle,
|
|
4364
|
-
inset,
|
|
4365
|
-
shapeWidth,
|
|
4366
|
-
shapeHeight,
|
|
4367
|
-
baseRadius: sceneGeometry.radius,
|
|
4368
|
-
radius
|
|
4369
|
-
});
|
|
4370
|
-
const isSameAsFrame = Math.abs(shapeWidth - frame.width) <= 1e-4 && Math.abs(shapeHeight - frame.height) <= 1e-4;
|
|
4371
|
-
if (shape === "rect" && radius <= 1e-4 && isSameAsFrame) {
|
|
4372
|
-
this.debug("overlay:shape:skip", {
|
|
4373
|
-
reason: "shape-rect-no-radius"
|
|
4374
|
-
});
|
|
4375
|
-
return [];
|
|
4376
|
-
}
|
|
4377
|
-
const baseOptions = {
|
|
4378
|
-
shape,
|
|
4379
|
-
width: shapeWidth,
|
|
4380
|
-
height: shapeHeight,
|
|
4381
|
-
radius,
|
|
4382
|
-
x: frame.width / 2,
|
|
4383
|
-
y: frame.height / 2,
|
|
4384
|
-
features: [],
|
|
4385
|
-
shapeStyle,
|
|
4386
|
-
canvasWidth: frame.width,
|
|
4387
|
-
canvasHeight: frame.height
|
|
4388
|
-
};
|
|
4389
|
-
try {
|
|
4390
|
-
const shapePathData = generateDielinePath(baseOptions);
|
|
4391
|
-
const outerRectPathData = `M 0 0 L ${frame.width} 0 L ${frame.width} ${frame.height} L 0 ${frame.height} Z`;
|
|
4392
|
-
const hatchPathData = `${outerRectPathData} ${shapePathData}`;
|
|
4393
|
-
if (!shapePathData || !hatchPathData) {
|
|
4394
|
-
this.debug("overlay:shape:skip", {
|
|
4395
|
-
reason: "path-generation-empty",
|
|
4396
|
-
shape,
|
|
4397
|
-
radius
|
|
4398
|
-
});
|
|
4399
|
-
return [];
|
|
4400
|
-
}
|
|
4401
|
-
const patternFill = this.getCropShapeHatchPattern();
|
|
4402
|
-
const hatchFill = patternFill || "rgba(255, 0, 0, 0.22)";
|
|
4403
|
-
const shapeBounds = getPathBounds(shapePathData);
|
|
4404
|
-
const hatchBounds = getPathBounds(hatchPathData);
|
|
4405
|
-
const frameRect = this.toLayoutSceneRect(frame);
|
|
4406
|
-
const hatchPathLength = hatchPathData.length;
|
|
4407
|
-
const shapePathLength = shapePathData.length;
|
|
4408
|
-
const specs = [
|
|
4409
|
-
{
|
|
4410
|
-
id: "image.cropShapeHatch",
|
|
4411
|
-
type: "path",
|
|
4412
|
-
data: { id: "image.cropShapeHatch", zIndex: 5 },
|
|
4413
|
-
layout: {
|
|
4414
|
-
reference: "custom",
|
|
4415
|
-
referenceRect: frameRect,
|
|
4416
|
-
alignX: "start",
|
|
4417
|
-
alignY: "start",
|
|
4418
|
-
offsetX: hatchBounds.x,
|
|
4419
|
-
offsetY: hatchBounds.y
|
|
4420
|
-
},
|
|
4421
|
-
props: {
|
|
4422
|
-
pathData: hatchPathData,
|
|
4423
|
-
originX: "left",
|
|
4424
|
-
originY: "top",
|
|
4425
|
-
fill: hatchFill,
|
|
4426
|
-
opacity: patternFill ? 1 : 0.8,
|
|
4427
|
-
stroke: "rgba(255, 0, 0, 0.9)",
|
|
4428
|
-
strokeWidth: (_b = (_a = this.canvasService) == null ? void 0 : _a.toSceneLength(1)) != null ? _b : 1,
|
|
4429
|
-
fillRule: "evenodd",
|
|
4430
|
-
selectable: false,
|
|
4431
|
-
evented: false,
|
|
4432
|
-
excludeFromExport: true,
|
|
4433
|
-
objectCaching: false
|
|
4434
|
-
}
|
|
4435
|
-
}
|
|
4436
|
-
];
|
|
4437
|
-
this.debug("overlay:shape:built", {
|
|
4438
|
-
shape,
|
|
4439
|
-
radius,
|
|
4440
|
-
inset,
|
|
4441
|
-
shapeWidth,
|
|
4442
|
-
shapeHeight,
|
|
4443
|
-
fillRule: "evenodd",
|
|
4444
|
-
shapePathLength,
|
|
4445
|
-
hatchPathLength,
|
|
4446
|
-
shapeBounds,
|
|
4447
|
-
hatchBounds,
|
|
4448
|
-
hatchFillType: hatchFill && typeof hatchFill === "object" ? "pattern" : "color",
|
|
4449
|
-
ids: specs.map((spec) => spec.id)
|
|
4450
|
-
});
|
|
4451
|
-
return specs;
|
|
4452
|
-
} catch (error) {
|
|
4453
|
-
this.debug("overlay:shape:error", {
|
|
4454
|
-
shape,
|
|
4455
|
-
radius,
|
|
4456
|
-
error: error instanceof Error ? error.message : String(error)
|
|
4457
|
-
});
|
|
4458
|
-
return [];
|
|
4459
|
-
}
|
|
4460
|
-
}
|
|
4461
4434
|
resolveRenderImageState(item) {
|
|
4462
4435
|
var _a;
|
|
4463
4436
|
const active = this.isToolActive;
|
|
@@ -4503,208 +4476,71 @@ var ImageTool = class {
|
|
|
4503
4476
|
selectable: this.isImageEditingVisible(),
|
|
4504
4477
|
evented: this.isImageEditingVisible(),
|
|
4505
4478
|
hasControls: this.isImageEditingVisible(),
|
|
4506
|
-
hasBorders: this.isImageEditingVisible(),
|
|
4507
|
-
opacity: render.opacity
|
|
4508
|
-
};
|
|
4509
|
-
}
|
|
4510
|
-
toSceneObjectScale(value) {
|
|
4511
|
-
if (!this.canvasService) return value;
|
|
4512
|
-
return value / this.canvasService.getSceneScale();
|
|
4513
|
-
}
|
|
4514
|
-
getCurrentSrc(obj) {
|
|
4515
|
-
var _a;
|
|
4516
|
-
if (!obj) return void 0;
|
|
4517
|
-
if (typeof obj.getSrc === "function") return obj.getSrc();
|
|
4518
|
-
return (_a = obj == null ? void 0 : obj._originalElement) == null ? void 0 : _a.src;
|
|
4519
|
-
}
|
|
4520
|
-
async buildImageSpecs(items, frame) {
|
|
4521
|
-
const specs = [];
|
|
4522
|
-
for (const item of items) {
|
|
4523
|
-
const render = this.resolveRenderImageState(item);
|
|
4524
|
-
if (!render.src) continue;
|
|
4525
|
-
const ensured = await this.ensureSourceSize(render.src);
|
|
4526
|
-
const sourceSize = ensured || this.getSourceSize(render.src);
|
|
4527
|
-
const props = this.computeCanvasProps(render, sourceSize, frame);
|
|
4528
|
-
specs.push({
|
|
4529
|
-
id: item.id,
|
|
4530
|
-
type: "image",
|
|
4531
|
-
src: render.src,
|
|
4532
|
-
data: {
|
|
4533
|
-
id: item.id,
|
|
4534
|
-
layerId: IMAGE_OBJECT_LAYER_ID,
|
|
4535
|
-
type: "image-item"
|
|
4536
|
-
},
|
|
4537
|
-
props
|
|
4538
|
-
});
|
|
4539
|
-
}
|
|
4540
|
-
return specs;
|
|
4541
|
-
}
|
|
4542
|
-
buildOverlaySpecs(frame, sceneGeometry) {
|
|
4543
|
-
const visible = this.isImageEditingVisible();
|
|
4544
|
-
if (!visible || frame.width <= 0 || frame.height <= 0 || !this.canvasService) {
|
|
4545
|
-
this.debug("overlay:hidden", {
|
|
4546
|
-
visible,
|
|
4547
|
-
frame,
|
|
4548
|
-
isToolActive: this.isToolActive,
|
|
4549
|
-
isImageSelectionActive: this.isImageSelectionActive,
|
|
4550
|
-
focusedImageId: this.focusedImageId
|
|
4551
|
-
});
|
|
4552
|
-
return [];
|
|
4553
|
-
}
|
|
4554
|
-
const viewport = this.canvasService.getSceneViewportRect();
|
|
4555
|
-
const canvasW = viewport.width || 0;
|
|
4556
|
-
const canvasH = viewport.height || 0;
|
|
4557
|
-
const canvasLeft = viewport.left || 0;
|
|
4558
|
-
const canvasTop = viewport.top || 0;
|
|
4559
|
-
const visual = this.getFrameVisualConfig();
|
|
4560
|
-
const strokeWidthScene = this.canvasService.toSceneLength(
|
|
4561
|
-
visual.strokeWidth
|
|
4562
|
-
);
|
|
4563
|
-
const dashLengthScene = this.canvasService.toSceneLength(visual.dashLength);
|
|
4564
|
-
const frameLeft = Math.max(
|
|
4565
|
-
canvasLeft,
|
|
4566
|
-
Math.min(canvasLeft + canvasW, frame.left)
|
|
4567
|
-
);
|
|
4568
|
-
const frameTop = Math.max(
|
|
4569
|
-
canvasTop,
|
|
4570
|
-
Math.min(canvasTop + canvasH, frame.top)
|
|
4571
|
-
);
|
|
4572
|
-
const frameRight = Math.max(
|
|
4573
|
-
frameLeft,
|
|
4574
|
-
Math.min(canvasLeft + canvasW, frame.left + frame.width)
|
|
4575
|
-
);
|
|
4576
|
-
const frameBottom = Math.max(
|
|
4577
|
-
frameTop,
|
|
4578
|
-
Math.min(canvasTop + canvasH, frame.top + frame.height)
|
|
4579
|
-
);
|
|
4580
|
-
const visibleFrameH = Math.max(0, frameBottom - frameTop);
|
|
4581
|
-
const topH = Math.max(0, frameTop - canvasTop);
|
|
4582
|
-
const bottomH = Math.max(0, canvasTop + canvasH - frameBottom);
|
|
4583
|
-
const leftW = Math.max(0, frameLeft - canvasLeft);
|
|
4584
|
-
const rightW = Math.max(0, canvasLeft + canvasW - frameRight);
|
|
4585
|
-
const viewportRect = this.toLayoutSceneRect({
|
|
4586
|
-
left: canvasLeft,
|
|
4587
|
-
top: canvasTop,
|
|
4588
|
-
width: canvasW,
|
|
4589
|
-
height: canvasH
|
|
4590
|
-
});
|
|
4591
|
-
const visibleFrameBandRect = this.toLayoutSceneRect({
|
|
4592
|
-
left: canvasLeft,
|
|
4593
|
-
top: frameTop,
|
|
4594
|
-
width: canvasW,
|
|
4595
|
-
height: visibleFrameH
|
|
4596
|
-
});
|
|
4597
|
-
const frameRect = this.toLayoutSceneRect(frame);
|
|
4598
|
-
const shapeOverlay = this.buildCropShapeOverlaySpecs(frame, sceneGeometry);
|
|
4599
|
-
const mask = [
|
|
4600
|
-
{
|
|
4601
|
-
id: "image.cropMask.top",
|
|
4602
|
-
type: "rect",
|
|
4603
|
-
data: { id: "image.cropMask.top", zIndex: 1 },
|
|
4604
|
-
layout: {
|
|
4605
|
-
reference: "custom",
|
|
4606
|
-
referenceRect: viewportRect,
|
|
4607
|
-
alignX: "start",
|
|
4608
|
-
alignY: "start",
|
|
4609
|
-
width: "100%",
|
|
4610
|
-
height: topH
|
|
4611
|
-
},
|
|
4612
|
-
props: {
|
|
4613
|
-
originX: "left",
|
|
4614
|
-
originY: "top",
|
|
4615
|
-
fill: visual.outerBackground,
|
|
4616
|
-
selectable: false,
|
|
4617
|
-
evented: false
|
|
4618
|
-
}
|
|
4619
|
-
},
|
|
4620
|
-
{
|
|
4621
|
-
id: "image.cropMask.bottom",
|
|
4622
|
-
type: "rect",
|
|
4623
|
-
data: { id: "image.cropMask.bottom", zIndex: 2 },
|
|
4624
|
-
layout: {
|
|
4625
|
-
reference: "custom",
|
|
4626
|
-
referenceRect: viewportRect,
|
|
4627
|
-
alignX: "start",
|
|
4628
|
-
alignY: "end",
|
|
4629
|
-
width: "100%",
|
|
4630
|
-
height: bottomH
|
|
4631
|
-
},
|
|
4632
|
-
props: {
|
|
4633
|
-
originX: "left",
|
|
4634
|
-
originY: "top",
|
|
4635
|
-
fill: visual.outerBackground,
|
|
4636
|
-
selectable: false,
|
|
4637
|
-
evented: false
|
|
4638
|
-
}
|
|
4639
|
-
},
|
|
4640
|
-
{
|
|
4641
|
-
id: "image.cropMask.left",
|
|
4642
|
-
type: "rect",
|
|
4643
|
-
data: { id: "image.cropMask.left", zIndex: 3 },
|
|
4644
|
-
layout: {
|
|
4645
|
-
reference: "custom",
|
|
4646
|
-
referenceRect: visibleFrameBandRect,
|
|
4647
|
-
alignX: "start",
|
|
4648
|
-
alignY: "start",
|
|
4649
|
-
width: leftW,
|
|
4650
|
-
height: "100%"
|
|
4651
|
-
},
|
|
4652
|
-
props: {
|
|
4653
|
-
originX: "left",
|
|
4654
|
-
originY: "top",
|
|
4655
|
-
fill: visual.outerBackground,
|
|
4656
|
-
selectable: false,
|
|
4657
|
-
evented: false
|
|
4658
|
-
}
|
|
4659
|
-
},
|
|
4660
|
-
{
|
|
4661
|
-
id: "image.cropMask.right",
|
|
4662
|
-
type: "rect",
|
|
4663
|
-
data: { id: "image.cropMask.right", zIndex: 4 },
|
|
4664
|
-
layout: {
|
|
4665
|
-
reference: "custom",
|
|
4666
|
-
referenceRect: visibleFrameBandRect,
|
|
4667
|
-
alignX: "end",
|
|
4668
|
-
alignY: "start",
|
|
4669
|
-
width: rightW,
|
|
4670
|
-
height: "100%"
|
|
4479
|
+
hasBorders: this.isImageEditingVisible(),
|
|
4480
|
+
opacity: render.opacity
|
|
4481
|
+
};
|
|
4482
|
+
}
|
|
4483
|
+
toSceneObjectScale(value) {
|
|
4484
|
+
if (!this.canvasService) return value;
|
|
4485
|
+
return value / this.canvasService.getSceneScale();
|
|
4486
|
+
}
|
|
4487
|
+
getCurrentSrc(obj) {
|
|
4488
|
+
var _a;
|
|
4489
|
+
if (!obj) return void 0;
|
|
4490
|
+
if (typeof obj.getSrc === "function") return obj.getSrc();
|
|
4491
|
+
return (_a = obj == null ? void 0 : obj._originalElement) == null ? void 0 : _a.src;
|
|
4492
|
+
}
|
|
4493
|
+
async buildImageSpecs(items, frame) {
|
|
4494
|
+
const specs = [];
|
|
4495
|
+
for (const item of items) {
|
|
4496
|
+
const render = this.resolveRenderImageState(item);
|
|
4497
|
+
if (!render.src) continue;
|
|
4498
|
+
const ensured = await this.ensureSourceSize(render.src);
|
|
4499
|
+
const sourceSize = ensured || this.getSourceSize(render.src);
|
|
4500
|
+
const props = this.computeCanvasProps(render, sourceSize, frame);
|
|
4501
|
+
specs.push({
|
|
4502
|
+
id: item.id,
|
|
4503
|
+
type: "image",
|
|
4504
|
+
src: render.src,
|
|
4505
|
+
data: {
|
|
4506
|
+
id: item.id,
|
|
4507
|
+
layerId: IMAGE_OBJECT_LAYER_ID,
|
|
4508
|
+
type: "image-item"
|
|
4671
4509
|
},
|
|
4672
|
-
props
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4510
|
+
props
|
|
4511
|
+
});
|
|
4512
|
+
}
|
|
4513
|
+
return specs;
|
|
4514
|
+
}
|
|
4515
|
+
buildOverlaySpecs(overlayState) {
|
|
4516
|
+
const visible = this.isImageEditingVisible();
|
|
4517
|
+
if (!visible || !overlayState || !this.canvasService) {
|
|
4518
|
+
this.debug("overlay:hidden", {
|
|
4519
|
+
visible,
|
|
4520
|
+
cutRect: overlayState == null ? void 0 : overlayState.layout.cutRect,
|
|
4521
|
+
isToolActive: this.isToolActive,
|
|
4522
|
+
isImageSelectionActive: this.isImageSelectionActive,
|
|
4523
|
+
focusedImageId: this.focusedImageId
|
|
4524
|
+
});
|
|
4525
|
+
return [];
|
|
4526
|
+
}
|
|
4527
|
+
const viewport = this.canvasService.getScreenViewportRect();
|
|
4528
|
+
const visual = this.getFrameVisualConfig();
|
|
4529
|
+
const specs = buildImageSessionOverlaySpecs({
|
|
4530
|
+
viewport: {
|
|
4531
|
+
left: viewport.left,
|
|
4532
|
+
top: viewport.top,
|
|
4533
|
+
width: viewport.width,
|
|
4534
|
+
height: viewport.height
|
|
4692
4535
|
},
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
strokeWidth: visual.strokeStyle === "hidden" ? 0 : strokeWidthScene,
|
|
4699
|
-
strokeDashArray: visual.strokeStyle === "dashed" ? [dashLengthScene, dashLengthScene] : void 0,
|
|
4700
|
-
selectable: false,
|
|
4701
|
-
evented: false
|
|
4702
|
-
}
|
|
4703
|
-
};
|
|
4704
|
-
const specs = shapeOverlay.length > 0 ? [...mask, ...shapeOverlay] : [...mask, ...shapeOverlay, frameSpec];
|
|
4536
|
+
layout: overlayState.layout,
|
|
4537
|
+
geometry: overlayState.geometry,
|
|
4538
|
+
visual,
|
|
4539
|
+
hatchPattern: this.getCropShapeHatchPattern()
|
|
4540
|
+
});
|
|
4705
4541
|
this.debug("overlay:built", {
|
|
4706
|
-
|
|
4707
|
-
shape:
|
|
4542
|
+
cutRect: overlayState.layout.cutRect,
|
|
4543
|
+
shape: overlayState.geometry.shape,
|
|
4708
4544
|
overlayIds: specs.map((spec) => {
|
|
4709
4545
|
var _a;
|
|
4710
4546
|
return {
|
|
@@ -4733,10 +4569,9 @@ var ImageTool = class {
|
|
|
4733
4569
|
}
|
|
4734
4570
|
const imageSpecs = await this.buildImageSpecs(renderItems, frame);
|
|
4735
4571
|
if (seq !== this.renderSeq) return;
|
|
4736
|
-
const
|
|
4737
|
-
if (seq !== this.renderSeq) return;
|
|
4572
|
+
const overlayState = this.resolveSessionOverlayState();
|
|
4738
4573
|
this.imageSpecs = imageSpecs;
|
|
4739
|
-
this.overlaySpecs = this.buildOverlaySpecs(
|
|
4574
|
+
this.overlaySpecs = this.buildOverlaySpecs(overlayState);
|
|
4740
4575
|
await this.canvasService.flushRenderFromProducers();
|
|
4741
4576
|
if (seq !== this.renderSeq) return;
|
|
4742
4577
|
this.refreshImageObjectInteractionState();
|
|
@@ -5656,6 +5491,259 @@ function readDielineState(configService, fallback) {
|
|
|
5656
5491
|
};
|
|
5657
5492
|
}
|
|
5658
5493
|
|
|
5494
|
+
// src/extensions/constraints.ts
|
|
5495
|
+
var ConstraintRegistry = class {
|
|
5496
|
+
static register(type, handler) {
|
|
5497
|
+
this.handlers.set(type, handler);
|
|
5498
|
+
}
|
|
5499
|
+
static apply(x, y, feature, context, constraints) {
|
|
5500
|
+
const list = constraints || feature.constraints;
|
|
5501
|
+
if (!list || list.length === 0) {
|
|
5502
|
+
return { x, y };
|
|
5503
|
+
}
|
|
5504
|
+
let currentX = x;
|
|
5505
|
+
let currentY = y;
|
|
5506
|
+
for (const constraint of list) {
|
|
5507
|
+
const handler = this.handlers.get(constraint.type);
|
|
5508
|
+
if (handler) {
|
|
5509
|
+
const result = handler(currentX, currentY, feature, context, constraint.params || {});
|
|
5510
|
+
currentX = result.x;
|
|
5511
|
+
currentY = result.y;
|
|
5512
|
+
}
|
|
5513
|
+
}
|
|
5514
|
+
return { x: currentX, y: currentY };
|
|
5515
|
+
}
|
|
5516
|
+
};
|
|
5517
|
+
ConstraintRegistry.handlers = /* @__PURE__ */ new Map();
|
|
5518
|
+
var pathConstraint = (x, y, feature, context, params) => {
|
|
5519
|
+
const { dielineWidth, dielineHeight, geometry } = context;
|
|
5520
|
+
if (!geometry) return { x, y };
|
|
5521
|
+
const minX = geometry.x - geometry.width / 2;
|
|
5522
|
+
const minY = geometry.y - geometry.height / 2;
|
|
5523
|
+
const absX = minX + x * geometry.width;
|
|
5524
|
+
const absY = minY + y * geometry.height;
|
|
5525
|
+
const nearest = getNearestPointOnDieline(
|
|
5526
|
+
{ x: absX, y: absY },
|
|
5527
|
+
geometry
|
|
5528
|
+
);
|
|
5529
|
+
let finalX = nearest.x;
|
|
5530
|
+
let finalY = nearest.y;
|
|
5531
|
+
const hasOffsetParams = params.minOffset !== void 0 || params.maxOffset !== void 0;
|
|
5532
|
+
if (hasOffsetParams && nearest.normal) {
|
|
5533
|
+
const dx = absX - nearest.x;
|
|
5534
|
+
const dy = absY - nearest.y;
|
|
5535
|
+
const nx2 = nearest.normal.x;
|
|
5536
|
+
const ny2 = nearest.normal.y;
|
|
5537
|
+
const dist = dx * nx2 + dy * ny2;
|
|
5538
|
+
const scale = dielineWidth > 0 ? geometry.width / dielineWidth : 1;
|
|
5539
|
+
const rawMin = params.minOffset !== void 0 ? params.minOffset : 0;
|
|
5540
|
+
const rawMax = params.maxOffset !== void 0 ? params.maxOffset : 0;
|
|
5541
|
+
const minOffset = rawMin * scale;
|
|
5542
|
+
const maxOffset = rawMax * scale;
|
|
5543
|
+
const clampedDist = Math.max(minOffset, Math.min(dist, maxOffset));
|
|
5544
|
+
finalX = nearest.x + nx2 * clampedDist;
|
|
5545
|
+
finalY = nearest.y + ny2 * clampedDist;
|
|
5546
|
+
}
|
|
5547
|
+
const nx = geometry.width > 0 ? (finalX - minX) / geometry.width : 0.5;
|
|
5548
|
+
const ny = geometry.height > 0 ? (finalY - minY) / geometry.height : 0.5;
|
|
5549
|
+
return { x: nx, y: ny };
|
|
5550
|
+
};
|
|
5551
|
+
var edgeConstraint = (x, y, feature, context, params) => {
|
|
5552
|
+
const { dielineWidth, dielineHeight } = context;
|
|
5553
|
+
const allowedEdges = params.allowedEdges || [
|
|
5554
|
+
"top",
|
|
5555
|
+
"bottom",
|
|
5556
|
+
"left",
|
|
5557
|
+
"right"
|
|
5558
|
+
];
|
|
5559
|
+
const confine = params.confine || false;
|
|
5560
|
+
const offset = params.offset || 0;
|
|
5561
|
+
const distances = [];
|
|
5562
|
+
if (allowedEdges.includes("top"))
|
|
5563
|
+
distances.push({ edge: "top", dist: y * dielineHeight });
|
|
5564
|
+
if (allowedEdges.includes("bottom"))
|
|
5565
|
+
distances.push({ edge: "bottom", dist: (1 - y) * dielineHeight });
|
|
5566
|
+
if (allowedEdges.includes("left"))
|
|
5567
|
+
distances.push({ edge: "left", dist: x * dielineWidth });
|
|
5568
|
+
if (allowedEdges.includes("right"))
|
|
5569
|
+
distances.push({ edge: "right", dist: (1 - x) * dielineWidth });
|
|
5570
|
+
if (distances.length === 0) return { x, y };
|
|
5571
|
+
distances.sort((a, b) => a.dist - b.dist);
|
|
5572
|
+
const nearest = distances[0].edge;
|
|
5573
|
+
let newX = x;
|
|
5574
|
+
let newY = y;
|
|
5575
|
+
const fw = feature.width || 0;
|
|
5576
|
+
const fh = feature.height || 0;
|
|
5577
|
+
switch (nearest) {
|
|
5578
|
+
case "top":
|
|
5579
|
+
newY = 0 + offset / dielineHeight;
|
|
5580
|
+
if (confine) {
|
|
5581
|
+
const minX = fw / 2 / dielineWidth;
|
|
5582
|
+
const maxX = 1 - minX;
|
|
5583
|
+
newX = Math.max(minX, Math.min(newX, maxX));
|
|
5584
|
+
}
|
|
5585
|
+
break;
|
|
5586
|
+
case "bottom":
|
|
5587
|
+
newY = 1 - offset / dielineHeight;
|
|
5588
|
+
if (confine) {
|
|
5589
|
+
const minX = fw / 2 / dielineWidth;
|
|
5590
|
+
const maxX = 1 - minX;
|
|
5591
|
+
newX = Math.max(minX, Math.min(newX, maxX));
|
|
5592
|
+
}
|
|
5593
|
+
break;
|
|
5594
|
+
case "left":
|
|
5595
|
+
newX = 0 + offset / dielineWidth;
|
|
5596
|
+
if (confine) {
|
|
5597
|
+
const minY = fh / 2 / dielineHeight;
|
|
5598
|
+
const maxY = 1 - minY;
|
|
5599
|
+
newY = Math.max(minY, Math.min(newY, maxY));
|
|
5600
|
+
}
|
|
5601
|
+
break;
|
|
5602
|
+
case "right":
|
|
5603
|
+
newX = 1 - offset / dielineWidth;
|
|
5604
|
+
if (confine) {
|
|
5605
|
+
const minY = fh / 2 / dielineHeight;
|
|
5606
|
+
const maxY = 1 - minY;
|
|
5607
|
+
newY = Math.max(minY, Math.min(newY, maxY));
|
|
5608
|
+
}
|
|
5609
|
+
break;
|
|
5610
|
+
}
|
|
5611
|
+
return { x: newX, y: newY };
|
|
5612
|
+
};
|
|
5613
|
+
var internalConstraint = (x, y, feature, context, params) => {
|
|
5614
|
+
const { dielineWidth, dielineHeight } = context;
|
|
5615
|
+
const margin = params.margin || 0;
|
|
5616
|
+
const fw = feature.width || 0;
|
|
5617
|
+
const fh = feature.height || 0;
|
|
5618
|
+
const minX = (margin + fw / 2) / dielineWidth;
|
|
5619
|
+
const maxX = 1 - (margin + fw / 2) / dielineWidth;
|
|
5620
|
+
const minY = (margin + fh / 2) / dielineHeight;
|
|
5621
|
+
const maxY = 1 - (margin + fh / 2) / dielineHeight;
|
|
5622
|
+
const clampedX = minX > maxX ? 0.5 : Math.max(minX, Math.min(x, maxX));
|
|
5623
|
+
const clampedY = minY > maxY ? 0.5 : Math.max(minY, Math.min(y, maxY));
|
|
5624
|
+
return { x: clampedX, y: clampedY };
|
|
5625
|
+
};
|
|
5626
|
+
var tangentBottomConstraint = (x, y, feature, context, params) => {
|
|
5627
|
+
const { dielineWidth, dielineHeight } = context;
|
|
5628
|
+
const gap = params.gap || 0;
|
|
5629
|
+
const confineX = params.confineX !== false;
|
|
5630
|
+
const extentY = feature.shape === "circle" ? feature.radius || 0 : (feature.height || 0) / 2;
|
|
5631
|
+
const newY = 1 + (extentY + gap) / dielineHeight;
|
|
5632
|
+
let newX = x;
|
|
5633
|
+
if (confineX) {
|
|
5634
|
+
const extentX = feature.shape === "circle" ? feature.radius || 0 : (feature.width || 0) / 2;
|
|
5635
|
+
const minX = extentX / dielineWidth;
|
|
5636
|
+
const maxX = 1 - extentX / dielineWidth;
|
|
5637
|
+
newX = minX > maxX ? 0.5 : Math.max(minX, Math.min(newX, maxX));
|
|
5638
|
+
}
|
|
5639
|
+
return { x: newX, y: newY };
|
|
5640
|
+
};
|
|
5641
|
+
var lowestTangentConstraint = (x, y, feature, context, params) => {
|
|
5642
|
+
const { dielineWidth, dielineHeight, geometry } = context;
|
|
5643
|
+
if (!geometry) return { x, y };
|
|
5644
|
+
const lowest = getLowestPointOnDieline(geometry);
|
|
5645
|
+
const minY = geometry.y - geometry.height / 2;
|
|
5646
|
+
const normY = (lowest.y - minY) / geometry.height;
|
|
5647
|
+
const gap = params.gap || 0;
|
|
5648
|
+
const confineX = params.confineX !== false;
|
|
5649
|
+
const extentY = feature.shape === "circle" ? feature.radius || 0 : (feature.height || 0) / 2;
|
|
5650
|
+
const newY = normY + (extentY + gap) / dielineHeight;
|
|
5651
|
+
let newX = x;
|
|
5652
|
+
if (confineX) {
|
|
5653
|
+
const extentX = feature.shape === "circle" ? feature.radius || 0 : (feature.width || 0) / 2;
|
|
5654
|
+
const minX = extentX / dielineWidth;
|
|
5655
|
+
const maxX = 1 - extentX / dielineWidth;
|
|
5656
|
+
newX = minX > maxX ? 0.5 : Math.max(minX, Math.min(newX, maxX));
|
|
5657
|
+
}
|
|
5658
|
+
return { x: newX, y: newY };
|
|
5659
|
+
};
|
|
5660
|
+
ConstraintRegistry.register("path", pathConstraint);
|
|
5661
|
+
ConstraintRegistry.register("edge", edgeConstraint);
|
|
5662
|
+
ConstraintRegistry.register("internal", internalConstraint);
|
|
5663
|
+
ConstraintRegistry.register("tangent-bottom", tangentBottomConstraint);
|
|
5664
|
+
ConstraintRegistry.register("lowest-tangent", lowestTangentConstraint);
|
|
5665
|
+
|
|
5666
|
+
// src/extensions/featureCoordinates.ts
|
|
5667
|
+
function resolveFeaturePosition2(feature, geometry) {
|
|
5668
|
+
const { x, y, width, height } = geometry;
|
|
5669
|
+
const left = x - width / 2;
|
|
5670
|
+
const top = y - height / 2;
|
|
5671
|
+
return {
|
|
5672
|
+
x: left + feature.x * width,
|
|
5673
|
+
y: top + feature.y * height
|
|
5674
|
+
};
|
|
5675
|
+
}
|
|
5676
|
+
function normalizePointInGeometry(point, geometry) {
|
|
5677
|
+
const left = geometry.x - geometry.width / 2;
|
|
5678
|
+
const top = geometry.y - geometry.height / 2;
|
|
5679
|
+
return {
|
|
5680
|
+
x: geometry.width > 0 ? (point.x - left) / geometry.width : 0.5,
|
|
5681
|
+
y: geometry.height > 0 ? (point.y - top) / geometry.height : 0.5
|
|
5682
|
+
};
|
|
5683
|
+
}
|
|
5684
|
+
|
|
5685
|
+
// src/extensions/featurePlacement.ts
|
|
5686
|
+
function scaleFeatureForRender(feature, scale, x, y) {
|
|
5687
|
+
return {
|
|
5688
|
+
...feature,
|
|
5689
|
+
x,
|
|
5690
|
+
y,
|
|
5691
|
+
width: feature.width !== void 0 ? feature.width * scale : void 0,
|
|
5692
|
+
height: feature.height !== void 0 ? feature.height * scale : void 0,
|
|
5693
|
+
radius: feature.radius !== void 0 ? feature.radius * scale : void 0
|
|
5694
|
+
};
|
|
5695
|
+
}
|
|
5696
|
+
function resolveFeaturePlacements(features, geometry) {
|
|
5697
|
+
const dielineWidth = geometry.scale > 0 ? geometry.width / geometry.scale : geometry.width;
|
|
5698
|
+
const dielineHeight = geometry.scale > 0 ? geometry.height / geometry.scale : geometry.height;
|
|
5699
|
+
return (features || []).map((feature) => {
|
|
5700
|
+
var _a;
|
|
5701
|
+
const activeConstraints = (_a = feature.constraints) == null ? void 0 : _a.filter(
|
|
5702
|
+
(constraint) => !constraint.validateOnly
|
|
5703
|
+
);
|
|
5704
|
+
const constrained = ConstraintRegistry.apply(
|
|
5705
|
+
feature.x,
|
|
5706
|
+
feature.y,
|
|
5707
|
+
feature,
|
|
5708
|
+
{
|
|
5709
|
+
dielineWidth,
|
|
5710
|
+
dielineHeight,
|
|
5711
|
+
geometry
|
|
5712
|
+
},
|
|
5713
|
+
activeConstraints
|
|
5714
|
+
);
|
|
5715
|
+
const center = resolveFeaturePosition2(
|
|
5716
|
+
{
|
|
5717
|
+
...feature,
|
|
5718
|
+
x: constrained.x,
|
|
5719
|
+
y: constrained.y
|
|
5720
|
+
},
|
|
5721
|
+
geometry
|
|
5722
|
+
);
|
|
5723
|
+
return {
|
|
5724
|
+
feature,
|
|
5725
|
+
normalizedX: constrained.x,
|
|
5726
|
+
normalizedY: constrained.y,
|
|
5727
|
+
centerX: center.x,
|
|
5728
|
+
centerY: center.y
|
|
5729
|
+
};
|
|
5730
|
+
});
|
|
5731
|
+
}
|
|
5732
|
+
function projectPlacedFeatures(placements, geometry, scale) {
|
|
5733
|
+
return placements.map((placement) => {
|
|
5734
|
+
const normalized = normalizePointInGeometry(
|
|
5735
|
+
{ x: placement.centerX, y: placement.centerY },
|
|
5736
|
+
geometry
|
|
5737
|
+
);
|
|
5738
|
+
return scaleFeatureForRender(
|
|
5739
|
+
placement.feature,
|
|
5740
|
+
scale,
|
|
5741
|
+
normalized.x,
|
|
5742
|
+
normalized.y
|
|
5743
|
+
);
|
|
5744
|
+
});
|
|
5745
|
+
}
|
|
5746
|
+
|
|
5659
5747
|
// src/extensions/dieline/renderBuilder.ts
|
|
5660
5748
|
var DEFAULT_IDS = {
|
|
5661
5749
|
inside: "dieline.inside",
|
|
@@ -5665,16 +5753,6 @@ var DEFAULT_IDS = {
|
|
|
5665
5753
|
clip: "dieline.clip.image",
|
|
5666
5754
|
clipSource: "dieline.effect.clip-path"
|
|
5667
5755
|
};
|
|
5668
|
-
function scaleFeatures(state, scale) {
|
|
5669
|
-
return (state.features || []).map((feature) => ({
|
|
5670
|
-
...feature,
|
|
5671
|
-
x: feature.x,
|
|
5672
|
-
y: feature.y,
|
|
5673
|
-
width: (feature.width || 0) * scale,
|
|
5674
|
-
height: (feature.height || 0) * scale,
|
|
5675
|
-
radius: (feature.radius || 0) * scale
|
|
5676
|
-
}));
|
|
5677
|
-
}
|
|
5678
5756
|
function buildDielineRenderBundle(options) {
|
|
5679
5757
|
const ids = { ...DEFAULT_IDS, ...options.ids || {} };
|
|
5680
5758
|
const {
|
|
@@ -5699,8 +5777,41 @@ function buildDielineRenderBundle(options) {
|
|
|
5699
5777
|
const cutH = sceneLayout.cutRect.height;
|
|
5700
5778
|
const visualOffset = (cutW - visualWidth) / 2;
|
|
5701
5779
|
const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
5702
|
-
const
|
|
5703
|
-
|
|
5780
|
+
const placements = resolveFeaturePlacements(state.features || [], {
|
|
5781
|
+
shape,
|
|
5782
|
+
shapeStyle,
|
|
5783
|
+
pathData: state.pathData,
|
|
5784
|
+
customSourceWidthPx: state.customSourceWidthPx,
|
|
5785
|
+
customSourceHeightPx: state.customSourceHeightPx,
|
|
5786
|
+
canvasWidth,
|
|
5787
|
+
canvasHeight,
|
|
5788
|
+
x: cx,
|
|
5789
|
+
y: cy,
|
|
5790
|
+
width: visualWidth,
|
|
5791
|
+
height: visualHeight,
|
|
5792
|
+
radius: visualRadius,
|
|
5793
|
+
scale
|
|
5794
|
+
});
|
|
5795
|
+
const absoluteFeatures = projectPlacedFeatures(
|
|
5796
|
+
placements,
|
|
5797
|
+
{
|
|
5798
|
+
x: cx,
|
|
5799
|
+
y: cy,
|
|
5800
|
+
width: visualWidth,
|
|
5801
|
+
height: visualHeight
|
|
5802
|
+
},
|
|
5803
|
+
scale
|
|
5804
|
+
);
|
|
5805
|
+
const cutFeatures = projectPlacedFeatures(
|
|
5806
|
+
placements.filter((placement) => !placement.feature.skipCut),
|
|
5807
|
+
{
|
|
5808
|
+
x: cx,
|
|
5809
|
+
y: cy,
|
|
5810
|
+
width: cutW,
|
|
5811
|
+
height: cutH
|
|
5812
|
+
},
|
|
5813
|
+
scale
|
|
5814
|
+
);
|
|
5704
5815
|
const common = {
|
|
5705
5816
|
shape,
|
|
5706
5817
|
shapeStyle,
|
|
@@ -5710,6 +5821,13 @@ function buildDielineRenderBundle(options) {
|
|
|
5710
5821
|
canvasWidth,
|
|
5711
5822
|
canvasHeight
|
|
5712
5823
|
};
|
|
5824
|
+
const cutFrameRect = {
|
|
5825
|
+
left: cx - cutW / 2,
|
|
5826
|
+
top: cy - cutH / 2,
|
|
5827
|
+
width: cutW,
|
|
5828
|
+
height: cutH,
|
|
5829
|
+
space: "screen"
|
|
5830
|
+
};
|
|
5713
5831
|
const specs = [];
|
|
5714
5832
|
if (insideColor && insideColor !== "transparent" && insideColor !== "rgba(0,0,0,0)" && !hasImages) {
|
|
5715
5833
|
specs.push({
|
|
@@ -5831,9 +5949,13 @@ function buildDielineRenderBundle(options) {
|
|
|
5831
5949
|
width: cutW,
|
|
5832
5950
|
height: cutH,
|
|
5833
5951
|
radius: cutR,
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5952
|
+
// Build the clip path in the cut frame's local coordinates so Fabric
|
|
5953
|
+
// does not have to infer placement from the standalone path bounds.
|
|
5954
|
+
x: cutW / 2,
|
|
5955
|
+
y: cutH / 2,
|
|
5956
|
+
features: cutFeatures,
|
|
5957
|
+
canvasWidth: cutW,
|
|
5958
|
+
canvasHeight: cutH
|
|
5837
5959
|
});
|
|
5838
5960
|
if (!clipPathData) {
|
|
5839
5961
|
return { specs, effects: [] };
|
|
@@ -5850,6 +5972,12 @@ function buildDielineRenderBundle(options) {
|
|
|
5850
5972
|
id: ids.clipSource,
|
|
5851
5973
|
type: "path",
|
|
5852
5974
|
space: "screen",
|
|
5975
|
+
layout: {
|
|
5976
|
+
reference: "custom",
|
|
5977
|
+
referenceRect: cutFrameRect,
|
|
5978
|
+
alignX: "start",
|
|
5979
|
+
alignY: "start"
|
|
5980
|
+
},
|
|
5853
5981
|
data: {
|
|
5854
5982
|
id: ids.clipSource,
|
|
5855
5983
|
type: "dieline-effect",
|
|
@@ -6129,15 +6257,31 @@ var DielineTool = class {
|
|
|
6129
6257
|
const visualRadius = radius * scale;
|
|
6130
6258
|
const visualOffset = (cutW - sceneLayout.trimRect.width) / 2;
|
|
6131
6259
|
const cutR = visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
6132
|
-
const
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6260
|
+
const placements = resolveFeaturePlacements(features || [], {
|
|
6261
|
+
shape,
|
|
6262
|
+
shapeStyle,
|
|
6263
|
+
pathData,
|
|
6264
|
+
customSourceWidthPx: this.state.customSourceWidthPx,
|
|
6265
|
+
customSourceHeightPx: this.state.customSourceHeightPx,
|
|
6266
|
+
canvasWidth: canvasW,
|
|
6267
|
+
canvasHeight: canvasH,
|
|
6268
|
+
x: cx,
|
|
6269
|
+
y: cy,
|
|
6270
|
+
width: sceneLayout.trimRect.width,
|
|
6271
|
+
height: sceneLayout.trimRect.height,
|
|
6272
|
+
radius: visualRadius,
|
|
6273
|
+
scale
|
|
6274
|
+
});
|
|
6275
|
+
const cutFeatures = projectPlacedFeatures(
|
|
6276
|
+
placements.filter((placement) => !placement.feature.skipCut),
|
|
6277
|
+
{
|
|
6278
|
+
x: cx,
|
|
6279
|
+
y: cy,
|
|
6280
|
+
width: cutW,
|
|
6281
|
+
height: cutH
|
|
6282
|
+
},
|
|
6283
|
+
scale
|
|
6284
|
+
);
|
|
6141
6285
|
const generatedPathData = generateDielinePath({
|
|
6142
6286
|
shape,
|
|
6143
6287
|
width: cutW,
|
|
@@ -6261,178 +6405,6 @@ var DielineTool = class {
|
|
|
6261
6405
|
var import_core5 = require("@pooder/core");
|
|
6262
6406
|
var import_fabric4 = require("fabric");
|
|
6263
6407
|
|
|
6264
|
-
// src/extensions/constraints.ts
|
|
6265
|
-
var ConstraintRegistry = class {
|
|
6266
|
-
static register(type, handler) {
|
|
6267
|
-
this.handlers.set(type, handler);
|
|
6268
|
-
}
|
|
6269
|
-
static apply(x, y, feature, context, constraints) {
|
|
6270
|
-
const list = constraints || feature.constraints;
|
|
6271
|
-
if (!list || list.length === 0) {
|
|
6272
|
-
return { x, y };
|
|
6273
|
-
}
|
|
6274
|
-
let currentX = x;
|
|
6275
|
-
let currentY = y;
|
|
6276
|
-
for (const constraint of list) {
|
|
6277
|
-
const handler = this.handlers.get(constraint.type);
|
|
6278
|
-
if (handler) {
|
|
6279
|
-
const result = handler(currentX, currentY, feature, context, constraint.params || {});
|
|
6280
|
-
currentX = result.x;
|
|
6281
|
-
currentY = result.y;
|
|
6282
|
-
}
|
|
6283
|
-
}
|
|
6284
|
-
return { x: currentX, y: currentY };
|
|
6285
|
-
}
|
|
6286
|
-
};
|
|
6287
|
-
ConstraintRegistry.handlers = /* @__PURE__ */ new Map();
|
|
6288
|
-
var pathConstraint = (x, y, feature, context, params) => {
|
|
6289
|
-
const { dielineWidth, dielineHeight, geometry } = context;
|
|
6290
|
-
if (!geometry) return { x, y };
|
|
6291
|
-
const minX = geometry.x - geometry.width / 2;
|
|
6292
|
-
const minY = geometry.y - geometry.height / 2;
|
|
6293
|
-
const absX = minX + x * geometry.width;
|
|
6294
|
-
const absY = minY + y * geometry.height;
|
|
6295
|
-
const nearest = getNearestPointOnDieline(
|
|
6296
|
-
{ x: absX, y: absY },
|
|
6297
|
-
geometry
|
|
6298
|
-
);
|
|
6299
|
-
let finalX = nearest.x;
|
|
6300
|
-
let finalY = nearest.y;
|
|
6301
|
-
const hasOffsetParams = params.minOffset !== void 0 || params.maxOffset !== void 0;
|
|
6302
|
-
if (hasOffsetParams && nearest.normal) {
|
|
6303
|
-
const dx = absX - nearest.x;
|
|
6304
|
-
const dy = absY - nearest.y;
|
|
6305
|
-
const nx2 = nearest.normal.x;
|
|
6306
|
-
const ny2 = nearest.normal.y;
|
|
6307
|
-
const dist = dx * nx2 + dy * ny2;
|
|
6308
|
-
const scale = dielineWidth > 0 ? geometry.width / dielineWidth : 1;
|
|
6309
|
-
const rawMin = params.minOffset !== void 0 ? params.minOffset : 0;
|
|
6310
|
-
const rawMax = params.maxOffset !== void 0 ? params.maxOffset : 0;
|
|
6311
|
-
const minOffset = rawMin * scale;
|
|
6312
|
-
const maxOffset = rawMax * scale;
|
|
6313
|
-
const clampedDist = Math.max(minOffset, Math.min(dist, maxOffset));
|
|
6314
|
-
finalX = nearest.x + nx2 * clampedDist;
|
|
6315
|
-
finalY = nearest.y + ny2 * clampedDist;
|
|
6316
|
-
}
|
|
6317
|
-
const nx = geometry.width > 0 ? (finalX - minX) / geometry.width : 0.5;
|
|
6318
|
-
const ny = geometry.height > 0 ? (finalY - minY) / geometry.height : 0.5;
|
|
6319
|
-
return { x: nx, y: ny };
|
|
6320
|
-
};
|
|
6321
|
-
var edgeConstraint = (x, y, feature, context, params) => {
|
|
6322
|
-
const { dielineWidth, dielineHeight } = context;
|
|
6323
|
-
const allowedEdges = params.allowedEdges || [
|
|
6324
|
-
"top",
|
|
6325
|
-
"bottom",
|
|
6326
|
-
"left",
|
|
6327
|
-
"right"
|
|
6328
|
-
];
|
|
6329
|
-
const confine = params.confine || false;
|
|
6330
|
-
const offset = params.offset || 0;
|
|
6331
|
-
const distances = [];
|
|
6332
|
-
if (allowedEdges.includes("top"))
|
|
6333
|
-
distances.push({ edge: "top", dist: y * dielineHeight });
|
|
6334
|
-
if (allowedEdges.includes("bottom"))
|
|
6335
|
-
distances.push({ edge: "bottom", dist: (1 - y) * dielineHeight });
|
|
6336
|
-
if (allowedEdges.includes("left"))
|
|
6337
|
-
distances.push({ edge: "left", dist: x * dielineWidth });
|
|
6338
|
-
if (allowedEdges.includes("right"))
|
|
6339
|
-
distances.push({ edge: "right", dist: (1 - x) * dielineWidth });
|
|
6340
|
-
if (distances.length === 0) return { x, y };
|
|
6341
|
-
distances.sort((a, b) => a.dist - b.dist);
|
|
6342
|
-
const nearest = distances[0].edge;
|
|
6343
|
-
let newX = x;
|
|
6344
|
-
let newY = y;
|
|
6345
|
-
const fw = feature.width || 0;
|
|
6346
|
-
const fh = feature.height || 0;
|
|
6347
|
-
switch (nearest) {
|
|
6348
|
-
case "top":
|
|
6349
|
-
newY = 0 + offset / dielineHeight;
|
|
6350
|
-
if (confine) {
|
|
6351
|
-
const minX = fw / 2 / dielineWidth;
|
|
6352
|
-
const maxX = 1 - minX;
|
|
6353
|
-
newX = Math.max(minX, Math.min(newX, maxX));
|
|
6354
|
-
}
|
|
6355
|
-
break;
|
|
6356
|
-
case "bottom":
|
|
6357
|
-
newY = 1 - offset / dielineHeight;
|
|
6358
|
-
if (confine) {
|
|
6359
|
-
const minX = fw / 2 / dielineWidth;
|
|
6360
|
-
const maxX = 1 - minX;
|
|
6361
|
-
newX = Math.max(minX, Math.min(newX, maxX));
|
|
6362
|
-
}
|
|
6363
|
-
break;
|
|
6364
|
-
case "left":
|
|
6365
|
-
newX = 0 + offset / dielineWidth;
|
|
6366
|
-
if (confine) {
|
|
6367
|
-
const minY = fh / 2 / dielineHeight;
|
|
6368
|
-
const maxY = 1 - minY;
|
|
6369
|
-
newY = Math.max(minY, Math.min(newY, maxY));
|
|
6370
|
-
}
|
|
6371
|
-
break;
|
|
6372
|
-
case "right":
|
|
6373
|
-
newX = 1 - offset / dielineWidth;
|
|
6374
|
-
if (confine) {
|
|
6375
|
-
const minY = fh / 2 / dielineHeight;
|
|
6376
|
-
const maxY = 1 - minY;
|
|
6377
|
-
newY = Math.max(minY, Math.min(newY, maxY));
|
|
6378
|
-
}
|
|
6379
|
-
break;
|
|
6380
|
-
}
|
|
6381
|
-
return { x: newX, y: newY };
|
|
6382
|
-
};
|
|
6383
|
-
var internalConstraint = (x, y, feature, context, params) => {
|
|
6384
|
-
const { dielineWidth, dielineHeight } = context;
|
|
6385
|
-
const margin = params.margin || 0;
|
|
6386
|
-
const fw = feature.width || 0;
|
|
6387
|
-
const fh = feature.height || 0;
|
|
6388
|
-
const minX = (margin + fw / 2) / dielineWidth;
|
|
6389
|
-
const maxX = 1 - (margin + fw / 2) / dielineWidth;
|
|
6390
|
-
const minY = (margin + fh / 2) / dielineHeight;
|
|
6391
|
-
const maxY = 1 - (margin + fh / 2) / dielineHeight;
|
|
6392
|
-
const clampedX = minX > maxX ? 0.5 : Math.max(minX, Math.min(x, maxX));
|
|
6393
|
-
const clampedY = minY > maxY ? 0.5 : Math.max(minY, Math.min(y, maxY));
|
|
6394
|
-
return { x: clampedX, y: clampedY };
|
|
6395
|
-
};
|
|
6396
|
-
var tangentBottomConstraint = (x, y, feature, context, params) => {
|
|
6397
|
-
const { dielineWidth, dielineHeight } = context;
|
|
6398
|
-
const gap = params.gap || 0;
|
|
6399
|
-
const confineX = params.confineX !== false;
|
|
6400
|
-
const extentY = feature.shape === "circle" ? feature.radius || 0 : (feature.height || 0) / 2;
|
|
6401
|
-
const newY = 1 + (extentY + gap) / dielineHeight;
|
|
6402
|
-
let newX = x;
|
|
6403
|
-
if (confineX) {
|
|
6404
|
-
const extentX = feature.shape === "circle" ? feature.radius || 0 : (feature.width || 0) / 2;
|
|
6405
|
-
const minX = extentX / dielineWidth;
|
|
6406
|
-
const maxX = 1 - extentX / dielineWidth;
|
|
6407
|
-
newX = minX > maxX ? 0.5 : Math.max(minX, Math.min(newX, maxX));
|
|
6408
|
-
}
|
|
6409
|
-
return { x: newX, y: newY };
|
|
6410
|
-
};
|
|
6411
|
-
var lowestTangentConstraint = (x, y, feature, context, params) => {
|
|
6412
|
-
const { dielineWidth, dielineHeight, geometry } = context;
|
|
6413
|
-
if (!geometry) return { x, y };
|
|
6414
|
-
const lowest = getLowestPointOnDieline(geometry);
|
|
6415
|
-
const minY = geometry.y - geometry.height / 2;
|
|
6416
|
-
const normY = (lowest.y - minY) / geometry.height;
|
|
6417
|
-
const gap = params.gap || 0;
|
|
6418
|
-
const confineX = params.confineX !== false;
|
|
6419
|
-
const extentY = feature.shape === "circle" ? feature.radius || 0 : (feature.height || 0) / 2;
|
|
6420
|
-
const newY = normY + (extentY + gap) / dielineHeight;
|
|
6421
|
-
let newX = x;
|
|
6422
|
-
if (confineX) {
|
|
6423
|
-
const extentX = feature.shape === "circle" ? feature.radius || 0 : (feature.width || 0) / 2;
|
|
6424
|
-
const minX = extentX / dielineWidth;
|
|
6425
|
-
const maxX = 1 - extentX / dielineWidth;
|
|
6426
|
-
newX = minX > maxX ? 0.5 : Math.max(minX, Math.min(newX, maxX));
|
|
6427
|
-
}
|
|
6428
|
-
return { x: newX, y: newY };
|
|
6429
|
-
};
|
|
6430
|
-
ConstraintRegistry.register("path", pathConstraint);
|
|
6431
|
-
ConstraintRegistry.register("edge", edgeConstraint);
|
|
6432
|
-
ConstraintRegistry.register("internal", internalConstraint);
|
|
6433
|
-
ConstraintRegistry.register("tangent-bottom", tangentBottomConstraint);
|
|
6434
|
-
ConstraintRegistry.register("lowest-tangent", lowestTangentConstraint);
|
|
6435
|
-
|
|
6436
6408
|
// src/extensions/featureComplete.ts
|
|
6437
6409
|
function validateFeaturesStrict(features, context) {
|
|
6438
6410
|
const eps = 1e-6;
|
|
@@ -7208,9 +7180,29 @@ var FeatureTool = class {
|
|
|
7208
7180
|
}
|
|
7209
7181
|
const groups = /* @__PURE__ */ new Map();
|
|
7210
7182
|
const singles = [];
|
|
7211
|
-
|
|
7183
|
+
const placements = resolveFeaturePlacements(
|
|
7184
|
+
this.workingFeatures,
|
|
7185
|
+
{
|
|
7186
|
+
shape: this.currentGeometry.shape,
|
|
7187
|
+
shapeStyle: this.currentGeometry.shapeStyle,
|
|
7188
|
+
pathData: this.currentGeometry.pathData,
|
|
7189
|
+
customSourceWidthPx: this.currentGeometry.customSourceWidthPx,
|
|
7190
|
+
customSourceHeightPx: this.currentGeometry.customSourceHeightPx,
|
|
7191
|
+
x: this.currentGeometry.x,
|
|
7192
|
+
y: this.currentGeometry.y,
|
|
7193
|
+
width: this.currentGeometry.width,
|
|
7194
|
+
height: this.currentGeometry.height,
|
|
7195
|
+
radius: this.currentGeometry.radius,
|
|
7196
|
+
scale: this.currentGeometry.scale || 1
|
|
7197
|
+
}
|
|
7198
|
+
);
|
|
7199
|
+
placements.forEach((placement, index) => {
|
|
7200
|
+
const feature = placement.feature;
|
|
7212
7201
|
const geometry = this.getGeometryForFeature(this.currentGeometry, feature);
|
|
7213
|
-
const position =
|
|
7202
|
+
const position = {
|
|
7203
|
+
x: placement.centerX,
|
|
7204
|
+
y: placement.centerY
|
|
7205
|
+
};
|
|
7214
7206
|
const scale = geometry.scale || 1;
|
|
7215
7207
|
const marker = {
|
|
7216
7208
|
feature,
|
|
@@ -7781,11 +7773,12 @@ var EXTENSION_LINE_LENGTH = 5;
|
|
|
7781
7773
|
var MIN_ARROW_SIZE = 4;
|
|
7782
7774
|
var THICKNESS_TO_STROKE_WIDTH_RATIO = 20;
|
|
7783
7775
|
var DEFAULT_THICKNESS = 20;
|
|
7784
|
-
var DEFAULT_GAP =
|
|
7776
|
+
var DEFAULT_GAP = 65;
|
|
7785
7777
|
var DEFAULT_FONT_SIZE = 10;
|
|
7786
7778
|
var DEFAULT_BACKGROUND_COLOR = "#f0f0f0";
|
|
7787
7779
|
var DEFAULT_TEXT_COLOR = "#333333";
|
|
7788
7780
|
var DEFAULT_LINE_COLOR = "#999999";
|
|
7781
|
+
var RULER_DEBUG_KEY = "ruler.debug";
|
|
7789
7782
|
var RULER_THICKNESS_MIN = 10;
|
|
7790
7783
|
var RULER_THICKNESS_MAX = 100;
|
|
7791
7784
|
var RULER_GAP_MIN = 0;
|
|
@@ -7804,6 +7797,7 @@ var RulerTool = class {
|
|
|
7804
7797
|
this.textColor = DEFAULT_TEXT_COLOR;
|
|
7805
7798
|
this.lineColor = DEFAULT_LINE_COLOR;
|
|
7806
7799
|
this.fontSize = DEFAULT_FONT_SIZE;
|
|
7800
|
+
this.debugEnabled = false;
|
|
7807
7801
|
this.renderSeq = 0;
|
|
7808
7802
|
this.numericProps = /* @__PURE__ */ new Set(["thickness", "gap", "fontSize"]);
|
|
7809
7803
|
this.specs = [];
|
|
@@ -7852,7 +7846,14 @@ var RulerTool = class {
|
|
|
7852
7846
|
this.syncConfig(configService);
|
|
7853
7847
|
configService.onAnyChange((e) => {
|
|
7854
7848
|
let shouldUpdate = false;
|
|
7855
|
-
if (e.key
|
|
7849
|
+
if (e.key === RULER_DEBUG_KEY) {
|
|
7850
|
+
this.debugEnabled = e.value === true;
|
|
7851
|
+
this.log("config:update", {
|
|
7852
|
+
key: e.key,
|
|
7853
|
+
raw: e.value,
|
|
7854
|
+
normalized: this.debugEnabled
|
|
7855
|
+
});
|
|
7856
|
+
} else if (e.key.startsWith("ruler.")) {
|
|
7856
7857
|
const prop = e.key.split(".")[1];
|
|
7857
7858
|
if (prop && prop in this) {
|
|
7858
7859
|
if (this.numericProps.has(prop)) {
|
|
@@ -7939,6 +7940,12 @@ var RulerTool = class {
|
|
|
7939
7940
|
min: RULER_FONT_SIZE_MIN,
|
|
7940
7941
|
max: RULER_FONT_SIZE_MAX,
|
|
7941
7942
|
default: DEFAULT_FONT_SIZE
|
|
7943
|
+
},
|
|
7944
|
+
{
|
|
7945
|
+
id: RULER_DEBUG_KEY,
|
|
7946
|
+
type: "boolean",
|
|
7947
|
+
label: "Ruler Debug Log",
|
|
7948
|
+
default: false
|
|
7942
7949
|
}
|
|
7943
7950
|
],
|
|
7944
7951
|
[import_core8.ContributionPointIds.COMMANDS]: [
|
|
@@ -7975,7 +7982,11 @@ var RulerTool = class {
|
|
|
7975
7982
|
]
|
|
7976
7983
|
};
|
|
7977
7984
|
}
|
|
7985
|
+
isDebugEnabled() {
|
|
7986
|
+
return this.debugEnabled;
|
|
7987
|
+
}
|
|
7978
7988
|
log(step, payload) {
|
|
7989
|
+
if (!this.isDebugEnabled()) return;
|
|
7979
7990
|
if (payload) {
|
|
7980
7991
|
console.debug(`[RulerTool] ${step}`, payload);
|
|
7981
7992
|
return;
|
|
@@ -8004,6 +8015,7 @@ var RulerTool = class {
|
|
|
8004
8015
|
configService.get("ruler.fontSize", this.fontSize),
|
|
8005
8016
|
DEFAULT_FONT_SIZE
|
|
8006
8017
|
);
|
|
8018
|
+
this.debugEnabled = configService.get(RULER_DEBUG_KEY, this.debugEnabled) === true;
|
|
8007
8019
|
this.log("config:loaded", {
|
|
8008
8020
|
thickness: this.thickness,
|
|
8009
8021
|
gap: this.gap,
|