mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
feat: sharpness (#1931)
* feat: sharpness * feat: fill sharp lines, et al. * fix: rotated positioning * chore: simplify path with Q * fix: hit test inside sharp elements * make sharp / round buttons work properly * fix tsc tests * update snapshots * update snapshots * fix: sharp arrow creation error * fix merge and test * avoid type assertion * remove duplicate helper Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
parent
930813387b
commit
41cb1fbeba
26 changed files with 841 additions and 42 deletions
|
@ -165,6 +165,9 @@ export const getArrowPoints = (
|
|||
shape: Drawable[],
|
||||
) => {
|
||||
const ops = getCurvePathOps(shape[0]);
|
||||
if (ops.length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = ops[ops.length - 1].data;
|
||||
const p3 = [data[4], data[5]] as Point;
|
||||
|
@ -339,10 +342,13 @@ export const getResizedElementAbsoluteCoords = (
|
|||
);
|
||||
|
||||
const gen = rough.generator();
|
||||
const curve = gen.curve(
|
||||
points as [number, number][],
|
||||
generateRoughOptions(element),
|
||||
);
|
||||
const curve =
|
||||
element.strokeSharpness === "sharp"
|
||||
? gen.linearPath(
|
||||
points as [number, number][],
|
||||
generateRoughOptions(element),
|
||||
)
|
||||
: gen.curve(points as [number, number][], generateRoughOptions(element));
|
||||
const ops = getCurvePathOps(curve);
|
||||
const [minX, minY, maxX, maxY] = getMinMaxXYFromCurvePathOps(ops);
|
||||
return [
|
||||
|
@ -356,13 +362,17 @@ export const getResizedElementAbsoluteCoords = (
|
|||
export const getElementPointsCoords = (
|
||||
element: ExcalidrawLinearElement,
|
||||
points: readonly (readonly [number, number])[],
|
||||
sharpness: ExcalidrawElement["strokeSharpness"],
|
||||
): [number, number, number, number] => {
|
||||
// This might be computationally heavey
|
||||
const gen = rough.generator();
|
||||
const curve = gen.curve(
|
||||
points as [number, number][],
|
||||
generateRoughOptions(element),
|
||||
);
|
||||
const curve =
|
||||
sharpness === "sharp"
|
||||
? gen.linearPath(
|
||||
points as [number, number][],
|
||||
generateRoughOptions(element),
|
||||
)
|
||||
: gen.curve(points as [number, number][], generateRoughOptions(element));
|
||||
const ops = getCurvePathOps(curve);
|
||||
const [minX, minY, maxX, maxY] = getMinMaxXYFromCurvePathOps(ops);
|
||||
return [
|
||||
|
|
|
@ -267,7 +267,7 @@ const hitTestLinear = (args: HitTestArgs): boolean => {
|
|||
|
||||
if (args.check === isInsideCheck) {
|
||||
const hit = shape.some((subshape) =>
|
||||
hitTestCurveInside(subshape, relX, relY),
|
||||
hitTestCurveInside(subshape, relX, relY, element.strokeSharpness),
|
||||
);
|
||||
if (hit) {
|
||||
return true;
|
||||
|
@ -688,22 +688,33 @@ const pointInBezierEquation = (
|
|||
return false;
|
||||
};
|
||||
|
||||
const hitTestCurveInside = (drawable: Drawable, x: number, y: number) => {
|
||||
const hitTestCurveInside = (
|
||||
drawable: Drawable,
|
||||
x: number,
|
||||
y: number,
|
||||
sharpness: ExcalidrawElement["strokeSharpness"],
|
||||
) => {
|
||||
const ops = getCurvePathOps(drawable);
|
||||
const points: Point[] = [];
|
||||
let odd = false; // select one line out of double lines
|
||||
for (const operation of ops) {
|
||||
if (operation.op === "move") {
|
||||
if (points.length) {
|
||||
break;
|
||||
odd = !odd;
|
||||
if (odd) {
|
||||
points.push([operation.data[0], operation.data[1]]);
|
||||
}
|
||||
points.push([operation.data[0], operation.data[1]]);
|
||||
} else if (operation.op === "bcurveTo") {
|
||||
points.push([operation.data[0], operation.data[1]]);
|
||||
points.push([operation.data[2], operation.data[3]]);
|
||||
points.push([operation.data[4], operation.data[5]]);
|
||||
if (odd) {
|
||||
points.push([operation.data[0], operation.data[1]]);
|
||||
points.push([operation.data[2], operation.data[3]]);
|
||||
points.push([operation.data[4], operation.data[5]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (points.length >= 4) {
|
||||
if (sharpness === "sharp") {
|
||||
return isPointInPolygon(points, x, y);
|
||||
}
|
||||
const polygonPoints = pointsOnBezierCurves(points as any, 10, 5);
|
||||
return isPointInPolygon(polygonPoints, x, y);
|
||||
}
|
||||
|
|
|
@ -508,8 +508,16 @@ export class LinearElementEditor {
|
|||
});
|
||||
}
|
||||
|
||||
const nextCoords = getElementPointsCoords(element, nextPoints);
|
||||
const prevCoords = getElementPointsCoords(element, points);
|
||||
const nextCoords = getElementPointsCoords(
|
||||
element,
|
||||
nextPoints,
|
||||
element.strokeSharpness || "round",
|
||||
);
|
||||
const prevCoords = getElementPointsCoords(
|
||||
element,
|
||||
points,
|
||||
element.strokeSharpness || "round",
|
||||
);
|
||||
const nextCenterX = (nextCoords[0] + nextCoords[2]) / 2;
|
||||
const nextCenterY = (nextCoords[1] + nextCoords[3]) / 2;
|
||||
const prevCenterX = (prevCoords[0] + prevCoords[2]) / 2;
|
||||
|
|
|
@ -31,6 +31,7 @@ it("clones arrow element", () => {
|
|||
fillStyle: "hachure",
|
||||
strokeWidth: 1,
|
||||
strokeStyle: "solid",
|
||||
strokeSharpness: "round",
|
||||
roughness: 1,
|
||||
opacity: 100,
|
||||
});
|
||||
|
@ -75,6 +76,7 @@ it("clones text element", () => {
|
|||
fillStyle: "hachure",
|
||||
strokeWidth: 1,
|
||||
strokeStyle: "solid",
|
||||
strokeSharpness: "round",
|
||||
roughness: 1,
|
||||
opacity: 100,
|
||||
text: "hello",
|
||||
|
|
|
@ -46,6 +46,7 @@ const _newElementBase = <T extends ExcalidrawElement>(
|
|||
height = 0,
|
||||
angle = 0,
|
||||
groupIds = [],
|
||||
strokeSharpness,
|
||||
boundElementIds = null,
|
||||
...rest
|
||||
}: ElementConstructorOpts & Omit<Partial<ExcalidrawGenericElement>, "type">,
|
||||
|
@ -65,6 +66,7 @@ const _newElementBase = <T extends ExcalidrawElement>(
|
|||
roughness,
|
||||
opacity,
|
||||
groupIds,
|
||||
strokeSharpness,
|
||||
seed: rest.seed ?? randomInteger(),
|
||||
version: rest.version || 1,
|
||||
versionNonce: rest.versionNonce ?? 0,
|
||||
|
|
|
@ -12,6 +12,7 @@ type _ExcalidrawElementBase = Readonly<{
|
|||
fillStyle: string;
|
||||
strokeWidth: number;
|
||||
strokeStyle: "solid" | "dashed" | "dotted";
|
||||
strokeSharpness: "round" | "sharp";
|
||||
roughness: number;
|
||||
opacity: number;
|
||||
width: number;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue