Fix linting

This commit is contained in:
Mathias Krafft 2025-04-07 13:59:50 +00:00
parent ac37d0a5be
commit 37412cd68a
2 changed files with 59 additions and 31 deletions

View file

@ -245,18 +245,20 @@ export const isPointWithinBounds = <P extends GlobalPoint | LocalPoint>(
export const perpendicularDistance = <P extends GlobalPoint | LocalPoint>( export const perpendicularDistance = <P extends GlobalPoint | LocalPoint>(
p: P, p: P,
start: P, start: P,
end: P): end: P,
number => { ): number => {
const dx = end[0] - start[0]; const dx = end[0] - start[0];
const dy = end[1] - start[1]; const dy = end[1] - start[1];
if (dx === 0 && dy === 0) { if (dx === 0 && dy === 0) {
return Math.hypot(p[0] - start[0], p[1] - start[1]); return Math.hypot(p[0] - start[0], p[1] - start[1]);
} }
// Equation of line distance // Equation of line distance
const numerator = Math.abs(dy * p[0] - dx * p[1] + end[0] * start[1] - end[1] * start[0]); const numerator = Math.abs(
dy * p[0] - dx * p[1] + end[0] * start[1] - end[1] * start[0],
);
const denom = Math.hypot(dx, dy); const denom = Math.hypot(dx, dy);
return numerator / denom; return numerator / denom;
} };
/** * Calculates the angle between three points in degrees. /** * Calculates the angle between three points in degrees.
* The angle is calculated at the first point (p0) using the second (p1) and third (p2) points. * The angle is calculated at the first point (p0) using the second (p1) and third (p2) points.
@ -266,7 +268,7 @@ export const perpendicularDistance = <P extends GlobalPoint | LocalPoint>(
* @param p1 The vertex point where the angle is calculated. * @param p1 The vertex point where the angle is calculated.
* @param p2 The second point used to form the angle. * @param p2 The second point used to form the angle.
* @returns The angle in degrees between the three points. * @returns The angle in degrees between the three points.
**/ **/
export const angleBetween = <P extends GlobalPoint | LocalPoint>( export const angleBetween = <P extends GlobalPoint | LocalPoint>(
p0: P, p0: P,
p1: P, p1: P,
@ -276,8 +278,12 @@ export const angleBetween = <P extends GlobalPoint | LocalPoint>(
const v2 = vectorFromPoint(p1, p2); const v2 = vectorFromPoint(p1, p2);
// dot and cross product // dot and cross product
const magnitude1 = Math.hypot(v1[0], v1[1]), magnitude2 = Math.hypot(v2[0], v2[1]); const magnitude1 = Math.hypot(v1[0], v1[1]);
if (magnitude1 === 0 || magnitude2 === 0) return 0 as Degrees; const magnitude2 = Math.hypot(v2[0], v2[1]);
if (magnitude1 === 0 || magnitude2 === 0) {
return 0 as Degrees;
}
const dot = vectorDot(v1, v2); const dot = vectorDot(v1, v2);
@ -287,4 +293,4 @@ export const angleBetween = <P extends GlobalPoint | LocalPoint>(
const rad = Math.acos(cos) as Radians; const rad = Math.acos(cos) as Radians;
return radiansToDegrees(rad); return radiansToDegrees(rad);
} };

View file

@ -1,3 +1,23 @@
import {
getCenterForBounds,
getCommonBoundingBox,
} from "@excalidraw/element/bounds";
import {
newArrowElement,
newElement,
newLinearElement,
} from "@excalidraw/element/newElement";
import {
angleBetween,
perpendicularDistance,
pointDistance,
} from "@excalidraw/math";
import { ROUNDNESS } from "@excalidraw/common";
import type { LocalPoint } from "@excalidraw/math";
import type { BoundingBox, Bounds } from "@excalidraw/element/bounds";
import type { import type {
ExcalidrawArrowElement, ExcalidrawArrowElement,
ExcalidrawDiamondElement, ExcalidrawDiamondElement,
@ -7,11 +27,6 @@ import type {
ExcalidrawLinearElement, ExcalidrawLinearElement,
ExcalidrawRectangleElement, ExcalidrawRectangleElement,
} from "@excalidraw/element/types"; } from "@excalidraw/element/types";
import type { BoundingBox, Bounds } from "@excalidraw/element/bounds";
import { getCenterForBounds, getCommonBoundingBox } from "@excalidraw/element/bounds";
import { newArrowElement, newElement, newLinearElement } from "@excalidraw/element/newElement";
import { angleBetween, GlobalPoint, LocalPoint, perpendicularDistance, pointDistance } from "@excalidraw/math";
import { ROUNDNESS } from "@excalidraw/common";
type Shape = type Shape =
| ExcalidrawRectangleElement["type"] | ExcalidrawRectangleElement["type"]
@ -53,7 +68,6 @@ const DEFAULT_OPTIONS = {
ellipseRadiusVarianceThreshold: 0.5, ellipseRadiusVarianceThreshold: 0.5,
} as const; // Use 'as const' for stricter typing of default values } as const; // Use 'as const' for stricter typing of default values
// Options for shape recognition, allowing partial overrides // Options for shape recognition, allowing partial overrides
type ShapeRecognitionOptions = typeof DEFAULT_OPTIONS; type ShapeRecognitionOptions = typeof DEFAULT_OPTIONS;
type PartialShapeRecognitionOptions = Partial<ShapeRecognitionOptions>; type PartialShapeRecognitionOptions = Partial<ShapeRecognitionOptions>;
@ -94,10 +108,9 @@ function simplifyRDP(
const right = simplifyRDP(points.slice(index), epsilon); const right = simplifyRDP(points.slice(index), epsilon);
// Concatenate results (omit duplicate point at junction) // Concatenate results (omit duplicate point at junction)
return left.slice(0, -1).concat(right); return left.slice(0, -1).concat(right);
} else {
// Not enough deviation, return straight line segment (keep only endpoints)
return [first, last];
} }
// Not enough deviation, return straight line segment (keep only endpoints)
return [first, last];
} }
/** /**
@ -238,7 +251,10 @@ function checkArrow(
const tipAngle = angleBetween(arrowTip, arrowBase, arrowTailEnd); const tipAngle = angleBetween(arrowTip, arrowBase, arrowTailEnd);
if (tipAngle <= options.arrowMinTipAngle || tipAngle >= options.arrowMaxTipAngle) { if (
tipAngle <= options.arrowMinTipAngle ||
tipAngle >= options.arrowMaxTipAngle
) {
return null; return null;
} }
@ -296,10 +312,9 @@ function checkQuadrilateral(
if (isAxisAligned(segments, options.rectangleOrientationAngleThreshold)) { if (isAxisAligned(segments, options.rectangleOrientationAngleThreshold)) {
return "rectangle"; return "rectangle";
} else {
// Not axis-aligned, but quadrilateral => classify as diamond
return "diamond";
} }
// Not axis-aligned, but quadrilateral => classify as diamond
return "diamond";
} }
/** Checks if the points form an ellipse shape. */ /** Checks if the points form an ellipse shape. */
@ -345,13 +360,18 @@ export const recognizeShape = (
} }
const boundingBoxDiagonal = Math.hypot(boundingBox.width, boundingBox.height); const boundingBoxDiagonal = Math.hypot(boundingBox.width, boundingBox.height);
const rdpTolerance = boundingBoxDiagonal * (options.rdpTolerancePercent / 100); const rdpTolerance =
boundingBoxDiagonal * (options.rdpTolerancePercent / 100);
const simplifiedPoints = simplifyRDP(points, rdpTolerance); const simplifiedPoints = simplifyRDP(points, rdpTolerance);
const isClosed = isShapeClosed(simplifiedPoints, boundingBoxDiagonal, options); const isClosed = isShapeClosed(
simplifiedPoints,
boundingBoxDiagonal,
options,
);
// --- Shape check order matters here --- // --- Shape check order matters here ---
let recognizedType: Shape = const recognizedType: Shape =
checkLine(simplifiedPoints, isClosed) ?? checkLine(simplifiedPoints, isClosed) ??
checkArrow(simplifiedPoints, isClosed, options) ?? checkArrow(simplifiedPoints, isClosed, options) ??
checkQuadrilateral(simplifiedPoints, isClosed, options) ?? checkQuadrilateral(simplifiedPoints, isClosed, options) ??
@ -365,7 +385,6 @@ export const recognizeShape = (
}; };
}; };
/** /**
* Converts a freedraw element to the detected shape * Converts a freedraw element to the detected shape
*/ */
@ -375,7 +394,9 @@ export const convertToShape = (
const recognizedShape = recognizeShape(freeDrawElement); const recognizedShape = recognizeShape(freeDrawElement);
switch (recognizedShape.type) { switch (recognizedShape.type) {
case "rectangle": case "diamond": case "ellipse": { case "rectangle":
case "diamond":
case "ellipse": {
return newElement({ return newElement({
...freeDrawElement, ...freeDrawElement,
roundness: { type: ROUNDNESS.PROPORTIONAL_RADIUS }, roundness: { type: ROUNDNESS.PROPORTIONAL_RADIUS },
@ -392,9 +413,9 @@ export const convertToShape = (
type: recognizedShape.type, type: recognizedShape.type,
points: [ points: [
recognizedShape.simplified[0], recognizedShape.simplified[0],
recognizedShape.simplified[recognizedShape.simplified.length - 2] recognizedShape.simplified[recognizedShape.simplified.length - 2],
], ],
roundness: { type: ROUNDNESS.PROPORTIONAL_RADIUS } roundness: { type: ROUNDNESS.PROPORTIONAL_RADIUS },
}); });
} }
case "line": { case "line": {
@ -403,11 +424,12 @@ export const convertToShape = (
type: recognizedShape.type, type: recognizedShape.type,
points: [ points: [
recognizedShape.simplified[0], recognizedShape.simplified[0],
recognizedShape.simplified[recognizedShape.simplified.length - 1] recognizedShape.simplified[recognizedShape.simplified.length - 1],
], ],
roundness: { type: ROUNDNESS.PROPORTIONAL_RADIUS } roundness: { type: ROUNDNESS.PROPORTIONAL_RADIUS },
}); });
} }
default: return freeDrawElement default:
return freeDrawElement;
} }
}; };