diff --git a/excalidraw-app/components/FreedrawDebugSliders.tsx b/excalidraw-app/components/FreedrawDebugSliders.tsx index 607e066c3..85d843e9a 100644 --- a/excalidraw-app/components/FreedrawDebugSliders.tsx +++ b/excalidraw-app/components/FreedrawDebugSliders.tsx @@ -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( + DRAWING_CONFIGS.default.streamline, + ); + const [simplify, setSimplify] = useState( + 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); diff --git a/packages/element/src/Shape.ts b/packages/element/src/Shape.ts index f634fa5c3..3c7b04f41 100644 --- a/packages/element/src/Shape.ts +++ b/packages/element/src/Shape.ts @@ -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", diff --git a/packages/element/src/freedraw.ts b/packages/element/src/freedraw.ts index 9fa9d9d2e..5d4696049 100644 --- a/packages/element/src/freedraw.ts +++ b/packages/element/src/freedraw.ts @@ -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 diff --git a/packages/element/src/newElement.ts b/packages/element/src/newElement.ts index 5d6f6835f..c1ad2787e 100644 --- a/packages/element/src/newElement.ts +++ b/packages/element/src/newElement.ts @@ -445,7 +445,7 @@ export const newFreeDrawElement = ( points?: ExcalidrawFreeDrawElement["points"]; simulatePressure: boolean; pressures?: ExcalidrawFreeDrawElement["pressures"]; - pressureSensitivity?: ExcalidrawFreeDrawElement["pressureSensitivity"]; + drawingConfigs?: ExcalidrawFreeDrawElement["drawingConfigs"]; } & ElementConstructorOpts, ): NonDeleted => { 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, + }, }; }; diff --git a/packages/element/src/types.ts b/packages/element/src/types.ts index c11fecee3..fd03e91b9 100644 --- a/packages/element/src/types.ts +++ b/packages/element/src/types.ts @@ -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" }; diff --git a/packages/excalidraw/actions/actionProperties.tsx b/packages/excalidraw/actions/actionProperties.tsx index d9a39330f..9cf67dce1 100644 --- a/packages/excalidraw/actions/actionProperties.tsx +++ b/packages/excalidraw/actions/actionProperties.tsx @@ -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; diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index 04f843770..6edfb88d2 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -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 { 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(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: { diff --git a/packages/excalidraw/data/restore.ts b/packages/excalidraw/data/restore.ts index fd0f43b61..9433745b9 100644 --- a/packages/excalidraw/data/restore.ts +++ b/packages/excalidraw/data/restore.ts @@ -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":