diff --git a/src/math.ts b/src/math.ts index 5b8f119fc..50faf886a 100644 --- a/src/math.ts +++ b/src/math.ts @@ -52,3 +52,45 @@ export function rotate( (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2 ]; } + +interface Line { + x1: number; + x2: number; + y1: number; + y2: number; +} + +interface Point { + x: number; + y: number; +} + +const vcp = (a: Point, b: Point) => { + return a.x * b.y - a.y * b.x; +}; + +//https://stackoverflow.com/a/565282/816116 +export const testLineSegmentIntersect = (l1: Line, l2: Line) => { + const r: Point = { x: l1.x2 - l1.x1, y: l1.y2 - l1.y1 }; + const s: Point = { x: l2.x2 - l2.x1, y: l2.y2 - l2.y1 }; + + // r x s + const div = vcp(r, s); + + // q - p + const sub = { x: l2.x1 - l1.x1, y: l2.y1 - l1.y1 }; + + // t = (q - p) x s / (r x s) + const t = vcp(sub, s) / div; + + // u = (q - p) x r / (r x s) + const u = vcp(sub, r) / div; + + if (div === 0 && vcp(sub, s) === 0) { + return true; + } else if (div !== 0 && t >= 0 && t <= 1 && u >= 0 && u <= 1) { + return true; + } + + return false; +}; diff --git a/src/scene/selection.ts b/src/scene/selection.ts index b1fbc13ee..9db217895 100644 --- a/src/scene/selection.ts +++ b/src/scene/selection.ts @@ -1,5 +1,6 @@ import { ExcalidrawElement } from "../element/types"; import { getElementAbsoluteCoords } from "../element"; +import { testLineSegmentIntersect } from "../math"; export function setSelection( elements: ExcalidrawElement[], @@ -18,12 +19,70 @@ export function setSelection( elementX2, elementY2 ] = getElementAbsoluteCoords(element); - element.isSelected = - element.type !== "selection" && - selectionX1 <= elementX1 && - selectionY1 <= elementY1 && - selectionX2 >= elementX2 && - selectionY2 >= elementY2; + if (element.type === "selection") { + element.isSelected = false; + } else if (element.type === "arrow") { + if ( + selectionX1 < elementX1 && + selectionX2 > elementX2 && + selectionY1 < elementY1 && + selectionY2 > elementY2 + ) { + element.isSelected = true; + } else { + // lt -> lb + const line1 = { + x1: selectionX1, + y1: selectionY1, + x2: selectionX1, + y2: selectionY2 + }; + + // rt -> rb + const line2 = { + x1: selectionX2, + y1: selectionY1, + x2: selectionX2, + y2: selectionY2 + }; + + // lt -> rt + const line3 = { + x1: selectionX1, + y1: selectionY1, + x2: selectionX2, + y2: selectionY1 + }; + + // lb -> rb + const line4 = { + x1: selectionX1, + y1: selectionY2, + x2: selectionX2, + y2: selectionY2 + }; + + const target = { + x1: elementX1, + y1: elementY1, + x2: elementX2, + y2: elementY2 + }; + + element.isSelected = + testLineSegmentIntersect(line1, target) || + testLineSegmentIntersect(line2, target) || + testLineSegmentIntersect(line3, target) || + testLineSegmentIntersect(line4, target); + } + } else { + element.isSelected = !( + (selectionX1 < elementX1 && selectionX2 < elementX1) || + (selectionX1 > elementX2 && selectionX2 > elementX2) || + (selectionY1 < elementY1 && selectionY2 < elementY1) || + (selectionY1 > elementY2 && selectionY2 > elementY2) + ); + } }); }