mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Rectangle distance and tests
This commit is contained in:
parent
9a8aabbeca
commit
47cc842415
5 changed files with 72 additions and 25 deletions
39
packages/math/rectangle.test.ts
Normal file
39
packages/math/rectangle.test.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { point } from "./point";
|
||||||
|
import { rectangle, rectangleDistanceFromPoint } from "./rectangle";
|
||||||
|
|
||||||
|
describe("rectangle distance", () => {
|
||||||
|
it("finds the shortest distance", () => {
|
||||||
|
expect(
|
||||||
|
rectangleDistanceFromPoint(
|
||||||
|
rectangle(point(-1, -1), point(1, 1)),
|
||||||
|
point(2, 0),
|
||||||
|
),
|
||||||
|
).toBe(1);
|
||||||
|
expect(
|
||||||
|
rectangleDistanceFromPoint(
|
||||||
|
rectangle(point(-1, -1), point(1, 1)),
|
||||||
|
point(0, 2),
|
||||||
|
),
|
||||||
|
).toBe(1);
|
||||||
|
expect(
|
||||||
|
rectangleDistanceFromPoint(
|
||||||
|
rectangle(point(-1, -1), point(1, 1)),
|
||||||
|
point(-2, 0),
|
||||||
|
),
|
||||||
|
).toBe(1);
|
||||||
|
expect(
|
||||||
|
rectangleDistanceFromPoint(
|
||||||
|
rectangle(point(-1, -1), point(1, 1)),
|
||||||
|
point(0, -2),
|
||||||
|
),
|
||||||
|
).toBe(1);
|
||||||
|
});
|
||||||
|
it("finds the corner as closest point", () => {
|
||||||
|
expect(
|
||||||
|
rectangleDistanceFromPoint(
|
||||||
|
rectangle(point(-1, -1), point(1, 1)),
|
||||||
|
point(2, 2),
|
||||||
|
),
|
||||||
|
).toBe(Math.sqrt(2));
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,19 +1,19 @@
|
||||||
import { invariant } from "../excalidraw/utils";
|
import { invariant } from "../excalidraw/utils";
|
||||||
|
import { point } from "./point";
|
||||||
|
import { segment, segmentDistanceToPoint } from "./segment";
|
||||||
import type { GenericPoint, Rectangle } from "./types";
|
import type { GenericPoint, Rectangle } from "./types";
|
||||||
|
|
||||||
export function rectangle<P extends GenericPoint>(
|
export function rectangle<P extends GenericPoint>(
|
||||||
a: P,
|
topLeft: P,
|
||||||
b: P,
|
bottomRight: P,
|
||||||
c: P,
|
|
||||||
d: P,
|
|
||||||
): Rectangle<P> {
|
): Rectangle<P> {
|
||||||
return [a, b, c, d] as Rectangle<P>;
|
return [topLeft, bottomRight] as Rectangle<P>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function rectangleFromQuad<P extends GenericPoint>(
|
export function rectangleFromPair<P extends GenericPoint>(
|
||||||
quad: [a: P, b: P, c: P, d: P],
|
pair: [a: P, b: P],
|
||||||
): Rectangle<P> {
|
): Rectangle<P> {
|
||||||
return quad as Rectangle<P>;
|
return pair as Rectangle<P>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function rectangleFromArray<P extends GenericPoint>(
|
export function rectangleFromArray<P extends GenericPoint>(
|
||||||
|
@ -26,3 +26,17 @@ export function rectangleFromArray<P extends GenericPoint>(
|
||||||
|
|
||||||
return pointArray as Rectangle<P>;
|
return pointArray as Rectangle<P>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function rectangleDistanceFromPoint<Point extends GenericPoint>(
|
||||||
|
r: Rectangle<Point>,
|
||||||
|
p: Point,
|
||||||
|
): number {
|
||||||
|
const sides = [
|
||||||
|
segment(point(r[0][0], r[0][1]), point(r[1][0], r[0][1])),
|
||||||
|
segment(point(r[1][0], r[0][1]), point(r[1][0], r[1][1])),
|
||||||
|
segment(point(r[1][0], r[1][1]), point(r[0][0], r[1][1])),
|
||||||
|
segment(point(r[0][0], r[1][1]), point(r[0][0], r[0][1])),
|
||||||
|
];
|
||||||
|
|
||||||
|
return Math.min(...sides.map((side) => segmentDistanceToPoint(p, side)));
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { invariant } from "../excalidraw/utils";
|
|
||||||
import {
|
import {
|
||||||
isPoint,
|
isPoint,
|
||||||
pointCenter,
|
pointCenter,
|
||||||
|
@ -23,10 +22,9 @@ import {
|
||||||
* @returns The line segment delineated by the points
|
* @returns The line segment delineated by the points
|
||||||
*/
|
*/
|
||||||
export function segment<P extends GenericPoint>(a: P, b: P): Segment<P> {
|
export function segment<P extends GenericPoint>(a: P, b: P): Segment<P> {
|
||||||
invariant(
|
if (pointsEqual(a, b)) {
|
||||||
!pointsEqual(a, b),
|
console.warn("The start and end points of the segment cannot match");
|
||||||
"The start and end points of the segment cannot match",
|
}
|
||||||
);
|
|
||||||
|
|
||||||
return [a, b] as Segment<P>;
|
return [a, b] as Segment<P>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ export type Triangle<P extends GenericPoint> = [a: P, b: P, c: P] & {
|
||||||
/**
|
/**
|
||||||
* A rectangular shape represented by 4 points at its corners
|
* A rectangular shape represented by 4 points at its corners
|
||||||
*/
|
*/
|
||||||
export type Rectangle<P extends GenericPoint> = [a: P, b: P, c: P, d: P] & {
|
export type Rectangle<P extends GenericPoint> = [a: P, b: P] & {
|
||||||
_brand: "excalimath__rectangle";
|
_brand: "excalimath__rectangle";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -253,15 +253,13 @@ const polylineFromPoints = <
|
||||||
return polyline;
|
return polyline;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getFreedrawShape = <
|
export const getFreedrawShape = (
|
||||||
Point extends GlobalPoint | LocalPoint | ViewportPoint,
|
|
||||||
>(
|
|
||||||
element: ExcalidrawFreeDrawElement,
|
element: ExcalidrawFreeDrawElement,
|
||||||
center: Point,
|
center: GlobalPoint,
|
||||||
isClosed: boolean = false,
|
isClosed: boolean = false,
|
||||||
): GeometricShape<Point> => {
|
): GeometricShape<GlobalPoint> => {
|
||||||
const transform = (p: Point): Point =>
|
const transform = (p: Readonly<LocalPoint>): GlobalPoint =>
|
||||||
pointRotateRads<Point>(
|
pointRotateRads(
|
||||||
pointFromVector(
|
pointFromVector(
|
||||||
vectorAdd(vectorFromPoint(p), vector(element.x, element.y)),
|
vectorAdd(vectorFromPoint(p), vector(element.x, element.y)),
|
||||||
),
|
),
|
||||||
|
@ -269,9 +267,7 @@ export const getFreedrawShape = <
|
||||||
element.angle,
|
element.angle,
|
||||||
);
|
);
|
||||||
|
|
||||||
const polyline = polylineFromPoints(
|
const polyline = polylineFromPoints(element.points.map((p) => transform(p)));
|
||||||
element.points.map((p) => transform(p as Point)),
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
isClosed
|
isClosed
|
||||||
|
@ -283,7 +279,7 @@ export const getFreedrawShape = <
|
||||||
type: "polyline",
|
type: "polyline",
|
||||||
data: polyline,
|
data: polyline,
|
||||||
}
|
}
|
||||||
) as GeometricShape<Point>;
|
) as GeometricShape<GlobalPoint>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getClosedCurveShape = <Point extends GlobalPoint | LocalPoint>(
|
export const getClosedCurveShape = <Point extends GlobalPoint | LocalPoint>(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue