mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
feed segments to worker
This commit is contained in:
parent
b0cdf1c296
commit
33d5886123
4 changed files with 280 additions and 297 deletions
|
@ -1,23 +1,43 @@
|
|||
import { GlobalPoint, pointFrom } from "../../math";
|
||||
import { pointsOnBezierCurves } from "points-on-curve";
|
||||
import {
|
||||
type Curve,
|
||||
type GlobalPoint,
|
||||
type LineSegment,
|
||||
type Radians,
|
||||
lineSegment,
|
||||
pointFrom,
|
||||
pointRotateRads,
|
||||
} from "../../math";
|
||||
import { AnimatedTrail } from "../animated-trail";
|
||||
import { AnimationFrameHandler } from "../animation-frame-handler";
|
||||
import App from "../components/App";
|
||||
import { type AnimationFrameHandler } from "../animation-frame-handler";
|
||||
import type App from "../components/App";
|
||||
import { LinearElementEditor } from "../element/linearElementEditor";
|
||||
import { isFrameLikeElement, isLinearElement } from "../element/typeChecks";
|
||||
import {
|
||||
import type {
|
||||
ElementsMap,
|
||||
ExcalidrawElement,
|
||||
ExcalidrawEllipseElement,
|
||||
ExcalidrawLinearElement,
|
||||
ExcalidrawRectanguloidElement,
|
||||
NonDeleted,
|
||||
} from "../element/types";
|
||||
import { getFrameChildren } from "../frame";
|
||||
import { selectGroupsForSelectedElements } from "../groups";
|
||||
import { easeOut } from "../utils";
|
||||
import { LassoWorkerInput, LassoWorkerOutput } from "./worker";
|
||||
import { getElementShape } from "../shapes";
|
||||
import { arrayToMap, easeOut } from "../utils";
|
||||
import type { LassoWorkerInput, LassoWorkerOutput } from "./worker";
|
||||
import {
|
||||
deconstructDiamondElement,
|
||||
deconstructRectanguloidElement,
|
||||
} from "../element/utils";
|
||||
import { getElementAbsoluteCoords } from "../element";
|
||||
|
||||
export class LassoTrail extends AnimatedTrail {
|
||||
private intersectedElements: Set<ExcalidrawElement["id"]> = new Set();
|
||||
private enclosedElements: Set<ExcalidrawElement["id"]> = new Set();
|
||||
private worker: Worker | null = null;
|
||||
private elementsSegments: Map<string, LineSegment<GlobalPoint>[]> | null =
|
||||
null;
|
||||
|
||||
constructor(animationFrameHandler: AnimationFrameHandler, app: App) {
|
||||
super(animationFrameHandler, app, {
|
||||
|
@ -132,10 +152,20 @@ export class LassoTrail extends AnimatedTrail {
|
|||
.getCurrentTrail()
|
||||
?.originalPoints?.map((p) => pointFrom<GlobalPoint>(p[0], p[1]));
|
||||
|
||||
if (!this.elementsSegments) {
|
||||
this.elementsSegments = new Map();
|
||||
const visibleElementsMap = arrayToMap(this.app.visibleElements);
|
||||
for (const element of this.app.visibleElements) {
|
||||
const segments = getElementLineSegments(element, visibleElementsMap);
|
||||
this.elementsSegments.set(element.id, segments);
|
||||
}
|
||||
}
|
||||
|
||||
if (lassoPath) {
|
||||
const message: LassoWorkerInput = {
|
||||
lassoPath,
|
||||
elements: this.app.visibleElements,
|
||||
elementsSegments: this.elementsSegments,
|
||||
intersectedElements: this.intersectedElements,
|
||||
enclosedElements: this.enclosedElements,
|
||||
};
|
||||
|
@ -149,9 +179,185 @@ export class LassoTrail extends AnimatedTrail {
|
|||
super.clearTrails();
|
||||
this.intersectedElements.clear();
|
||||
this.enclosedElements.clear();
|
||||
this.elementsSegments = null;
|
||||
this.app.setState({
|
||||
lassoSelection: null,
|
||||
});
|
||||
this.worker?.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an element, return the line segments that make up the element.
|
||||
*
|
||||
* Uses helpers from /math
|
||||
*/
|
||||
const getElementLineSegments = (
|
||||
element: ExcalidrawElement,
|
||||
elementsMap: ElementsMap,
|
||||
): LineSegment<GlobalPoint>[] => {
|
||||
const shape = getElementShape(element, elementsMap);
|
||||
const [x1, y1, x2, y2, cx, cy] = getElementAbsoluteCoords(
|
||||
element,
|
||||
elementsMap,
|
||||
);
|
||||
const center = pointFrom<GlobalPoint>(cx, cy);
|
||||
|
||||
if (shape.type === "polycurve") {
|
||||
const curves = shape.data;
|
||||
const points = curves
|
||||
.map((curve) => pointsOnBezierCurves(curve, 10))
|
||||
.flat();
|
||||
let i = 0;
|
||||
const segments: LineSegment<GlobalPoint>[] = [];
|
||||
while (i < points.length - 1) {
|
||||
segments.push(
|
||||
lineSegment(
|
||||
pointFrom(points[i][0], points[i][1]),
|
||||
pointFrom(points[i + 1][0], points[i + 1][1]),
|
||||
),
|
||||
);
|
||||
i++;
|
||||
}
|
||||
|
||||
return segments;
|
||||
} else if (shape.type === "polyline") {
|
||||
return shape.data as LineSegment<GlobalPoint>[];
|
||||
} else if (isRectanguloidElement(element)) {
|
||||
const [sides, corners] = deconstructRectanguloidElement(element);
|
||||
const cornerSegments: LineSegment<GlobalPoint>[] = corners
|
||||
.map((corner) => getSegmentsOnCurve(corner, center, element.angle))
|
||||
.flat();
|
||||
const rotatedSides = getRotatedSides(sides, center, element.angle);
|
||||
return [...rotatedSides, ...cornerSegments];
|
||||
} else if (element.type === "diamond") {
|
||||
const [sides, corners] = deconstructDiamondElement(element);
|
||||
const cornerSegments = corners
|
||||
.map((corner) => getSegmentsOnCurve(corner, center, element.angle))
|
||||
.flat();
|
||||
const rotatedSides = getRotatedSides(sides, center, element.angle);
|
||||
|
||||
return [...rotatedSides, ...cornerSegments];
|
||||
} else if (shape.type === "polygon") {
|
||||
const points = shape.data as GlobalPoint[];
|
||||
const segments: LineSegment<GlobalPoint>[] = [];
|
||||
for (let i = 0; i < points.length - 1; i++) {
|
||||
segments.push(lineSegment(points[i], points[i + 1]));
|
||||
}
|
||||
return segments;
|
||||
} else if (shape.type === "ellipse") {
|
||||
return getSegmentsOnEllipse(element as ExcalidrawEllipseElement);
|
||||
}
|
||||
|
||||
const [nw, ne, sw, se, , , w, e] = (
|
||||
[
|
||||
[x1, y1],
|
||||
[x2, y1],
|
||||
[x1, y2],
|
||||
[x2, y2],
|
||||
[cx, y1],
|
||||
[cx, y2],
|
||||
[x1, cy],
|
||||
[x2, cy],
|
||||
] as GlobalPoint[]
|
||||
).map((point) => pointRotateRads(point, center, element.angle));
|
||||
|
||||
return [
|
||||
lineSegment(nw, ne),
|
||||
lineSegment(sw, se),
|
||||
lineSegment(nw, sw),
|
||||
lineSegment(ne, se),
|
||||
lineSegment(nw, e),
|
||||
lineSegment(sw, e),
|
||||
lineSegment(ne, w),
|
||||
lineSegment(se, w),
|
||||
];
|
||||
};
|
||||
|
||||
const isRectanguloidElement = (
|
||||
element: ExcalidrawElement,
|
||||
): element is ExcalidrawRectanguloidElement => {
|
||||
return (
|
||||
element != null &&
|
||||
(element.type === "rectangle" ||
|
||||
element.type === "image" ||
|
||||
element.type === "iframe" ||
|
||||
element.type === "embeddable" ||
|
||||
element.type === "frame" ||
|
||||
element.type === "magicframe" ||
|
||||
(element.type === "text" && !element.containerId))
|
||||
);
|
||||
};
|
||||
|
||||
const getRotatedSides = (
|
||||
sides: LineSegment<GlobalPoint>[],
|
||||
center: GlobalPoint,
|
||||
angle: Radians,
|
||||
) => {
|
||||
return sides.map((side) => {
|
||||
return lineSegment(
|
||||
pointRotateRads<GlobalPoint>(side[0], center, angle),
|
||||
pointRotateRads<GlobalPoint>(side[1], center, angle),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const getSegmentsOnCurve = (
|
||||
curve: Curve<GlobalPoint>,
|
||||
center: GlobalPoint,
|
||||
angle: Radians,
|
||||
): LineSegment<GlobalPoint>[] => {
|
||||
const points = pointsOnBezierCurves(curve, 10);
|
||||
let i = 0;
|
||||
const segments: LineSegment<GlobalPoint>[] = [];
|
||||
while (i < points.length - 1) {
|
||||
segments.push(
|
||||
lineSegment(
|
||||
pointRotateRads<GlobalPoint>(
|
||||
pointFrom(points[i][0], points[i][1]),
|
||||
center,
|
||||
angle,
|
||||
),
|
||||
pointRotateRads<GlobalPoint>(
|
||||
pointFrom(points[i + 1][0], points[i + 1][1]),
|
||||
center,
|
||||
angle,
|
||||
),
|
||||
),
|
||||
);
|
||||
i++;
|
||||
}
|
||||
|
||||
return segments;
|
||||
};
|
||||
|
||||
const getSegmentsOnEllipse = (
|
||||
ellipse: ExcalidrawEllipseElement,
|
||||
): LineSegment<GlobalPoint>[] => {
|
||||
const center = pointFrom<GlobalPoint>(
|
||||
ellipse.x + ellipse.width / 2,
|
||||
ellipse.y + ellipse.height / 2,
|
||||
);
|
||||
|
||||
const a = ellipse.width / 2;
|
||||
const b = ellipse.height / 2;
|
||||
|
||||
const segments: LineSegment<GlobalPoint>[] = [];
|
||||
const points: GlobalPoint[] = [];
|
||||
const n = 90;
|
||||
const deltaT = (Math.PI * 2) / n;
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
const t = i * deltaT;
|
||||
const x = center[0] + a * Math.cos(t);
|
||||
const y = center[1] + b * Math.sin(t);
|
||||
points.push(pointRotateRads(pointFrom(x, y), center, ellipse.angle));
|
||||
}
|
||||
|
||||
for (let i = 0; i < points.length - 1; i++) {
|
||||
segments.push(lineSegment(points[i], points[i + 1]));
|
||||
}
|
||||
|
||||
segments.push(lineSegment(points[points.length - 1], points[0]));
|
||||
return segments;
|
||||
};
|
||||
|
|
|
@ -1,18 +1,8 @@
|
|||
import {
|
||||
GlobalPoint,
|
||||
LineSegment,
|
||||
LocalPoint,
|
||||
Radians,
|
||||
} from "../../math/types";
|
||||
import { pointFrom, pointRotateRads } from "../../math/point";
|
||||
import { polygonFromPoints } from "../../math/polygon";
|
||||
import { ElementsMap, ExcalidrawElement } from "../element/types";
|
||||
import { pointsOnBezierCurves, simplify } from "points-on-curve";
|
||||
import { lineSegment } from "../../math/segment";
|
||||
import throttle from "lodash.throttle";
|
||||
import { RoughGenerator } from "roughjs/bin/generator";
|
||||
import { Point } from "roughjs/bin/geometry";
|
||||
import { Drawable, Op } from "roughjs/bin/core";
|
||||
import type { GlobalPoint, LineSegment } from "../../math/types";
|
||||
import { polygonFromPoints, polygonIncludesPoint } from "../../math/polygon";
|
||||
import type { ExcalidrawElement } from "../element/types";
|
||||
import { lineSegment, lineSegmentIntersectionPoints } from "../../math/segment";
|
||||
import { simplify } from "points-on-curve";
|
||||
|
||||
// variables to track processing state and latest input data
|
||||
// for "backpressure" purposes
|
||||
|
@ -38,7 +28,9 @@ self.onmessage = (event: MessageEvent<LassoWorkerInput>) => {
|
|||
// function to process the latest data
|
||||
const processInputData = () => {
|
||||
// If no data to process, return
|
||||
if (!latestInputData) return;
|
||||
if (!latestInputData) {
|
||||
return;
|
||||
}
|
||||
|
||||
// capture the current data to process and reset latestData
|
||||
const dataToProcess = latestInputData;
|
||||
|
@ -79,9 +71,12 @@ const processInputData = () => {
|
|||
}
|
||||
};
|
||||
|
||||
type ElementsSegments = Map<string, LineSegment<GlobalPoint>[]>;
|
||||
|
||||
export type LassoWorkerInput = {
|
||||
lassoPath: GlobalPoint[];
|
||||
elements: readonly ExcalidrawElement[];
|
||||
elementsSegments: ElementsSegments;
|
||||
intersectedElements: Set<ExcalidrawElement["id"]>;
|
||||
enclosedElements: Set<ExcalidrawElement["id"]>;
|
||||
};
|
||||
|
@ -90,28 +85,34 @@ export type LassoWorkerOutput = {
|
|||
selectedElementIds: string[];
|
||||
};
|
||||
|
||||
export const updateSelection = throttle(
|
||||
(input: LassoWorkerInput): LassoWorkerOutput => {
|
||||
const { lassoPath, elements, intersectedElements, enclosedElements } =
|
||||
input;
|
||||
|
||||
const elementsMap = arrayToMap(elements);
|
||||
export const updateSelection = (input: LassoWorkerInput): LassoWorkerOutput => {
|
||||
const {
|
||||
lassoPath,
|
||||
elements,
|
||||
elementsSegments,
|
||||
intersectedElements,
|
||||
enclosedElements,
|
||||
} = input;
|
||||
// simplify the path to reduce the number of points
|
||||
const simplifiedPath = simplify(lassoPath, 0.75) as GlobalPoint[];
|
||||
const path = simplify(lassoPath, 2) as GlobalPoint[];
|
||||
// close the path to form a polygon for enclosure check
|
||||
const closedPath = polygonFromPoints(simplifiedPath);
|
||||
const closedPath = polygonFromPoints(path);
|
||||
// as the path might not enclose a shape anymore, clear before checking
|
||||
enclosedElements.clear();
|
||||
for (const [, element] of elementsMap) {
|
||||
for (const element of elements) {
|
||||
if (
|
||||
!intersectedElements.has(element.id) &&
|
||||
!enclosedElements.has(element.id)
|
||||
) {
|
||||
const enclosed = enclosureTest(closedPath, element, elementsMap);
|
||||
const enclosed = enclosureTest(closedPath, element, elementsSegments);
|
||||
if (enclosed) {
|
||||
enclosedElements.add(element.id);
|
||||
} else {
|
||||
const intersects = intersectionTest(closedPath, element, elementsMap);
|
||||
const intersects = intersectionTest(
|
||||
closedPath,
|
||||
element,
|
||||
elementsSegments,
|
||||
);
|
||||
if (intersects) {
|
||||
intersectedElements.add(element.id);
|
||||
}
|
||||
|
@ -124,270 +125,46 @@ export const updateSelection = throttle(
|
|||
return {
|
||||
selectedElementIds: results,
|
||||
};
|
||||
},
|
||||
100,
|
||||
);
|
||||
};
|
||||
|
||||
const enclosureTest = (
|
||||
lassoPath: GlobalPoint[],
|
||||
element: ExcalidrawElement,
|
||||
elementsMap: ElementsMap,
|
||||
elementsSegments: ElementsSegments,
|
||||
): boolean => {
|
||||
const lassoPolygon = polygonFromPoints(lassoPath);
|
||||
const segments = getElementLineSegments(element, elementsMap);
|
||||
const segments = elementsSegments.get(element.id);
|
||||
if (!segments) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return segments.some((segment) => {
|
||||
return segment.some((point) => isPointInPolygon(point, lassoPolygon));
|
||||
return segment.some((point) => polygonIncludesPoint(point, lassoPolygon));
|
||||
});
|
||||
};
|
||||
|
||||
// // Helper function to check if a point is inside a polygon
|
||||
const isPointInPolygon = (
|
||||
point: GlobalPoint,
|
||||
polygon: GlobalPoint[],
|
||||
): boolean => {
|
||||
let isInside = false;
|
||||
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
||||
const xi = polygon[i][0],
|
||||
yi = polygon[i][1];
|
||||
const xj = polygon[j][0],
|
||||
yj = polygon[j][1];
|
||||
|
||||
const intersect =
|
||||
yi > point[1] !== yj > point[1] &&
|
||||
point[0] < ((xj - xi) * (point[1] - yi)) / (yj - yi) + xi;
|
||||
if (intersect) isInside = !isInside;
|
||||
}
|
||||
return isInside;
|
||||
};
|
||||
|
||||
const intersectionTest = (
|
||||
lassoPath: GlobalPoint[],
|
||||
element: ExcalidrawElement,
|
||||
elementsMap: ElementsMap,
|
||||
elementsSegments: ElementsSegments,
|
||||
): boolean => {
|
||||
const elementSegments = getElementLineSegments(element, elementsMap);
|
||||
const elementSegments = elementsSegments.get(element.id);
|
||||
if (!elementSegments) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const lassoSegments = lassoPath.reduce((acc, point, index) => {
|
||||
if (index === 0) return acc;
|
||||
acc.push([lassoPath[index - 1], point] as [GlobalPoint, GlobalPoint]);
|
||||
if (index === 0) {
|
||||
return acc;
|
||||
}, [] as [GlobalPoint, GlobalPoint][]);
|
||||
}
|
||||
acc.push(lineSegment(lassoPath[index - 1], point));
|
||||
return acc;
|
||||
}, [] as LineSegment<GlobalPoint>[]);
|
||||
|
||||
return lassoSegments.some((lassoSegment) =>
|
||||
elementSegments.some((elementSegment) =>
|
||||
doLineSegmentsIntersect(lassoSegment, elementSegment),
|
||||
elementSegments.some(
|
||||
(elementSegment) =>
|
||||
lineSegmentIntersectionPoints(lassoSegment, elementSegment) !== null,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
// Helper function to check if two line segments intersect
|
||||
const doLineSegmentsIntersect = (
|
||||
[p1, p2]: [GlobalPoint, GlobalPoint],
|
||||
[p3, p4]: [GlobalPoint, GlobalPoint],
|
||||
): boolean => {
|
||||
const denominator =
|
||||
(p4[1] - p3[1]) * (p2[0] - p1[0]) - (p4[0] - p3[0]) * (p2[1] - p1[1]);
|
||||
|
||||
if (denominator === 0) return false;
|
||||
|
||||
const ua =
|
||||
((p4[0] - p3[0]) * (p1[1] - p3[1]) - (p4[1] - p3[1]) * (p1[0] - p3[0])) /
|
||||
denominator;
|
||||
const ub =
|
||||
((p2[0] - p1[0]) * (p1[1] - p3[1]) - (p2[1] - p1[1]) * (p1[0] - p3[0])) /
|
||||
denominator;
|
||||
|
||||
return ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1;
|
||||
};
|
||||
|
||||
const getCurvePathOps = (shape: Drawable): Op[] => {
|
||||
for (const set of shape.sets) {
|
||||
if (set.type === "path") {
|
||||
return set.ops;
|
||||
}
|
||||
}
|
||||
return shape.sets[0].ops;
|
||||
};
|
||||
|
||||
const getElementLineSegments = (
|
||||
element: ExcalidrawElement,
|
||||
elementsMap: ElementsMap,
|
||||
): LineSegment<GlobalPoint>[] => {
|
||||
const [x1, y1, x2, y2, cx, cy] = [
|
||||
element.x,
|
||||
element.y,
|
||||
element.x + element.width,
|
||||
element.y + element.height,
|
||||
element.x + element.width / 2,
|
||||
element.y + element.height / 2,
|
||||
];
|
||||
|
||||
const center: GlobalPoint = pointFrom(cx, cy);
|
||||
|
||||
if (
|
||||
element.type === "line" ||
|
||||
element.type === "arrow" ||
|
||||
element.type === "freedraw"
|
||||
) {
|
||||
const segments: LineSegment<GlobalPoint>[] = [];
|
||||
|
||||
const getPointsOnCurve = () => {
|
||||
const generator = new RoughGenerator();
|
||||
|
||||
const drawable = generator.curve(element.points as unknown as Point[]);
|
||||
|
||||
const ops = getCurvePathOps(drawable);
|
||||
|
||||
const _points: LocalPoint[] = [];
|
||||
// let odd = false;
|
||||
// for (const operation of ops) {
|
||||
// if (operation.op === "move") {
|
||||
// odd = !odd;
|
||||
// if (odd) {
|
||||
// if (
|
||||
// Array.isArray(operation.data) &&
|
||||
// operation.data.length >= 2 &&
|
||||
// operation.data.every(
|
||||
// (d) => d !== undefined && typeof d === "number",
|
||||
// )
|
||||
// ) {
|
||||
// _points.push(pointFrom(operation.data[0], operation.data[1]));
|
||||
// }
|
||||
// }
|
||||
// } else if (operation.op === "bcurveTo") {
|
||||
// if (odd) {
|
||||
// if (
|
||||
// Array.isArray(operation.data) &&
|
||||
// operation.data.length === 6 &&
|
||||
// operation.data.every(
|
||||
// (d) => d !== undefined && typeof d === "number",
|
||||
// )
|
||||
// ) {
|
||||
// _points.push(pointFrom(operation.data[0], operation.data[1]));
|
||||
// _points.push(pointFrom(operation.data[2], operation.data[3]));
|
||||
// _points.push(pointFrom(operation.data[4], operation.data[5]));
|
||||
// }
|
||||
// }
|
||||
// } else if (operation.op === "lineTo") {
|
||||
// if (
|
||||
// Array.isArray(operation.data) &&
|
||||
// operation.data.length >= 2 &&
|
||||
// odd &&
|
||||
// operation.data.every(
|
||||
// (d) => d !== undefined && typeof d === "number",
|
||||
// )
|
||||
// ) {
|
||||
// _points.push(pointFrom(operation.data[0], operation.data[1]));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return pointsOnBezierCurves(_points, 10, 5);
|
||||
};
|
||||
|
||||
let i = 0;
|
||||
|
||||
// const points =
|
||||
// element.roughness !== 0 && element.type !== "freedraw"
|
||||
// ? getPointsOnCurve()
|
||||
// : element.points;
|
||||
|
||||
const points = element.points;
|
||||
|
||||
while (i < points.length - 1) {
|
||||
segments.push(
|
||||
lineSegment(
|
||||
pointRotateRads(
|
||||
pointFrom(
|
||||
element.points[i][0] + element.x,
|
||||
element.points[i][1] + element.y,
|
||||
),
|
||||
center,
|
||||
element.angle,
|
||||
),
|
||||
pointRotateRads(
|
||||
pointFrom(
|
||||
element.points[i + 1][0] + element.x,
|
||||
element.points[i + 1][1] + element.y,
|
||||
),
|
||||
center,
|
||||
element.angle,
|
||||
),
|
||||
),
|
||||
);
|
||||
i++;
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
const [nw, ne, sw, se, n, s, w, e] = (
|
||||
[
|
||||
[x1, y1],
|
||||
[x2, y1],
|
||||
[x1, y2],
|
||||
[x2, y2],
|
||||
[cx, y1],
|
||||
[cx, y2],
|
||||
[x1, cy],
|
||||
[x2, cy],
|
||||
] as GlobalPoint[]
|
||||
).map((point) => pointRotateRads(point, center, element.angle));
|
||||
|
||||
if (element.type === "diamond") {
|
||||
return [
|
||||
lineSegment(n, w),
|
||||
lineSegment(n, e),
|
||||
lineSegment(s, w),
|
||||
lineSegment(s, e),
|
||||
];
|
||||
}
|
||||
|
||||
if (element.type === "ellipse") {
|
||||
return [
|
||||
lineSegment(n, w),
|
||||
lineSegment(n, e),
|
||||
lineSegment(s, w),
|
||||
lineSegment(s, e),
|
||||
lineSegment(n, w),
|
||||
lineSegment(n, e),
|
||||
lineSegment(s, w),
|
||||
lineSegment(s, e),
|
||||
];
|
||||
}
|
||||
|
||||
if (element.type === "frame" || element.type === "magicframe") {
|
||||
return [
|
||||
lineSegment(nw, ne),
|
||||
lineSegment(ne, se),
|
||||
lineSegment(se, sw),
|
||||
lineSegment(sw, nw),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
lineSegment(nw, ne),
|
||||
lineSegment(sw, se),
|
||||
lineSegment(nw, sw),
|
||||
lineSegment(ne, se),
|
||||
lineSegment(nw, e),
|
||||
lineSegment(sw, e),
|
||||
lineSegment(ne, w),
|
||||
lineSegment(se, w),
|
||||
];
|
||||
};
|
||||
|
||||
// This is a copy of arrayToMap from utils.ts
|
||||
// copy to avoid accessing DOM related things in worker
|
||||
const arrayToMap = <T extends { id: string } | string>(
|
||||
items: readonly T[] | Map<string, T>,
|
||||
) => {
|
||||
if (items instanceof Map) {
|
||||
return items;
|
||||
}
|
||||
return items.reduce((acc: Map<string, T>, element) => {
|
||||
acc.set(typeof element === "string" ? element : element.id, element);
|
||||
return acc;
|
||||
}, new Map());
|
||||
};
|
||||
|
|
|
@ -60,7 +60,7 @@ import { LinearElementEditor } from "../element/linearElementEditor";
|
|||
import { getContainingFrame } from "../frame";
|
||||
import { ShapeCache } from "../scene/ShapeCache";
|
||||
import { getVerticalOffset } from "../fonts";
|
||||
import { GlobalPoint, isRightAngleRads } from "../../math";
|
||||
import { isRightAngleRads } from "../../math";
|
||||
import { getCornerRadius } from "../shapes";
|
||||
import { getUncroppedImageElement } from "../element/cropElement";
|
||||
import { getLineHeightInPx } from "../element/textMeasurements";
|
||||
|
|
|
@ -42,7 +42,7 @@ import type { ContextMenuItems } from "./components/ContextMenu";
|
|||
import type { SnapLine } from "./snapping";
|
||||
import type { Merge, MaybePromise, ValueOf, MakeBrand } from "./utility-types";
|
||||
import type { StoreActionType } from "./store";
|
||||
import { GlobalPoint } from "../math";
|
||||
import type { GlobalPoint } from "../math";
|
||||
|
||||
export type SocketId = string & { _brand: "SocketId" };
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue