mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Refactoring points
Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
parent
8ca4cf3260
commit
b4cb314090
40 changed files with 746 additions and 783 deletions
|
@ -5,6 +5,7 @@ import { getElementAbsoluteCoords } from ".";
|
|||
import { useExcalidrawAppState } from "../components/App";
|
||||
|
||||
import "./ElementCanvasButtons.scss";
|
||||
import { point } from "../../math";
|
||||
|
||||
const CONTAINER_PADDING = 5;
|
||||
|
||||
|
@ -14,8 +15,8 @@ const getContainerCoords = (
|
|||
elementsMap: ElementsMap,
|
||||
) => {
|
||||
const [x1, y1] = getElementAbsoluteCoords(element, elementsMap);
|
||||
const { x: viewportX, y: viewportY } = sceneCoordsToViewportCoords(
|
||||
{ sceneX: x1 + element.width, sceneY: y1 },
|
||||
const [viewportX, viewportY] = sceneCoordsToViewportCoords(
|
||||
point(x1 + element.width, y1),
|
||||
appState,
|
||||
);
|
||||
const x = viewportX - appState.offsetLeft + 10;
|
||||
|
|
|
@ -49,7 +49,7 @@ import type { ElementUpdate } from "./mutateElement";
|
|||
import { mutateElement } from "./mutateElement";
|
||||
import type Scene from "../scene/Scene";
|
||||
import { LinearElementEditor } from "./linearElementEditor";
|
||||
import { arrayToMap, tupleToCoors } from "../utils";
|
||||
import { arrayToMap } from "../utils";
|
||||
import { KEYS } from "../keys";
|
||||
import { getBoundTextElement, handleBindTextResize } from "./textElement";
|
||||
import { aabbForElement, getElementShape, pointInsideBounds } from "../shapes";
|
||||
|
@ -389,7 +389,7 @@ export const getSuggestedBindingsForArrows = (
|
|||
export const maybeBindLinearElement = (
|
||||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||
appState: AppState,
|
||||
pointerCoords: { x: number; y: number },
|
||||
pointerCoords: GlobalPoint,
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
elements: readonly NonDeletedExcalidrawElement[],
|
||||
): void => {
|
||||
|
@ -508,10 +508,7 @@ const unbindLinearElement = (
|
|||
};
|
||||
|
||||
export const getHoveredElementForBinding = (
|
||||
pointerCoords: {
|
||||
x: number;
|
||||
y: number;
|
||||
},
|
||||
pointer: GlobalPoint,
|
||||
elements: readonly NonDeletedExcalidrawElement[],
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
fullShape?: boolean,
|
||||
|
@ -522,7 +519,7 @@ export const getHoveredElementForBinding = (
|
|||
isBindableElement(element, false) &&
|
||||
bindingBorderTest(
|
||||
element,
|
||||
pointerCoords,
|
||||
pointer,
|
||||
elementsMap,
|
||||
// disable fullshape snapping for frame elements so we
|
||||
// can bind to frame children
|
||||
|
@ -1177,14 +1174,12 @@ const getLinearElementEdgeCoors = (
|
|||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||
startOrEnd: "start" | "end",
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
): { x: number; y: number } => {
|
||||
): GlobalPoint => {
|
||||
const index = startOrEnd === "start" ? 0 : -1;
|
||||
return tupleToCoors(
|
||||
LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||
linearElement,
|
||||
index,
|
||||
elementsMap,
|
||||
),
|
||||
return LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||
linearElement,
|
||||
index,
|
||||
elementsMap,
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1330,7 +1325,7 @@ const newBoundElements = (
|
|||
|
||||
export const bindingBorderTest = (
|
||||
element: NonDeleted<ExcalidrawBindableElement>,
|
||||
{ x, y }: { x: number; y: number },
|
||||
[x, y]: GlobalPoint,
|
||||
elementsMap: NonDeletedSceneElementsMap,
|
||||
fullShape?: boolean,
|
||||
): boolean => {
|
||||
|
|
|
@ -42,35 +42,33 @@ export const shouldTestInside = (element: ExcalidrawElement) => {
|
|||
};
|
||||
|
||||
export type HitTestArgs<Point extends GlobalPoint | LocalPoint> = {
|
||||
x: number;
|
||||
y: number;
|
||||
sceneCoords: Point;
|
||||
element: ExcalidrawElement;
|
||||
shape: GeometricShape<Point>;
|
||||
threshold?: number;
|
||||
frameNameBound?: FrameNameBounds | null;
|
||||
};
|
||||
|
||||
export const hitElementItself = <Point extends GlobalPoint | LocalPoint>({
|
||||
x,
|
||||
y,
|
||||
export const hitElementItself = ({
|
||||
sceneCoords,
|
||||
element,
|
||||
shape,
|
||||
threshold = 10,
|
||||
frameNameBound = null,
|
||||
}: HitTestArgs<Point>) => {
|
||||
}: HitTestArgs<GlobalPoint>) => {
|
||||
let hit = shouldTestInside(element)
|
||||
? // Since `inShape` tests STRICTLY againt the insides of a shape
|
||||
// we would need `onShape` as well to include the "borders"
|
||||
isPointInShape(point(x, y), shape) ||
|
||||
isPointOnShape(point(x, y), shape, threshold)
|
||||
: isPointOnShape(point(x, y), shape, threshold);
|
||||
isPointInShape(sceneCoords, shape) ||
|
||||
isPointOnShape(sceneCoords, shape, threshold)
|
||||
: isPointOnShape(sceneCoords, shape, threshold);
|
||||
|
||||
// hit test against a frame's name
|
||||
if (!hit && frameNameBound) {
|
||||
hit = isPointInShape(point(x, y), {
|
||||
hit = isPointInShape(sceneCoords, {
|
||||
type: "polygon",
|
||||
data: getPolygonShape(frameNameBound as ExcalidrawRectangleElement)
|
||||
.data as Polygon<Point>,
|
||||
.data as Polygon<GlobalPoint>,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -78,8 +76,7 @@ export const hitElementItself = <Point extends GlobalPoint | LocalPoint>({
|
|||
};
|
||||
|
||||
export const hitElementBoundingBox = (
|
||||
x: number,
|
||||
y: number,
|
||||
scenePointer: GlobalPoint,
|
||||
element: ExcalidrawElement,
|
||||
elementsMap: ElementsMap,
|
||||
tolerance = 0,
|
||||
|
@ -89,31 +86,27 @@ export const hitElementBoundingBox = (
|
|||
y1 -= tolerance;
|
||||
x2 += tolerance;
|
||||
y2 += tolerance;
|
||||
return isPointWithinBounds(point(x1, y1), point(x, y), point(x2, y2));
|
||||
return isPointWithinBounds(point(x1, y1), scenePointer, point(x2, y2));
|
||||
};
|
||||
|
||||
export const hitElementBoundingBoxOnly = <
|
||||
Point extends GlobalPoint | LocalPoint,
|
||||
>(
|
||||
hitArgs: HitTestArgs<Point>,
|
||||
export const hitElementBoundingBoxOnly = (
|
||||
hitArgs: HitTestArgs<GlobalPoint>,
|
||||
elementsMap: ElementsMap,
|
||||
) => {
|
||||
return (
|
||||
!hitElementItself(hitArgs) &&
|
||||
// bound text is considered part of the element (even if it's outside the bounding box)
|
||||
!hitElementBoundText(
|
||||
hitArgs.x,
|
||||
hitArgs.y,
|
||||
hitArgs.sceneCoords,
|
||||
getBoundTextShape(hitArgs.element, elementsMap),
|
||||
) &&
|
||||
hitElementBoundingBox(hitArgs.x, hitArgs.y, hitArgs.element, elementsMap)
|
||||
hitElementBoundingBox(hitArgs.sceneCoords, hitArgs.element, elementsMap)
|
||||
);
|
||||
};
|
||||
|
||||
export const hitElementBoundText = <Point extends GlobalPoint | LocalPoint>(
|
||||
x: number,
|
||||
y: number,
|
||||
textShape: GeometricShape<Point> | null,
|
||||
export const hitElementBoundText = (
|
||||
scenePointer: GlobalPoint,
|
||||
textShape: GeometricShape<GlobalPoint> | null,
|
||||
): boolean => {
|
||||
return !!textShape && isPointInShape(point(x, y), textShape);
|
||||
return !!textShape && isPointInShape(scenePointer, textShape);
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@ import type {
|
|||
Triangle,
|
||||
Vector,
|
||||
Radians,
|
||||
ViewportPoint,
|
||||
} from "../../math";
|
||||
import {
|
||||
point,
|
||||
|
@ -21,7 +22,9 @@ export const HEADING_LEFT = [-1, 0] as Heading;
|
|||
export const HEADING_UP = [0, -1] as Heading;
|
||||
export type Heading = [1, 0] | [0, 1] | [-1, 0] | [0, -1];
|
||||
|
||||
export const headingForDiamond = <Point extends GlobalPoint | LocalPoint>(
|
||||
export const headingForDiamond = <
|
||||
Point extends GlobalPoint | LocalPoint | ViewportPoint,
|
||||
>(
|
||||
a: Point,
|
||||
b: Point,
|
||||
) => {
|
||||
|
|
|
@ -20,7 +20,6 @@ import {
|
|||
} from "./bounds";
|
||||
import type {
|
||||
AppState,
|
||||
PointerCoords,
|
||||
InteractiveCanvasAppState,
|
||||
AppClassProperties,
|
||||
NullableGridSize,
|
||||
|
@ -32,7 +31,7 @@ import {
|
|||
getHoveredElementForBinding,
|
||||
isBindingEnabled,
|
||||
} from "./binding";
|
||||
import { invariant, toBrandedType, tupleToCoors } from "../utils";
|
||||
import { invariant, toBrandedType } from "../utils";
|
||||
import {
|
||||
isBindingElement,
|
||||
isElbowArrow,
|
||||
|
@ -56,6 +55,8 @@ import {
|
|||
type GlobalPoint,
|
||||
type LocalPoint,
|
||||
pointDistance,
|
||||
pointSubtract,
|
||||
pointFromPair,
|
||||
} from "../../math";
|
||||
import {
|
||||
getBezierCurveLength,
|
||||
|
@ -83,7 +84,7 @@ export class LinearElementEditor {
|
|||
/** index */
|
||||
lastClickedPoint: number;
|
||||
lastClickedIsEndPoint: boolean;
|
||||
origin: Readonly<{ x: number; y: number }> | null;
|
||||
origin: GlobalPoint | null;
|
||||
segmentMidpoint: {
|
||||
value: GlobalPoint | null;
|
||||
index: number | null;
|
||||
|
@ -94,7 +95,7 @@ export class LinearElementEditor {
|
|||
/** whether you're dragging a point */
|
||||
public readonly isDragging: boolean;
|
||||
public readonly lastUncommittedPoint: LocalPoint | null;
|
||||
public readonly pointerOffset: Readonly<{ x: number; y: number }>;
|
||||
public readonly pointerOffset: GlobalPoint;
|
||||
public readonly startBindingElement:
|
||||
| ExcalidrawBindableElement
|
||||
| null
|
||||
|
@ -115,7 +116,7 @@ export class LinearElementEditor {
|
|||
this.selectedPointsIndices = null;
|
||||
this.lastUncommittedPoint = null;
|
||||
this.isDragging = false;
|
||||
this.pointerOffset = { x: 0, y: 0 };
|
||||
this.pointerOffset = point(0, 0);
|
||||
this.startBindingElement = "keep";
|
||||
this.endBindingElement = "keep";
|
||||
this.pointerDownState = {
|
||||
|
@ -219,11 +220,10 @@ export class LinearElementEditor {
|
|||
static handlePointDragging(
|
||||
event: PointerEvent,
|
||||
app: AppClassProperties,
|
||||
scenePointerX: number,
|
||||
scenePointerY: number,
|
||||
scenePointer: GlobalPoint,
|
||||
maybeSuggestBinding: (
|
||||
element: NonDeleted<ExcalidrawLinearElement>,
|
||||
pointSceneCoords: { x: number; y: number }[],
|
||||
pointSceneCoords: GlobalPoint[],
|
||||
) => void,
|
||||
linearElementEditor: LinearElementEditor,
|
||||
scene: Scene,
|
||||
|
@ -287,7 +287,7 @@ export class LinearElementEditor {
|
|||
element,
|
||||
elementsMap,
|
||||
referencePoint,
|
||||
point(scenePointerX, scenePointerY),
|
||||
scenePointer,
|
||||
event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
|
||||
);
|
||||
|
||||
|
@ -309,8 +309,7 @@ export class LinearElementEditor {
|
|||
const newDraggingPointPosition = LinearElementEditor.createPointAt(
|
||||
element,
|
||||
elementsMap,
|
||||
scenePointerX - linearElementEditor.pointerOffset.x,
|
||||
scenePointerY - linearElementEditor.pointerOffset.y,
|
||||
pointSubtract(scenePointer, linearElementEditor.pointerOffset),
|
||||
event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
|
||||
);
|
||||
|
||||
|
@ -325,8 +324,10 @@ export class LinearElementEditor {
|
|||
? LinearElementEditor.createPointAt(
|
||||
element,
|
||||
elementsMap,
|
||||
scenePointerX - linearElementEditor.pointerOffset.x,
|
||||
scenePointerY - linearElementEditor.pointerOffset.y,
|
||||
pointSubtract(
|
||||
scenePointer,
|
||||
linearElementEditor.pointerOffset,
|
||||
),
|
||||
event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
|
||||
)
|
||||
: point(
|
||||
|
@ -350,17 +351,15 @@ export class LinearElementEditor {
|
|||
|
||||
// suggest bindings for first and last point if selected
|
||||
if (isBindingElement(element, false)) {
|
||||
const coords: { x: number; y: number }[] = [];
|
||||
const coords: GlobalPoint[] = [];
|
||||
|
||||
const firstSelectedIndex = selectedPointsIndices[0];
|
||||
if (firstSelectedIndex === 0) {
|
||||
coords.push(
|
||||
tupleToCoors(
|
||||
LinearElementEditor.getPointGlobalCoordinates(
|
||||
element,
|
||||
element.points[0],
|
||||
elementsMap,
|
||||
),
|
||||
LinearElementEditor.getPointGlobalCoordinates(
|
||||
element,
|
||||
element.points[0],
|
||||
elementsMap,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -369,12 +368,10 @@ export class LinearElementEditor {
|
|||
selectedPointsIndices[selectedPointsIndices.length - 1];
|
||||
if (lastSelectedIndex === element.points.length - 1) {
|
||||
coords.push(
|
||||
tupleToCoors(
|
||||
LinearElementEditor.getPointGlobalCoordinates(
|
||||
element,
|
||||
element.points[lastSelectedIndex],
|
||||
elementsMap,
|
||||
),
|
||||
LinearElementEditor.getPointGlobalCoordinates(
|
||||
element,
|
||||
element.points[lastSelectedIndex],
|
||||
elementsMap,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -439,12 +436,10 @@ export class LinearElementEditor {
|
|||
|
||||
const bindingElement = isBindingEnabled(appState)
|
||||
? getHoveredElementForBinding(
|
||||
tupleToCoors(
|
||||
LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||
element,
|
||||
selectedPoint!,
|
||||
elementsMap,
|
||||
),
|
||||
LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||
element,
|
||||
selectedPoint!,
|
||||
elementsMap,
|
||||
),
|
||||
elements,
|
||||
elementsMap,
|
||||
|
@ -481,7 +476,7 @@ export class LinearElementEditor {
|
|||
? [pointerDownState.lastClickedPoint]
|
||||
: selectedPointsIndices,
|
||||
isDragging: false,
|
||||
pointerOffset: { x: 0, y: 0 },
|
||||
pointerOffset: point(0, 0),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -556,7 +551,7 @@ export class LinearElementEditor {
|
|||
|
||||
static getSegmentMidpointHitCoords = (
|
||||
linearElementEditor: LinearElementEditor,
|
||||
scenePointer: { x: number; y: number },
|
||||
scenePointer: GlobalPoint,
|
||||
appState: AppState,
|
||||
elementsMap: ElementsMap,
|
||||
): GlobalPoint | null => {
|
||||
|
@ -569,8 +564,7 @@ export class LinearElementEditor {
|
|||
element,
|
||||
elementsMap,
|
||||
appState.zoom,
|
||||
scenePointer.x,
|
||||
scenePointer.y,
|
||||
scenePointer,
|
||||
);
|
||||
if (clickedPointIndex >= 0) {
|
||||
return null;
|
||||
|
@ -594,7 +588,7 @@ export class LinearElementEditor {
|
|||
existingSegmentMidpointHitCoords[0],
|
||||
existingSegmentMidpointHitCoords[1],
|
||||
),
|
||||
point(scenePointer.x, scenePointer.y),
|
||||
scenePointer,
|
||||
);
|
||||
if (distance <= threshold) {
|
||||
return existingSegmentMidpointHitCoords;
|
||||
|
@ -607,7 +601,7 @@ export class LinearElementEditor {
|
|||
if (midPoints[index] !== null) {
|
||||
const distance = pointDistance(
|
||||
point(midPoints[index]![0], midPoints[index]![1]),
|
||||
point(scenePointer.x, scenePointer.y),
|
||||
scenePointer,
|
||||
);
|
||||
if (distance <= threshold) {
|
||||
return midPoints[index];
|
||||
|
@ -705,7 +699,7 @@ export class LinearElementEditor {
|
|||
event: React.PointerEvent<HTMLElement>,
|
||||
app: AppClassProperties,
|
||||
store: Store,
|
||||
scenePointer: { x: number; y: number },
|
||||
scenePointer: GlobalPoint,
|
||||
linearElementEditor: LinearElementEditor,
|
||||
scene: Scene,
|
||||
): {
|
||||
|
@ -759,8 +753,7 @@ export class LinearElementEditor {
|
|||
LinearElementEditor.createPointAt(
|
||||
element,
|
||||
elementsMap,
|
||||
scenePointer.x,
|
||||
scenePointer.y,
|
||||
scenePointer,
|
||||
event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
|
||||
),
|
||||
],
|
||||
|
@ -774,7 +767,7 @@ export class LinearElementEditor {
|
|||
prevSelectedPointsIndices: linearElementEditor.selectedPointsIndices,
|
||||
lastClickedPoint: -1,
|
||||
lastClickedIsEndPoint: false,
|
||||
origin: { x: scenePointer.x, y: scenePointer.y },
|
||||
origin: scenePointer,
|
||||
segmentMidpoint: {
|
||||
value: segmentMidpoint,
|
||||
index: segmentMidpointIndex,
|
||||
|
@ -798,8 +791,7 @@ export class LinearElementEditor {
|
|||
element,
|
||||
elementsMap,
|
||||
appState.zoom,
|
||||
scenePointer.x,
|
||||
scenePointer.y,
|
||||
scenePointer,
|
||||
);
|
||||
// if we clicked on a point, set the element as hitElement otherwise
|
||||
// it would get deselected if the point is outside the hitbox area
|
||||
|
@ -828,7 +820,7 @@ export class LinearElementEditor {
|
|||
const cy = (y1 + y2) / 2;
|
||||
const targetPoint =
|
||||
clickedPointIndex > -1 &&
|
||||
pointRotateRads(
|
||||
pointRotateRads<GlobalPoint>(
|
||||
point(
|
||||
element.x + element.points[clickedPointIndex][0],
|
||||
element.y + element.points[clickedPointIndex][1],
|
||||
|
@ -853,7 +845,7 @@ export class LinearElementEditor {
|
|||
prevSelectedPointsIndices: linearElementEditor.selectedPointsIndices,
|
||||
lastClickedPoint: clickedPointIndex,
|
||||
lastClickedIsEndPoint: clickedPointIndex === element.points.length - 1,
|
||||
origin: { x: scenePointer.x, y: scenePointer.y },
|
||||
origin: scenePointer,
|
||||
segmentMidpoint: {
|
||||
value: segmentMidpoint,
|
||||
index: segmentMidpointIndex,
|
||||
|
@ -862,11 +854,8 @@ export class LinearElementEditor {
|
|||
},
|
||||
selectedPointsIndices: nextSelectedPointsIndices,
|
||||
pointerOffset: targetPoint
|
||||
? {
|
||||
x: scenePointer.x - targetPoint[0],
|
||||
y: scenePointer.y - targetPoint[1],
|
||||
}
|
||||
: { x: 0, y: 0 },
|
||||
? pointSubtract(scenePointer, targetPoint)
|
||||
: point(0, 0),
|
||||
};
|
||||
|
||||
return ret;
|
||||
|
@ -887,8 +876,7 @@ export class LinearElementEditor {
|
|||
|
||||
static handlePointerMove(
|
||||
event: React.PointerEvent<HTMLCanvasElement>,
|
||||
scenePointerX: number,
|
||||
scenePointerY: number,
|
||||
scenePointer: GlobalPoint,
|
||||
app: AppClassProperties,
|
||||
elementsMap: NonDeletedSceneElementsMap | SceneElementsMap,
|
||||
): LinearElementEditor | null {
|
||||
|
@ -928,7 +916,7 @@ export class LinearElementEditor {
|
|||
element,
|
||||
elementsMap,
|
||||
lastCommittedPoint,
|
||||
point(scenePointerX, scenePointerY),
|
||||
scenePointer,
|
||||
event[KEYS.CTRL_OR_CMD] ? null : app.getEffectiveGridSize(),
|
||||
);
|
||||
|
||||
|
@ -940,8 +928,10 @@ export class LinearElementEditor {
|
|||
newPoint = LinearElementEditor.createPointAt(
|
||||
element,
|
||||
elementsMap,
|
||||
scenePointerX - appState.editingLinearElement.pointerOffset.x,
|
||||
scenePointerY - appState.editingLinearElement.pointerOffset.y,
|
||||
pointSubtract(
|
||||
scenePointer,
|
||||
appState.editingLinearElement.pointerOffset,
|
||||
),
|
||||
event[KEYS.CTRL_OR_CMD] || isElbowArrow(element)
|
||||
? null
|
||||
: app.getEffectiveGridSize(),
|
||||
|
@ -1057,8 +1047,7 @@ export class LinearElementEditor {
|
|||
element: NonDeleted<ExcalidrawLinearElement>,
|
||||
elementsMap: ElementsMap,
|
||||
zoom: AppState["zoom"],
|
||||
x: number,
|
||||
y: number,
|
||||
p: GlobalPoint,
|
||||
) {
|
||||
const pointHandles = LinearElementEditor.getPointsGlobalCoordinates(
|
||||
element,
|
||||
|
@ -1069,9 +1058,9 @@ export class LinearElementEditor {
|
|||
// points on the left, thus should take precedence when clicking, if they
|
||||
// overlap
|
||||
while (--idx > -1) {
|
||||
const p = pointHandles[idx];
|
||||
const handles = pointHandles[idx];
|
||||
if (
|
||||
pointDistance(point(x, y), point(p[0], p[1])) * zoom.value <
|
||||
pointDistance(p, pointFromPair(handles)) * zoom.value <
|
||||
// +1px to account for outline stroke
|
||||
LinearElementEditor.POINT_HANDLE_SIZE + 1
|
||||
) {
|
||||
|
@ -1084,11 +1073,14 @@ export class LinearElementEditor {
|
|||
static createPointAt(
|
||||
element: NonDeleted<ExcalidrawLinearElement>,
|
||||
elementsMap: ElementsMap,
|
||||
scenePointerX: number,
|
||||
scenePointerY: number,
|
||||
scenePointer: GlobalPoint,
|
||||
gridSize: NullableGridSize,
|
||||
): LocalPoint {
|
||||
const pointerOnGrid = getGridPoint(scenePointerX, scenePointerY, gridSize);
|
||||
const pointerOnGrid = getGridPoint(
|
||||
scenePointer[0],
|
||||
scenePointer[1],
|
||||
gridSize,
|
||||
);
|
||||
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
|
||||
const cx = (x1 + x2) / 2;
|
||||
const cy = (y1 + y2) / 2;
|
||||
|
@ -1337,7 +1329,7 @@ export class LinearElementEditor {
|
|||
|
||||
static shouldAddMidpoint(
|
||||
linearElementEditor: LinearElementEditor,
|
||||
pointerCoords: PointerCoords,
|
||||
pointerCoords: GlobalPoint,
|
||||
appState: AppState,
|
||||
elementsMap: ElementsMap,
|
||||
) {
|
||||
|
@ -1367,10 +1359,7 @@ export class LinearElementEditor {
|
|||
}
|
||||
|
||||
const origin = linearElementEditor.pointerDownState.origin!;
|
||||
const dist = pointDistance(
|
||||
point(origin.x, origin.y),
|
||||
point(pointerCoords.x, pointerCoords.y),
|
||||
);
|
||||
const dist = pointDistance(origin, pointerCoords);
|
||||
if (
|
||||
!appState.editingLinearElement &&
|
||||
dist < DRAGGING_THRESHOLD / appState.zoom.value
|
||||
|
@ -1382,7 +1371,7 @@ export class LinearElementEditor {
|
|||
|
||||
static addMidpoint(
|
||||
linearElementEditor: LinearElementEditor,
|
||||
pointerCoords: PointerCoords,
|
||||
pointerCoords: GlobalPoint,
|
||||
app: AppClassProperties,
|
||||
snapToGrid: boolean,
|
||||
elementsMap: ElementsMap,
|
||||
|
@ -1406,8 +1395,7 @@ export class LinearElementEditor {
|
|||
const midpoint = LinearElementEditor.createPointAt(
|
||||
element,
|
||||
elementsMap,
|
||||
pointerCoords.x,
|
||||
pointerCoords.y,
|
||||
pointerCoords,
|
||||
snapToGrid && !isElbowArrow(element) ? app.getEffectiveGridSize() : null,
|
||||
);
|
||||
const points = [
|
||||
|
|
|
@ -1089,8 +1089,7 @@ export const getResizeOffsetXY = (
|
|||
transformHandleType: MaybeTransformHandleType,
|
||||
selectedElements: NonDeletedExcalidrawElement[],
|
||||
elementsMap: ElementsMap,
|
||||
x: number,
|
||||
y: number,
|
||||
[x, y]: GlobalPoint,
|
||||
): [number, number] => {
|
||||
const [x1, y1, x2, y2] =
|
||||
selectedElements.length === 1
|
||||
|
|
|
@ -92,7 +92,7 @@ export const resizeTest = <Point extends GlobalPoint | LocalPoint>(
|
|||
if (!(isLinearElement(element) && element.points.length <= 2)) {
|
||||
const SPACING = SIDE_RESIZING_THRESHOLD / zoom.value;
|
||||
const sides = getSelectionBorders(
|
||||
point(x1 - SPACING, y1 - SPACING),
|
||||
point<Point>(x1 - SPACING, y1 - SPACING),
|
||||
point(x2 + SPACING, y2 + SPACING),
|
||||
point(cx, cy),
|
||||
element.angle,
|
||||
|
@ -101,7 +101,11 @@ export const resizeTest = <Point extends GlobalPoint | LocalPoint>(
|
|||
for (const [dir, side] of Object.entries(sides)) {
|
||||
// test to see if x, y are on the line segment
|
||||
if (
|
||||
pointOnLineSegment(point(x, y), side as LineSegment<Point>, SPACING)
|
||||
pointOnLineSegment(
|
||||
point<Point>(x, y),
|
||||
side as LineSegment<Point>,
|
||||
SPACING,
|
||||
)
|
||||
) {
|
||||
return dir as TransformHandleType;
|
||||
}
|
||||
|
@ -115,8 +119,7 @@ export const resizeTest = <Point extends GlobalPoint | LocalPoint>(
|
|||
export const getElementWithTransformHandleType = (
|
||||
elements: readonly NonDeletedExcalidrawElement[],
|
||||
appState: AppState,
|
||||
scenePointerX: number,
|
||||
scenePointerY: number,
|
||||
scenePointer: GlobalPoint,
|
||||
zoom: Zoom,
|
||||
pointerType: PointerType,
|
||||
elementsMap: ElementsMap,
|
||||
|
@ -130,8 +133,8 @@ export const getElementWithTransformHandleType = (
|
|||
element,
|
||||
elementsMap,
|
||||
appState,
|
||||
scenePointerX,
|
||||
scenePointerY,
|
||||
scenePointer[0],
|
||||
scenePointer[1],
|
||||
zoom,
|
||||
pointerType,
|
||||
device,
|
||||
|
@ -140,12 +143,9 @@ export const getElementWithTransformHandleType = (
|
|||
}, null as { element: NonDeletedExcalidrawElement; transformHandleType: MaybeTransformHandleType } | null);
|
||||
};
|
||||
|
||||
export const getTransformHandleTypeFromCoords = <
|
||||
Point extends GlobalPoint | LocalPoint,
|
||||
>(
|
||||
export const getTransformHandleTypeFromCoords = (
|
||||
[x1, y1, x2, y2]: Bounds,
|
||||
scenePointerX: number,
|
||||
scenePointerY: number,
|
||||
scenePointer: GlobalPoint,
|
||||
zoom: Zoom,
|
||||
pointerType: PointerType,
|
||||
device: Device,
|
||||
|
@ -163,7 +163,7 @@ export const getTransformHandleTypeFromCoords = <
|
|||
transformHandles[key as Exclude<TransformHandleType, "rotation">]!;
|
||||
return (
|
||||
transformHandle &&
|
||||
isInsideTransformHandle(transformHandle, scenePointerX, scenePointerY)
|
||||
isInsideTransformHandle(transformHandle, scenePointer[0], scenePointer[1])
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -178,7 +178,7 @@ export const getTransformHandleTypeFromCoords = <
|
|||
const SPACING = SIDE_RESIZING_THRESHOLD / zoom.value;
|
||||
|
||||
const sides = getSelectionBorders(
|
||||
point(x1 - SPACING, y1 - SPACING),
|
||||
point<GlobalPoint>(x1 - SPACING, y1 - SPACING),
|
||||
point(x2 + SPACING, y2 + SPACING),
|
||||
point(cx, cy),
|
||||
0 as Radians,
|
||||
|
@ -188,8 +188,8 @@ export const getTransformHandleTypeFromCoords = <
|
|||
// test to see if x, y are on the line segment
|
||||
if (
|
||||
pointOnLineSegment(
|
||||
point(scenePointerX, scenePointerY),
|
||||
side as LineSegment<Point>,
|
||||
scenePointer,
|
||||
side as LineSegment<GlobalPoint>,
|
||||
SPACING,
|
||||
)
|
||||
) {
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
import BinaryHeap from "../binaryheap";
|
||||
import { getSizeFromPoints } from "../points";
|
||||
import { aabbForElement, pointInsideBounds } from "../shapes";
|
||||
import { isAnyTrue, toBrandedType, tupleToCoors } from "../utils";
|
||||
import { isAnyTrue, toBrandedType } from "../utils";
|
||||
import {
|
||||
bindPointToSnapToElementOutline,
|
||||
distanceToBindableElement,
|
||||
|
@ -1081,13 +1081,13 @@ const getHoveredElements = (
|
|||
const elements = Array.from(elementsMap.values());
|
||||
return [
|
||||
getHoveredElementForBinding(
|
||||
tupleToCoors(origStartGlobalPoint),
|
||||
origStartGlobalPoint,
|
||||
elements,
|
||||
nonDeletedSceneElementsMap,
|
||||
true,
|
||||
),
|
||||
getHoveredElementForBinding(
|
||||
tupleToCoors(origEndGlobalPoint),
|
||||
origEndGlobalPoint,
|
||||
elements,
|
||||
nonDeletedSceneElementsMap,
|
||||
true,
|
||||
|
|
|
@ -5,6 +5,7 @@ import { SHIFT_LOCKING_ANGLE } from "../constants";
|
|||
import type { AppState, Offsets, Zoom } from "../types";
|
||||
import { getCommonBounds, getElementBounds } from "./bounds";
|
||||
import { viewportCoordsToSceneCoords } from "../utils";
|
||||
import { point } from "../../math";
|
||||
|
||||
// TODO: remove invisible elements consistently actions, so that invisible elements are not recorded by the store, exported, broadcasted or persisted
|
||||
// - perhaps could be as part of a standalone 'cleanup' action, in addition to 'finalize'
|
||||
|
@ -33,25 +34,22 @@ export const isElementInViewport = (
|
|||
) => {
|
||||
const [x1, y1, x2, y2] = getElementBounds(element, elementsMap); // scene coordinates
|
||||
const topLeftSceneCoords = viewportCoordsToSceneCoords(
|
||||
{
|
||||
clientX: viewTransformations.offsetLeft,
|
||||
clientY: viewTransformations.offsetTop,
|
||||
},
|
||||
point(viewTransformations.offsetLeft, viewTransformations.offsetTop),
|
||||
viewTransformations,
|
||||
);
|
||||
const bottomRightSceneCoords = viewportCoordsToSceneCoords(
|
||||
{
|
||||
clientX: viewTransformations.offsetLeft + width,
|
||||
clientY: viewTransformations.offsetTop + height,
|
||||
},
|
||||
point(
|
||||
viewTransformations.offsetLeft + width,
|
||||
viewTransformations.offsetTop + height,
|
||||
),
|
||||
viewTransformations,
|
||||
);
|
||||
|
||||
return (
|
||||
topLeftSceneCoords.x <= x2 &&
|
||||
topLeftSceneCoords.y <= y2 &&
|
||||
bottomRightSceneCoords.x >= x1 &&
|
||||
bottomRightSceneCoords.y >= y1
|
||||
topLeftSceneCoords[0] <= x2 &&
|
||||
topLeftSceneCoords[1] <= y2 &&
|
||||
bottomRightSceneCoords[0] >= x1 &&
|
||||
bottomRightSceneCoords[1] >= y1
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -71,25 +69,25 @@ export const isElementCompletelyInViewport = (
|
|||
) => {
|
||||
const [x1, y1, x2, y2] = getCommonBounds(elements, elementsMap); // scene coordinates
|
||||
const topLeftSceneCoords = viewportCoordsToSceneCoords(
|
||||
{
|
||||
clientX: viewTransformations.offsetLeft + (padding?.left || 0),
|
||||
clientY: viewTransformations.offsetTop + (padding?.top || 0),
|
||||
},
|
||||
point(
|
||||
viewTransformations.offsetLeft + (padding?.left || 0),
|
||||
viewTransformations.offsetTop + (padding?.top || 0),
|
||||
),
|
||||
viewTransformations,
|
||||
);
|
||||
const bottomRightSceneCoords = viewportCoordsToSceneCoords(
|
||||
{
|
||||
clientX: viewTransformations.offsetLeft + width - (padding?.right || 0),
|
||||
clientY: viewTransformations.offsetTop + height - (padding?.bottom || 0),
|
||||
},
|
||||
point(
|
||||
viewTransformations.offsetLeft + width - (padding?.right || 0),
|
||||
viewTransformations.offsetTop + height - (padding?.bottom || 0),
|
||||
),
|
||||
viewTransformations,
|
||||
);
|
||||
|
||||
return (
|
||||
x1 >= topLeftSceneCoords.x &&
|
||||
y1 >= topLeftSceneCoords.y &&
|
||||
x2 <= bottomRightSceneCoords.x &&
|
||||
y2 <= bottomRightSceneCoords.y
|
||||
x1 >= topLeftSceneCoords[0] &&
|
||||
y1 >= topLeftSceneCoords[1] &&
|
||||
x2 <= bottomRightSceneCoords[0] &&
|
||||
y2 <= bottomRightSceneCoords[1]
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ import {
|
|||
updateOriginalContainerCache,
|
||||
} from "./containerCache";
|
||||
import type { ExtractSetType } from "../utility-types";
|
||||
import type { GlobalPoint } from "../../math";
|
||||
import { point } from "../../math";
|
||||
|
||||
export const normalizeText = (text: string) => {
|
||||
return (
|
||||
|
@ -674,12 +676,12 @@ export const getContainerCenter = (
|
|||
container: ExcalidrawElement,
|
||||
appState: AppState,
|
||||
elementsMap: ElementsMap,
|
||||
) => {
|
||||
): GlobalPoint => {
|
||||
if (!isArrowElement(container)) {
|
||||
return {
|
||||
x: container.x + container.width / 2,
|
||||
y: container.y + container.height / 2,
|
||||
};
|
||||
return point(
|
||||
container.x + container.width / 2,
|
||||
container.y + container.height / 2,
|
||||
);
|
||||
}
|
||||
const points = LinearElementEditor.getPointsGlobalCoordinates(
|
||||
container,
|
||||
|
@ -692,7 +694,7 @@ export const getContainerCenter = (
|
|||
container.points[index],
|
||||
elementsMap,
|
||||
);
|
||||
return { x: midPoint[0], y: midPoint[1] };
|
||||
return point(midPoint[0], midPoint[1]);
|
||||
}
|
||||
const index = container.points.length / 2 - 1;
|
||||
let midSegmentMidpoint = LinearElementEditor.getEditorMidPoints(
|
||||
|
@ -709,7 +711,7 @@ export const getContainerCenter = (
|
|||
elementsMap,
|
||||
);
|
||||
}
|
||||
return { x: midSegmentMidpoint[0], y: midSegmentMidpoint[1] };
|
||||
return point(midSegmentMidpoint[0], midSegmentMidpoint[1]);
|
||||
};
|
||||
|
||||
export const getContainerCoords = (container: NonDeletedExcalidrawElement) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue