immich/web/src/lib/utils/people-utils.ts

72 lines
2.1 KiB
TypeScript
Raw Normal View History

feat(web): re-assign person faces (2) (#4949) * feat: unassign person faces * multiple improvements * chore: regenerate api * feat: improve face interactions in photos * fix: tests * fix: tests * optimize * fix: wrong assignment on complex-multiple re-assignments * fix: thumbnails with large photos * fix: complex reassign * fix: don't send people with faces * fix: person thumbnail generation * chore: regenerate api * add tess * feat: face box even when zoomed * fix: change feature photo * feat: make the blue icon hoverable * chore: regenerate api * feat: use websocket * fix: loading spinner when clicking on the done button * fix: use the svelte way * fix: tests * simplify * fix: unused vars * fix: remove unused code * fix: add migration * chore: regenerate api * ci: add unit tests * chore: regenerate api * feat: if a new person is created for a face and the server takes more than 15 seconds to generate the person thumbnail, don't wait for it * reorganize * chore: regenerate api * feat: global edit * pr feedback * pr feedback * simplify * revert test * fix: face generation * fix: tests * fix: face generation * fix merge * feat: search names in unmerge face selector modal * fix: merge face selector * simplify feature photo generation * fix: change endpoint * pr feedback * chore: fix merge * chore: fix merge * fix: tests * fix: edit & hide buttons * fix: tests * feat: show if person is hidden * feat: rename face to person * feat: split in new panel * copy-paste-error * pr feedback * fix: feature photo * do not leak faces * fix: unmerge modal * fix: merge modal event * feat(server): remove duplicates * fix: title for image thumbnails * fix: disable side panel when there's no face until next PR --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-12-05 16:43:15 +01:00
import type { Faces } from '$lib/stores/people.store';
import type { ZoomImageWheelState } from '@zoom-image/core';
const getContainedSize = (img: HTMLImageElement): { width: number; height: number } => {
const ratio = img.naturalWidth / img.naturalHeight;
let width = img.height * ratio;
let height = img.height;
if (width > img.width) {
width = img.width;
height = img.width / ratio;
}
return { width, height };
};
export interface boundingBox {
top: number;
left: number;
width: number;
height: number;
}
export const getBoundingBox = (
faces: Faces[],
zoom: ZoomImageWheelState,
photoViewer: HTMLImageElement | null,
): boundingBox[] => {
const boxes: boundingBox[] = [];
if (photoViewer === null) {
return boxes;
}
const clientHeight = photoViewer.clientHeight;
const clientWidth = photoViewer.clientWidth;
const { width, height } = getContainedSize(photoViewer);
for (const face of faces) {
/*
*
* Create the coordinates of the box based on the displayed image.
* The coordinates must take into account margins due to the 'object-fit: contain;' css property of the photo-viewer.
*
*/
const coordinates = {
x1:
(width / face.imageWidth) * zoom.currentZoom * face.boundingBoxX1 +
((clientWidth - width) / 2) * zoom.currentZoom +
zoom.currentPositionX,
x2:
(width / face.imageWidth) * zoom.currentZoom * face.boundingBoxX2 +
((clientWidth - width) / 2) * zoom.currentZoom +
zoom.currentPositionX,
y1:
(height / face.imageHeight) * zoom.currentZoom * face.boundingBoxY1 +
((clientHeight - height) / 2) * zoom.currentZoom +
zoom.currentPositionY,
y2:
(height / face.imageHeight) * zoom.currentZoom * face.boundingBoxY2 +
((clientHeight - height) / 2) * zoom.currentZoom +
zoom.currentPositionY,
};
boxes.push({
top: Math.round(coordinates.y1),
left: Math.round(coordinates.x1),
width: Math.round(coordinates.x2 - coordinates.x1),
height: Math.round(coordinates.y2 - coordinates.y1),
});
}
return boxes;
};