Skip to content

Commit f2bb668

Browse files
committed
Add mode to option
1 parent df3940d commit f2bb668

File tree

3 files changed

+135
-2
lines changed

3 files changed

+135
-2
lines changed

src/options/gravity.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type {
33
Gravity,
44
FPGravity,
55
ObjGravity,
6+
ObjwGravity,
67
BaseGravity,
78
} from "../types/gravity";
89
import {
@@ -33,6 +34,7 @@ const currentAllTypes = {
3334
sm: true,
3435
fp: true,
3536
obj: true,
37+
objw: true,
3638
};
3739

3840
const getOpt = (options: GravityOptionsPartial): Gravity | undefined =>
@@ -64,6 +66,9 @@ const build = (
6466
if (gravityOpts.class_names && type !== "obj")
6567
throw new Error("gravity.class_names can be used only with type obj");
6668
// @ts-expect-error: Let's ignore an error.
69+
if (gravityOpts.class_weights && type !== "objw")
70+
throw new Error("gravity.class_weights can be used only with type objw");
71+
// @ts-expect-error: Let's ignore an error.
6772
if ((gravityOpts.x || gravityOpts.y) && type !== "fp")
6873
throw new Error("gravity.x and gravity.y can be used only with type fp");
6974

@@ -90,6 +95,28 @@ const build = (
9095

9196
const class_names = gravityObj.class_names;
9297
return withHead(`${type}:${class_names.join(":")}`, headless);
98+
}
99+
100+
if (type === "objw") {
101+
const gravityObjw = gravityOpts as ObjwGravity;
102+
103+
guardIsUndef(gravityObjw.class_weights, "gravity.class_weights");
104+
guardIsNotArray(gravityObjw.class_weights, "gravity.class_weights");
105+
106+
const weightPairs = gravityObjw.class_weights.map(item => {
107+
if (
108+
typeof item !== "object" ||
109+
!item.class ||
110+
typeof item.weight !== "number"
111+
) {
112+
throw new Error(
113+
"Each item in gravity.class_weights must have 'class' and 'weight' properties"
114+
);
115+
}
116+
return `${item.class}:${item.weight}`;
117+
});
118+
119+
return withHead(`${type}:${weightPairs.join(":")}`, headless);
93120
} else {
94121
const gravityBase = gravityOpts as BaseGravity;
95122
const x_offset =

src/types/gravity.ts

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,26 @@ interface ObjGravity {
106106
class_names: string[];
107107
}
108108

109+
/**
110+
* **PRO feature.**
111+
*
112+
* Object-weighted gravity. imgproxy detects objects of provided classes on the image, calculates the resulting image center using their positions, and adds weights to these positions.
113+
*
114+
* If class weights are omited, imgproxy will use all the detected objects with equal weights.
115+
*
116+
* @param {string} type - Must be `objw`.
117+
* @param {Array<{class: string, weight: number}>} class_weights - Array of objects with class names and their weights.
118+
*
119+
* @example
120+
* {gravity: {type: "objw", class_weights: [{class: "face", weight: 1}, {class: "person", weight: 0.5}]}}
121+
*
122+
* @see https://docs.imgproxy.net/generating_the_url?id=gravity
123+
*/
124+
interface ObjwGravity {
125+
type: "objw";
126+
class_weights: Array<{ class: string; weight: number }>;
127+
}
128+
109129
/**
110130
* *Gravity option*
111131
*
@@ -138,6 +158,12 @@ interface ObjGravity {
138158
* If class names are omited, imgproxy will use all the detected objects.
139159
* @param {string} type - Must be `obj`.
140160
* @param {string[]} class_names - Array of class names.
161+
*
162+
* *Object-weighted gravity*. **PRO feature.**
163+
* imgproxy detects objects of provided classes on the image, calculates the resulting image center using their positions, and adds weights to these positions.
164+
* If class weights are omited, imgproxy will use all the detected objects with equal weights.
165+
* @param {string} type - Must be `objw`.
166+
* @param {Array<{class: string, weight: number}>} class_weights - Array of objects with class names and their weights.
141167
*
142168
* *FP gravity*.
143169
* The gravity focus point.
@@ -164,13 +190,21 @@ interface ObjGravity {
164190
*
165191
* @example <caption>Object-oriented gravity</caption>
166192
* {gravity: {type: "obj", class_names: ["face", "person"]}}
193+
*
194+
* @example <caption>Object-weighted gravity</caption>
195+
* {gravity: {type: "objw", class_weights: [{class: "face", weight: 1}, {class: "person", weight: 0.5}]}}
167196
*
168197
* @example <caption>FP gravity</caption>
169198
* {gravity: {type: "fp", x: 0.5, y: 0.5}}
170199
*
171200
* @see https://docs.imgproxy.net/generating_the_url?id=gravity
172201
*/
173-
type Gravity = BaseGravity | SmartGravity | ObjGravity | FPGravity;
202+
type Gravity =
203+
| BaseGravity
204+
| SmartGravity
205+
| ObjGravity
206+
| ObjwGravity
207+
| FPGravity;
174208

175209
/**
176210
* *Gravity option*
@@ -184,4 +218,11 @@ interface GravityOptionsPartial {
184218
g?: Gravity;
185219
}
186220

187-
export { BaseGravity, FPGravity, ObjGravity, Gravity, GravityOptionsPartial };
221+
export {
222+
BaseGravity,
223+
FPGravity,
224+
ObjGravity,
225+
ObjwGravity,
226+
Gravity,
227+
GravityOptionsPartial,
228+
};

tests/optionsBasic/gravity.test.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,5 +240,70 @@ describe("gravity", () => {
240240
).toEqual("g:fp:0:0");
241241
});
242242
});
243+
244+
describe("ObjwGravity", () => {
245+
it("should throw an error if gravity includes property class_weights but type is not 'objw'", () => {
246+
expect(() =>
247+
build({
248+
gravity: {
249+
type: "no",
250+
// @ts-expect-error: Let's ignore an error (check for users with vanilla js).
251+
class_weights: [{ class: "face", weight: 1 }],
252+
},
253+
})
254+
).toThrow(`gravity.class_weights can be used only with type objw`);
255+
});
256+
257+
it("should throw an error if class_weights is undefined", () => {
258+
expect(() =>
259+
build({
260+
// @ts-expect-error: Let's ignore an error (check for users with vanilla js).
261+
gravity: {
262+
type: "objw",
263+
},
264+
})
265+
).toThrow(`gravity.class_weights is undefined`);
266+
});
267+
268+
it("should throw an error if class_weights is not an array", () => {
269+
expect(() =>
270+
build({
271+
gravity: {
272+
type: "objw",
273+
// @ts-expect-error: Let's ignore an error (check for users with vanilla js).
274+
class_weights: "face:1",
275+
},
276+
})
277+
).toThrow(`gravity.class_weights is not an array`);
278+
});
279+
280+
it("should throw an error if a class_weights item is missing class or weight", () => {
281+
expect(() =>
282+
build({
283+
gravity: {
284+
type: "objw",
285+
// @ts-expect-error: Let's ignore an error (check for users with vanilla js).
286+
class_weights: [{ class: "face" }],
287+
},
288+
})
289+
).toThrow(
290+
`Each item in gravity.class_weights must have 'class' and 'weight' properties`
291+
);
292+
});
293+
294+
it("should return g:objw:face:1:person:0.5 if gravity is {type: 'objw', class_weights: [{class: 'face', weight: 1}, {class: 'person', weight: 0.5}]} ", () => {
295+
expect(
296+
build({
297+
gravity: {
298+
type: "objw",
299+
class_weights: [
300+
{ class: "face", weight: 1 },
301+
{ class: "person", weight: 0.5 },
302+
],
303+
},
304+
})
305+
).toEqual("g:objw:face:1:person:0.5");
306+
});
307+
});
243308
});
244309
});

0 commit comments

Comments
 (0)