chore: Additional master merge

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
Mark Tolmacs
2025-10-27 16:25:31 +01:00
parent e08ebdec28
commit 4d04e3e9a1
4 changed files with 124 additions and 29 deletions

View File

@@ -1,12 +1,29 @@
import { LaserPointer, type Point } from "@excalidraw/laser-pointer";
import { clamp, round, type LocalPoint } from "@excalidraw/math";
import {
clamp,
lineSegment,
pointFrom,
pointRotateRads,
round,
type LocalPoint,
} from "@excalidraw/math";
import getStroke from "perfect-freehand";
import { invariant } from "@excalidraw/common";
import type { GlobalPoint, Radians } from "@excalidraw/math";
import { getElementBounds } from "./bounds";
import type { StrokeOptions } from "perfect-freehand";
import type { ExcalidrawFreeDrawElement, PointerType } from "./types";
import type {
ElementsMap,
ExcalidrawFreeDrawElement,
PointerType,
} from "./types";
export const STROKE_OPTIONS: Record<
PointerType | "default",
@@ -276,3 +293,81 @@ const _legacy_getSvgPathFromStroke = (points: number[][]): string => {
.join(" ")
.replace(TO_FIXED_PRECISION, "$1");
};
export function getFreedrawOutlineAsSegments(
element: ExcalidrawFreeDrawElement,
points: [number, number][],
elementsMap: ElementsMap,
) {
const bounds = getElementBounds(
{
...element,
angle: 0 as Radians,
},
elementsMap,
);
const center = pointFrom<GlobalPoint>(
(bounds[0] + bounds[2]) / 2,
(bounds[1] + bounds[3]) / 2,
);
invariant(points.length >= 2, "Freepath outline must have at least 2 points");
return points.slice(2).reduce(
(acc, curr) => {
acc.push(
lineSegment<GlobalPoint>(
acc[acc.length - 1][1],
pointRotateRads(
pointFrom<GlobalPoint>(curr[0] + element.x, curr[1] + element.y),
center,
element.angle,
),
),
);
return acc;
},
[
lineSegment<GlobalPoint>(
pointRotateRads(
pointFrom<GlobalPoint>(
points[0][0] + element.x,
points[0][1] + element.y,
),
center,
element.angle,
),
pointRotateRads(
pointFrom<GlobalPoint>(
points[1][0] + element.x,
points[1][1] + element.y,
),
center,
element.angle,
),
),
],
);
}
export function getFreedrawOutlinePoints(element: ExcalidrawFreeDrawElement) {
// If input points are empty (should they ever be?) return a dot
const inputPoints = element.simulatePressure
? element.points
: element.points.length
? element.points.map(([x, y], i) => [x, y, element.pressures[i]])
: [[0, 0, 0.5]];
// Consider changing the options for simulated pressure vs real pressure
const options: StrokeOptions = {
simulatePressure: element.simulatePressure,
size: element.strokeWidth * 4.25,
thinning: 0.6,
smoothing: 0.5,
streamline: 0.5,
easing: (t) => Math.sin((t * Math.PI) / 2), // https://easings.net/#easeOutSine
last: !!element.lastCommittedPoint, // LastCommittedPoint is added on pointerup
};
return getStroke(inputPoints as number[][], options) as [number, number][];
}

View File

@@ -9050,13 +9050,13 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte
"backgroundColor": "transparent",
"boundElements": null,
"customData": undefined,
"drawingConfigs": {
"fillStyle": "solid",
"frameId": null,
"freedrawOptions": {
"fixedStrokeWidth": true,
"simplify": "0.10000",
"streamline": "0.35000",
},
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": 50,
"id": "id0",
@@ -9157,13 +9157,13 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte
"backgroundColor": "transparent",
"boundElements": null,
"customData": undefined,
"drawingConfigs": {
"fillStyle": "solid",
"frameId": null,
"freedrawOptions": {
"fixedStrokeWidth": true,
"simplify": "0.10000",
"streamline": "0.35000",
},
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": 50,
"index": "a0",
@@ -12234,13 +12234,13 @@ exports[`history > singleplayer undo/redo > should create entry when selecting f
"backgroundColor": "transparent",
"boundElements": null,
"customData": undefined,
"drawingConfigs": {
"fillStyle": "solid",
"frameId": null,
"freedrawOptions": {
"fixedStrokeWidth": true,
"simplify": "0.10000",
"streamline": "0.35000",
},
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": 10,
"id": "id5",
@@ -12289,13 +12289,13 @@ exports[`history > singleplayer undo/redo > should create entry when selecting f
"backgroundColor": "transparent",
"boundElements": null,
"customData": undefined,
"drawingConfigs": {
"fillStyle": "solid",
"frameId": null,
"freedrawOptions": {
"fixedStrokeWidth": true,
"simplify": "0.10000",
"streamline": "0.35000",
},
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": 10,
"id": "id9",
@@ -12434,13 +12434,13 @@ exports[`history > singleplayer undo/redo > should create entry when selecting f
"backgroundColor": "transparent",
"boundElements": null,
"customData": undefined,
"drawingConfigs": {
"fillStyle": "solid",
"frameId": null,
"freedrawOptions": {
"fixedStrokeWidth": true,
"simplify": "0.10000",
"streamline": "0.35000",
},
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": 10,
"index": "a2",

View File

@@ -7029,13 +7029,13 @@ exports[`regression tests > draw every type of shape > [end of test] undo stack
"backgroundColor": "transparent",
"boundElements": null,
"customData": undefined,
"drawingConfigs": {
"fillStyle": "solid",
"frameId": null,
"freedrawOptions": {
"fixedStrokeWidth": true,
"simplify": "0.10000",
"streamline": "0.35000",
},
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": 10,
"index": "a7",
@@ -9352,13 +9352,13 @@ exports[`regression tests > key 7 selects freedraw tool > [end of test] undo sta
"backgroundColor": "transparent",
"boundElements": null,
"customData": undefined,
"drawingConfigs": {
"fillStyle": "solid",
"frameId": null,
"freedrawOptions": {
"fixedStrokeWidth": true,
"simplify": "0.10000",
"streamline": "0.35000",
},
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": 30,
"index": "a0",
@@ -10387,13 +10387,13 @@ exports[`regression tests > key p selects freedraw tool > [end of test] undo sta
"backgroundColor": "transparent",
"boundElements": null,
"customData": undefined,
"drawingConfigs": {
"fillStyle": "solid",
"frameId": null,
"freedrawOptions": {
"fixedStrokeWidth": true,
"simplify": "0.10000",
"streamline": "0.35000",
},
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": 30,
"index": "a0",

View File

@@ -168,13 +168,13 @@ exports[`restoreElements > should restore freedraw element correctly 1`] = `
"backgroundColor": "transparent",
"boundElements": [],
"customData": undefined,
"drawingConfigs": {
"fillStyle": "solid",
"frameId": null,
"freedrawOptions": {
"fixedStrokeWidth": true,
"simplify": "0.10000",
"streamline": "0.25000",
},
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
"height": 100,
"id": "id-freedraw01",