mirror of
https://github.com/immich-app/immich.git
synced 2026-01-24 18:34:41 -08:00
327 lines
11 KiB
TypeScript
327 lines
11 KiB
TypeScript
import type { EditActions } from '$lib/managers/edit/edit-manager.svelte';
|
|
import { buildAffineFromEdits, normalizeTransformEdits } from '$lib/utils/editor';
|
|
import { AssetEditAction, MirrorAxis } from '@immich/sdk';
|
|
|
|
type NormalizedParameters = {
|
|
rotation: number;
|
|
mirrorHorizontal: boolean;
|
|
mirrorVertical: boolean;
|
|
};
|
|
|
|
function normalizedToEdits(params: NormalizedParameters): EditActions {
|
|
const edits: EditActions = [];
|
|
|
|
if (params.mirrorHorizontal) {
|
|
edits.push({
|
|
action: AssetEditAction.Mirror,
|
|
parameters: { axis: MirrorAxis.Horizontal },
|
|
});
|
|
}
|
|
|
|
if (params.mirrorVertical) {
|
|
edits.push({
|
|
action: AssetEditAction.Mirror,
|
|
parameters: { axis: MirrorAxis.Vertical },
|
|
});
|
|
}
|
|
|
|
if (params.rotation !== 0) {
|
|
edits.push({
|
|
action: AssetEditAction.Rotate,
|
|
parameters: { angle: params.rotation },
|
|
});
|
|
}
|
|
|
|
return edits;
|
|
}
|
|
|
|
function compareEditAffines(editsA: EditActions, editsB: EditActions): boolean {
|
|
const normA = buildAffineFromEdits(editsA);
|
|
const normB = buildAffineFromEdits(editsB);
|
|
|
|
return (
|
|
Math.abs(normA.a - normB.a) < 0.0001 &&
|
|
Math.abs(normA.b - normB.b) < 0.0001 &&
|
|
Math.abs(normA.c - normB.c) < 0.0001 &&
|
|
Math.abs(normA.d - normB.d) < 0.0001
|
|
);
|
|
}
|
|
|
|
describe('edit normalization', () => {
|
|
it('should handle no edits', () => {
|
|
const edits: EditActions = [];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle a single 90° rotation', () => {
|
|
const edits: EditActions = [{ action: AssetEditAction.Rotate, parameters: { angle: 90 } }];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle a single 180° rotation', () => {
|
|
const edits: EditActions = [{ action: AssetEditAction.Rotate, parameters: { angle: 180 } }];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle a single 270° rotation', () => {
|
|
const edits: EditActions = [{ action: AssetEditAction.Rotate, parameters: { angle: 270 } }];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle a single horizontal mirror', () => {
|
|
const edits: EditActions = [{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Horizontal } }];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle a single vertical mirror', () => {
|
|
const edits: EditActions = [{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Vertical } }];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle 90° rotation + horizontal mirror', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 90 } },
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Horizontal } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle 90° rotation + vertical mirror', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 90 } },
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Vertical } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle 90° rotation + both mirrors', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 90 } },
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Horizontal } },
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Vertical } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle 180° rotation + horizontal mirror', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 180 } },
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Horizontal } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle 180° rotation + vertical mirror', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 180 } },
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Vertical } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle 180° rotation + both mirrors', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 180 } },
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Horizontal } },
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Vertical } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle 270° rotation + horizontal mirror', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 270 } },
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Horizontal } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle 270° rotation + vertical mirror', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 270 } },
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Vertical } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle 270° rotation + both mirrors', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 270 } },
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Horizontal } },
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Vertical } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle horizontal mirror + 90° rotation', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Horizontal } },
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 90 } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle horizontal mirror + 180° rotation', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Horizontal } },
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 180 } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle horizontal mirror + 270° rotation', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Horizontal } },
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 270 } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle vertical mirror + 90° rotation', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Vertical } },
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 90 } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle vertical mirror + 180° rotation', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Vertical } },
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 180 } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle vertical mirror + 270° rotation', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Vertical } },
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 270 } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle both mirrors + 90° rotation', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Horizontal } },
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Vertical } },
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 90 } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle both mirrors + 180° rotation', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Horizontal } },
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Vertical } },
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 180 } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
|
|
it('should handle both mirrors + 270° rotation', () => {
|
|
const edits: EditActions = [
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Horizontal } },
|
|
{ action: AssetEditAction.Mirror, parameters: { axis: MirrorAxis.Vertical } },
|
|
{ action: AssetEditAction.Rotate, parameters: { angle: 270 } },
|
|
];
|
|
|
|
const result = normalizeTransformEdits(edits);
|
|
const normalizedEdits = normalizedToEdits(result);
|
|
|
|
expect(compareEditAffines(normalizedEdits, edits)).toBe(true);
|
|
});
|
|
});
|