Fill a looped curve with the selected background color (#1315)

This commit is contained in:
Preet 2020-04-09 01:46:47 -07:00 committed by GitHub
parent fe6f482e96
commit 57bbc9fe55
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 240 additions and 32 deletions

View file

@ -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;

View file

@ -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)