mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
feat: support background fill for freedraw shapes (#4610)
* feat: support background fill for freedraw shapes * refactor & support fill style * make filled freedraw shapes selectable from inside * get hit test on solid freedraw shapes to somewhat work * fix SVG export of unclosed freedraw shapes & improve types * fix lint * type tweaks * reuse `hitTestCurveInside` for collision tests Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
parent
ae8b1d8bf7
commit
0cdd0eebf1
4 changed files with 89 additions and 27 deletions
|
@ -185,7 +185,7 @@ const getLinearElementAbsoluteCoords = (
|
|||
maxY + element.y,
|
||||
];
|
||||
} else {
|
||||
const shape = getShapeForElement(element) as Drawable[];
|
||||
const shape = getShapeForElement(element)!;
|
||||
|
||||
// first element is always the curve
|
||||
const ops = getCurvePathOps(shape[0]);
|
||||
|
@ -326,7 +326,7 @@ const getLinearElementRotatedBounds = (
|
|||
return [minX, minY, maxX, maxY];
|
||||
}
|
||||
|
||||
const shape = getShapeForElement(element) as Drawable[];
|
||||
const shape = getShapeForElement(element)!;
|
||||
|
||||
// first element is always the curve
|
||||
const ops = getCurvePathOps(shape[0]);
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
NonDeleted,
|
||||
ExcalidrawFreeDrawElement,
|
||||
ExcalidrawImageElement,
|
||||
ExcalidrawLinearElement,
|
||||
} from "./types";
|
||||
|
||||
import { getElementAbsoluteCoords, getCurvePathOps, Bounds } from "./bounds";
|
||||
|
@ -361,6 +362,14 @@ const hitTestFreeDrawElement = (
|
|||
B = element.points[i + 1];
|
||||
}
|
||||
|
||||
const shape = getShapeForElement(element);
|
||||
|
||||
// for filled freedraw shapes, support
|
||||
// selecting from inside
|
||||
if (shape && shape.sets.length) {
|
||||
return hitTestRoughShape(shape, x, y, threshold);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
@ -383,7 +392,11 @@ const hitTestLinear = (args: HitTestArgs): boolean => {
|
|||
}
|
||||
const [relX, relY] = GAPoint.toTuple(point);
|
||||
|
||||
const shape = getShapeForElement(element) as Drawable[];
|
||||
const shape = getShapeForElement(element as ExcalidrawLinearElement);
|
||||
|
||||
if (!shape) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.check === isInsideCheck) {
|
||||
const hit = shape.some((subshape) =>
|
||||
|
@ -821,7 +834,7 @@ const hitTestCurveInside = (
|
|||
sharpness: ExcalidrawElement["strokeSharpness"],
|
||||
) => {
|
||||
const ops = getCurvePathOps(drawable);
|
||||
const points: Point[] = [];
|
||||
const points: Mutable<Point>[] = [];
|
||||
let odd = false; // select one line out of double lines
|
||||
for (const operation of ops) {
|
||||
if (operation.op === "move") {
|
||||
|
@ -835,13 +848,17 @@ const hitTestCurveInside = (
|
|||
points.push([operation.data[2], operation.data[3]]);
|
||||
points.push([operation.data[4], operation.data[5]]);
|
||||
}
|
||||
} else if (operation.op === "lineTo") {
|
||||
if (odd) {
|
||||
points.push([operation.data[0], operation.data[1]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (points.length >= 4) {
|
||||
if (sharpness === "sharp") {
|
||||
return isPointInPolygon(points, x, y);
|
||||
}
|
||||
const polygonPoints = pointsOnBezierCurves(points as any, 10, 5);
|
||||
const polygonPoints = pointsOnBezierCurves(points, 10, 5);
|
||||
return isPointInPolygon(polygonPoints, x, y);
|
||||
}
|
||||
return false;
|
||||
|
@ -896,9 +913,10 @@ const hitTestRoughShape = (
|
|||
// position of the previous operation
|
||||
return retVal;
|
||||
} else if (op === "lineTo") {
|
||||
// TODO: Implement this
|
||||
return hitTestCurveInside(drawable, x, y, "sharp");
|
||||
} else if (op === "qcurveTo") {
|
||||
// TODO: Implement this
|
||||
console.warn("qcurveTo is not implemented yet");
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue