lost-sia
Version:
Single Image Annotation Tool
193 lines (173 loc) • 5.38 kB
text/typescript
import type { Point, Vector2 } from "../types";
const convertImageCoordinatesToStage = (
imageCoordinates: Point[],
imageSize: Vector2,
stageSize: Vector2,
): Point[] => {
// the image is scaled to match the width of the canvas
// assume the aspect ratio is kept
const imageToCanvasScale = stageSize.x / imageSize.x;
const stageCoordinates = imageCoordinates.map((imagePoint: Point) => ({
x: imagePoint.x * imageToCanvasScale,
y: imagePoint.y * imageToCanvasScale,
}));
return stageCoordinates;
};
const convertPercentagedCoordinatesToImage = (
percentagedCoordinates: Point[],
imageSize: Vector2,
): Point[] => {
const imageCoordinates: Point[] = percentagedCoordinates.map(
(point: Point) => {
return {
x: point.x * imageSize.x,
y: point.y * imageSize.y,
};
},
);
return imageCoordinates;
};
const convertPercentagedCoordinatesToStage = (
percentagedCoordinates: Point[],
imageSize: Vector2,
stageSize: Vector2,
): Point[] => {
const imageCoordinates = convertPercentagedCoordinatesToImage(
percentagedCoordinates,
imageSize,
);
const stageCoordinates = convertImageCoordinatesToStage(
imageCoordinates,
imageSize,
stageSize,
);
return stageCoordinates;
};
const convertStageCoordinatesToImage = (
stageCoordinates: Point[],
imageToStageFactor: number,
): Point[] => {
const coordinatesInImageSpace: Point[] = stageCoordinates.map(
(coordinate: Point) => {
return {
x: coordinate.x / imageToStageFactor,
y: coordinate.y / imageToStageFactor,
};
},
);
return coordinatesInImageSpace;
};
const convertStageCoordinatesToPercentaged = (
scaledCoordinates: Point[],
imageToStageFactor: number,
imageSize: Vector2,
): Point[] => {
const imageCoordinates: Point[] = convertStageCoordinatesToImage(
scaledCoordinates,
imageToStageFactor,
);
// make sure the coordinates are inside the image bounds
const polishedImageCoordinates = imageCoordinates.map((point: Point) => {
if (point.x < 0) point.x = 0;
if (point.y < 0) point.y = 0;
if (point.x > imageSize.x) point.x = imageSize.x;
if (point.y > imageSize.y) point.y = imageSize.y;
return point;
});
// someone decided to use percentages as the image coordinates
// convert them from pixel coordinates back to percentages here
const percentagedCoordinates = polishedImageCoordinates.map(
(point: Point) => {
return {
x: point.x / imageSize.x,
y: point.y / imageSize.y,
};
},
);
return percentagedCoordinates;
};
/**
* converts coordinates from the stage system into the page system
* the coordinates are
* @param pageToStageOffset vector2 from the start of the page to the start of the stage
* @param svgScale the scaling factor of the canvas svg element (can be zoomed in by user)
* @returns
*/
const convertStageToPage = (
stageCoordinates: Point,
pageToStageOffset: Point,
svgScale: number,
svgTranslation: Vector2,
): Point => {
const scaledStageCoordinates: Point = {
x: stageCoordinates.x + svgTranslation.x,
y: stageCoordinates.y + svgTranslation.y,
};
// now we need to counter the scaling to get from the transformation system into the stage system
const transformedStageCoordinates: Point = {
x: scaledStageCoordinates.x * svgScale,
y: scaledStageCoordinates.y * svgScale,
};
// convert them into (translated) stage coordinates by subtracting the offset between the page (0,0) and the stage (0,0)
const pageCoordinates: Point = {
x: transformedStageCoordinates.x + pageToStageOffset.x,
y: transformedStageCoordinates.y + pageToStageOffset.y,
};
return pageCoordinates;
};
/**
* Get point that is closest to the left browser side.
*
* @param points list of points {x,y}
* @returns {object} A list of point [{x,y}...]. Multiple points are
* returned when multiple points have the same distance to the left side.
*/
export const getMostLeftPoints = (points: Point[]): Point[] => {
let minX = Infinity;
let minXList: Point[] = [];
points.forEach((point: Point) => {
if (point.x < minX) {
// new most left point - replace list
minX = point.x;
minXList = [];
minXList.push(point);
} else if (point.x === minX) {
// same x as current most left point - add to list
minXList.push(point);
}
});
return minXList;
};
/**
* Get point that is closest to the top of the browser.
*
* @param points list of points [x,y]
* @returns A list of point [[x,y]...]. Multiple points are
* returned when multiple points have the same distance to the top.
*/
export const getTopPoint = (points: Point[]): Point[] => {
let minY = Infinity;
let minYList: Point[] = [];
points.forEach((point: Point) => {
if (point.y < minY) {
// new hightest point - replace list
minY = point.y;
minYList = [];
minYList.push(point);
} else if (point.y === minY) {
// same height as highest point - add to list
minYList.push(point);
}
});
return minYList;
};
export default {
convertImageCoordinatesToStage,
convertPercentagedCoordinatesToImage,
convertPercentagedCoordinatesToStage,
convertStageCoordinatesToImage,
convertStageCoordinatesToPercentaged,
convertStageToPage,
getMostLeftPoints,
getTopPoint,
};