aboutsummaryrefslogtreecommitdiffstats
path: root/packages/excalidraw/element/utils.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/excalidraw/element/utils.ts')
-rw-r--r--packages/excalidraw/element/utils.ts355
1 files changed, 355 insertions, 0 deletions
diff --git a/packages/excalidraw/element/utils.ts b/packages/excalidraw/element/utils.ts
new file mode 100644
index 0000000..d85cd78
--- /dev/null
+++ b/packages/excalidraw/element/utils.ts
@@ -0,0 +1,355 @@
+import { getDiamondPoints } from ".";
+import type { Curve, LineSegment } from "@excalidraw/math";
+import {
+ curve,
+ lineSegment,
+ pointFrom,
+ pointFromVector,
+ rectangle,
+ vectorFromPoint,
+ vectorNormalize,
+ vectorScale,
+ type GlobalPoint,
+} from "@excalidraw/math";
+import { getCornerRadius } from "../shapes";
+import type {
+ ExcalidrawDiamondElement,
+ ExcalidrawRectanguloidElement,
+} from "./types";
+
+/**
+ * Get the building components of a rectanguloid element in the form of
+ * line segments and curves.
+ *
+ * @param element Target rectanguloid element
+ * @param offset Optional offset to expand the rectanguloid shape
+ * @returns Tuple of line segments (0) and curves (1)
+ */
+export function deconstructRectanguloidElement(
+ element: ExcalidrawRectanguloidElement,
+ offset: number = 0,
+): [LineSegment<GlobalPoint>[], Curve<GlobalPoint>[]] {
+ const roundness = getCornerRadius(
+ Math.min(element.width, element.height),
+ element,
+ );
+
+ if (roundness <= 0) {
+ const r = rectangle(
+ pointFrom(element.x - offset, element.y - offset),
+ pointFrom(
+ element.x + element.width + offset,
+ element.y + element.height + offset,
+ ),
+ );
+
+ const top = lineSegment<GlobalPoint>(
+ pointFrom<GlobalPoint>(r[0][0] + roundness, r[0][1]),
+ pointFrom<GlobalPoint>(r[1][0] - roundness, r[0][1]),
+ );
+ const right = lineSegment<GlobalPoint>(
+ pointFrom<GlobalPoint>(r[1][0], r[0][1] + roundness),
+ pointFrom<GlobalPoint>(r[1][0], r[1][1] - roundness),
+ );
+ const bottom = lineSegment<GlobalPoint>(
+ pointFrom<GlobalPoint>(r[0][0] + roundness, r[1][1]),
+ pointFrom<GlobalPoint>(r[1][0] - roundness, r[1][1]),
+ );
+ const left = lineSegment<GlobalPoint>(
+ pointFrom<GlobalPoint>(r[0][0], r[1][1] - roundness),
+ pointFrom<GlobalPoint>(r[0][0], r[0][1] + roundness),
+ );
+ const sides = [top, right, bottom, left];
+
+ return [sides, []];
+ }
+
+ const center = pointFrom<GlobalPoint>(
+ element.x + element.width / 2,
+ element.y + element.height / 2,
+ );
+
+ const r = rectangle(
+ pointFrom(element.x, element.y),
+ pointFrom(element.x + element.width, element.y + element.height),
+ );
+
+ const top = lineSegment<GlobalPoint>(
+ pointFrom<GlobalPoint>(r[0][0] + roundness, r[0][1]),
+ pointFrom<GlobalPoint>(r[1][0] - roundness, r[0][1]),
+ );
+ const right = lineSegment<GlobalPoint>(
+ pointFrom<GlobalPoint>(r[1][0], r[0][1] + roundness),
+ pointFrom<GlobalPoint>(r[1][0], r[1][1] - roundness),
+ );
+ const bottom = lineSegment<GlobalPoint>(
+ pointFrom<GlobalPoint>(r[0][0] + roundness, r[1][1]),
+ pointFrom<GlobalPoint>(r[1][0] - roundness, r[1][1]),
+ );
+ const left = lineSegment<GlobalPoint>(
+ pointFrom<GlobalPoint>(r[0][0], r[1][1] - roundness),
+ pointFrom<GlobalPoint>(r[0][0], r[0][1] + roundness),
+ );
+
+ const offsets = [
+ vectorScale(
+ vectorNormalize(
+ vectorFromPoint(pointFrom(r[0][0] - offset, r[0][1] - offset), center),
+ ),
+ offset,
+ ), // TOP LEFT
+ vectorScale(
+ vectorNormalize(
+ vectorFromPoint(pointFrom(r[1][0] + offset, r[0][1] - offset), center),
+ ),
+ offset,
+ ), //TOP RIGHT
+ vectorScale(
+ vectorNormalize(
+ vectorFromPoint(pointFrom(r[1][0] + offset, r[1][1] + offset), center),
+ ),
+ offset,
+ ), // BOTTOM RIGHT
+ vectorScale(
+ vectorNormalize(
+ vectorFromPoint(pointFrom(r[0][0] - offset, r[1][1] + offset), center),
+ ),
+ offset,
+ ), // BOTTOM LEFT
+ ];
+
+ const corners = [
+ curve(
+ pointFromVector(offsets[0], left[1]),
+ pointFromVector(
+ offsets[0],
+ pointFrom<GlobalPoint>(
+ left[1][0] + (2 / 3) * (r[0][0] - left[1][0]),
+ left[1][1] + (2 / 3) * (r[0][1] - left[1][1]),
+ ),
+ ),
+ pointFromVector(
+ offsets[0],
+ pointFrom<GlobalPoint>(
+ top[0][0] + (2 / 3) * (r[0][0] - top[0][0]),
+ top[0][1] + (2 / 3) * (r[0][1] - top[0][1]),
+ ),
+ ),
+ pointFromVector(offsets[0], top[0]),
+ ), // TOP LEFT
+ curve(
+ pointFromVector(offsets[1], top[1]),
+ pointFromVector(
+ offsets[1],
+ pointFrom<GlobalPoint>(
+ top[1][0] + (2 / 3) * (r[1][0] - top[1][0]),
+ top[1][1] + (2 / 3) * (r[0][1] - top[1][1]),
+ ),
+ ),
+ pointFromVector(
+ offsets[1],
+ pointFrom<GlobalPoint>(
+ right[0][0] + (2 / 3) * (r[1][0] - right[0][0]),
+ right[0][1] + (2 / 3) * (r[0][1] - right[0][1]),
+ ),
+ ),
+ pointFromVector(offsets[1], right[0]),
+ ), // TOP RIGHT
+ curve(
+ pointFromVector(offsets[2], right[1]),
+ pointFromVector(
+ offsets[2],
+ pointFrom<GlobalPoint>(
+ right[1][0] + (2 / 3) * (r[1][0] - right[1][0]),
+ right[1][1] + (2 / 3) * (r[1][1] - right[1][1]),
+ ),
+ ),
+ pointFromVector(
+ offsets[2],
+ pointFrom<GlobalPoint>(
+ bottom[1][0] + (2 / 3) * (r[1][0] - bottom[1][0]),
+ bottom[1][1] + (2 / 3) * (r[1][1] - bottom[1][1]),
+ ),
+ ),
+ pointFromVector(offsets[2], bottom[1]),
+ ), // BOTTOM RIGHT
+ curve(
+ pointFromVector(offsets[3], bottom[0]),
+ pointFromVector(
+ offsets[3],
+ pointFrom<GlobalPoint>(
+ bottom[0][0] + (2 / 3) * (r[0][0] - bottom[0][0]),
+ bottom[0][1] + (2 / 3) * (r[1][1] - bottom[0][1]),
+ ),
+ ),
+ pointFromVector(
+ offsets[3],
+ pointFrom<GlobalPoint>(
+ left[0][0] + (2 / 3) * (r[0][0] - left[0][0]),
+ left[0][1] + (2 / 3) * (r[1][1] - left[0][1]),
+ ),
+ ),
+ pointFromVector(offsets[3], left[0]),
+ ), // BOTTOM LEFT
+ ];
+
+ const sides = [
+ lineSegment<GlobalPoint>(corners[0][3], corners[1][0]),
+ lineSegment<GlobalPoint>(corners[1][3], corners[2][0]),
+ lineSegment<GlobalPoint>(corners[2][3], corners[3][0]),
+ lineSegment<GlobalPoint>(corners[3][3], corners[0][0]),
+ ];
+
+ return [sides, corners];
+}
+
+/**
+ * Get the building components of a diamond element in the form of
+ * line segments and curves as a tuple, in this order.
+ *
+ * @param element The element to deconstruct
+ * @param offset An optional offset
+ * @returns Tuple of line segments (0) and curves (1)
+ */
+export function deconstructDiamondElement(
+ element: ExcalidrawDiamondElement,
+ offset: number = 0,
+): [LineSegment<GlobalPoint>[], Curve<GlobalPoint>[]] {
+ const [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY] =
+ getDiamondPoints(element);
+ const verticalRadius = getCornerRadius(Math.abs(topX - leftX), element);
+ const horizontalRadius = getCornerRadius(Math.abs(rightY - topY), element);
+
+ if (element.roundness?.type == null) {
+ const [top, right, bottom, left]: GlobalPoint[] = [
+ pointFrom(element.x + topX, element.y + topY - offset),
+ pointFrom(element.x + rightX + offset, element.y + rightY),
+ pointFrom(element.x + bottomX, element.y + bottomY + offset),
+ pointFrom(element.x + leftX - offset, element.y + leftY),
+ ];
+
+ // Create the line segment parts of the diamond
+ // NOTE: Horizontal and vertical seems to be flipped here
+ const topRight = lineSegment<GlobalPoint>(
+ pointFrom(top[0] + verticalRadius, top[1] + horizontalRadius),
+ pointFrom(right[0] - verticalRadius, right[1] - horizontalRadius),
+ );
+ const bottomRight = lineSegment<GlobalPoint>(
+ pointFrom(right[0] - verticalRadius, right[1] + horizontalRadius),
+ pointFrom(bottom[0] + verticalRadius, bottom[1] - horizontalRadius),
+ );
+ const bottomLeft = lineSegment<GlobalPoint>(
+ pointFrom(bottom[0] - verticalRadius, bottom[1] - horizontalRadius),
+ pointFrom(left[0] + verticalRadius, left[1] + horizontalRadius),
+ );
+ const topLeft = lineSegment<GlobalPoint>(
+ pointFrom(left[0] + verticalRadius, left[1] - horizontalRadius),
+ pointFrom(top[0] - verticalRadius, top[1] + horizontalRadius),
+ );
+
+ return [[topRight, bottomRight, bottomLeft, topLeft], []];
+ }
+
+ const center = pointFrom<GlobalPoint>(
+ element.x + element.width / 2,
+ element.y + element.height / 2,
+ );
+
+ const [top, right, bottom, left]: GlobalPoint[] = [
+ pointFrom(element.x + topX, element.y + topY),
+ pointFrom(element.x + rightX, element.y + rightY),
+ pointFrom(element.x + bottomX, element.y + bottomY),
+ pointFrom(element.x + leftX, element.y + leftY),
+ ];
+
+ const offsets = [
+ vectorScale(vectorNormalize(vectorFromPoint(right, center)), offset), // RIGHT
+ vectorScale(vectorNormalize(vectorFromPoint(bottom, center)), offset), // BOTTOM
+ vectorScale(vectorNormalize(vectorFromPoint(left, center)), offset), // LEFT
+ vectorScale(vectorNormalize(vectorFromPoint(top, center)), offset), // TOP
+ ];
+
+ const corners = [
+ curve(
+ pointFromVector(
+ offsets[0],
+ pointFrom<GlobalPoint>(
+ right[0] - verticalRadius,
+ right[1] - horizontalRadius,
+ ),
+ ),
+ pointFromVector(offsets[0], right),
+ pointFromVector(offsets[0], right),
+ pointFromVector(
+ offsets[0],
+ pointFrom<GlobalPoint>(
+ right[0] - verticalRadius,
+ right[1] + horizontalRadius,
+ ),
+ ),
+ ), // RIGHT
+ curve(
+ pointFromVector(
+ offsets[1],
+ pointFrom<GlobalPoint>(
+ bottom[0] + verticalRadius,
+ bottom[1] - horizontalRadius,
+ ),
+ ),
+ pointFromVector(offsets[1], bottom),
+ pointFromVector(offsets[1], bottom),
+ pointFromVector(
+ offsets[1],
+ pointFrom<GlobalPoint>(
+ bottom[0] - verticalRadius,
+ bottom[1] - horizontalRadius,
+ ),
+ ),
+ ), // BOTTOM
+ curve(
+ pointFromVector(
+ offsets[2],
+ pointFrom<GlobalPoint>(
+ left[0] + verticalRadius,
+ left[1] + horizontalRadius,
+ ),
+ ),
+ pointFromVector(offsets[2], left),
+ pointFromVector(offsets[2], left),
+ pointFromVector(
+ offsets[2],
+ pointFrom<GlobalPoint>(
+ left[0] + verticalRadius,
+ left[1] - horizontalRadius,
+ ),
+ ),
+ ), // LEFT
+ curve(
+ pointFromVector(
+ offsets[3],
+ pointFrom<GlobalPoint>(
+ top[0] - verticalRadius,
+ top[1] + horizontalRadius,
+ ),
+ ),
+ pointFromVector(offsets[3], top),
+ pointFromVector(offsets[3], top),
+ pointFromVector(
+ offsets[3],
+ pointFrom<GlobalPoint>(
+ top[0] + verticalRadius,
+ top[1] + horizontalRadius,
+ ),
+ ),
+ ), // TOP
+ ];
+
+ const sides = [
+ lineSegment<GlobalPoint>(corners[0][3], corners[1][0]),
+ lineSegment<GlobalPoint>(corners[1][3], corners[2][0]),
+ lineSegment<GlobalPoint>(corners[2][3], corners[3][0]),
+ lineSegment<GlobalPoint>(corners[3][3], corners[0][0]),
+ ];
+
+ return [sides, corners];
+}