7702560b12
* 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>
72 lines
2.1 KiB
TypeScript
72 lines
2.1 KiB
TypeScript
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;
|
|
};
|