import { Point } from "roughjs/bin/geometry"; // https://stackoverflow.com/a/6853926/232122 export function distanceBetweenPointAndSegment( x: number, y: number, x1: number, y1: number, x2: number, y2: number, ) { const A = x - x1; const B = y - y1; const C = x2 - x1; const D = y2 - y1; const dot = A * C + B * D; const lenSquare = C * C + D * D; let param = -1; if (lenSquare !== 0) { // in case of 0 length line param = dot / lenSquare; } let xx, yy; if (param < 0) { xx = x1; yy = y1; } else if (param > 1) { xx = x2; yy = y2; } else { xx = x1 + param * C; yy = y1 + param * D; } const dx = x - xx; const dy = y - yy; return Math.hypot(dx, dy); } export function rotate( x1: number, y1: number, x2: number, y2: number, angle: number, ) { // π‘Žβ€²π‘₯=(π‘Žπ‘₯βˆ’π‘π‘₯)cosπœƒβˆ’(π‘Žπ‘¦βˆ’π‘π‘¦)sinπœƒ+𝑐π‘₯ // π‘Žβ€²π‘¦=(π‘Žπ‘₯βˆ’π‘π‘₯)sinπœƒ+(π‘Žπ‘¦βˆ’π‘π‘¦)cosπœƒ+𝑐𝑦. // https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line return [ (x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2, (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2, ]; } export const getPointOnAPath = (point: Point, path: Point[]) => { const [px, py] = point; const [start, ...other] = path; let [lastX, lastY] = start; let kLine: number = 0; let idx: number = 0; // if any item in the array is true, it means that a point is // on some segment of a line based path const retVal = other.some(([x2, y2], i) => { // we always take a line when dealing with line segments const x1 = lastX; const y1 = lastY; lastX = x2; lastY = y2; // if a point is not within the domain of the line segment // it is not on the line segment if (px < x1 || px > x2) { return false; } // check if all points lie on the same line // y1 = kx1 + b, y2 = kx2 + b // y2 - y1 = k(x2 - x2) -> k = (y2 - y1) / (x2 - x1) // coefficient for the line (p0, p1) const kL = (y2 - y1) / (x2 - x1); // coefficient for the line segment (p0, point) const kP1 = (py - y1) / (px - x1); // coefficient for the line segment (point, p1) const kP2 = (py - y2) / (px - x2); // because we are basing both lines from the same starting point // the only option for collinearity is having same coefficients // using it for floating point comparisons const epsilon = 0.3; // if coefficient is more than an arbitrary epsilon, // these lines are nor collinear if (Math.abs(kP1 - kL) > epsilon && Math.abs(kP2 - kL) > epsilon) { return false; } // store the coefficient because we are goint to need it kLine = kL; idx = i; return true; }); // Return a coordinate that is always on the line segment if (retVal === true) { return { x: point[0], y: kLine * point[0], segment: idx }; } return null; };