put streamline & simplify into ele obj too

This commit is contained in:
Ryan Di 2025-06-13 18:12:56 +10:00
parent c08840358b
commit 37b75263f8
8 changed files with 57 additions and 36 deletions

View File

@ -1,15 +1,20 @@
import { DRAWING_CONFIGS } from "@excalidraw/element";
import { useState, useEffect } from "react";
export const FreedrawDebugSliders = () => {
const [streamline, setStreamline] = useState(0.62);
const [simplify, setSimplify] = useState(0.3);
const [streamline, setStreamline] = useState<number>(
DRAWING_CONFIGS.default.streamline,
);
const [simplify, setSimplify] = useState<number>(
DRAWING_CONFIGS.default.simplify,
);
useEffect(() => {
if (!window.h) {
window.h = {} as any;
}
if (!window.h.debugFreedraw) {
window.h.debugFreedraw = { streamline: 0.62, simplify: 0.3 };
window.h.debugFreedraw = DRAWING_CONFIGS.default;
}
setStreamline(window.h.debugFreedraw.streamline);

View File

@ -513,12 +513,12 @@ export const _generateElementShape = (
if (isPathALoop(element.points)) {
const points =
element.pressureSensitivity === null
element.drawingConfigs === null
? simplify(element.points as LocalPoint[], 0.75)
: simplify(element.points as LocalPoint[], 1.5);
shape =
element.pressureSensitivity === null
element.drawingConfigs === null
? generator.curve(points, {
...generateRoughOptions(element),
stroke: "none",

View File

@ -8,6 +8,13 @@ import type { StrokeOptions } from "perfect-freehand";
import type { ExcalidrawFreeDrawElement } from "./types";
export const DRAWING_CONFIGS = {
default: {
streamline: 0.25,
simplify: 0.1,
},
} as const;
/**
* Calculates simulated pressure based on velocity between consecutive points.
* Fast movement (large distances) -> lower pressure
@ -16,7 +23,7 @@ import type { ExcalidrawFreeDrawElement } from "./types";
const calculateVelocityBasedPressure = (
points: readonly LocalPoint[],
index: number,
pressureSensitivity: number | null,
pressureSensitivity: number | undefined,
maxDistance = 8, // Maximum expected distance for normalization
): number => {
// Handle pressure sensitivity
@ -64,11 +71,11 @@ export const getFreedrawStroke = (
calculateVelocityBasedPressure(
element.points,
i,
element.pressureSensitivity,
element.drawingConfigs?.pressureSensitivity,
),
]);
} else {
const sensitivity = element.pressureSensitivity ?? 1;
const sensitivity = element.drawingConfigs?.pressureSensitivity ?? 1;
points = element.points.map(([x, y]: LocalPoint, i) => {
if (sensitivity === 0) {
return [x, y, 0.5];
@ -84,28 +91,20 @@ export const getFreedrawStroke = (
}
const streamline =
(typeof window !== "undefined" &&
window.h &&
window.h.debugFreedraw?.streamline) ??
debugParams?.streamline ??
0.62;
element.drawingConfigs?.streamline ?? DRAWING_CONFIGS.default.streamline;
const simplify =
(typeof window !== "undefined" &&
window.h &&
window.h.debugFreedraw?.simplify) ??
debugParams?.simplify ??
0.3;
element.drawingConfigs?.simplify ?? DRAWING_CONFIGS.default.simplify;
const laser = new LaserPointer({
size: element.strokeWidth,
streamline: streamline === false ? 0.62 : streamline,
simplify: simplify === false ? 0.3 : simplify,
streamline,
simplify,
sizeMapping: ({ pressure: t }) => {
if (element.simulatePressure) {
return t + 0.2;
}
if (element.pressureSensitivity === 0) {
if (element.drawingConfigs?.pressureSensitivity === 0) {
return 1;
}
@ -133,7 +132,7 @@ export const getFreeDrawSvgPath = (
debugParams?: { streamline?: number; simplify?: number },
): string => {
// legacy, for backwards compatibility
if (element.pressureSensitivity === null) {
if (element.drawingConfigs === null) {
return _legacy_getFreeDrawSvgPath(element);
}
@ -188,7 +187,7 @@ function _legacy_getFreeDrawSvgPath(element: ExcalidrawFreeDrawElement) {
? element.points.map(([x, y], i) => [x, y, element.pressures[i]])
: [[0, 0, 0.5]];
const sensitivity = element.pressureSensitivity;
const sensitivity = element.drawingConfigs?.pressureSensitivity;
// Consider changing the options for simulated pressure vs real pressure
const options: StrokeOptions = {
@ -196,7 +195,7 @@ function _legacy_getFreeDrawSvgPath(element: ExcalidrawFreeDrawElement) {
// if sensitivity is not set, times 4.25 for backwards compatibility
size: element.strokeWidth * (sensitivity !== null ? 1 : 4.25),
// if sensitivity is not set, set thinning to 0.6 for backwards compatibility
thinning: sensitivity !== null ? 0.5 * sensitivity : 0.6,
thinning: sensitivity !== undefined ? 0.5 * sensitivity : 0.6,
smoothing: 0.5,
streamline: 0.5,
easing: (t) => Math.sin((t * Math.PI) / 2), // https://easings.net/#easeOutSine

View File

@ -445,7 +445,7 @@ export const newFreeDrawElement = (
points?: ExcalidrawFreeDrawElement["points"];
simulatePressure: boolean;
pressures?: ExcalidrawFreeDrawElement["pressures"];
pressureSensitivity?: ExcalidrawFreeDrawElement["pressureSensitivity"];
drawingConfigs?: ExcalidrawFreeDrawElement["drawingConfigs"];
} & ElementConstructorOpts,
): NonDeleted<ExcalidrawFreeDrawElement> => {
return {
@ -454,7 +454,11 @@ export const newFreeDrawElement = (
pressures: opts.pressures || [],
simulatePressure: opts.simulatePressure,
lastCommittedPoint: null,
pressureSensitivity: opts.pressureSensitivity ?? 1,
drawingConfigs: opts.drawingConfigs || {
pressureSensitivity: 1,
streamline: 0.25,
simplify: 0.1,
},
};
};

View File

@ -377,9 +377,13 @@ export type ExcalidrawFreeDrawElement = _ExcalidrawElementBase &
type: "freedraw";
points: readonly LocalPoint[];
pressures: readonly number[];
pressureSensitivity: number | null;
simulatePressure: boolean;
lastCommittedPoint: LocalPoint | null;
drawingConfigs: {
streamline?: number;
simplify?: number;
pressureSensitivity?: number;
} | null;
}>;
export type FileId = string & { _brand: "FileId" };

View File

@ -693,12 +693,12 @@ export const actionChangePressureSensitivity = register({
const commonPressureSensitivity = selectedElements
.filter(isFreeDrawElement)
.reduce((acc, element) => {
const sensitivity = element.pressureSensitivity ?? 1;
const sensitivity = element.drawingConfigs?.pressureSensitivity ?? 1;
if (acc !== null && acc !== sensitivity) {
return null; // No common value
}
return sensitivity;
}, firstElement?.pressureSensitivity ?? null);
}, firstElement?.drawingConfigs?.pressureSensitivity ?? null);
const currentValue =
commonPressureSensitivity ?? appState.currentItemPressureSensitivity;

View File

@ -104,7 +104,11 @@ import {
Emitter,
} from "@excalidraw/common";
import { getCommonBounds, getElementAbsoluteCoords } from "@excalidraw/element";
import {
DRAWING_CONFIGS,
getCommonBounds,
getElementAbsoluteCoords,
} from "@excalidraw/element";
import {
bindOrUnbindLinearElements,
@ -7588,7 +7592,14 @@ class App extends React.Component<AppProps, AppState> {
opacity: this.state.currentItemOpacity,
roundness: null,
simulatePressure,
pressureSensitivity: this.state.currentItemPressureSensitivity,
drawingConfigs: {
pressureSensitivity: this.state.currentItemPressureSensitivity,
streamline:
window.h?.debugFreedraw?.streamline ??
DRAWING_CONFIGS.default.streamline,
simplify:
window.h?.debugFreedraw?.simplify ?? DRAWING_CONFIGS.default.simplify,
},
locked: false,
frameId: topLayerFrame ? topLayerFrame.id : null,
points: [pointFrom<LocalPoint>(0, 0)],
@ -11384,10 +11395,7 @@ export const createTestHook = () => {
window.h = window.h || ({} as Window["h"]);
// Initialize debug freedraw parameters
window.h.debugFreedraw = window.h.debugFreedraw || {
streamline: 0.62,
simplify: 0.3,
};
window.h.debugFreedraw = window.h.debugFreedraw || DRAWING_CONFIGS.default;
Object.defineProperties(window.h, {
elements: {

View File

@ -302,7 +302,8 @@ const restoreElement = (
lastCommittedPoint: null,
simulatePressure: element.simulatePressure,
pressures: element.pressures,
pressureSensitivity: element.pressureSensitivity ?? null,
// legacy, for backwards compatibility
drawingConfigs: element.drawingConfigs ?? null,
});
}
case "image":