From 8db8827c6f6e73accf1f8e3b7c2998a3710b367f Mon Sep 17 00:00:00 2001 From: Bakhtiiar Muzakparov Date: Wed, 15 Jan 2020 22:07:19 +0300 Subject: [PATCH] feat: add line shape (#371) * feat: add line shape * fix: align line tool * fix: hitbox bug sw to ne * fix: add stroke width n sloppiness for line * fix: center line inside a panel box * fix: use color as a unique key --- src/components/ColorPicker.tsx | 1 + src/element/bounds.ts | 9 +++++++++ src/element/collision.ts | 10 +++++++++- src/element/handlerRectangles.ts | 2 +- src/element/index.ts | 3 ++- src/renderer/renderElement.ts | 22 +++++++++++++++++++++- src/scene/comparisons.ts | 3 ++- src/shapes.tsx | 16 ++++++++++++++++ 8 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/components/ColorPicker.tsx b/src/components/ColorPicker.tsx index 09209e04..9aa953a7 100644 --- a/src/components/ColorPicker.tsx +++ b/src/components/ColorPicker.tsx @@ -33,6 +33,7 @@ const Picker = function({ title={color} tabIndex={0} style={{ backgroundColor: color }} + key={color} > {color === "transparent" ? (
diff --git a/src/element/bounds.ts b/src/element/bounds.ts index 6a7cfcf1..d3dbee14 100644 --- a/src/element/bounds.ts +++ b/src/element/bounds.ts @@ -48,3 +48,12 @@ export function getArrowPoints(element: ExcalidrawElement) { return [x1, y1, x2, y2, x3, y3, x4, y4]; } + +export function getLinePoints(element: ExcalidrawElement) { + const x1 = 0; + const y1 = 0; + const x2 = element.width; + const y2 = element.height; + + return [x1, y1, x2, y2]; +} diff --git a/src/element/collision.ts b/src/element/collision.ts index 2201bbc1..abcf2652 100644 --- a/src/element/collision.ts +++ b/src/element/collision.ts @@ -4,7 +4,8 @@ import { ExcalidrawElement } from "./types"; import { getArrowPoints, getDiamondPoints, - getElementAbsoluteCoords + getElementAbsoluteCoords, + getLinePoints } from "./bounds"; export function hitTest( @@ -153,6 +154,13 @@ export function hitTest( // / distanceBetweenPointAndSegment(x, y, x4, y4, x2, y2) < lineThreshold ); + } else if (element.type === "line") { + const [x1, y1, x2, y2] = getLinePoints(element); + // The computation is done at the origin, we need to add a translation + x -= element.x; + y -= element.y; + + return distanceBetweenPointAndSegment(x, y, x1, y1, x2, y2) < lineThreshold; } else if (element.type === "text") { const [x1, y1, x2, y2] = getElementAbsoluteCoords(element); diff --git a/src/element/handlerRectangles.ts b/src/element/handlerRectangles.ts index 98575110..ab6243ef 100644 --- a/src/element/handlerRectangles.ts +++ b/src/element/handlerRectangles.ts @@ -76,7 +76,7 @@ export function handlerRectangles( 8 ]; // se - if (element.type === "arrow") { + if (element.type === "arrow" || element.type === "line") { return { nw: handlers.nw, se: handlers.se diff --git a/src/element/index.ts b/src/element/index.ts index 4914221d..0e8aac4a 100644 --- a/src/element/index.ts +++ b/src/element/index.ts @@ -2,7 +2,8 @@ export { newElement, duplicateElement } from "./newElement"; export { getElementAbsoluteCoords, getDiamondPoints, - getArrowPoints + getArrowPoints, + getLinePoints } from "./bounds"; export { handlerRectangles } from "./handlerRectangles"; diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts index a296a61f..104b713e 100644 --- a/src/renderer/renderElement.ts +++ b/src/renderer/renderElement.ts @@ -1,6 +1,10 @@ import { ExcalidrawElement } from "../element/types"; import { isTextElement } from "../element/typeChecks"; -import { getDiamondPoints, getArrowPoints } from "../element/bounds"; +import { + getDiamondPoints, + getArrowPoints, + getLinePoints +} from "../element/bounds"; import { RoughCanvas } from "roughjs/bin/canvas"; import { Drawable } from "roughjs/bin/core"; @@ -118,6 +122,22 @@ export function renderElement( (element.shape as Drawable[]).forEach(shape => rc.draw(shape)); context.globalAlpha = 1; return; + } else if (element.type === "line") { + const [x1, y1, x2, y2] = getLinePoints(element); + const options = { + stroke: element.strokeColor, + strokeWidth: element.strokeWidth, + roughness: element.roughness, + seed: element.seed + }; + + if (!element.shape) { + element.shape = generator.line(x1, y1, x2, y2, options); + } + + context.globalAlpha = element.opacity / 100; + rc.draw(element.shape as Drawable); + context.globalAlpha = 1; } else if (isTextElement(element)) { context.globalAlpha = element.opacity / 100; const font = context.font; diff --git a/src/scene/comparisons.ts b/src/scene/comparisons.ts index 62b4b5df..20380e00 100644 --- a/src/scene/comparisons.ts +++ b/src/scene/comparisons.ts @@ -18,7 +18,8 @@ export const hasStroke = (elements: readonly ExcalidrawElement[]) => (element.type === "rectangle" || element.type === "ellipse" || element.type === "diamond" || - element.type === "arrow") + element.type === "arrow" || + element.type === "line") ); export const hasText = (elements: readonly ExcalidrawElement[]) => diff --git a/src/shapes.tsx b/src/shapes.tsx index 6fc164aa..17a40be9 100644 --- a/src/shapes.tsx +++ b/src/shapes.tsx @@ -55,6 +55,22 @@ export const SHAPES = [ ), value: "text" + }, + { + icon: ( + // custom + + + + ), + value: "line" } ];