mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Fill a looped curve with the selected background color (#1315)
This commit is contained in:
parent
fe6f482e96
commit
57bbc9fe55
11 changed files with 240 additions and 32 deletions
|
@ -1,6 +1,6 @@
|
|||
import { ExcalidrawElement, ExcalidrawLinearElement } from "./types";
|
||||
import { rotate } from "../math";
|
||||
import { Drawable } from "roughjs/bin/core";
|
||||
import { Drawable, Op } from "roughjs/bin/core";
|
||||
import { Point } from "../types";
|
||||
import { getShapeForElement } from "../renderer/renderElement";
|
||||
import { isLinearElement } from "./typeChecks";
|
||||
|
@ -36,6 +36,15 @@ export function getDiamondPoints(element: ExcalidrawElement) {
|
|||
return [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY];
|
||||
}
|
||||
|
||||
export function getCurvePathOps(shape: Drawable): Op[] {
|
||||
for (const set of shape.sets) {
|
||||
if (set.type === "path") {
|
||||
return set.ops;
|
||||
}
|
||||
}
|
||||
return shape.sets[0].ops;
|
||||
}
|
||||
|
||||
export function getLinearElementAbsoluteBounds(
|
||||
element: ExcalidrawLinearElement,
|
||||
): [number, number, number, number] {
|
||||
|
@ -63,7 +72,7 @@ export function getLinearElementAbsoluteBounds(
|
|||
const shape = getShapeForElement(element) as Drawable[];
|
||||
|
||||
// first element is always the curve
|
||||
const ops = shape[0].sets[0].ops;
|
||||
const ops = getCurvePathOps(shape[0]);
|
||||
|
||||
let currentP: Point = [0, 0];
|
||||
|
||||
|
@ -128,7 +137,7 @@ export function getArrowPoints(
|
|||
element: ExcalidrawLinearElement,
|
||||
shape: Drawable[],
|
||||
) {
|
||||
const ops = shape[0].sets[0].ops;
|
||||
const ops = getCurvePathOps(shape[0]);
|
||||
|
||||
const data = ops[ops.length - 1].data;
|
||||
const p3 = [data[4], data[5]] as Point;
|
||||
|
|
|
@ -1,23 +1,35 @@
|
|||
import { distanceBetweenPointAndSegment } from "../math";
|
||||
import {
|
||||
distanceBetweenPointAndSegment,
|
||||
isPathALoop,
|
||||
rotate,
|
||||
isPointInPolygon,
|
||||
} from "../math";
|
||||
import { getPointsOnBezierCurves } from "roughjs/bin/geometry";
|
||||
|
||||
import { NonDeletedExcalidrawElement } from "./types";
|
||||
|
||||
import { getDiamondPoints, getElementAbsoluteCoords } from "./bounds";
|
||||
import {
|
||||
getDiamondPoints,
|
||||
getElementAbsoluteCoords,
|
||||
getCurvePathOps,
|
||||
} from "./bounds";
|
||||
import { Point } from "../types";
|
||||
import { Drawable, OpSet } from "roughjs/bin/core";
|
||||
import { Drawable } from "roughjs/bin/core";
|
||||
import { AppState } from "../types";
|
||||
import { getShapeForElement } from "../renderer/renderElement";
|
||||
import { isLinearElement } from "./typeChecks";
|
||||
import { rotate } from "../math";
|
||||
|
||||
function isElementDraggableFromInside(
|
||||
element: NonDeletedExcalidrawElement,
|
||||
appState: AppState,
|
||||
): boolean {
|
||||
return (
|
||||
const dragFromInside =
|
||||
element.backgroundColor !== "transparent" ||
|
||||
appState.selectedElementIds[element.id]
|
||||
);
|
||||
appState.selectedElementIds[element.id];
|
||||
if (element.type === "line") {
|
||||
return dragFromInside && isPathALoop(element.points);
|
||||
}
|
||||
return dragFromInside;
|
||||
}
|
||||
|
||||
export function hitTest(
|
||||
|
@ -178,9 +190,18 @@ export function hitTest(
|
|||
const relX = x - element.x;
|
||||
const relY = y - element.y;
|
||||
|
||||
if (isElementDraggableFromInside(element, appState)) {
|
||||
const hit = shape.some((subshape) =>
|
||||
hitTestCurveInside(subshape, relX, relY, lineThreshold),
|
||||
);
|
||||
if (hit) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// hit thest all "subshapes" of the linear element
|
||||
return shape.some((subshape) =>
|
||||
hitTestRoughShape(subshape.sets, relX, relY, lineThreshold),
|
||||
hitTestRoughShape(subshape, relX, relY, lineThreshold),
|
||||
);
|
||||
} else if (element.type === "text") {
|
||||
return x >= x1 && x <= x2 && y >= y1 && y <= y2;
|
||||
|
@ -224,14 +245,41 @@ const pointInBezierEquation = (
|
|||
return false;
|
||||
};
|
||||
|
||||
const hitTestCurveInside = (
|
||||
drawable: Drawable,
|
||||
x: number,
|
||||
y: number,
|
||||
lineThreshold: number,
|
||||
) => {
|
||||
const ops = getCurvePathOps(drawable);
|
||||
const points: Point[] = [];
|
||||
for (const operation of ops) {
|
||||
if (operation.op === "move") {
|
||||
if (points.length) {
|
||||
break;
|
||||
}
|
||||
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 (points.length >= 4) {
|
||||
const polygonPoints = getPointsOnBezierCurves(points as any, 50);
|
||||
return isPointInPolygon(polygonPoints, x, y);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const hitTestRoughShape = (
|
||||
opSet: OpSet[],
|
||||
drawable: Drawable,
|
||||
x: number,
|
||||
y: number,
|
||||
lineThreshold: number,
|
||||
) => {
|
||||
// read operations from first opSet
|
||||
const ops = opSet[0].ops;
|
||||
const ops = getCurvePathOps(drawable);
|
||||
|
||||
// set start position as (0,0) just in case
|
||||
// move operation does not exist (unlikely but it is worth safekeeping it)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue