This repository has been archived by the owner on Nov 24, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 137
freehand selection integration or demo ? #23
Comments
The way I accomplished through a custom Selector was to create a svg element on top of Annotation and then create paths that follow the datapoints. |
@danilofuchs hi, do you mind sharing your selector code? i couldn't find it in your fork |
@dimitrius1986 Unfortunately, the code in which I implemented this functionality is private. I can, however, paste this snippet of how it was implemented: import { IAnnotation, IGeometry } from "react-image-annotation";
interface IPoint {
x: number;
y: number;
}
export interface IGeometryDraw extends IGeometry {
coordinates: IPoint[];
x: number;
y: number;
boxX: number;
boxY: number;
boxHeight: number;
boxWidth: number;
type: string;
}
interface IAnotacaoDraw extends IAnnotation {
geometry: IGeometryDraw;
}
const MARGIN = 12;
const marginToPercentage = (container: { width: number; height: number }) => ({
marginX: (MARGIN / container.width) * 100,
marginY: (MARGIN / container.height) * 100
});
export const TYPE = "DRAWING";
export function intersects(
{ x, y }: { x: number; y: number },
geometry: IGeometryDraw,
container: { width: number; height: number }
): boolean {
const { marginX, marginY } = marginToPercentage(container);
if (x < geometry.boxX - marginX) {
return false;
}
if (y < geometry.boxY - marginY) {
return false;
}
if (x > geometry.boxX + geometry.boxWidth + marginX) {
return false;
}
if (y > geometry.boxY + geometry.boxHeight + marginY) {
return false;
}
return true;
}
export function area(
geometry: IGeometryDraw,
container: { width: number; height: number }
): number {
return geometry.boxHeight * geometry.boxWidth;
}
export const methods = {
onMouseDown: (annotation: IAnotacaoDraw, e: any) => {
return onPointerDown(annotation, e);
},
onMouseMove: (annotation: IAnotacaoDraw, e: any) => {
return onPointerMove(annotation, e);
},
onMouseUp: (annotation: IAnotacaoDraw, e: any) => {
return onPointerUp(annotation, e);
},
onTouchStart: (annotation: IAnotacaoDraw, e: any) => {
return onPointerDown(annotation, e);
},
onTouchMove: (annotation: IAnotacaoDraw, e: any) => {
return onPointerMove(annotation, e);
},
onTouchEnd: (annotation: IAnotacaoDraw, e: any) => {
return onPointerUp(annotation, e);
}
};
function onPointerDown(annotation: IAnotacaoDraw, e: any) {
if (!annotation.geometry) {
const newPoint = relativeCoordinatesForEvent(e);
return {
...annotation,
selection: {
...annotation.selection,
showEditor: false,
mode: "SELECTING"
},
geometry: {
coordinates: [],
x: newPoint.x,
y: newPoint.y,
boxX: newPoint.x,
boxY: newPoint.y,
boxHeight: 0,
boxWidth: 0,
type: TYPE
},
data: {
id: Math.random()
}
};
} else {
return {};
}
}
function onPointerMove(annotation: IAnotacaoDraw, e: any) {
if (annotation.selection && annotation.selection.mode === "SELECTING") {
let { y, boxX, boxY, boxHeight, boxWidth } = annotation.geometry;
const newPoint = relativeCoordinatesForEvent(e);
if (newPoint.y < y || !y) {
y = newPoint.y;
}
if (newPoint.y < boxY || !boxY) {
boxHeight += boxY - newPoint.y;
boxY = newPoint.y;
} else if (newPoint.y > boxY + boxHeight || !boxHeight) {
boxHeight = newPoint.y - boxY;
}
if (newPoint.x < boxX || !boxX) {
boxWidth += boxX - newPoint.x;
boxX = newPoint.x;
} else if (newPoint.x > boxX + boxWidth || !boxWidth) {
boxWidth = newPoint.x - boxX;
}
const middle = annotation.geometry.coordinates.reduce(
(prev, curr) => ({
x: prev.x + curr.x,
y: prev.y + curr.y
}),
{ x: 0, y: 0 }
);
middle.x /= annotation.geometry.coordinates.length;
middle.y /= annotation.geometry.coordinates.length;
return {
...annotation,
selection: {
...annotation.selection,
showEditor: false,
mode: "SELECTING"
},
geometry: {
coordinates: [
...annotation.geometry.coordinates,
relativeCoordinatesForEvent(e)
],
x: middle.x,
y,
boxX,
boxY,
boxHeight,
boxWidth,
// ...getCoordPercentage(e),
type: TYPE
}
};
} else {
return annotation;
}
}
function onPointerUp(annotation: IAnotacaoDraw, e: any) {
if (annotation.selection) {
const { geometry } = annotation;
if (!geometry) {
return {};
}
switch (annotation.selection.mode) {
case "SELECTING":
return {
...annotation,
selection: {
...annotation.selection,
showEditor: true,
mode: "EDITING"
}
};
default:
break;
}
}
return annotation;
}
function relativeCoordinatesForEvent(e: any): IPoint {
if (isTouchEvent(e)) {
if (isValidTouchEvent(e)) {
e.preventDefault();
return getTouchRelativeCoordinates(e);
} else {
return {
x: 0,
y: 0
};
}
} else {
return getMouseRelativeCoordinates(e);
}
}
const isTouchEvent = (e: any) => e.targetTouches !== undefined;
const isValidTouchEvent = (e: any) => e.targetTouches.length === 1;
const getTouchRelativeCoordinates = (e: any) => {
const touch = e.targetTouches[0];
const boundingRect = e.currentTarget.getBoundingClientRect();
// https://idiallo.com/javascript/element-postion
// https://stackoverflow.com/questions/25630035/javascript-getboundingclientrect-changes-while-scrolling
const offsetX = touch.pageX - boundingRect.left;
const offsetY = touch.pageY - (boundingRect.top + window.scrollY);
return {
x: (offsetX / boundingRect.width) * 100,
y: (offsetY / boundingRect.height) * 100
};
};
function getMouseRelativeCoordinates(e: any) {
return {
x: (e.nativeEvent.offsetX / e.currentTarget.offsetWidth) * 100,
y: (e.nativeEvent.offsetY / e.currentTarget.offsetHeight) * 100
};
}
export default {
TYPE,
intersects,
area,
methods
}; Please desconsider any Typescript types if you intend to use this library with plain js. |
oh wow thank you so much :) you are the best!!!! |
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Hi, in my opinion, freehand selection is a common requirement in image annotation. And i can see, react-image-annotation provide a method for freehand selection realization in the doc. Then, further than that, is it possible to integrate the freehand selection to react-image-annotation immediately or provide a demo showing how to achieve it immediately ? Looking forward to your help, thx a lot.
The text was updated successfully, but these errors were encountered: