mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-04-14 16:40:58 -04:00
* lasso without 'real' shape detection * select a single linear el * improve ux * feed segments to worker * simplify path threshold adaptive to zoom * add a tiny threshold for checks * refactor code * lasso tests * fix: ts * do not capture lasso tool * try worker-loader in next config * update config * refactor * lint * feat: show active tool when using "more tools" * keep lasso if selected from toolbar * fix incorrect checks for resetting to selection * shift for additive selection * bound text related fixes * lint * keep alt toggled lasso selection if shift pressed * fix regression * fix 'dead' lassos * lint * use workerpool and polyfill * fix worker bundled with window related code * refactor * add file extension for worker constructor error * another attempt at constructor error * attempt at build issue * attempt with dynamic import * test not importing from math * narrow down imports * Reusing existing workers infrastructure (fallback to the main thread, type-safety) * Points on curve inside the shared chunk * Give up on experimental code splitting * Remove potentially unnecessary optimisation * Removing workers as the complexit is much worse, while perf. does not seem to be much better * fix selecting text containers and containing frames together * render fill directly from animated trail * do not re-render static when setting selected element ids in lasso * remove unnecessary property * tweak trail animation * slice points to remove notch * always start alt-lasso from initial point * revert build & worker changes (unused) * remove `lasso` from `hasStrokeColor` * label change * remove unused props * remove unsafe optimization * snaps --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com> Co-authored-by: Marcel Mraz <marcel@excalidraw.com>
179 lines
3.9 KiB
TypeScript
179 lines
3.9 KiB
TypeScript
import { line, linesIntersectAt } from "./line";
|
|
import {
|
|
isPoint,
|
|
pointCenter,
|
|
pointFromVector,
|
|
pointRotateRads,
|
|
} from "./point";
|
|
import { PRECISION } from "./utils";
|
|
import {
|
|
vectorAdd,
|
|
vectorCross,
|
|
vectorFromPoint,
|
|
vectorScale,
|
|
vectorSubtract,
|
|
} from "./vector";
|
|
|
|
import type { GlobalPoint, LineSegment, LocalPoint, Radians } from "./types";
|
|
|
|
/**
|
|
* Create a line segment from two points.
|
|
*
|
|
* @param points The two points delimiting the line segment on each end
|
|
* @returns The line segment delineated by the points
|
|
*/
|
|
export function lineSegment<P extends GlobalPoint | LocalPoint>(
|
|
a: P,
|
|
b: P,
|
|
): LineSegment<P> {
|
|
return [a, b] as LineSegment<P>;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param segment
|
|
* @returns
|
|
*/
|
|
export const isLineSegment = <Point extends GlobalPoint | LocalPoint>(
|
|
segment: unknown,
|
|
): segment is LineSegment<Point> =>
|
|
Array.isArray(segment) &&
|
|
segment.length === 2 &&
|
|
isPoint(segment[0]) &&
|
|
isPoint(segment[0]);
|
|
|
|
/**
|
|
* Return the coordinates resulting from rotating the given line about an origin by an angle in radians
|
|
* note that when the origin is not given, the midpoint of the given line is used as the origin.
|
|
*
|
|
* @param l
|
|
* @param angle
|
|
* @param origin
|
|
* @returns
|
|
*/
|
|
export const lineSegmentRotate = <Point extends LocalPoint | GlobalPoint>(
|
|
l: LineSegment<Point>,
|
|
angle: Radians,
|
|
origin?: Point,
|
|
): LineSegment<Point> => {
|
|
return lineSegment(
|
|
pointRotateRads(l[0], origin || pointCenter(l[0], l[1]), angle),
|
|
pointRotateRads(l[1], origin || pointCenter(l[0], l[1]), angle),
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Calculates the point two line segments with a definite start and end point
|
|
* intersect at.
|
|
*/
|
|
export const segmentsIntersectAt = <Point extends GlobalPoint | LocalPoint>(
|
|
a: Readonly<LineSegment<Point>>,
|
|
b: Readonly<LineSegment<Point>>,
|
|
): Point | null => {
|
|
const a0 = vectorFromPoint(a[0]);
|
|
const a1 = vectorFromPoint(a[1]);
|
|
const b0 = vectorFromPoint(b[0]);
|
|
const b1 = vectorFromPoint(b[1]);
|
|
const r = vectorSubtract(a1, a0);
|
|
const s = vectorSubtract(b1, b0);
|
|
const denominator = vectorCross(r, s);
|
|
|
|
if (denominator === 0) {
|
|
return null;
|
|
}
|
|
|
|
const i = vectorSubtract(vectorFromPoint(b[0]), vectorFromPoint(a[0]));
|
|
const u = vectorCross(i, r) / denominator;
|
|
const t = vectorCross(i, s) / denominator;
|
|
|
|
if (u === 0) {
|
|
return null;
|
|
}
|
|
|
|
const p = vectorAdd(a0, vectorScale(r, t));
|
|
|
|
if (t >= 0 && t < 1 && u >= 0 && u < 1) {
|
|
return pointFromVector<Point>(p);
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
export const pointOnLineSegment = <Point extends LocalPoint | GlobalPoint>(
|
|
point: Point,
|
|
line: LineSegment<Point>,
|
|
threshold = PRECISION,
|
|
) => {
|
|
const distance = distanceToLineSegment(point, line);
|
|
|
|
if (distance === 0) {
|
|
return true;
|
|
}
|
|
|
|
return distance < threshold;
|
|
};
|
|
|
|
export const distanceToLineSegment = <Point extends LocalPoint | GlobalPoint>(
|
|
point: Point,
|
|
line: LineSegment<Point>,
|
|
) => {
|
|
const [x, y] = point;
|
|
const [[x1, y1], [x2, y2]] = line;
|
|
|
|
const A = x - x1;
|
|
const B = y - y1;
|
|
const C = x2 - x1;
|
|
const D = y2 - y1;
|
|
|
|
const dot = A * C + B * D;
|
|
const len_sq = C * C + D * D;
|
|
let param = -1;
|
|
if (len_sq !== 0) {
|
|
param = dot / len_sq;
|
|
}
|
|
|
|
let xx;
|
|
let 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.sqrt(dx * dx + dy * dy);
|
|
};
|
|
|
|
/**
|
|
* Returns the intersection point of a segment and a line
|
|
*
|
|
* @param l
|
|
* @param s
|
|
* @returns
|
|
*/
|
|
export function lineSegmentIntersectionPoints<
|
|
Point extends GlobalPoint | LocalPoint,
|
|
>(
|
|
l: LineSegment<Point>,
|
|
s: LineSegment<Point>,
|
|
threshold?: number,
|
|
): Point | null {
|
|
const candidate = linesIntersectAt(line(l[0], l[1]), line(s[0], s[1]));
|
|
|
|
if (
|
|
!candidate ||
|
|
!pointOnLineSegment(candidate, s, threshold) ||
|
|
!pointOnLineSegment(candidate, l, threshold)
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
return candidate;
|
|
}
|