@lmb-it/kitsconcerto-native 0.1.285 → 0.1.286
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/apps/mobile/src/ui/accordion/index.js +2 -2
- package/components/ImageCropper/compression.cjs +63 -0
- package/components/ImageCropper/compression.cjs.map +1 -0
- package/components/ImageCropper/compression.js +58 -0
- package/components/ImageCropper/compression.js.map +1 -0
- package/components/ImageCropper/constants.cjs +35 -0
- package/components/ImageCropper/constants.cjs.map +1 -0
- package/components/ImageCropper/constants.js +23 -0
- package/components/ImageCropper/constants.js.map +1 -0
- package/components/ImageCropper/cropperState.cjs +119 -0
- package/components/ImageCropper/cropperState.cjs.map +1 -0
- package/components/ImageCropper/cropperState.js +114 -0
- package/components/ImageCropper/cropperState.js.map +1 -0
- package/components/ImageCropper/encode.cjs +45 -0
- package/components/ImageCropper/encode.cjs.map +1 -0
- package/components/ImageCropper/encode.js +42 -0
- package/components/ImageCropper/encode.js.map +1 -0
- package/components/ImageCropper/encoderRegistry.cjs +13 -0
- package/components/ImageCropper/encoderRegistry.cjs.map +1 -0
- package/components/ImageCropper/encoderRegistry.js +9 -0
- package/components/ImageCropper/encoderRegistry.js.map +1 -0
- package/components/ImageCropper/encoders/rnImageEncoder.cjs +54 -0
- package/components/ImageCropper/encoders/rnImageEncoder.cjs.map +1 -0
- package/components/ImageCropper/encoders/rnImageEncoder.js +51 -0
- package/components/ImageCropper/encoders/rnImageEncoder.js.map +1 -0
- package/components/ImageCropper/geometry.cjs +208 -0
- package/components/ImageCropper/geometry.cjs.map +1 -0
- package/components/ImageCropper/geometry.js +186 -0
- package/components/ImageCropper/geometry.js.map +1 -0
- package/components/ImageCropper/index.cjs +436 -0
- package/components/ImageCropper/index.cjs.map +1 -0
- package/components/ImageCropper/index.js +434 -0
- package/components/ImageCropper/index.js.map +1 -0
- package/components/ImageCropper/pinchZoom.cjs +8 -0
- package/components/ImageCropper/pinchZoom.cjs.map +1 -0
- package/components/ImageCropper/pinchZoom.js +5 -0
- package/components/ImageCropper/pinchZoom.js.map +1 -0
- package/components/ImageCropper/useImageSource.cjs +87 -0
- package/components/ImageCropper/useImageSource.cjs.map +1 -0
- package/components/ImageCropper/useImageSource.js +84 -0
- package/components/ImageCropper/useImageSource.js.map +1 -0
- package/hooks/useComponentDefaults.cjs +12 -0
- package/hooks/useComponentDefaults.cjs.map +1 -1
- package/hooks/useComponentDefaults.js +12 -0
- package/hooks/useComponentDefaults.js.map +1 -1
- package/index.cjs +120 -115
- package/index.cjs.map +1 -1
- package/index.d.ts +394 -3
- package/index.js +2 -0
- package/index.js.map +1 -1
- package/package.json +13 -1
- package/packages/types/src/Theme/defaultTheme.cjs +111 -0
- package/packages/types/src/Theme/defaultTheme.cjs.map +1 -1
- package/packages/types/src/Theme/defaultTheme.js +111 -0
- package/packages/types/src/Theme/defaultTheme.js.map +1 -1
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
const clamp = (value, min, max) => {
|
|
2
|
+
if (max < min) return min;
|
|
3
|
+
return value < min ? min : value > max ? max : value;
|
|
4
|
+
};
|
|
5
|
+
const aspectOf = (aspect) => aspect[0] / aspect[1];
|
|
6
|
+
const coverScale = (content, target) => Math.max(target.width / content.width, target.height / content.height);
|
|
7
|
+
const rectRight = (r) => r.x + r.width;
|
|
8
|
+
const rectBottom = (r) => r.y + r.height;
|
|
9
|
+
const rectCenter = (r) => ({
|
|
10
|
+
x: r.x + r.width / 2,
|
|
11
|
+
y: r.y + r.height / 2
|
|
12
|
+
});
|
|
13
|
+
const largestRectOfRatio = (bounds, ratio) => {
|
|
14
|
+
const byWidth = { width: bounds.width, height: bounds.width / ratio };
|
|
15
|
+
return byWidth.height <= bounds.height ? byWidth : { width: bounds.height * ratio, height: bounds.height };
|
|
16
|
+
};
|
|
17
|
+
const centerRect = (size, within) => ({
|
|
18
|
+
x: (within.width - size.width) / 2,
|
|
19
|
+
y: (within.height - size.height) / 2,
|
|
20
|
+
width: size.width,
|
|
21
|
+
height: size.height
|
|
22
|
+
});
|
|
23
|
+
const clampRectInside = (rect, bounds) => ({
|
|
24
|
+
...rect,
|
|
25
|
+
x: clamp(rect.x, bounds.x, rectRight(bounds) - rect.width),
|
|
26
|
+
y: clamp(rect.y, bounds.y, rectBottom(bounds) - rect.height)
|
|
27
|
+
});
|
|
28
|
+
const orientedSize = (source, rotation) => rotation === 90 || rotation === 270 ? { width: source.height, height: source.width } : { width: source.width, height: source.height };
|
|
29
|
+
const orientedPointToSource = (p, source, rotation) => {
|
|
30
|
+
switch (rotation) {
|
|
31
|
+
case 90:
|
|
32
|
+
return { x: p.y, y: source.height - p.x };
|
|
33
|
+
case 180:
|
|
34
|
+
return { x: source.width - p.x, y: source.height - p.y };
|
|
35
|
+
case 270:
|
|
36
|
+
return { x: source.width - p.y, y: p.x };
|
|
37
|
+
default:
|
|
38
|
+
return { x: p.x, y: p.y };
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const orientedRectToSource = (rect, source, rotation) => {
|
|
42
|
+
const a = orientedPointToSource({ x: rect.x, y: rect.y }, source, rotation);
|
|
43
|
+
const b = orientedPointToSource(
|
|
44
|
+
{ x: rect.x + rect.width, y: rect.y + rect.height },
|
|
45
|
+
source,
|
|
46
|
+
rotation
|
|
47
|
+
);
|
|
48
|
+
return {
|
|
49
|
+
x: Math.min(a.x, b.x),
|
|
50
|
+
y: Math.min(a.y, b.y),
|
|
51
|
+
width: Math.abs(b.x - a.x),
|
|
52
|
+
height: Math.abs(b.y - a.y)
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
const imageRect = (oriented, t) => ({
|
|
56
|
+
x: t.x,
|
|
57
|
+
y: t.y,
|
|
58
|
+
width: oriented.width * t.scale,
|
|
59
|
+
height: oriented.height * t.scale
|
|
60
|
+
});
|
|
61
|
+
const viewportToOriented = (p, t) => ({
|
|
62
|
+
x: (p.x - t.x) / t.scale,
|
|
63
|
+
y: (p.y - t.y) / t.scale
|
|
64
|
+
});
|
|
65
|
+
const zoomAbout = (t, anchor, nextScale) => {
|
|
66
|
+
const k = nextScale / t.scale;
|
|
67
|
+
return {
|
|
68
|
+
scale: nextScale,
|
|
69
|
+
x: anchor.x - (anchor.x - t.x) * k,
|
|
70
|
+
y: anchor.y - (anchor.y - t.y) * k
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
const minScaleToCover = (oriented, crop) => coverScale(oriented, crop);
|
|
74
|
+
const coverAxis = (imgStart, imgLen, cropStart, cropLen) => imgLen < cropLen ? cropStart + (cropLen - imgLen) / 2 : clamp(imgStart, cropStart + cropLen - imgLen, cropStart);
|
|
75
|
+
const clampTransformToCover = (t, oriented, crop) => {
|
|
76
|
+
const img = imageRect(oriented, t);
|
|
77
|
+
return {
|
|
78
|
+
scale: t.scale,
|
|
79
|
+
x: coverAxis(img.x, img.width, crop.x, crop.width),
|
|
80
|
+
y: coverAxis(img.y, img.height, crop.y, crop.height)
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
const anchorFor = (rect, handle) => {
|
|
84
|
+
const c = rectCenter(rect);
|
|
85
|
+
switch (handle) {
|
|
86
|
+
case "se":
|
|
87
|
+
return { x: rect.x, y: rect.y };
|
|
88
|
+
case "sw":
|
|
89
|
+
return { x: rectRight(rect), y: rect.y };
|
|
90
|
+
case "ne":
|
|
91
|
+
return { x: rect.x, y: rectBottom(rect) };
|
|
92
|
+
case "nw":
|
|
93
|
+
return { x: rectRight(rect), y: rectBottom(rect) };
|
|
94
|
+
case "e":
|
|
95
|
+
return { x: rect.x, y: c.y };
|
|
96
|
+
case "w":
|
|
97
|
+
return { x: rectRight(rect), y: c.y };
|
|
98
|
+
case "s":
|
|
99
|
+
return { x: c.x, y: rect.y };
|
|
100
|
+
default:
|
|
101
|
+
return { x: c.x, y: rectBottom(rect) };
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const growsLeft = (h) => h === "w" || h === "nw" || h === "sw";
|
|
105
|
+
const growsUp = (h) => h === "n" || h === "ne" || h === "nw";
|
|
106
|
+
const movesX = (h) => h !== "n" && h !== "s";
|
|
107
|
+
const movesY = (h) => h !== "e" && h !== "w";
|
|
108
|
+
const resizeRect = (rect, handle, pointer, options = {}) => {
|
|
109
|
+
const { ratio, min, bounds } = options;
|
|
110
|
+
const anchor = anchorFor(rect, handle);
|
|
111
|
+
let width = movesX(handle) ? growsLeft(handle) ? anchor.x - pointer.x : pointer.x - anchor.x : rect.width;
|
|
112
|
+
let height = movesY(handle) ? growsUp(handle) ? anchor.y - pointer.y : pointer.y - anchor.y : rect.height;
|
|
113
|
+
if (ratio) {
|
|
114
|
+
if (movesX(handle) && movesY(handle)) {
|
|
115
|
+
width = Math.max(width, height * ratio);
|
|
116
|
+
height = width / ratio;
|
|
117
|
+
} else if (movesX(handle)) {
|
|
118
|
+
height = width / ratio;
|
|
119
|
+
} else {
|
|
120
|
+
width = height * ratio;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (bounds) {
|
|
124
|
+
const c = rectCenter(rect);
|
|
125
|
+
const maxWidth = Math.max(0, movesX(handle) ? growsLeft(handle) ? anchor.x - bounds.x : rectRight(bounds) - anchor.x : 2 * Math.min(c.x - bounds.x, rectRight(bounds) - c.x));
|
|
126
|
+
const maxHeight = Math.max(0, movesY(handle) ? growsUp(handle) ? anchor.y - bounds.y : rectBottom(bounds) - anchor.y : 2 * Math.min(c.y - bounds.y, rectBottom(bounds) - c.y));
|
|
127
|
+
if (ratio) {
|
|
128
|
+
if (width > 0 && height > 0) {
|
|
129
|
+
const shrink = Math.min(1, maxWidth / width, maxHeight / height);
|
|
130
|
+
if (shrink < 1 && Number.isFinite(shrink)) {
|
|
131
|
+
width *= shrink;
|
|
132
|
+
height *= shrink;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
if (width > 0) width = Math.min(width, maxWidth);
|
|
137
|
+
if (height > 0) height = Math.min(height, maxHeight);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (min) {
|
|
141
|
+
const minWidth = ratio ? Math.max(min.width, min.height * ratio) : min.width;
|
|
142
|
+
const minHeight = ratio ? minWidth / ratio : min.height;
|
|
143
|
+
if (width < minWidth) {
|
|
144
|
+
width = minWidth;
|
|
145
|
+
if (ratio) height = minHeight;
|
|
146
|
+
}
|
|
147
|
+
if (height < minHeight) {
|
|
148
|
+
height = minHeight;
|
|
149
|
+
if (ratio) width = minWidth;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const x = movesX(handle) ? growsLeft(handle) ? anchor.x - width : anchor.x : anchor.x - width / 2;
|
|
153
|
+
const y = movesY(handle) ? growsUp(handle) ? anchor.y - height : anchor.y : anchor.y - height / 2;
|
|
154
|
+
return { x, y, width, height };
|
|
155
|
+
};
|
|
156
|
+
const cropToSourceRect = (crop, t, source, rotation = 0) => {
|
|
157
|
+
const topLeft = viewportToOriented({ x: crop.x, y: crop.y }, t);
|
|
158
|
+
const bottomRight = viewportToOriented({ x: rectRight(crop), y: rectBottom(crop) }, t);
|
|
159
|
+
const oriented = {
|
|
160
|
+
x: topLeft.x,
|
|
161
|
+
y: topLeft.y,
|
|
162
|
+
width: bottomRight.x - topLeft.x,
|
|
163
|
+
height: bottomRight.y - topLeft.y
|
|
164
|
+
};
|
|
165
|
+
const inSource = orientedRectToSource(oriented, source, rotation);
|
|
166
|
+
const x = clamp(inSource.x, 0, source.width);
|
|
167
|
+
const y = clamp(inSource.y, 0, source.height);
|
|
168
|
+
return {
|
|
169
|
+
x,
|
|
170
|
+
y,
|
|
171
|
+
width: clamp(inSource.width, 0, source.width - x),
|
|
172
|
+
height: clamp(inSource.height, 0, source.height - y)
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
const fitWithin = (size, max) => {
|
|
176
|
+
const limits = [
|
|
177
|
+
max.width && max.width > 0 ? max.width / size.width : 1,
|
|
178
|
+
max.height && max.height > 0 ? max.height / size.height : 1
|
|
179
|
+
];
|
|
180
|
+
const scale = Math.min(1, ...limits);
|
|
181
|
+
return { width: size.width * scale, height: size.height * scale };
|
|
182
|
+
};
|
|
183
|
+
const rotateCw = (rotation) => (rotation + 90) % 360;
|
|
184
|
+
|
|
185
|
+
export { aspectOf, centerRect, clamp, clampRectInside, clampTransformToCover, coverScale, cropToSourceRect, fitWithin, imageRect, largestRectOfRatio, minScaleToCover, orientedPointToSource, orientedRectToSource, orientedSize, rectBottom, rectCenter, rectRight, resizeRect, rotateCw, viewportToOriented, zoomAbout };
|
|
186
|
+
//# sourceMappingURL=geometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.js","sources":["../../../../src/components/ImageCropper/geometry.ts"],"sourcesContent":["import type {\n CropAspect,\n CropHandle,\n CropRotation,\n ICropPoint,\n ICropRect,\n ICropResizeOptions,\n ICropSize,\n ICropTransform,\n} from \"@lmb-it/kitsconcerto-types\";\n\n/**\n * The cropper's arithmetic. No React, no DOM, no React Native — this file is\n * the half of the component that both platforms run unchanged.\n *\n * ⛔ IT IS SPLIT OUT BECAUSE THE TWO RENDERERS AGREEING IS NOT OPTIONAL. A web\n * cropper and a native cropper that each derive \"where did the user actually\n * crop\" from their own copy of this maths will diverge — and the way you find\n * out is a profile photo that is framed correctly on the phone and off-centre\n * on the dashboard, from one upload. Every number that decides the OUTPUT comes\n * from here; the platform files only draw and listen.\n *\n * ⚠️ READ `geometry.types.ts` FIRST FOR THE THREE COORDINATE SPACES. Every\n * function below belongs to exactly one, and the ones that cross between spaces\n * say so in their name (`viewportToOriented`, `orientedRectToSource`).\n */\n\n/* ─── Scalars ─────────────────────────────────────────────────────────────── */\n\n/**\n * Constrain `value` to [`min`, `max`].\n *\n * ⚠️ `max < min` RETURNS `min`, IT DOES NOT THROW OR RETURN NaN. Inverted\n * bounds are not a caller error here — they are the ordinary way a range\n * collapses, e.g. the legal x of an image narrower than the window it has to\n * cover. Returning `min` pins such a case to one deterministic edge instead of\n * letting it oscillate between two. Callers that need the midpoint instead\n * detect the collapse themselves; {@link clampTransformToCover} does.\n */\nexport const clamp = (value: number, min: number, max: number): number => {\n if (max < min) return min;\n return value < min ? min : value > max ? max : value;\n};\n\n/** `[16, 9]` → `1.777…`. Width ÷ height, the way CSS `aspect-ratio` reads. */\nexport const aspectOf = (aspect: CropAspect): number => aspect[0] / aspect[1];\n\n/**\n * The SMALLEST uniform scale at which `content` fully covers `target` —\n * `background-size: cover`.\n *\n * ⚠️ RETURNS `Infinity` FOR A ZERO-SIZED `content`, by plain division. That is\n * the honest answer (nothing scales up from nothing) and it propagates loudly\n * rather than silently producing a 1. Callers hold off until the image has\n * loaded and its natural size is known.\n */\nexport const coverScale = (content: ICropSize, target: ICropSize): number =>\n Math.max(target.width / content.width, target.height / content.height);\n\n/** The LARGEST uniform scale at which `content` fits inside `target` — `contain`. */\nexport const containScale = (content: ICropSize, target: ICropSize): number =>\n Math.min(target.width / content.width, target.height / content.height);\n\n/* ─── Rects ───────────────────────────────────────────────────────────────── */\n\nexport const rectRight = (r: ICropRect): number => r.x + r.width;\nexport const rectBottom = (r: ICropRect): number => r.y + r.height;\nexport const rectCenter = (r: ICropRect): ICropPoint => ({\n x: r.x + r.width / 2,\n y: r.y + r.height / 2,\n});\n\n/** The largest `ratio`-shaped size that fits inside `bounds`. */\nexport const largestRectOfRatio = (bounds: ICropSize, ratio: number): ICropSize => {\n const byWidth = {width: bounds.width, height: bounds.width / ratio};\n return byWidth.height <= bounds.height\n ? byWidth\n : {width: bounds.height * ratio, height: bounds.height};\n};\n\n/** Place `size` centred inside `within`. */\nexport const centerRect = (size: ICropSize, within: ICropSize): ICropRect => ({\n x: (within.width - size.width) / 2,\n y: (within.height - size.height) / 2,\n width: size.width,\n height: size.height,\n});\n\n/**\n * Move `rect` — never resize it — until it lies inside `bounds`.\n *\n * ⚠️ A RECT LARGER THAN `bounds` IS PINNED TO THE TOP-LEFT rather than\n * resized, via {@link clamp}'s inverted-bounds rule. Resizing here would make\n * this function silently change a crop's aspect, which is the one thing the\n * crop window is not allowed to do on its own.\n */\nexport const clampRectInside = (rect: ICropRect, bounds: ICropRect): ICropRect => ({\n ...rect,\n x: clamp(rect.x, bounds.x, rectRight(bounds) - rect.width),\n y: clamp(rect.y, bounds.y, rectBottom(bounds) - rect.height),\n});\n\n/* ─── Rotation ────────────────────────────────────────────────────────────── */\n\n/** Source size after a 90°-step rotation — swapped for 90 and 270. */\nexport const orientedSize = (source: ICropSize, rotation: CropRotation): ICropSize =>\n rotation === 90 || rotation === 270\n ? {width: source.height, height: source.width}\n : {width: source.width, height: source.height};\n\n/**\n * An ORIENTED point back to the SOURCE pixel it came from.\n *\n * ⛔ THIS IS THE INVERSE OF THE DISPLAY ROTATION, NOT THE ROTATION ITSELF, and\n * getting that backwards is a 90°-wrong crop that still looks plausible in a\n * unit test written the same wrong way round. The round-trip property in the\n * test file is what actually pins it: four 90° steps must land back on the\n * identity.\n */\nexport const orientedPointToSource = (\n p: ICropPoint,\n source: ICropSize,\n rotation: CropRotation,\n): ICropPoint => {\n switch (rotation) {\n case 90:\n return {x: p.y, y: source.height - p.x};\n case 180:\n return {x: source.width - p.x, y: source.height - p.y};\n case 270:\n return {x: source.width - p.y, y: p.x};\n default:\n return {x: p.x, y: p.y};\n }\n};\n\n/**\n * An ORIENTED rect back to SOURCE pixels.\n *\n * ⚠️ MAPPED BY ITS CORNERS AND RE-NORMALISED, not by per-rotation width/height\n * formulas. The corner map is one expression that is right for all four cases;\n * the formulas are four chances to transpose a width for a height, and the\n * wrong one of those is a crop that is correct at 0° and subtly off at 90°.\n */\nexport const orientedRectToSource = (\n rect: ICropRect,\n source: ICropSize,\n rotation: CropRotation,\n): ICropRect => {\n const a = orientedPointToSource({x: rect.x, y: rect.y}, source, rotation);\n const b = orientedPointToSource(\n {x: rect.x + rect.width, y: rect.y + rect.height},\n source,\n rotation,\n );\n return {\n x: Math.min(a.x, b.x),\n y: Math.min(a.y, b.y),\n width: Math.abs(b.x - a.x),\n height: Math.abs(b.y - a.y),\n };\n};\n\n/**\n * A SOURCE rect forward into ORIENTED space — the inverse of\n * {@link orientedRectToSource}.\n *\n * ⛔ IT EXISTS FOR ENCODERS THAT CROP AN ALREADY-ROTATED FILE. The native path\n * rotates once into a temporary file and then crops it, because the library\n * that can crop cannot rotate and the one that can rotate cannot crop. That\n * crop has to be expressed in the ROTATED image's coordinates, and the rect the\n * component computed is in the source's. Web needs none of this — a canvas\n * rotates and crops in one draw — which is exactly why this is a separate\n * function and not a change to the one above.\n *\n * ⚠️ IMPLEMENTED BY CALLING THE INVERSE ROTATION, not by writing four more\n * cases. Turning a rect back is the same operation as turning it, with the\n * complementary angle and the sides swapped; two sets of hand-written cases are\n * two chances to disagree, and the round-trip test is what would have to catch\n * it.\n */\nexport const sourceRectToOriented = (\n rect: ICropRect,\n source: ICropSize,\n rotation: CropRotation,\n): ICropRect => {\n const oriented = orientedSize(source, rotation);\n const inverse = (((360 - rotation) % 360) as CropRotation);\n return orientedRectToSource(rect, oriented, inverse);\n};\n\n/* ─── The image on the stage ──────────────────────────────────────────────── */\n\n/** Where the oriented image lands in VIEWPORT space under `t`. */\nexport const imageRect = (oriented: ICropSize, t: ICropTransform): ICropRect => ({\n x: t.x,\n y: t.y,\n width: oriented.width * t.scale,\n height: oriented.height * t.scale,\n});\n\n/** A VIEWPORT point to the ORIENTED image pixel under it. */\nexport const viewportToOriented = (p: ICropPoint, t: ICropTransform): ICropPoint => ({\n x: (p.x - t.x) / t.scale,\n y: (p.y - t.y) / t.scale,\n});\n\n/**\n * Change scale while keeping whatever image pixel is under `anchor` under\n * `anchor` — scroll-to-zoom at the cursor, pinch about the two fingers' midpoint.\n *\n * ⛔ WITHOUT THE ANCHOR TERM, ZOOM PULLS THE IMAGE TOWARDS THE ORIGIN. Scaling\n * `x` and `y` alone is a zoom about the stage's top-left corner; the subject\n * drifts out from under the finger, and every user reads that as the control\n * fighting them. The fixed-point condition is one line of algebra: the oriented\n * point `o = (anchor − t) / t.scale` must still satisfy\n * `anchor = o·next + t′`, hence `t′ = anchor − (anchor − t)·(next / t.scale)`.\n */\nexport const zoomAbout = (\n t: ICropTransform,\n anchor: ICropPoint,\n nextScale: number,\n): ICropTransform => {\n const k = nextScale / t.scale;\n return {\n scale: nextScale,\n x: anchor.x - (anchor.x - t.x) * k,\n y: anchor.y - (anchor.y - t.y) * k,\n };\n};\n\n/** The smallest scale at which the oriented image still covers `crop`. */\nexport const minScaleToCover = (oriented: ICropSize, crop: ICropSize): number =>\n coverScale(oriented, crop);\n\n/**\n * One axis of {@link clampTransformToCover}.\n *\n * ⚠️ AN IMAGE TOO SMALL TO COVER IS CENTRED, NOT PINNED. This is the case\n * {@link clamp} would resolve to one edge, and an edge is the wrong answer\n * here: it puts all of the uncovered gap on one side, which reads as the image\n * having slipped rather than as it being too small. Callers normally prevent\n * the case entirely by flooring scale at {@link minScaleToCover}; this is what\n * happens in the frame before they do.\n */\nconst coverAxis = (imgStart: number, imgLen: number, cropStart: number, cropLen: number): number =>\n imgLen < cropLen\n ? cropStart + (cropLen - imgLen) / 2\n : clamp(imgStart, cropStart + cropLen - imgLen, cropStart);\n\n/**\n * Slide the image — scale untouched — until it fully covers `crop`.\n *\n * This is the invariant that keeps empty corners out of a crop: whatever the\n * drag did, the window stays filled.\n */\nexport const clampTransformToCover = (\n t: ICropTransform,\n oriented: ICropSize,\n crop: ICropRect,\n): ICropTransform => {\n const img = imageRect(oriented, t);\n return {\n scale: t.scale,\n x: coverAxis(img.x, img.width, crop.x, crop.width),\n y: coverAxis(img.y, img.height, crop.y, crop.height),\n };\n};\n\n/* ─── The eight handles ───────────────────────────────────────────────────── */\n\n/** The corner or edge-midpoint a given handle pivots around. */\nconst anchorFor = (rect: ICropRect, handle: CropHandle): ICropPoint => {\n const c = rectCenter(rect);\n switch (handle) {\n case 'se': return {x: rect.x, y: rect.y};\n case 'sw': return {x: rectRight(rect), y: rect.y};\n case 'ne': return {x: rect.x, y: rectBottom(rect)};\n case 'nw': return {x: rectRight(rect), y: rectBottom(rect)};\n case 'e': return {x: rect.x, y: c.y};\n case 'w': return {x: rectRight(rect), y: c.y};\n case 's': return {x: c.x, y: rect.y};\n default: return {x: c.x, y: rectBottom(rect)}; // 'n'\n }\n};\n\nconst growsLeft = (h: CropHandle): boolean => h === 'w' || h === 'nw' || h === 'sw';\nconst growsUp = (h: CropHandle): boolean => h === 'n' || h === 'ne' || h === 'nw';\nconst movesX = (h: CropHandle): boolean => h !== 'n' && h !== 's';\nconst movesY = (h: CropHandle): boolean => h !== 'e' && h !== 'w';\n\n/**\n * Resize `rect` by dragging `handle` to `pointer`.\n *\n * All eight handles, one rule: **the anchor never moves.** For a corner that is\n * the opposite corner; for an edge it is the opposite edge, with the rect\n * growing symmetrically about that edge's midpoint on the free axis. Everything\n * else — ratio, floor, bounds — is applied to the size, then the rect is\n * rebuilt from the anchor.\n *\n * ⛔ THE RECT DOES NOT FLIP THROUGH ITS ANCHOR. Drag the south-east grip up past\n * the top-left corner and the width goes negative; taking `Math.abs` of it\n * would turn the crop inside out and leave the handle under the cursor on the\n * wrong side, which is how a drag ends up selecting a region the user never saw\n * highlighted. The negative is kept, and `min` floors it — so the crop stops at\n * its smallest and waits, which is what every other cropper does.\n *\n * ⚠️ `min` BEATS `bounds` WHEN THEY CONFLICT. A stage smaller than the minimum\n * crop cannot satisfy both; returning the minimum keeps the crop usable and\n * visibly overflowing, where honouring bounds would hand back a sliver the user\n * cannot grab. Callers that can resize the stage should; this is the fallback.\n */\nexport const resizeRect = (\n rect: ICropRect,\n handle: CropHandle,\n pointer: ICropPoint,\n options: ICropResizeOptions = {},\n): ICropRect => {\n const {ratio, min, bounds} = options;\n const anchor = anchorFor(rect, handle);\n\n /* 1 — what the pointer is asking for, signed, on each axis the handle owns. */\n let width = movesX(handle)\n ? (growsLeft(handle) ? anchor.x - pointer.x : pointer.x - anchor.x)\n : rect.width;\n let height = movesY(handle)\n ? (growsUp(handle) ? anchor.y - pointer.y : pointer.y - anchor.y)\n : rect.height;\n\n /* 2 — the ratio decides the axis the handle does not own. On a corner both\n axes are owned, so the larger request wins and the other follows. */\n if (ratio) {\n if (movesX(handle) && movesY(handle)) {\n width = Math.max(width, height * ratio);\n height = width / ratio;\n } else if (movesX(handle)) {\n height = width / ratio;\n } else {\n width = height * ratio;\n }\n }\n\n /* 3 — bounds, as a shrink about the anchor so the ratio survives it.\n\n ⛔ ONLY WHERE THE REQUEST IS POSITIVE, AND THAT GUARD IS NOT DEFENSIVE\n NOISE. Dragging a grip PAST its anchor makes the request negative — step\n 1 keeps the sign on purpose, see the non-flipping note. Shrinking means\n multiplying by `maxBound / requested`, and when `requested` is negative\n that quotient is negative: the rect does not shrink, it FLIPS AND\n INFLATES. Measured before the guard: the south grip of a 600×600 window\n dragged up to y = -500 on an 800×600 stage returned an 800×800 rect,\n larger than the stage it was being clamped into.\n\n ⚠️ PER AXIS IN THE FREE CASE, because a negative request on one axis\n does not excuse the other. `ne` dragged far down-right asks for a\n positive width and a negative height at once; a single combined guard\n let that width through unclamped and the rect reached x + width = 5000\n inside a 600-wide bounds. With a ratio the two axes share a sign, so one\n test covers both.\n\n ⚠️ THE CEILINGS ARE FLOORED AT ZERO for the same family of reason: an\n anchor already outside `bounds` makes one negative, and the sign error\n returns by a different door. */\n if (bounds) {\n const c = rectCenter(rect);\n const maxWidth = Math.max(0, movesX(handle)\n ? (growsLeft(handle) ? anchor.x - bounds.x : rectRight(bounds) - anchor.x)\n : 2 * Math.min(c.x - bounds.x, rectRight(bounds) - c.x));\n const maxHeight = Math.max(0, movesY(handle)\n ? (growsUp(handle) ? anchor.y - bounds.y : rectBottom(bounds) - anchor.y)\n : 2 * Math.min(c.y - bounds.y, rectBottom(bounds) - c.y));\n\n if (ratio) {\n if (width > 0 && height > 0) {\n const shrink = Math.min(1, maxWidth / width, maxHeight / height);\n if (shrink < 1 && Number.isFinite(shrink)) {\n width *= shrink;\n height *= shrink;\n }\n }\n } else {\n if (width > 0) width = Math.min(width, maxWidth);\n if (height > 0) height = Math.min(height, maxHeight);\n }\n }\n\n /* 4 — the floor, last, so it is the one thing that always holds. */\n if (min) {\n const minWidth = ratio ? Math.max(min.width, min.height * ratio) : min.width;\n const minHeight = ratio ? minWidth / ratio : min.height;\n if (width < minWidth) {\n width = minWidth;\n if (ratio) height = minHeight;\n }\n if (height < minHeight) {\n height = minHeight;\n if (ratio) width = minWidth;\n }\n }\n\n /* 5 — rebuild from the anchor, which has not moved. */\n const x = movesX(handle)\n ? (growsLeft(handle) ? anchor.x - width : anchor.x)\n : anchor.x - width / 2;\n const y = movesY(handle)\n ? (growsUp(handle) ? anchor.y - height : anchor.y)\n : anchor.y - height / 2;\n\n return {x, y, width, height};\n};\n\n/* ─── Start state, and the answer ─────────────────────────────────────────── */\n\n/**\n * The state a cropper opens in: the biggest `ratio`-shaped window the stage\n * allows, with the image scaled just enough to fill it and centred behind it.\n */\nexport const initialState = (\n source: ICropSize,\n viewport: ICropSize,\n ratio: number,\n rotation: CropRotation = 0,\n): {crop: ICropRect; transform: ICropTransform} => {\n const crop = centerRect(largestRectOfRatio(viewport, ratio), viewport);\n const oriented = orientedSize(source, rotation);\n const scale = minScaleToCover(oriented, crop);\n const img = imageRect(oriented, {scale, x: 0, y: 0});\n return {\n crop,\n transform: {\n scale,\n x: crop.x + (crop.width - img.width) / 2,\n y: crop.y + (crop.height - img.height) / 2,\n },\n };\n};\n\n/**\n * The crop window, in SOURCE pixels — the one number the encoders need.\n *\n * ⚠️ CLAMPED TO THE SOURCE AT THE END, because floating point does not care\n * about your invariants. A cover-clamped transform is exact in theory and a\n * fraction of a pixel outside in practice, and a decoder handed `x = -0.0001`\n * either throws or silently shifts the whole crop.\n */\nexport const cropToSourceRect = (\n crop: ICropRect,\n t: ICropTransform,\n source: ICropSize,\n rotation: CropRotation = 0,\n): ICropRect => {\n const topLeft = viewportToOriented({x: crop.x, y: crop.y}, t);\n const bottomRight = viewportToOriented({x: rectRight(crop), y: rectBottom(crop)}, t);\n const oriented: ICropRect = {\n x: topLeft.x,\n y: topLeft.y,\n width: bottomRight.x - topLeft.x,\n height: bottomRight.y - topLeft.y,\n };\n const inSource = orientedRectToSource(oriented, source, rotation);\n const x = clamp(inSource.x, 0, source.width);\n const y = clamp(inSource.y, 0, source.height);\n return {\n x,\n y,\n width: clamp(inSource.width, 0, source.width - x),\n height: clamp(inSource.height, 0, source.height - y),\n };\n};\n\n/**\n * Scale `size` DOWN to fit inside `max`, preserving its ratio.\n *\n * ⚠️ NEVER SCALES UP. A 200px avatar asked for at most 1024px stays 200px —\n * upscaling invents detail and costs bytes for it. An absent or non-positive\n * bound is ignored rather than treated as zero.\n */\nexport const fitWithin = (size: ICropSize, max: Partial<ICropSize>): ICropSize => {\n const limits = [\n max.width && max.width > 0 ? max.width / size.width : 1,\n max.height && max.height > 0 ? max.height / size.height : 1,\n ];\n const scale = Math.min(1, ...limits);\n return {width: size.width * scale, height: size.height * scale};\n};\n\n/** The next 90° step clockwise. */\nexport const rotateCw = (rotation: CropRotation): CropRotation =>\n (((rotation + 90) % 360) as CropRotation);\n"],"names":[],"mappings":"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"}
|