65be7973be
* rotate rectanble with fixed angle * rotate dashed rectangle with fixed angle * fix rotate handler rect * fix canvas size with rotation * angle in element base * fix bug in calculating canvas size * trial only for rectangle * hitTest for rectangle rotation * properly resize rotated rectangle * fix canvas size calculation * giving up... workaround for now * **experimental** handler to rotate rectangle * remove rotation on copy for debugging * update snapshots * better rotation handler with atan2 * rotate when drawImage * add rotation handler * hitTest for any shapes * fix hitTest for curved lines * rotate text element * rotation locking * hint messaage for rotating * show proper handlers on mobile (a workaround, there should be a better way) * refactor hitTest * support exporting png * support exporting svg * fix rotating curved line * refactor drawElementFromCanvas with getElementAbsoluteCoords * fix export png and svg * adjust resize positions for lines (N, E, S, W) * do not make handlers big on mobile * Update src/locales/en.json Alright! Co-Authored-By: Lipis <lipiridis@gmail.com> * do not show rotation/resizing hints on mobile * proper calculation for N and W positions * simplify calculation * use "rotation" as property name for clarification (may increase bundle size) * update snapshots excluding rotation handle * refactor with adjustPositionWithRotation * refactor with adjustXYWithRotation * forgot to rename rotation * rename internal function * initialize element angle on restore * rotate wysiwyg editor * fix shift-rotate around 270deg * improve rotation locking * refactor adjustXYWithRotation * avoid rotation degree becomes >=360 * refactor with generateHandler Co-authored-by: Lipis <lipiridis@gmail.com> Co-authored-by: dwelle <luzar.david@gmail.com>
147 lines
3.8 KiB
TypeScript
147 lines
3.8 KiB
TypeScript
import { Point } from "./types";
|
||
|
||
// 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 function adjustXYWithRotation(
|
||
side: "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se",
|
||
position: { x: number; y: number },
|
||
deltaX: number,
|
||
deltaY: number,
|
||
angle: number,
|
||
) {
|
||
let { x, y } = position;
|
||
if (side === "e" || side === "ne" || side === "se") {
|
||
x -= (deltaX / 2) * (1 - Math.cos(angle));
|
||
y -= (deltaX / 2) * -Math.sin(angle);
|
||
}
|
||
if (side === "s" || side === "sw" || side === "se") {
|
||
x -= (deltaY / 2) * Math.sin(angle);
|
||
y -= (deltaY / 2) * (1 - Math.cos(angle));
|
||
}
|
||
if (side === "w" || side === "nw" || side === "sw") {
|
||
x += (deltaX / 2) * (1 + Math.cos(angle));
|
||
y += (deltaX / 2) * Math.sin(angle);
|
||
}
|
||
if (side === "n" || side === "nw" || side === "ne") {
|
||
x += (deltaY / 2) * -Math.sin(angle);
|
||
y += (deltaY / 2) * (1 + Math.cos(angle));
|
||
}
|
||
return { x, y };
|
||
}
|
||
|
||
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;
|
||
};
|