fix: remove scene from getElementAbsoluteCoords and dependent functions and use elementsMap (#7663)

* fix: remove scene from getElementAbsoluteCoords and dependent functions and use elementsMap

* lint

* fix

* use non deleted elements where possible

* use non deleted elements map in actions

* pass elementsMap instead of array to elementOverlapsWithFrame

* lint

* fix

* pass elementsMap to getElementsCorners

* pass elementsMap to getEligibleElementsForBinding

* pass elementsMap in bindOrUnbindSelectedElements and unbindLinearElements

* pass elementsMap in elementsAreInFrameBounds,elementOverlapsWithFrame,isCursorInFrame,getElementsInResizingFrame

* pass elementsMap in getElementsWithinSelection, getElementsCompletelyInFrame, isElementContainingFrame, getElementsInNewFrame

* pass elementsMap to getElementWithTransformHandleType

* pass elementsMap to getVisibleGaps, getMaximumGroups,getReferenceSnapPoints,snapDraggedElements

* lint

* pass elementsMap to bindTextToShapeAfterDuplication,bindLinearElementToElement,getTextBindableContainerAtPosition

* revert changes for bindTextToShapeAfterDuplication
This commit is contained in:
Aakansha Doshi
2024-02-16 11:35:01 +05:30
committed by GitHub
parent 73bf50e8a8
commit 47f87f4ecb
36 changed files with 779 additions and 270 deletions

View File

@@ -91,6 +91,7 @@ export const hitTest = (
) {
return isPointHittingElementBoundingBox(
element,
elementsMap,
point,
threshold,
frameNameBoundsCache,
@@ -116,6 +117,7 @@ export const hitTest = (
appState,
frameNameBoundsCache,
point,
elementsMap,
);
};
@@ -145,9 +147,11 @@ export const isHittingElementBoundingBoxWithoutHittingElement = (
appState,
frameNameBoundsCache,
[x, y],
elementsMap,
) &&
isPointHittingElementBoundingBox(
element,
elementsMap,
[x, y],
threshold,
frameNameBoundsCache,
@@ -160,6 +164,7 @@ export const isHittingElementNotConsideringBoundingBox = (
appState: AppState,
frameNameBoundsCache: FrameNameBoundsCache | null,
point: Point,
elementsMap: ElementsMap,
): boolean => {
const threshold = 10 / appState.zoom.value;
const check = isTextElement(element)
@@ -169,6 +174,7 @@ export const isHittingElementNotConsideringBoundingBox = (
: isNearCheck;
return hitTestPointAgainstElement({
element,
elementsMap,
point,
threshold,
check,
@@ -183,6 +189,7 @@ const isElementSelected = (
export const isPointHittingElementBoundingBox = (
element: NonDeleted<ExcalidrawElement>,
elementsMap: ElementsMap,
[x, y]: Point,
threshold: number,
frameNameBoundsCache: FrameNameBoundsCache | null,
@@ -194,6 +201,7 @@ export const isPointHittingElementBoundingBox = (
if (isFrameLikeElement(element)) {
return hitTestPointAgainstElement({
element,
elementsMap,
point: [x, y],
threshold,
check: isInsideCheck,
@@ -201,7 +209,7 @@ export const isPointHittingElementBoundingBox = (
});
}
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
const elementCenterX = (x1 + x2) / 2;
const elementCenterY = (y1 + y2) / 2;
// reverse rotate to take element's angle into account.
@@ -224,12 +232,14 @@ export const isPointHittingElementBoundingBox = (
export const bindingBorderTest = (
element: NonDeleted<ExcalidrawBindableElement>,
{ x, y }: { x: number; y: number },
elementsMap: ElementsMap,
): boolean => {
const threshold = maxBindingGap(element, element.width, element.height);
const check = isOutsideCheck;
const point: Point = [x, y];
return hitTestPointAgainstElement({
element,
elementsMap,
point,
threshold,
check,
@@ -251,6 +261,7 @@ export const maxBindingGap = (
type HitTestArgs = {
element: NonDeletedExcalidrawElement;
elementsMap: ElementsMap;
point: Point;
threshold: number;
check: (distance: number, threshold: number) => boolean;
@@ -266,19 +277,28 @@ const hitTestPointAgainstElement = (args: HitTestArgs): boolean => {
case "text":
case "diamond":
case "ellipse":
const distance = distanceToBindableElement(args.element, args.point);
const distance = distanceToBindableElement(
args.element,
args.point,
args.elementsMap,
);
return args.check(distance, args.threshold);
case "freedraw": {
if (
!args.check(
distanceToRectangle(args.element, args.point),
distanceToRectangle(args.element, args.point, args.elementsMap),
args.threshold,
)
) {
return false;
}
return hitTestFreeDrawElement(args.element, args.point, args.threshold);
return hitTestFreeDrawElement(
args.element,
args.point,
args.threshold,
args.elementsMap,
);
}
case "arrow":
case "line":
@@ -293,7 +313,7 @@ const hitTestPointAgainstElement = (args: HitTestArgs): boolean => {
// check distance to frame element first
if (
args.check(
distanceToBindableElement(args.element, args.point),
distanceToBindableElement(args.element, args.point, args.elementsMap),
args.threshold,
)
) {
@@ -316,6 +336,7 @@ const hitTestPointAgainstElement = (args: HitTestArgs): boolean => {
export const distanceToBindableElement = (
element: ExcalidrawBindableElement,
point: Point,
elementsMap: ElementsMap,
): number => {
switch (element.type) {
case "rectangle":
@@ -325,11 +346,11 @@ export const distanceToBindableElement = (
case "embeddable":
case "frame":
case "magicframe":
return distanceToRectangle(element, point);
return distanceToRectangle(element, point, elementsMap);
case "diamond":
return distanceToDiamond(element, point);
return distanceToDiamond(element, point, elementsMap);
case "ellipse":
return distanceToEllipse(element, point);
return distanceToEllipse(element, point, elementsMap);
}
};
@@ -358,8 +379,13 @@ const distanceToRectangle = (
| ExcalidrawIframeLikeElement
| ExcalidrawFrameLikeElement,
point: Point,
elementsMap: ElementsMap,
): number => {
const [, pointRel, hwidth, hheight] = pointRelativeToElement(element, point);
const [, pointRel, hwidth, hheight] = pointRelativeToElement(
element,
point,
elementsMap,
);
return Math.max(
GAPoint.distanceToLine(pointRel, GALine.equation(0, 1, -hheight)),
GAPoint.distanceToLine(pointRel, GALine.equation(1, 0, -hwidth)),
@@ -377,8 +403,13 @@ const distanceToRectangleBox = (box: RectangleBox, point: Point): number => {
const distanceToDiamond = (
element: ExcalidrawDiamondElement,
point: Point,
elementsMap: ElementsMap,
): number => {
const [, pointRel, hwidth, hheight] = pointRelativeToElement(element, point);
const [, pointRel, hwidth, hheight] = pointRelativeToElement(
element,
point,
elementsMap,
);
const side = GALine.equation(hheight, hwidth, -hheight * hwidth);
return GAPoint.distanceToLine(pointRel, side);
};
@@ -386,16 +417,22 @@ const distanceToDiamond = (
const distanceToEllipse = (
element: ExcalidrawEllipseElement,
point: Point,
elementsMap: ElementsMap,
): number => {
const [pointRel, tangent] = ellipseParamsForTest(element, point);
const [pointRel, tangent] = ellipseParamsForTest(element, point, elementsMap);
return -GALine.sign(tangent) * GAPoint.distanceToLine(pointRel, tangent);
};
const ellipseParamsForTest = (
element: ExcalidrawEllipseElement,
point: Point,
elementsMap: ElementsMap,
): [GA.Point, GA.Line] => {
const [, pointRel, hwidth, hheight] = pointRelativeToElement(element, point);
const [, pointRel, hwidth, hheight] = pointRelativeToElement(
element,
point,
elementsMap,
);
const [px, py] = GAPoint.toTuple(pointRel);
// We're working in positive quadrant, so start with `t = 45deg`, `tx=cos(t)`
@@ -440,6 +477,7 @@ const hitTestFreeDrawElement = (
element: ExcalidrawFreeDrawElement,
point: Point,
threshold: number,
elementsMap: ElementsMap,
): boolean => {
// Check point-distance-to-line-segment for every segment in the
// element's points (its input points, not its outline points).
@@ -454,7 +492,10 @@ const hitTestFreeDrawElement = (
y = point[1] - element.y;
} else {
// Counter-rotate the point around center before testing
const [minX, minY, maxX, maxY] = getElementAbsoluteCoords(element);
const [minX, minY, maxX, maxY] = getElementAbsoluteCoords(
element,
elementsMap,
);
const rotatedPoint = rotatePoint(
point,
[minX + (maxX - minX) / 2, minY + (maxY - minY) / 2],
@@ -520,6 +561,7 @@ const hitTestLinear = (args: HitTestArgs): boolean => {
const [point, pointAbs, hwidth, hheight] = pointRelativeToElement(
args.element,
args.point,
args.elementsMap,
);
const side1 = GALine.equation(0, 1, -hheight);
const side2 = GALine.equation(1, 0, -hwidth);
@@ -572,9 +614,10 @@ const hitTestLinear = (args: HitTestArgs): boolean => {
const pointRelativeToElement = (
element: ExcalidrawElement,
pointTuple: Point,
elementsMap: ElementsMap,
): [GA.Point, GA.Point, number, number] => {
const point = GAPoint.from(pointTuple);
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
const center = coordsCenter(x1, y1, x2, y2);
// GA has angle orientation opposite to `rotate`
const rotate = GATransform.rotation(center, element.angle);
@@ -609,11 +652,12 @@ const pointRelativeToDivElement = (
// Returns point in absolute coordinates
export const pointInAbsoluteCoords = (
element: ExcalidrawElement,
elementsMap: ElementsMap,
// Point relative to the element position
point: Point,
): Point => {
const [x, y] = point;
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
const cx = (x2 - x1) / 2;
const cy = (y2 - y1) / 2;
const [rotatedX, rotatedY] = rotate(x, y, cx, cy, element.angle);
@@ -622,8 +666,9 @@ export const pointInAbsoluteCoords = (
const relativizationToElementCenter = (
element: ExcalidrawElement,
elementsMap: ElementsMap,
): GA.Transform => {
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
const center = coordsCenter(x1, y1, x2, y2);
// GA has angle orientation opposite to `rotate`
const rotate = GATransform.rotation(center, element.angle);
@@ -649,12 +694,14 @@ const coordsCenter = (
// of the element.
export const determineFocusDistance = (
element: ExcalidrawBindableElement,
// Point on the line, in absolute coordinates
a: Point,
// Another point on the line, in absolute coordinates (closer to element)
b: Point,
elementsMap: ElementsMap,
): number => {
const relateToCenter = relativizationToElementCenter(element);
const relateToCenter = relativizationToElementCenter(element, elementsMap);
const aRel = GATransform.apply(relateToCenter, GAPoint.from(a));
const bRel = GATransform.apply(relateToCenter, GAPoint.from(b));
const line = GALine.through(aRel, bRel);
@@ -693,13 +740,14 @@ export const determineFocusPoint = (
// returned focusPoint
focus: number,
adjecentPoint: Point,
elementsMap: ElementsMap,
): Point => {
if (focus === 0) {
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
const [x1, y1, x2, y2] = getElementAbsoluteCoords(element, elementsMap);
const center = coordsCenter(x1, y1, x2, y2);
return GAPoint.toTuple(center);
}
const relateToCenter = relativizationToElementCenter(element);
const relateToCenter = relativizationToElementCenter(element, elementsMap);
const adjecentPointRel = GATransform.apply(
relateToCenter,
GAPoint.from(adjecentPoint),
@@ -728,14 +776,16 @@ export const determineFocusPoint = (
// and the `element`, in ascending order of distance from `a`.
export const intersectElementWithLine = (
element: ExcalidrawBindableElement,
// Point on the line, in absolute coordinates
a: Point,
// Another point on the line, in absolute coordinates
b: Point,
// If given, the element is inflated by this value
gap: number = 0,
elementsMap: ElementsMap,
): Point[] => {
const relateToCenter = relativizationToElementCenter(element);
const relateToCenter = relativizationToElementCenter(element, elementsMap);
const aRel = GATransform.apply(relateToCenter, GAPoint.from(a));
const bRel = GATransform.apply(relateToCenter, GAPoint.from(b));
const line = GALine.through(aRel, bRel);