feat: improve freedraw shape (#3984)

This commit is contained in:
Steve Ruiz 2021-09-18 15:56:55 +01:00 committed by GitHub
parent 7d1fddc144
commit 7199d13f48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 40 additions and 25 deletions

View File

@ -35,7 +35,7 @@
"nanoid": "3.1.22", "nanoid": "3.1.22",
"open-color": "1.8.0", "open-color": "1.8.0",
"pako": "1.0.11", "pako": "1.0.11",
"perfect-freehand": "0.4.7", "perfect-freehand": "1.0.6",
"png-chunk-text": "1.0.0", "png-chunk-text": "1.0.0",
"png-chunks-encode": "1.0.0", "png-chunks-encode": "1.0.0",
"png-chunks-extract": "1.0.0", "png-chunks-extract": "1.0.0",

View File

@ -32,7 +32,7 @@ import { isPathALoop } from "../math";
import rough from "roughjs/bin/rough"; import rough from "roughjs/bin/rough";
import { Zoom } from "../types"; import { Zoom } from "../types";
import { getDefaultAppState } from "../appState"; import { getDefaultAppState } from "../appState";
import getFreeDrawShape from "perfect-freehand"; import { getStroke, StrokeOptions } from "perfect-freehand";
import { MAX_DECIMALS_FOR_SVG_EXPORT } from "../constants"; import { MAX_DECIMALS_FOR_SVG_EXPORT } from "../constants";
const defaultAppState = getDefaultAppState(); const defaultAppState = getDefaultAppState();
@ -789,40 +789,55 @@ export function getFreeDrawPath2D(element: ExcalidrawFreeDrawElement) {
} }
export function getFreeDrawSvgPath(element: ExcalidrawFreeDrawElement) { export function getFreeDrawSvgPath(element: ExcalidrawFreeDrawElement) {
// If input points are empty (should they ever be?) return a dot
const inputPoints = element.simulatePressure const inputPoints = element.simulatePressure
? element.points ? element.points
: element.points.length : element.points.length
? element.points.map(([x, y], i) => [x, y, element.pressures[i]]) ? element.points.map(([x, y], i) => [x, y, element.pressures[i]])
: [[0, 0, 0]]; : [[0, 0, 0.5]];
// Consider changing the options for simulated pressure vs real pressure // Consider changing the options for simulated pressure vs real pressure
const options = { const options: StrokeOptions = {
simulatePressure: element.simulatePressure, simulatePressure: element.simulatePressure,
size: element.strokeWidth * 6, size: element.strokeWidth * 4.25,
thinning: 0.5, thinning: 0.6,
smoothing: 0.5, smoothing: 0.5,
streamline: 0.5, streamline: 0.5,
easing: (t: number) => t * (2 - t), easing: (t) => Math.sin((t * Math.PI) / 2), // https://easings.net/#easeOutSine
last: true, last: false,
}; };
const points = getFreeDrawShape(inputPoints as number[][], options); return getSvgPathFromStroke(getStroke(inputPoints as number[][], options));
const d: (string | number)[] = [];
let [p0, p1] = points;
d.push("M", p0[0], p0[1], "Q");
for (let i = 0; i < points.length; i++) {
d.push(p0[0], p0[1], (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2);
p0 = p1;
p1 = points[i];
} }
p1 = points[0]; function med(A: number[], B: number[]) {
d.push(p0[0], p0[1], (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2); return [(A[0] + B[0]) / 2, (A[1] + B[1]) / 2];
}
d.push("Z");
// Trim SVG path data so number are each two decimal points. This
return d.join(" "); // improves SVG exports, and prevents rendering errors on points
// with long decimals.
const TO_FIXED_PRECISION = /(\s?[A-Z]?,?-?[0-9]*\.[0-9]{0,2})(([0-9]|e|-)*)/g;
function getSvgPathFromStroke(points: number[][]): string {
if (!points.length) {
return "";
}
const max = points.length - 1;
return points
.reduce(
(acc, point, i, arr) => {
if (i === max) {
acc.push(point, med(point, arr[0]), "L", arr[0], "Z");
} else {
acc.push(point, med(point, arr[i + 1]));
}
return acc;
},
["M", points[0], "Q"],
)
.join(" ")
.replaceAll(TO_FIXED_PRECISION, "$1");
} }

View File

@ -9260,10 +9260,10 @@ pepjs@0.5.3:
version "0.5.3" version "0.5.3"
resolved "https://registry.npmjs.org/pepjs/-/pepjs-0.5.3.tgz" resolved "https://registry.npmjs.org/pepjs/-/pepjs-0.5.3.tgz"
perfect-freehand@0.4.7: perfect-freehand@1.0.6:
version "0.4.7" version "1.0.6"
resolved "https://registry.yarnpkg.com/perfect-freehand/-/perfect-freehand-0.4.7.tgz#4d85fd64881ba81b2a4eaa6ac4e8983ccb21dd43" resolved "https://registry.yarnpkg.com/perfect-freehand/-/perfect-freehand-1.0.6.tgz#feeb25450241f036ec13b43fa84bbb16f8e78e0f"
integrity sha512-SSSFL8VzXiOHQdUTyNyOb0JC+btVZRy9bi6jos7Nb7PBTI0PHX5jM6RgCTSrubQ8Ul9qOYWmWgJBrwVGHwyJZQ== integrity sha512-wWkFwpgUirsfBDTb9nG6+VnFR0ge119QKU2Nu96vR4MHZMPGfOsQRD7cUk+9CK5P+TUmnrtX8yOEzUrQ6KHJoA==
performance-now@^2.1.0: performance-now@^2.1.0:
version "2.1.0" version "2.1.0"