mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-12-05 20:14:36 +01:00
feat: Basic outlining polygon
This commit is contained in:
91
packages/element/src/freedraw.ts
Normal file
91
packages/element/src/freedraw.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import {
|
||||||
|
type LineSegment,
|
||||||
|
lineSegment,
|
||||||
|
type LocalPoint,
|
||||||
|
pointFrom,
|
||||||
|
pointFromVector,
|
||||||
|
vectorAntiNormal,
|
||||||
|
vectorFromPoint,
|
||||||
|
vectorNormal,
|
||||||
|
vectorNormalize,
|
||||||
|
vectorScale,
|
||||||
|
} from "@excalidraw/math";
|
||||||
|
|
||||||
|
import { type ExcalidrawFreeDrawElement } from "./types";
|
||||||
|
|
||||||
|
function generateSegments(
|
||||||
|
input:
|
||||||
|
| readonly [x: number, y: number, pressure: number][]
|
||||||
|
| readonly [x: number, y: number][],
|
||||||
|
): LineSegment<LocalPoint>[] {
|
||||||
|
if (input.length < 2) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const offset = (
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
pressure: number,
|
||||||
|
direction: "left" | "right",
|
||||||
|
) => {
|
||||||
|
const p = pointFrom<LocalPoint>(x, y);
|
||||||
|
const v = vectorNormalize(vectorFromPoint(p));
|
||||||
|
const normal = direction === "left" ? vectorAntiNormal(v) : vectorNormal(v);
|
||||||
|
const scaled = vectorScale(normal, pressure / 2);
|
||||||
|
|
||||||
|
return pointFromVector(scaled, p);
|
||||||
|
};
|
||||||
|
|
||||||
|
let idx = 0;
|
||||||
|
|
||||||
|
const segments = Array(input.length * 2 - 1);
|
||||||
|
segments[idx++] = lineSegment(
|
||||||
|
offset(input[0][0], input[0][1], input[0][2] ?? 5, "left"),
|
||||||
|
offset(input[1][0], input[1][1], input[0][2] ?? 5, "left"),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 2; i < input.length; i++) {
|
||||||
|
const point = input[i];
|
||||||
|
const prev = segments[idx - 1][1];
|
||||||
|
|
||||||
|
segments[idx++] = lineSegment(
|
||||||
|
prev,
|
||||||
|
offset(point[0], point[1], point[2] ?? 5, "left"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const prev = segments[idx - 1][1];
|
||||||
|
segments[idx++] = lineSegment(
|
||||||
|
prev,
|
||||||
|
offset(
|
||||||
|
input[input.length - 1][0],
|
||||||
|
input[input.length - 1][1],
|
||||||
|
input[input.length - 1][2] ?? 5,
|
||||||
|
"right",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = input.length - 2; i >= 0; i--) {
|
||||||
|
const point = input[i];
|
||||||
|
const prev = segments[idx - 1][1];
|
||||||
|
|
||||||
|
segments[idx++] = lineSegment(
|
||||||
|
prev,
|
||||||
|
offset(point[0], point[1], point[2] ?? 5, "right"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStroke(
|
||||||
|
input:
|
||||||
|
| readonly [x: number, y: number, pressure: number][]
|
||||||
|
| readonly [x: number, y: number][],
|
||||||
|
options: any,
|
||||||
|
element: ExcalidrawFreeDrawElement,
|
||||||
|
): LocalPoint[] {
|
||||||
|
const segments = generateSegments(input);
|
||||||
|
|
||||||
|
return [segments[0][0], ...segments.map((s) => s[1])];
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import rough from "roughjs/bin/rough";
|
import rough from "roughjs/bin/rough";
|
||||||
import { getStroke } from "perfect-freehand";
|
//import { getStroke } from "perfect-freehand";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type GlobalPoint,
|
type GlobalPoint,
|
||||||
@@ -80,6 +80,7 @@ import type {
|
|||||||
|
|
||||||
import type { StrokeOptions } from "perfect-freehand";
|
import type { StrokeOptions } from "perfect-freehand";
|
||||||
import type { RoughCanvas } from "roughjs/bin/canvas";
|
import type { RoughCanvas } from "roughjs/bin/canvas";
|
||||||
|
import { getStroke } from "./freedraw";
|
||||||
|
|
||||||
// using a stronger invert (100% vs our regular 93%) and saturate
|
// using a stronger invert (100% vs our regular 93%) and saturate
|
||||||
// as a temp hack to make images in dark theme look closer to original
|
// as a temp hack to make images in dark theme look closer to original
|
||||||
@@ -1102,10 +1103,10 @@ export function getFreedrawOutlineAsSegments(
|
|||||||
export function getFreedrawOutlinePoints(element: ExcalidrawFreeDrawElement) {
|
export function getFreedrawOutlinePoints(element: ExcalidrawFreeDrawElement) {
|
||||||
// If input points are empty (should they ever be?) return a dot
|
// If input points are empty (should they ever be?) return a dot
|
||||||
const inputPoints = element.simulatePressure
|
const inputPoints = element.simulatePressure
|
||||||
? element.points
|
? (element.points as readonly [number, number][])
|
||||||
: 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.5]];
|
: [[0, 0, 0.5]]) as [number, number, number][]);
|
||||||
|
|
||||||
// Consider changing the options for simulated pressure vs real pressure
|
// Consider changing the options for simulated pressure vs real pressure
|
||||||
const options: StrokeOptions = {
|
const options: StrokeOptions = {
|
||||||
@@ -1118,7 +1119,7 @@ export function getFreedrawOutlinePoints(element: ExcalidrawFreeDrawElement) {
|
|||||||
last: true,
|
last: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
return getStroke(inputPoints as number[][], options) as [number, number][];
|
return getStroke(inputPoints, options, element) as [number, number][];
|
||||||
}
|
}
|
||||||
|
|
||||||
function med(A: number[], B: number[]) {
|
function med(A: number[], B: number[]) {
|
||||||
|
|||||||
@@ -158,3 +158,8 @@ export const vectorNormalize = (v: Vector): Vector => {
|
|||||||
* Calculate the right-hand normal of the vector.
|
* Calculate the right-hand normal of the vector.
|
||||||
*/
|
*/
|
||||||
export const vectorNormal = (v: Vector): Vector => vector(v[1], -v[0]);
|
export const vectorNormal = (v: Vector): Vector => vector(v[1], -v[0]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the left-hand normal of the vector.
|
||||||
|
*/
|
||||||
|
export const vectorAntiNormal = (v: Vector): Vector => vector(-v[1], v[0]);
|
||||||
|
|||||||
Reference in New Issue
Block a user