Further math refactor and simplifications

This commit is contained in:
Mark Tolmacs 2024-09-23 17:13:40 +02:00
parent 41885b4bb3
commit 0e2f8c958e
No known key found for this signature in database
18 changed files with 262 additions and 175 deletions

View file

@ -48,7 +48,6 @@ import type { MarkOptional, Mutable } from "../utility-types";
import { detectLineHeight, getContainerElement } from "../element/textElement";
import { normalizeLink } from "./url";
import { syncInvalidIndices } from "../fractionalIndex";
import { getSizeFromPoints } from "../points";
import { getLineHeight } from "../fonts";
import { normalizeFixedPoint } from "../element/binding";
import {
@ -57,7 +56,7 @@ import {
getNormalizedZoom,
} from "../scene";
import type { LocalPoint, Radians } from "../../math";
import { isFiniteNumber, point } from "../../math";
import { pointExtent, isFiniteNumber, point } from "../../math";
type RestoredAppState = Omit<
AppState,
@ -288,7 +287,7 @@ const restoreElement = (
points,
x,
y,
...getSizeFromPoints(points),
...pointExtent(points),
});
case "arrow": {
const { startArrowhead = null, endArrowhead = "arrow" } = element;
@ -315,7 +314,7 @@ const restoreElement = (
x,
y,
elbowed: (element as ExcalidrawArrowElement).elbowed,
...getSizeFromPoints(points),
...pointExtent(points),
});
}

View file

@ -48,12 +48,11 @@ import {
getFontString,
toBrandedType,
} from "../utils";
import { getSizeFromPoints } from "../points";
import { randomId } from "../random";
import { syncInvalidIndices } from "../fractionalIndex";
import { getLineHeight } from "../fonts";
import { isArrowElement } from "../element/typeChecks";
import { point, type LocalPoint } from "../../math";
import { pointExtent, point, type LocalPoint } from "../../math";
export type ValidLinearElement = {
type: "arrow" | "line";
@ -556,7 +555,7 @@ export const convertToExcalidrawElements = (
Object.assign(
excalidrawElement,
getSizeFromPoints(excalidrawElement.points),
pointExtent(excalidrawElement.points),
);
break;
}

View file

@ -19,7 +19,6 @@ import {
isLinearElement,
isTextElement,
} from "./typeChecks";
import { rescalePoints } from "../points";
import { getBoundTextElement, getContainerElement } from "./textElement";
import { LinearElementEditor } from "./linearElementEditor";
import { ShapeCache } from "../scene/ShapeCache";
@ -38,6 +37,7 @@ import {
pointDistance,
pointFromArray,
pointRotateRads,
pointRescaleFromTopLeft,
} from "../../math";
import type { Mutable } from "../utility-types";
@ -859,10 +859,10 @@ export const getResizedElementAbsoluteCoords = (
];
}
const points = rescalePoints(
const points = pointRescaleFromTopLeft(
0,
nextWidth,
rescalePoints(1, nextHeight, element.points, normalizePoints),
pointRescaleFromTopLeft(1, nextHeight, element.points, normalizePoints),
normalizePoints,
);

View file

@ -1,10 +1,10 @@
import type { ExcalidrawElement } from "./types";
import Scene from "../scene/Scene";
import { getSizeFromPoints } from "../points";
import { randomInteger } from "../random";
import { getUpdatedTimestamp } from "../utils";
import type { Mutable } from "../utility-types";
import { ShapeCache } from "../scene/ShapeCache";
import { pointExtent } from "../../math";
export type ElementUpdate<TElement extends ExcalidrawElement> = Omit<
Partial<TElement>,
@ -27,7 +27,7 @@ export const mutateElement = <TElement extends Mutable<ExcalidrawElement>>(
const { points, fileId } = updates as any;
if (typeof points !== "undefined") {
updates = { ...getSizeFromPoints(points), ...updates };
updates = { ...pointExtent(points), ...updates };
}
for (const key in updates) {

View file

@ -1,5 +1,4 @@
import { MIN_FONT_SIZE, SHIFT_LOCKING_ANGLE } from "../constants";
import { rescalePoints } from "../points";
import type {
ExcalidrawLinearElement,
ExcalidrawTextElement,
@ -62,6 +61,7 @@ import {
pointFromPair,
pointRotateRads,
type Radians,
pointRescaleFromTopLeft,
} from "../../math";
// Returns true when transform (resizing/rotation) happened
@ -191,10 +191,10 @@ export const rescalePointsInElement = (
) =>
isLinearElement(element) || isFreeDrawElement(element)
? {
points: rescalePoints(
points: pointRescaleFromTopLeft(
0,
width,
rescalePoints(1, height, element.points, normalizePoints),
pointRescaleFromTopLeft(1, height, element.points, normalizePoints),
normalizePoints,
),
}
@ -674,14 +674,14 @@ export const resizeSingleElement = (
let rescaledElementPointsY;
let rescaledPoints;
if (isLinearElement(element) || isFreeDrawElement(element)) {
rescaledElementPointsY = rescalePoints(
rescaledElementPointsY = pointRescaleFromTopLeft(
1,
eleNewHeight,
(stateAtResizeStart as ExcalidrawLinearElement).points,
true,
);
rescaledPoints = rescalePoints(
rescaledPoints = pointRescaleFromTopLeft(
0,
eleNewWidth,
rescaledElementPointsY,

View file

@ -1,5 +1,6 @@
import type { Radians } from "../../math";
import {
pointExtent,
point,
pointScaleFromOrigin,
pointTranslate,
@ -12,7 +13,6 @@ import {
type Vector,
} from "../../math";
import BinaryHeap from "../binaryheap";
import { getSizeFromPoints } from "../points";
import { aabbForElement, pointInsideBounds } from "../shapes";
import { isAnyTrue, toBrandedType } from "../utils";
import {
@ -955,7 +955,7 @@ const normalizedArrowElementUpdate = (
points,
x: offsetX + (externalOffsetX ?? 0),
y: offsetY + (externalOffsetY ?? 0),
...getSizeFromPoints(points),
...pointExtent(points),
};
};

View file

@ -1,59 +0,0 @@
import { pointFromPair, type GlobalPoint, type LocalPoint } from "../math";
export const getSizeFromPoints = (
points: readonly (GlobalPoint | LocalPoint)[],
) => {
const xs = points.map((point) => point[0]);
const ys = points.map((point) => point[1]);
return {
width: Math.max(...xs) - Math.min(...xs),
height: Math.max(...ys) - Math.min(...ys),
};
};
/** @arg dimension, 0 for rescaling only x, 1 for y */
export const rescalePoints = <Point extends GlobalPoint | LocalPoint>(
dimension: 0 | 1,
newSize: number,
points: readonly Point[],
normalize: boolean,
): Point[] => {
const coordinates = points.map((point) => point[dimension]);
const maxCoordinate = Math.max(...coordinates);
const minCoordinate = Math.min(...coordinates);
const size = maxCoordinate - minCoordinate;
const scale = size === 0 ? 1 : newSize / size;
let nextMinCoordinate = Infinity;
const scaledPoints = points.map((point): Point => {
const newCoordinate = point[dimension] * scale;
const newPoint = [...point];
newPoint[dimension] = newCoordinate;
if (newCoordinate < nextMinCoordinate) {
nextMinCoordinate = newCoordinate;
}
return newPoint as Point;
});
if (!normalize) {
return scaledPoints;
}
if (scaledPoints.length === 2) {
// we don't translate two-point lines
return scaledPoints;
}
const translation = minCoordinate - nextMinCoordinate;
const nextPoints = scaledPoints.map((scaledPoint) =>
pointFromPair<Point>(
scaledPoint.map((value, currentDimension) => {
return currentDimension === dimension ? value + translation : value;
}) as [number, number],
),
);
return nextPoints;
};

View file

@ -8447,10 +8447,10 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] appState 1`
"value": null,
},
},
"pointerOffset": {
"x": 0,
"y": 0,
},
"pointerOffset": [
0,
0,
],
"segmentMidPointHoveredCoords": null,
"selectedPointsIndices": null,
"startBindingElement": "keep",
@ -8667,10 +8667,10 @@ exports[`regression tests > key 6 selects line tool > [end of test] appState 1`]
"value": null,
},
},
"pointerOffset": {
"x": 0,
"y": 0,
},
"pointerOffset": [
0,
0,
],
"segmentMidPointHoveredCoords": null,
"selectedPointsIndices": null,
"startBindingElement": "keep",
@ -9077,10 +9077,10 @@ exports[`regression tests > key a selects arrow tool > [end of test] appState 1`
"value": null,
},
},
"pointerOffset": {
"x": 0,
"y": 0,
},
"pointerOffset": [
0,
0,
],
"segmentMidPointHoveredCoords": null,
"selectedPointsIndices": null,
"startBindingElement": "keep",
@ -9474,10 +9474,10 @@ exports[`regression tests > key l selects line tool > [end of test] appState 1`]
"value": null,
},
},
"pointerOffset": {
"x": 0,
"y": 0,
},
"pointerOffset": [
0,
0,
],
"segmentMidPointHoveredCoords": null,
"selectedPointsIndices": null,
"startBindingElement": "keep",

View file

@ -1,5 +1,5 @@
import type { LineSegment } from "../math";
import { isLineSegment, lineSegment, point, type GlobalPoint } from "../math";
import type { LineSegment } from "../utils";
import type { BoundingBox, Bounds } from "./element/bounds";
import { isBounds } from "./element/typeChecks";