@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/package.json
CHANGED
|
@@ -26,6 +26,10 @@ import {
|
|
|
26
26
|
readDielineState,
|
|
27
27
|
} from "./model";
|
|
28
28
|
import { buildDielineRenderBundle } from "./renderBuilder";
|
|
29
|
+
import {
|
|
30
|
+
projectPlacedFeatures,
|
|
31
|
+
resolveFeaturePlacements,
|
|
32
|
+
} from "../featurePlacement";
|
|
29
33
|
|
|
30
34
|
export class DielineTool implements Extension {
|
|
31
35
|
id = "pooder.kit.dieline";
|
|
@@ -320,15 +324,31 @@ export class DielineTool implements Extension {
|
|
|
320
324
|
const cutR =
|
|
321
325
|
visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
322
326
|
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
327
|
+
const placements = resolveFeaturePlacements(features || [], {
|
|
328
|
+
shape,
|
|
329
|
+
shapeStyle,
|
|
330
|
+
pathData,
|
|
331
|
+
customSourceWidthPx: this.state.customSourceWidthPx,
|
|
332
|
+
customSourceHeightPx: this.state.customSourceHeightPx,
|
|
333
|
+
canvasWidth: canvasW,
|
|
334
|
+
canvasHeight: canvasH,
|
|
335
|
+
x: cx,
|
|
336
|
+
y: cy,
|
|
337
|
+
width: sceneLayout.trimRect.width,
|
|
338
|
+
height: sceneLayout.trimRect.height,
|
|
339
|
+
radius: visualRadius,
|
|
340
|
+
scale,
|
|
341
|
+
});
|
|
342
|
+
const cutFeatures = projectPlacedFeatures(
|
|
343
|
+
placements.filter((placement) => !placement.feature.skipCut),
|
|
344
|
+
{
|
|
345
|
+
x: cx,
|
|
346
|
+
y: cy,
|
|
347
|
+
width: cutW,
|
|
348
|
+
height: cutH,
|
|
349
|
+
},
|
|
350
|
+
scale,
|
|
351
|
+
);
|
|
332
352
|
|
|
333
353
|
const generatedPathData = generateDielinePath({
|
|
334
354
|
shape,
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import type { Pattern } from "fabric";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
RenderEffectSpec,
|
|
4
|
+
RenderObjectSpec,
|
|
5
|
+
VisibilityExpr,
|
|
6
|
+
} from "../../services";
|
|
3
7
|
import type { SceneLayoutSnapshot } from "../../shared/scene/sceneLayoutModel";
|
|
4
8
|
import { generateBleedZonePath, generateDielinePath } from "../geometry";
|
|
9
|
+
import {
|
|
10
|
+
projectPlacedFeatures,
|
|
11
|
+
resolveFeaturePlacements,
|
|
12
|
+
} from "../featurePlacement";
|
|
5
13
|
import { IMAGE_OBJECT_LAYER_ID } from "../../shared/constants/layers";
|
|
6
14
|
import type { DielineState } from "./model";
|
|
7
15
|
|
|
@@ -41,17 +49,6 @@ const DEFAULT_IDS: DielineRenderIds = {
|
|
|
41
49
|
clipSource: "dieline.effect.clip-path",
|
|
42
50
|
};
|
|
43
51
|
|
|
44
|
-
function scaleFeatures(state: DielineState, scale: number) {
|
|
45
|
-
return (state.features || []).map((feature) => ({
|
|
46
|
-
...feature,
|
|
47
|
-
x: feature.x,
|
|
48
|
-
y: feature.y,
|
|
49
|
-
width: (feature.width || 0) * scale,
|
|
50
|
-
height: (feature.height || 0) * scale,
|
|
51
|
-
radius: (feature.radius || 0) * scale,
|
|
52
|
-
}));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
52
|
export function buildDielineRenderBundle(
|
|
56
53
|
options: DielineRenderOptions,
|
|
57
54
|
): DielineRenderBundle {
|
|
@@ -67,7 +64,8 @@ export function buildDielineRenderBundle(
|
|
|
67
64
|
clipTargetPassIds = [IMAGE_OBJECT_LAYER_ID],
|
|
68
65
|
clipVisibility,
|
|
69
66
|
} = options;
|
|
70
|
-
const { shape, shapeStyle, radius, mainLine, offsetLine, insideColor } =
|
|
67
|
+
const { shape, shapeStyle, radius, mainLine, offsetLine, insideColor } =
|
|
68
|
+
state;
|
|
71
69
|
|
|
72
70
|
const scale = sceneLayout.scale;
|
|
73
71
|
const cx = sceneLayout.trimRect.centerX;
|
|
@@ -80,8 +78,41 @@ export function buildDielineRenderBundle(
|
|
|
80
78
|
const visualOffset = (cutW - visualWidth) / 2;
|
|
81
79
|
const cutR =
|
|
82
80
|
visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
83
|
-
const
|
|
84
|
-
|
|
81
|
+
const placements = resolveFeaturePlacements(state.features || [], {
|
|
82
|
+
shape,
|
|
83
|
+
shapeStyle,
|
|
84
|
+
pathData: state.pathData,
|
|
85
|
+
customSourceWidthPx: state.customSourceWidthPx,
|
|
86
|
+
customSourceHeightPx: state.customSourceHeightPx,
|
|
87
|
+
canvasWidth,
|
|
88
|
+
canvasHeight,
|
|
89
|
+
x: cx,
|
|
90
|
+
y: cy,
|
|
91
|
+
width: visualWidth,
|
|
92
|
+
height: visualHeight,
|
|
93
|
+
radius: visualRadius,
|
|
94
|
+
scale,
|
|
95
|
+
});
|
|
96
|
+
const absoluteFeatures = projectPlacedFeatures(
|
|
97
|
+
placements,
|
|
98
|
+
{
|
|
99
|
+
x: cx,
|
|
100
|
+
y: cy,
|
|
101
|
+
width: visualWidth,
|
|
102
|
+
height: visualHeight,
|
|
103
|
+
},
|
|
104
|
+
scale,
|
|
105
|
+
);
|
|
106
|
+
const cutFeatures = projectPlacedFeatures(
|
|
107
|
+
placements.filter((placement) => !placement.feature.skipCut),
|
|
108
|
+
{
|
|
109
|
+
x: cx,
|
|
110
|
+
y: cy,
|
|
111
|
+
width: cutW,
|
|
112
|
+
height: cutH,
|
|
113
|
+
},
|
|
114
|
+
scale,
|
|
115
|
+
);
|
|
85
116
|
|
|
86
117
|
const common = {
|
|
87
118
|
shape,
|
|
@@ -92,6 +123,13 @@ export function buildDielineRenderBundle(
|
|
|
92
123
|
canvasWidth,
|
|
93
124
|
canvasHeight,
|
|
94
125
|
};
|
|
126
|
+
const cutFrameRect = {
|
|
127
|
+
left: cx - cutW / 2,
|
|
128
|
+
top: cy - cutH / 2,
|
|
129
|
+
width: cutW,
|
|
130
|
+
height: cutH,
|
|
131
|
+
space: "screen" as const,
|
|
132
|
+
};
|
|
95
133
|
|
|
96
134
|
const specs: RenderObjectSpec[] = [];
|
|
97
135
|
|
|
@@ -232,9 +270,13 @@ export function buildDielineRenderBundle(
|
|
|
232
270
|
width: cutW,
|
|
233
271
|
height: cutH,
|
|
234
272
|
radius: cutR,
|
|
235
|
-
|
|
236
|
-
|
|
273
|
+
// Build the clip path in the cut frame's local coordinates so Fabric
|
|
274
|
+
// does not have to infer placement from the standalone path bounds.
|
|
275
|
+
x: cutW / 2,
|
|
276
|
+
y: cutH / 2,
|
|
237
277
|
features: cutFeatures,
|
|
278
|
+
canvasWidth: cutW,
|
|
279
|
+
canvasHeight: cutH,
|
|
238
280
|
});
|
|
239
281
|
|
|
240
282
|
if (!clipPathData) {
|
|
@@ -253,6 +295,12 @@ export function buildDielineRenderBundle(
|
|
|
253
295
|
id: ids.clipSource,
|
|
254
296
|
type: "path",
|
|
255
297
|
space: "screen",
|
|
298
|
+
layout: {
|
|
299
|
+
reference: "custom",
|
|
300
|
+
referenceRect: cutFrameRect,
|
|
301
|
+
alignX: "start",
|
|
302
|
+
alignY: "start",
|
|
303
|
+
},
|
|
256
304
|
data: {
|
|
257
305
|
id: ids.clipSource,
|
|
258
306
|
type: "dieline-effect",
|
|
@@ -13,9 +13,9 @@ import {
|
|
|
13
13
|
RenderObjectSpec,
|
|
14
14
|
RenderPassSpec,
|
|
15
15
|
} from "../../services";
|
|
16
|
-
import { resolveFeaturePosition } from "../geometry";
|
|
17
16
|
import { ConstraintRegistry, ConstraintFeature } from "../constraints";
|
|
18
17
|
import { completeFeaturesStrict } from "../featureComplete";
|
|
18
|
+
import { resolveFeaturePlacements } from "../featurePlacement";
|
|
19
19
|
import {
|
|
20
20
|
computeSceneLayout,
|
|
21
21
|
readSizeState,
|
|
@@ -935,10 +935,30 @@ export class FeatureTool implements Extension {
|
|
|
935
935
|
|
|
936
936
|
const groups = new Map<string, MarkerRenderState[]>();
|
|
937
937
|
const singles: MarkerRenderState[] = [];
|
|
938
|
+
const placements = resolveFeaturePlacements(
|
|
939
|
+
this.workingFeatures,
|
|
940
|
+
{
|
|
941
|
+
shape: this.currentGeometry.shape,
|
|
942
|
+
shapeStyle: this.currentGeometry.shapeStyle,
|
|
943
|
+
pathData: this.currentGeometry.pathData,
|
|
944
|
+
customSourceWidthPx: this.currentGeometry.customSourceWidthPx,
|
|
945
|
+
customSourceHeightPx: this.currentGeometry.customSourceHeightPx,
|
|
946
|
+
x: this.currentGeometry.x,
|
|
947
|
+
y: this.currentGeometry.y,
|
|
948
|
+
width: this.currentGeometry.width,
|
|
949
|
+
height: this.currentGeometry.height,
|
|
950
|
+
radius: this.currentGeometry.radius,
|
|
951
|
+
scale: this.currentGeometry.scale || 1,
|
|
952
|
+
},
|
|
953
|
+
);
|
|
938
954
|
|
|
939
|
-
|
|
955
|
+
placements.forEach((placement, index) => {
|
|
956
|
+
const feature = placement.feature;
|
|
940
957
|
const geometry = this.getGeometryForFeature(this.currentGeometry!, feature);
|
|
941
|
-
const position =
|
|
958
|
+
const position = {
|
|
959
|
+
x: placement.centerX,
|
|
960
|
+
y: placement.centerY,
|
|
961
|
+
};
|
|
942
962
|
const scale = geometry.scale || 1;
|
|
943
963
|
const marker: MarkerRenderState = {
|
|
944
964
|
feature,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { DielineFeature } from "./geometry";
|
|
2
|
+
|
|
3
|
+
export interface FeatureCoordinateGeometry {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function resolveFeaturePosition(
|
|
11
|
+
feature: Pick<DielineFeature, "x" | "y">,
|
|
12
|
+
geometry: FeatureCoordinateGeometry,
|
|
13
|
+
): { x: number; y: number } {
|
|
14
|
+
const { x, y, width, height } = geometry;
|
|
15
|
+
const left = x - width / 2;
|
|
16
|
+
const top = y - height / 2;
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
x: left + feature.x * width,
|
|
20
|
+
y: top + feature.y * height,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function normalizePointInGeometry(
|
|
25
|
+
point: { x: number; y: number },
|
|
26
|
+
geometry: FeatureCoordinateGeometry,
|
|
27
|
+
): { x: number; y: number } {
|
|
28
|
+
const left = geometry.x - geometry.width / 2;
|
|
29
|
+
const top = geometry.y - geometry.height / 2;
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
x: geometry.width > 0 ? (point.x - left) / geometry.width : 0.5,
|
|
33
|
+
y: geometry.height > 0 ? (point.y - top) / geometry.height : 0.5,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { ConstraintFeature, ConstraintRegistry } from "./constraints";
|
|
2
|
+
import type { GeometryOptions } from "./geometry";
|
|
3
|
+
import {
|
|
4
|
+
normalizePointInGeometry,
|
|
5
|
+
resolveFeaturePosition,
|
|
6
|
+
} from "./featureCoordinates";
|
|
7
|
+
|
|
8
|
+
export interface FeaturePlacementGeometry
|
|
9
|
+
extends Pick<
|
|
10
|
+
GeometryOptions,
|
|
11
|
+
| "shape"
|
|
12
|
+
| "shapeStyle"
|
|
13
|
+
| "pathData"
|
|
14
|
+
| "customSourceWidthPx"
|
|
15
|
+
| "customSourceHeightPx"
|
|
16
|
+
| "canvasWidth"
|
|
17
|
+
| "canvasHeight"
|
|
18
|
+
> {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
radius: number;
|
|
24
|
+
scale: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface FeatureProjectionGeometry {
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
width: number;
|
|
31
|
+
height: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface FeaturePlacement<TFeature extends ConstraintFeature = ConstraintFeature> {
|
|
35
|
+
feature: TFeature;
|
|
36
|
+
normalizedX: number;
|
|
37
|
+
normalizedY: number;
|
|
38
|
+
centerX: number;
|
|
39
|
+
centerY: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function scaleFeatureForRender<TFeature extends ConstraintFeature>(
|
|
43
|
+
feature: TFeature,
|
|
44
|
+
scale: number,
|
|
45
|
+
x: number,
|
|
46
|
+
y: number,
|
|
47
|
+
): TFeature {
|
|
48
|
+
return {
|
|
49
|
+
...feature,
|
|
50
|
+
x,
|
|
51
|
+
y,
|
|
52
|
+
width: feature.width !== undefined ? feature.width * scale : undefined,
|
|
53
|
+
height: feature.height !== undefined ? feature.height * scale : undefined,
|
|
54
|
+
radius: feature.radius !== undefined ? feature.radius * scale : undefined,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function resolveFeaturePlacements<TFeature extends ConstraintFeature>(
|
|
59
|
+
features: TFeature[],
|
|
60
|
+
geometry: FeaturePlacementGeometry,
|
|
61
|
+
): FeaturePlacement<TFeature>[] {
|
|
62
|
+
const dielineWidth =
|
|
63
|
+
geometry.scale > 0 ? geometry.width / geometry.scale : geometry.width;
|
|
64
|
+
const dielineHeight =
|
|
65
|
+
geometry.scale > 0 ? geometry.height / geometry.scale : geometry.height;
|
|
66
|
+
|
|
67
|
+
return (features || []).map((feature) => {
|
|
68
|
+
const activeConstraints = feature.constraints?.filter(
|
|
69
|
+
(constraint) => !constraint.validateOnly,
|
|
70
|
+
);
|
|
71
|
+
const constrained = ConstraintRegistry.apply(
|
|
72
|
+
feature.x,
|
|
73
|
+
feature.y,
|
|
74
|
+
feature,
|
|
75
|
+
{
|
|
76
|
+
dielineWidth,
|
|
77
|
+
dielineHeight,
|
|
78
|
+
geometry,
|
|
79
|
+
},
|
|
80
|
+
activeConstraints,
|
|
81
|
+
);
|
|
82
|
+
const center = resolveFeaturePosition(
|
|
83
|
+
{
|
|
84
|
+
...feature,
|
|
85
|
+
x: constrained.x,
|
|
86
|
+
y: constrained.y,
|
|
87
|
+
},
|
|
88
|
+
geometry,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
feature,
|
|
93
|
+
normalizedX: constrained.x,
|
|
94
|
+
normalizedY: constrained.y,
|
|
95
|
+
centerX: center.x,
|
|
96
|
+
centerY: center.y,
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function projectPlacedFeatures<TFeature extends ConstraintFeature>(
|
|
102
|
+
placements: FeaturePlacement<TFeature>[],
|
|
103
|
+
geometry: FeatureProjectionGeometry,
|
|
104
|
+
scale: number,
|
|
105
|
+
): TFeature[] {
|
|
106
|
+
return placements.map((placement) => {
|
|
107
|
+
const normalized = normalizePointInGeometry(
|
|
108
|
+
{ x: placement.centerX, y: placement.centerY },
|
|
109
|
+
geometry,
|
|
110
|
+
);
|
|
111
|
+
return scaleFeatureForRender(
|
|
112
|
+
placement.feature,
|
|
113
|
+
scale,
|
|
114
|
+
normalized.x,
|
|
115
|
+
normalized.y,
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
}
|