@pooder/kit 6.2.2 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.test-dist/src/extensions/image/ImageTool.js +134 -119
- package/.test-dist/src/extensions/image/commands.js +60 -40
- package/.test-dist/src/extensions/image/imageOperations.js +75 -0
- package/.test-dist/src/extensions/image/index.js +1 -0
- package/.test-dist/src/extensions/image/model.js +4 -0
- package/.test-dist/tests/run.js +39 -5
- package/CHANGELOG.md +6 -0
- package/dist/index.d.mts +250 -163
- package/dist/index.d.ts +250 -163
- package/dist/index.js +265 -157
- package/dist/index.mjs +261 -156
- package/package.json +1 -1
- package/src/extensions/image/ImageTool.ts +172 -145
- package/src/extensions/image/commands.ts +69 -48
- package/src/extensions/image/imageOperations.ts +135 -0
- package/src/extensions/image/index.ts +1 -0
- package/src/extensions/image/model.ts +13 -1
- package/tests/run.ts +49 -8
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { FrameRect } from "../../shared/scene/frame";
|
|
2
|
+
import {
|
|
3
|
+
getCoverScale as getCoverScaleFromRect,
|
|
4
|
+
type SourceSize,
|
|
5
|
+
} from "../../shared/imaging/sourceSizeCache";
|
|
6
|
+
|
|
7
|
+
export interface ImageOperationArea {
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
centerX: number;
|
|
11
|
+
centerY: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ImageOperationViewport {
|
|
15
|
+
left: number;
|
|
16
|
+
top: number;
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type ImageOperationAreaSpec =
|
|
22
|
+
| { type: "frame" }
|
|
23
|
+
| { type: "viewport" }
|
|
24
|
+
| ({
|
|
25
|
+
type: "custom";
|
|
26
|
+
} & ImageOperationArea);
|
|
27
|
+
|
|
28
|
+
export type ImageOperation =
|
|
29
|
+
| { type: "cover"; area?: ImageOperationAreaSpec }
|
|
30
|
+
| { type: "contain"; area?: ImageOperationAreaSpec }
|
|
31
|
+
| { type: "maximizeWidth"; area?: ImageOperationAreaSpec }
|
|
32
|
+
| { type: "maximizeHeight"; area?: ImageOperationAreaSpec }
|
|
33
|
+
| { type: "center"; area?: ImageOperationAreaSpec }
|
|
34
|
+
| { type: "resetTransform" };
|
|
35
|
+
|
|
36
|
+
export interface ComputeImageOperationArgs {
|
|
37
|
+
frame: FrameRect;
|
|
38
|
+
source: SourceSize;
|
|
39
|
+
operation: ImageOperation;
|
|
40
|
+
area: ImageOperationArea;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function clampNormalizedAnchor(value: number): number {
|
|
44
|
+
return Math.max(-1, Math.min(2, value));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function toNormalizedAnchor(center: number, start: number, size: number): number {
|
|
48
|
+
return clampNormalizedAnchor((center - start) / Math.max(1, size));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function resolveAbsoluteScale(
|
|
52
|
+
operation: ImageOperation,
|
|
53
|
+
area: ImageOperationArea,
|
|
54
|
+
source: SourceSize,
|
|
55
|
+
): number | null {
|
|
56
|
+
const widthScale = Math.max(1, area.width) / Math.max(1, source.width);
|
|
57
|
+
const heightScale = Math.max(1, area.height) / Math.max(1, source.height);
|
|
58
|
+
|
|
59
|
+
switch (operation.type) {
|
|
60
|
+
case "cover":
|
|
61
|
+
return Math.max(widthScale, heightScale);
|
|
62
|
+
case "contain":
|
|
63
|
+
return Math.min(widthScale, heightScale);
|
|
64
|
+
case "maximizeWidth":
|
|
65
|
+
return widthScale;
|
|
66
|
+
case "maximizeHeight":
|
|
67
|
+
return heightScale;
|
|
68
|
+
default:
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function resolveImageOperationArea(args: {
|
|
74
|
+
frame: FrameRect;
|
|
75
|
+
viewport: ImageOperationViewport;
|
|
76
|
+
area?: ImageOperationAreaSpec;
|
|
77
|
+
}): ImageOperationArea {
|
|
78
|
+
const spec = args.area || { type: "frame" };
|
|
79
|
+
|
|
80
|
+
if (spec.type === "custom") {
|
|
81
|
+
return {
|
|
82
|
+
width: Math.max(1, spec.width),
|
|
83
|
+
height: Math.max(1, spec.height),
|
|
84
|
+
centerX: spec.centerX,
|
|
85
|
+
centerY: spec.centerY,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (spec.type === "viewport") {
|
|
90
|
+
return {
|
|
91
|
+
width: Math.max(1, args.viewport.width),
|
|
92
|
+
height: Math.max(1, args.viewport.height),
|
|
93
|
+
centerX: args.viewport.left + args.viewport.width / 2,
|
|
94
|
+
centerY: args.viewport.top + args.viewport.height / 2,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
width: Math.max(1, args.frame.width),
|
|
100
|
+
height: Math.max(1, args.frame.height),
|
|
101
|
+
centerX: args.frame.left + args.frame.width / 2,
|
|
102
|
+
centerY: args.frame.top + args.frame.height / 2,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function computeImageOperationUpdates(
|
|
107
|
+
args: ComputeImageOperationArgs,
|
|
108
|
+
): { scale?: number; left?: number; top?: number; angle?: number } {
|
|
109
|
+
const { frame, source, operation, area } = args;
|
|
110
|
+
|
|
111
|
+
if (operation.type === "resetTransform") {
|
|
112
|
+
return {
|
|
113
|
+
scale: 1,
|
|
114
|
+
left: 0.5,
|
|
115
|
+
top: 0.5,
|
|
116
|
+
angle: 0,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const left = toNormalizedAnchor(area.centerX, frame.left, frame.width);
|
|
121
|
+
const top = toNormalizedAnchor(area.centerY, frame.top, frame.height);
|
|
122
|
+
|
|
123
|
+
if (operation.type === "center") {
|
|
124
|
+
return { left, top };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const absoluteScale = resolveAbsoluteScale(operation, area, source);
|
|
128
|
+
const coverScale = getCoverScaleFromRect(frame, source);
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
scale: Math.max(0.05, (absoluteScale || coverScale) / coverScale),
|
|
132
|
+
left,
|
|
133
|
+
top,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
@@ -1 +1,13 @@
|
|
|
1
|
-
export type {
|
|
1
|
+
export type {
|
|
2
|
+
ImageItem,
|
|
3
|
+
ImageTransformUpdates,
|
|
4
|
+
ImageViewState,
|
|
5
|
+
} from "./ImageTool";
|
|
6
|
+
|
|
7
|
+
import type { ImageViewState } from "./ImageTool";
|
|
8
|
+
|
|
9
|
+
export function hasAnyImageInViewState(
|
|
10
|
+
state: ImageViewState | null | undefined,
|
|
11
|
+
): boolean {
|
|
12
|
+
return Boolean(state?.hasAnyImage);
|
|
13
|
+
}
|
package/tests/run.ts
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
normalizePointInGeometry,
|
|
26
26
|
resolveFeaturePosition,
|
|
27
27
|
} from "../src/extensions/featureCoordinates";
|
|
28
|
+
import { hasAnyImageInViewState } from "../src/extensions/image/model";
|
|
28
29
|
|
|
29
30
|
function assert(condition: unknown, message: string) {
|
|
30
31
|
if (!condition) throw new Error(message);
|
|
@@ -288,6 +289,46 @@ function testVisibilityDsl() {
|
|
|
288
289
|
);
|
|
289
290
|
}
|
|
290
291
|
|
|
292
|
+
function testImageViewStateHelper() {
|
|
293
|
+
assert(hasAnyImageInViewState(null) === false, "null image state should be empty");
|
|
294
|
+
assert(
|
|
295
|
+
hasAnyImageInViewState({
|
|
296
|
+
items: [],
|
|
297
|
+
hasAnyImage: false,
|
|
298
|
+
focusedId: null,
|
|
299
|
+
focusedItem: null,
|
|
300
|
+
isToolActive: false,
|
|
301
|
+
isImageSelectionActive: false,
|
|
302
|
+
hasWorkingChanges: false,
|
|
303
|
+
source: "committed",
|
|
304
|
+
}) === false,
|
|
305
|
+
"empty image state should report false",
|
|
306
|
+
);
|
|
307
|
+
assert(
|
|
308
|
+
hasAnyImageInViewState({
|
|
309
|
+
items: [
|
|
310
|
+
{
|
|
311
|
+
id: "img-1",
|
|
312
|
+
url: "blob:test",
|
|
313
|
+
opacity: 1,
|
|
314
|
+
},
|
|
315
|
+
],
|
|
316
|
+
hasAnyImage: true,
|
|
317
|
+
focusedId: "img-1",
|
|
318
|
+
focusedItem: {
|
|
319
|
+
id: "img-1",
|
|
320
|
+
url: "blob:test",
|
|
321
|
+
opacity: 1,
|
|
322
|
+
},
|
|
323
|
+
isToolActive: true,
|
|
324
|
+
isImageSelectionActive: true,
|
|
325
|
+
hasWorkingChanges: true,
|
|
326
|
+
source: "working",
|
|
327
|
+
}) === true,
|
|
328
|
+
"non-empty image state should report true",
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
291
332
|
function testContributionCompatibility() {
|
|
292
333
|
const imageCommandNames = createImageCommands({} as any).map(
|
|
293
334
|
(entry) => entry.command,
|
|
@@ -303,13 +344,12 @@ function testContributionCompatibility() {
|
|
|
303
344
|
const expectedImageCommands = [
|
|
304
345
|
"addImage",
|
|
305
346
|
"upsertImage",
|
|
306
|
-
"
|
|
307
|
-
"
|
|
308
|
-
"
|
|
347
|
+
"applyImageOperation",
|
|
348
|
+
"getImageViewState",
|
|
349
|
+
"setImageTransform",
|
|
350
|
+
"imageSessionReset",
|
|
309
351
|
"completeImages",
|
|
310
352
|
"exportUserCroppedImage",
|
|
311
|
-
"fitImageToArea",
|
|
312
|
-
"fitImageToDefaultArea",
|
|
313
353
|
"focusImage",
|
|
314
354
|
"removeImage",
|
|
315
355
|
"updateImage",
|
|
@@ -432,9 +472,10 @@ function main() {
|
|
|
432
472
|
testBridgeSelection();
|
|
433
473
|
testMaskOps();
|
|
434
474
|
testEdgeScale();
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
475
|
+
testFeaturePlacementProjection();
|
|
476
|
+
testVisibilityDsl();
|
|
477
|
+
testImageViewStateHelper();
|
|
478
|
+
testContributionCompatibility();
|
|
438
479
|
console.log("ok");
|
|
439
480
|
}
|
|
440
481
|
|