Refactor Element Functions (#233)

* Remove `generatedraw` from element object

- Create a function that renders a single element
- Refactor rendering selected elements

* Replace getElementAbsoluteXY with getElementAbsoluteCoords
This commit is contained in:
Gasim Gasimzada 2020-01-07 19:04:52 +04:00 committed by GitHub
parent 85365e5bcb
commit 829a65b8cb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 206 additions and 184 deletions

View file

@ -0,0 +1,67 @@
import { getElementAbsoluteCoords } from "./bounds";
import { ExcalidrawElement } from "./types";
const _ce = ({ x, y, w, h }: { x: number; y: number; w: number; h: number }) =>
({
type: "test",
strokeColor: "#000",
backgroundColor: "#000",
fillStyle: "solid",
strokeWidth: 1,
roughness: 1,
opacity: 1,
x,
y,
width: w,
height: h
} as ExcalidrawElement);
describe("getElementAbsoluteCoords", () => {
it("test x1 coordinate if width is positive or zero", () => {
const [x1] = getElementAbsoluteCoords(_ce({ x: 10, y: 0, w: 10, h: 0 }));
expect(x1).toEqual(10);
});
it("test x1 coordinate if width is negative", () => {
const [x1] = getElementAbsoluteCoords(_ce({ x: 20, y: 0, w: -10, h: 0 }));
expect(x1).toEqual(10);
});
it("test x2 coordinate if width is positive or zero", () => {
const [, , x2] = getElementAbsoluteCoords(
_ce({ x: 10, y: 0, w: 10, h: 0 })
);
expect(x2).toEqual(20);
});
it("test x2 coordinate if width is negative", () => {
const [, , x2] = getElementAbsoluteCoords(
_ce({ x: 10, y: 0, w: -10, h: 0 })
);
expect(x2).toEqual(10);
});
it("test y1 coordinate if height is positive or zero", () => {
const [, y1] = getElementAbsoluteCoords(_ce({ x: 0, y: 10, w: 0, h: 10 }));
expect(y1).toEqual(10);
});
it("test y1 coordinate if height is negative", () => {
const [, y1] = getElementAbsoluteCoords(_ce({ x: 0, y: 20, w: 0, h: -10 }));
expect(y1).toEqual(10);
});
it("test y2 coordinate if height is positive or zero", () => {
const [, , , y2] = getElementAbsoluteCoords(
_ce({ x: 0, y: 10, w: 0, h: 10 })
);
expect(y2).toEqual(20);
});
it("test y2 coordinate if height is negative", () => {
const [, , , y2] = getElementAbsoluteCoords(
_ce({ x: 0, y: 10, w: 0, h: -10 })
);
expect(y2).toEqual(10);
});
});

View file

@ -5,17 +5,13 @@ import { rotate } from "../math";
// This set of functions retrieves the absolute position of the 4 points.
// We can't just always normalize it since we need to remember the fact that an arrow
// is pointing left or right.
export function getElementAbsoluteX1(element: ExcalidrawElement) {
return element.width >= 0 ? element.x : element.x + element.width;
}
export function getElementAbsoluteX2(element: ExcalidrawElement) {
return element.width >= 0 ? element.x + element.width : element.x;
}
export function getElementAbsoluteY1(element: ExcalidrawElement) {
return element.height >= 0 ? element.y : element.y + element.height;
}
export function getElementAbsoluteY2(element: ExcalidrawElement) {
return element.height >= 0 ? element.y + element.height : element.y;
export function getElementAbsoluteCoords(element: ExcalidrawElement) {
return [
element.width >= 0 ? element.x : element.x + element.width, // x1
element.height >= 0 ? element.y : element.y + element.height, // y1
element.width >= 0 ? element.x + element.width : element.x, // x2
element.height >= 0 ? element.y + element.height : element.y // y2
];
}
export function getDiamondPoints(element: ExcalidrawElement) {

View file

@ -2,12 +2,9 @@ import { distanceBetweenPointAndSegment } from "../math";
import { ExcalidrawElement } from "./types";
import {
getElementAbsoluteX1,
getElementAbsoluteX2,
getElementAbsoluteY1,
getElementAbsoluteY2,
getArrowPoints,
getDiamondPoints
getDiamondPoints,
getElementAbsoluteCoords
} from "./bounds";
export function hitTest(
@ -55,10 +52,7 @@ export function hitTest(
return Math.hypot(a * tx - px, b * ty - py) < lineThreshold;
} else if (element.type === "rectangle") {
const x1 = getElementAbsoluteX1(element);
const x2 = getElementAbsoluteX2(element);
const y1 = getElementAbsoluteY1(element);
const y2 = getElementAbsoluteY2(element);
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
// (x1, y1) --A-- (x2, y1)
// |D |B
@ -109,10 +103,7 @@ export function hitTest(
distanceBetweenPointAndSegment(x, y, x4, y4, x2, y2) < lineThreshold
);
} else if (element.type === "text") {
const x1 = getElementAbsoluteX1(element);
const x2 = getElementAbsoluteX2(element);
const y1 = getElementAbsoluteY1(element);
const y2 = getElementAbsoluteY2(element);
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
return x >= x1 && x <= x2 && y >= y1 && y <= y2;
} else if (element.type === "selection") {

View file

@ -1,145 +0,0 @@
import rough from "roughjs/bin/wrappers/rough";
import { withCustomMathRandom } from "../random";
import { ExcalidrawElement } from "./types";
import { isTextElement } from "./typeChecks";
import { getDiamondPoints, getArrowPoints } from "./bounds";
// Casting second argument (DrawingSurface) to any,
// because it is requred by TS definitions and not required at runtime
const generator = rough.generator(null, null as any);
export function generateDraw(element: ExcalidrawElement) {
if (element.type === "selection") {
element.draw = (rc, context, { scrollX, scrollY }) => {
const fillStyle = context.fillStyle;
context.fillStyle = "rgba(0, 0, 255, 0.10)";
context.fillRect(
element.x + scrollX,
element.y + scrollY,
element.width,
element.height
);
context.fillStyle = fillStyle;
};
} else if (element.type === "rectangle") {
const shape = withCustomMathRandom(element.seed, () => {
return generator.rectangle(0, 0, element.width, element.height, {
stroke: element.strokeColor,
fill: element.backgroundColor,
fillStyle: element.fillStyle,
strokeWidth: element.strokeWidth,
roughness: element.roughness
});
});
element.draw = (rc, context, { scrollX, scrollY }) => {
context.globalAlpha = element.opacity / 100;
context.translate(element.x + scrollX, element.y + scrollY);
rc.draw(shape);
context.translate(-element.x - scrollX, -element.y - scrollY);
context.globalAlpha = 1;
};
} else if (element.type === "diamond") {
const shape = withCustomMathRandom(element.seed, () => {
const [
topX,
topY,
rightX,
rightY,
bottomX,
bottomY,
leftX,
leftY
] = getDiamondPoints(element);
return generator.polygon(
[
[topX, topY],
[rightX, rightY],
[bottomX, bottomY],
[leftX, leftY]
],
{
stroke: element.strokeColor,
fill: element.backgroundColor,
fillStyle: element.fillStyle,
strokeWidth: element.strokeWidth,
roughness: element.roughness
}
);
});
element.draw = (rc, context, { scrollX, scrollY }) => {
context.globalAlpha = element.opacity / 100;
context.translate(element.x + scrollX, element.y + scrollY);
rc.draw(shape);
context.translate(-element.x - scrollX, -element.y - scrollY);
context.globalAlpha = 1;
};
} else if (element.type === "ellipse") {
const shape = withCustomMathRandom(element.seed, () =>
generator.ellipse(
element.width / 2,
element.height / 2,
element.width,
element.height,
{
stroke: element.strokeColor,
fill: element.backgroundColor,
fillStyle: element.fillStyle,
strokeWidth: element.strokeWidth,
roughness: element.roughness
}
)
);
element.draw = (rc, context, { scrollX, scrollY }) => {
context.globalAlpha = element.opacity / 100;
context.translate(element.x + scrollX, element.y + scrollY);
rc.draw(shape);
context.translate(-element.x - scrollX, -element.y - scrollY);
context.globalAlpha = 1;
};
} else if (element.type === "arrow") {
const [x1, y1, x2, y2, x3, y3, x4, y4] = getArrowPoints(element);
const options = {
stroke: element.strokeColor,
strokeWidth: element.strokeWidth,
roughness: element.roughness
};
const shapes = withCustomMathRandom(element.seed, () => [
// \
generator.line(x3, y3, x2, y2, options),
// -----
generator.line(x1, y1, x2, y2, options),
// /
generator.line(x4, y4, x2, y2, options)
]);
element.draw = (rc, context, { scrollX, scrollY }) => {
context.globalAlpha = element.opacity / 100;
context.translate(element.x + scrollX, element.y + scrollY);
shapes.forEach(shape => rc.draw(shape));
context.translate(-element.x - scrollX, -element.y - scrollY);
context.globalAlpha = 1;
};
return;
} else if (isTextElement(element)) {
element.draw = (rc, context, { scrollX, scrollY }) => {
context.globalAlpha = element.opacity / 100;
const font = context.font;
context.font = element.font;
const fillStyle = context.fillStyle;
context.fillStyle = element.strokeColor;
context.fillText(
element.text,
element.x + scrollX,
element.y + element.actualBoundingBoxAscent + scrollY
);
context.fillStyle = fillStyle;
context.font = font;
context.globalAlpha = 1;
};
} else {
throw new Error("Unimplemented type " + element.type);
}
}

View file

@ -1,9 +1,6 @@
export { newElement } from "./newElement";
export {
getElementAbsoluteX1,
getElementAbsoluteX2,
getElementAbsoluteY1,
getElementAbsoluteY2,
getElementAbsoluteCoords,
getDiamondPoints,
getArrowPoints
} from "./bounds";
@ -11,5 +8,4 @@ export {
export { handlerRectangles } from "./handlerRectangles";
export { hitTest } from "./collision";
export { resizeTest } from "./resizeTest";
export { generateDraw } from "./generateDraw";
export { isTextElement } from "./typeChecks";

View file

@ -1,6 +1,3 @@
import { RoughCanvas } from "roughjs/bin/canvas";
import { SceneState } from "../scene/types";
import { randomSeed } from "../random";
export function newElement(
@ -17,24 +14,19 @@ export function newElement(
height = 0
) {
const element = {
type: type,
x: x,
y: y,
width: width,
height: height,
type,
x,
y,
width,
height,
strokeColor,
backgroundColor,
fillStyle,
strokeWidth,
roughness,
opacity,
isSelected: false,
strokeColor: strokeColor,
backgroundColor: backgroundColor,
fillStyle: fillStyle,
strokeWidth: strokeWidth,
roughness: roughness,
opacity: opacity,
seed: randomSeed(),
draw(
rc: RoughCanvas,
context: CanvasRenderingContext2D,
sceneState: SceneState
) {}
seed: randomSeed()
};
return element;
}