|
|
@ -13,6 +13,7 @@ import type {
|
|
|
|
ExcalidrawElbowArrowElement,
|
|
|
|
ExcalidrawElbowArrowElement,
|
|
|
|
FixedPoint,
|
|
|
|
FixedPoint,
|
|
|
|
SceneElementsMap,
|
|
|
|
SceneElementsMap,
|
|
|
|
|
|
|
|
FixedPointBinding,
|
|
|
|
} from "./types";
|
|
|
|
} from "./types";
|
|
|
|
|
|
|
|
|
|
|
|
import type { Bounds } from "./bounds";
|
|
|
|
import type { Bounds } from "./bounds";
|
|
|
@ -64,18 +65,13 @@ import {
|
|
|
|
line,
|
|
|
|
line,
|
|
|
|
linesIntersectAt,
|
|
|
|
linesIntersectAt,
|
|
|
|
pointDistance,
|
|
|
|
pointDistance,
|
|
|
|
pointOnLineSegment,
|
|
|
|
|
|
|
|
pointFromVector,
|
|
|
|
pointFromVector,
|
|
|
|
vectorScale,
|
|
|
|
vectorScale,
|
|
|
|
vectorNormalize,
|
|
|
|
vectorNormalize,
|
|
|
|
vectorRotate,
|
|
|
|
vectorCross,
|
|
|
|
lineClosestPoint,
|
|
|
|
|
|
|
|
vectorDot,
|
|
|
|
|
|
|
|
} from "../../math";
|
|
|
|
} from "../../math";
|
|
|
|
import { intersectElementWithLineSegment } from "./collision";
|
|
|
|
import { intersectElementWithLineSegment } from "./collision";
|
|
|
|
import { distanceToBindableElement } from "./distance";
|
|
|
|
import { distanceToBindableElement } from "./distance";
|
|
|
|
import { debugClear, debugDrawPoint } from "../visualdebug";
|
|
|
|
|
|
|
|
import { ellipse } from "../../math/ellipse";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export type SuggestedBinding =
|
|
|
|
export type SuggestedBinding =
|
|
|
|
| NonDeleted<ExcalidrawBindableElement>
|
|
|
|
| NonDeleted<ExcalidrawBindableElement>
|
|
|
@ -121,7 +117,6 @@ export const bindOrUnbindLinearElement = (
|
|
|
|
endBindingElement: ExcalidrawBindableElement | null | "keep",
|
|
|
|
endBindingElement: ExcalidrawBindableElement | null | "keep",
|
|
|
|
elementsMap: NonDeletedSceneElementsMap,
|
|
|
|
elementsMap: NonDeletedSceneElementsMap,
|
|
|
|
scene: Scene,
|
|
|
|
scene: Scene,
|
|
|
|
zoom?: AppState["zoom"],
|
|
|
|
|
|
|
|
): void => {
|
|
|
|
): void => {
|
|
|
|
const boundToElementIds: Set<ExcalidrawBindableElement["id"]> = new Set();
|
|
|
|
const boundToElementIds: Set<ExcalidrawBindableElement["id"]> = new Set();
|
|
|
|
const unboundFromElementIds: Set<ExcalidrawBindableElement["id"]> = new Set();
|
|
|
|
const unboundFromElementIds: Set<ExcalidrawBindableElement["id"]> = new Set();
|
|
|
@ -133,7 +128,6 @@ export const bindOrUnbindLinearElement = (
|
|
|
|
boundToElementIds,
|
|
|
|
boundToElementIds,
|
|
|
|
unboundFromElementIds,
|
|
|
|
unboundFromElementIds,
|
|
|
|
elementsMap,
|
|
|
|
elementsMap,
|
|
|
|
zoom,
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
bindOrUnbindLinearElementEdge(
|
|
|
|
bindOrUnbindLinearElementEdge(
|
|
|
|
linearElement,
|
|
|
|
linearElement,
|
|
|
@ -143,7 +137,6 @@ export const bindOrUnbindLinearElement = (
|
|
|
|
boundToElementIds,
|
|
|
|
boundToElementIds,
|
|
|
|
unboundFromElementIds,
|
|
|
|
unboundFromElementIds,
|
|
|
|
elementsMap,
|
|
|
|
elementsMap,
|
|
|
|
zoom,
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const onlyUnbound = Array.from(unboundFromElementIds).filter(
|
|
|
|
const onlyUnbound = Array.from(unboundFromElementIds).filter(
|
|
|
@ -170,7 +163,6 @@ const bindOrUnbindLinearElementEdge = (
|
|
|
|
// Is mutated
|
|
|
|
// Is mutated
|
|
|
|
unboundFromElementIds: Set<ExcalidrawBindableElement["id"]>,
|
|
|
|
unboundFromElementIds: Set<ExcalidrawBindableElement["id"]>,
|
|
|
|
elementsMap: NonDeletedSceneElementsMap,
|
|
|
|
elementsMap: NonDeletedSceneElementsMap,
|
|
|
|
zoom?: AppState["zoom"],
|
|
|
|
|
|
|
|
): void => {
|
|
|
|
): void => {
|
|
|
|
// "keep" is for method chaining convenience, a "no-op", so just bail out
|
|
|
|
// "keep" is for method chaining convenience, a "no-op", so just bail out
|
|
|
|
if (bindableElement === "keep") {
|
|
|
|
if (bindableElement === "keep") {
|
|
|
@ -207,18 +199,11 @@ const bindOrUnbindLinearElementEdge = (
|
|
|
|
bindableElement,
|
|
|
|
bindableElement,
|
|
|
|
startOrEnd,
|
|
|
|
startOrEnd,
|
|
|
|
elementsMap,
|
|
|
|
elementsMap,
|
|
|
|
zoom,
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
boundToElementIds.add(bindableElement.id);
|
|
|
|
boundToElementIds.add(bindableElement.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
bindLinearElement(
|
|
|
|
bindLinearElement(linearElement, bindableElement, startOrEnd, elementsMap);
|
|
|
|
linearElement,
|
|
|
|
|
|
|
|
bindableElement,
|
|
|
|
|
|
|
|
startOrEnd,
|
|
|
|
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
zoom,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
boundToElementIds.add(bindableElement.id);
|
|
|
|
boundToElementIds.add(bindableElement.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
@ -380,14 +365,7 @@ export const bindOrUnbindLinearElements = (
|
|
|
|
zoom,
|
|
|
|
zoom,
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
bindOrUnbindLinearElement(
|
|
|
|
bindOrUnbindLinearElement(selectedElement, start, end, elementsMap, scene);
|
|
|
|
selectedElement,
|
|
|
|
|
|
|
|
start,
|
|
|
|
|
|
|
|
end,
|
|
|
|
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
scene,
|
|
|
|
|
|
|
|
zoom,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
@ -436,7 +414,6 @@ export const maybeBindLinearElement = (
|
|
|
|
appState.startBoundElement,
|
|
|
|
appState.startBoundElement,
|
|
|
|
"start",
|
|
|
|
"start",
|
|
|
|
elementsMap,
|
|
|
|
elementsMap,
|
|
|
|
appState.zoom,
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -457,13 +434,7 @@ export const maybeBindLinearElement = (
|
|
|
|
"end",
|
|
|
|
"end",
|
|
|
|
)
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
bindLinearElement(
|
|
|
|
bindLinearElement(linearElement, hoveredElement, "end", elementsMap);
|
|
|
|
linearElement,
|
|
|
|
|
|
|
|
hoveredElement,
|
|
|
|
|
|
|
|
"end",
|
|
|
|
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
appState.zoom,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
@ -493,31 +464,35 @@ export const bindLinearElement = (
|
|
|
|
hoveredElement: ExcalidrawBindableElement,
|
|
|
|
hoveredElement: ExcalidrawBindableElement,
|
|
|
|
startOrEnd: "start" | "end",
|
|
|
|
startOrEnd: "start" | "end",
|
|
|
|
elementsMap: NonDeletedSceneElementsMap,
|
|
|
|
elementsMap: NonDeletedSceneElementsMap,
|
|
|
|
zoom?: AppState["zoom"],
|
|
|
|
|
|
|
|
): void => {
|
|
|
|
): void => {
|
|
|
|
if (!isArrowElement(linearElement)) {
|
|
|
|
if (!isArrowElement(linearElement)) {
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const binding: PointBinding = {
|
|
|
|
|
|
|
|
|
|
|
|
const binding: PointBinding | FixedPointBinding = {
|
|
|
|
elementId: hoveredElement.id,
|
|
|
|
elementId: hoveredElement.id,
|
|
|
|
...normalizePointBinding(
|
|
|
|
|
|
|
|
calculateFocusAndGap(
|
|
|
|
|
|
|
|
linearElement,
|
|
|
|
|
|
|
|
hoveredElement,
|
|
|
|
|
|
|
|
startOrEnd,
|
|
|
|
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
zoom,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
hoveredElement,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
...(isElbowArrow(linearElement)
|
|
|
|
...(isElbowArrow(linearElement)
|
|
|
|
? calculateFixedPointForElbowArrowBinding(
|
|
|
|
? {
|
|
|
|
linearElement,
|
|
|
|
...calculateFixedPointForElbowArrowBinding(
|
|
|
|
hoveredElement,
|
|
|
|
linearElement,
|
|
|
|
startOrEnd,
|
|
|
|
hoveredElement,
|
|
|
|
elementsMap,
|
|
|
|
startOrEnd,
|
|
|
|
)
|
|
|
|
elementsMap,
|
|
|
|
: { fixedPoint: null }),
|
|
|
|
),
|
|
|
|
|
|
|
|
focus: 0,
|
|
|
|
|
|
|
|
gap: 0,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
: {
|
|
|
|
|
|
|
|
...normalizePointBinding(
|
|
|
|
|
|
|
|
calculateFocusAndGap(
|
|
|
|
|
|
|
|
linearElement,
|
|
|
|
|
|
|
|
hoveredElement,
|
|
|
|
|
|
|
|
startOrEnd,
|
|
|
|
|
|
|
|
elementsMap,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
hoveredElement,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
}),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
mutateElement(linearElement, {
|
|
|
|
mutateElement(linearElement, {
|
|
|
@ -713,7 +688,6 @@ const calculateFocusAndGap = (
|
|
|
|
hoveredElement: ExcalidrawBindableElement,
|
|
|
|
hoveredElement: ExcalidrawBindableElement,
|
|
|
|
startOrEnd: "start" | "end",
|
|
|
|
startOrEnd: "start" | "end",
|
|
|
|
elementsMap: NonDeletedSceneElementsMap,
|
|
|
|
elementsMap: NonDeletedSceneElementsMap,
|
|
|
|
zoom?: AppState["zoom"],
|
|
|
|
|
|
|
|
): { focus: number; gap: number } => {
|
|
|
|
): { focus: number; gap: number } => {
|
|
|
|
const direction = startOrEnd === "start" ? -1 : 1;
|
|
|
|
const direction = startOrEnd === "start" ? -1 : 1;
|
|
|
|
const edgePointIndex = direction === -1 ? 0 : linearElement.points.length - 1;
|
|
|
|
const edgePointIndex = direction === -1 ? 0 : linearElement.points.length - 1;
|
|
|
@ -730,28 +704,9 @@ const calculateFocusAndGap = (
|
|
|
|
elementsMap,
|
|
|
|
elementsMap,
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const focus = determineFocusDistance(
|
|
|
|
|
|
|
|
hoveredElement,
|
|
|
|
|
|
|
|
adjacentPoint,
|
|
|
|
|
|
|
|
edgePoint,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
const focusPointAbsolute = determineFocusPoint(
|
|
|
|
|
|
|
|
hoveredElement,
|
|
|
|
|
|
|
|
focus,
|
|
|
|
|
|
|
|
adjacentPoint,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
const intersection =
|
|
|
|
|
|
|
|
intersectElementWithLineSegment(
|
|
|
|
|
|
|
|
hoveredElement,
|
|
|
|
|
|
|
|
lineSegment(adjacentPoint, focusPointAbsolute),
|
|
|
|
|
|
|
|
).sort(
|
|
|
|
|
|
|
|
(g, h) => pointDistanceSq(g, edgePoint) - pointDistanceSq(h, edgePoint),
|
|
|
|
|
|
|
|
)[0] ?? edgePoint;
|
|
|
|
|
|
|
|
const gap = Math.max(1, pointDistance(intersection, edgePoint));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
focus,
|
|
|
|
focus: determineFocusDistance(hoveredElement, adjacentPoint, edgePoint),
|
|
|
|
gap,
|
|
|
|
gap: Math.max(1, distanceToBindableElement(hoveredElement, edgePoint)),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
@ -1264,27 +1219,16 @@ const updateBoundPoint = (
|
|
|
|
elementsMap,
|
|
|
|
elementsMap,
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const intersection =
|
|
|
|
newEdgePoint =
|
|
|
|
intersectElementWithLineSegment(
|
|
|
|
intersectElementWithLineSegment(
|
|
|
|
bindableElement,
|
|
|
|
bindableElement,
|
|
|
|
lineSegment<GlobalPoint>(adjacentPoint, focusPointAbsolute),
|
|
|
|
lineSegment<GlobalPoint>(adjacentPoint, focusPointAbsolute),
|
|
|
|
|
|
|
|
binding.gap,
|
|
|
|
).sort(
|
|
|
|
).sort(
|
|
|
|
(g, h) =>
|
|
|
|
(g, h) =>
|
|
|
|
pointDistanceSq(g!, edgePointAbsolute) -
|
|
|
|
pointDistanceSq(g, edgePointAbsolute) -
|
|
|
|
pointDistanceSq(h!, edgePointAbsolute),
|
|
|
|
pointDistanceSq(h, edgePointAbsolute),
|
|
|
|
)[0] ?? edgePointAbsolute;
|
|
|
|
)[0] ?? edgePointAbsolute;
|
|
|
|
|
|
|
|
|
|
|
|
const gapOffsetPoint = intersection
|
|
|
|
|
|
|
|
? pointFromVector(
|
|
|
|
|
|
|
|
vectorScale(
|
|
|
|
|
|
|
|
vectorNormalize(vectorFromPoint(adjacentPoint, intersection)),
|
|
|
|
|
|
|
|
binding.gap,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
intersection,
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
: edgePointAbsolute;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
newEdgePoint = gapOffsetPoint;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return LinearElementEditor.pointFromAbsoluteCoords(
|
|
|
|
return LinearElementEditor.pointFromAbsoluteCoords(
|
|
|
@ -1579,12 +1523,10 @@ const determineFocusDistance = (
|
|
|
|
element.x + element.width / 2,
|
|
|
|
element.x + element.width / 2,
|
|
|
|
element.y + element.height / 2,
|
|
|
|
element.y + element.height / 2,
|
|
|
|
);
|
|
|
|
);
|
|
|
|
const linear = vectorFromPoint(b, a);
|
|
|
|
const intersection = [
|
|
|
|
const dot1 = Math.abs(
|
|
|
|
linesIntersectAt(
|
|
|
|
vectorDot(
|
|
|
|
line(a, b),
|
|
|
|
linear,
|
|
|
|
line(
|
|
|
|
// One of the diagonals
|
|
|
|
|
|
|
|
vectorFromPoint(
|
|
|
|
|
|
|
|
pointFrom<GlobalPoint>(element.x, element.y),
|
|
|
|
pointFrom<GlobalPoint>(element.x, element.y),
|
|
|
|
pointFrom<GlobalPoint>(
|
|
|
|
pointFrom<GlobalPoint>(
|
|
|
|
element.x + element.width,
|
|
|
|
element.x + element.width,
|
|
|
@ -1592,42 +1534,24 @@ const determineFocusDistance = (
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
linesIntersectAt(
|
|
|
|
const dot2 = Math.abs(
|
|
|
|
line(a, b),
|
|
|
|
vectorDot(
|
|
|
|
line(
|
|
|
|
linear,
|
|
|
|
|
|
|
|
// The other diagonal
|
|
|
|
|
|
|
|
vectorFromPoint(
|
|
|
|
|
|
|
|
pointFrom<GlobalPoint>(element.x + element.width, element.y),
|
|
|
|
pointFrom<GlobalPoint>(element.x + element.width, element.y),
|
|
|
|
pointFrom<GlobalPoint>(element.x, element.y + element.height),
|
|
|
|
pointFrom<GlobalPoint>(element.x, element.y + element.height),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
]
|
|
|
|
const intersect =
|
|
|
|
.filter((p): p is GlobalPoint => p !== null)
|
|
|
|
linesIntersectAt(
|
|
|
|
.sort((g, h) => pointDistanceSq(g, b) - pointDistanceSq(h, b))[0];
|
|
|
|
// The bigger inclination of the diagonal and the linear element is the one
|
|
|
|
|
|
|
|
// that determines the intersection point
|
|
|
|
|
|
|
|
dot1 > dot2
|
|
|
|
|
|
|
|
? line(
|
|
|
|
|
|
|
|
pointFrom<GlobalPoint>(element.x + element.width, element.y),
|
|
|
|
|
|
|
|
pointFrom<GlobalPoint>(element.x, element.y + element.height),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
: line(
|
|
|
|
|
|
|
|
pointFrom<GlobalPoint>(element.x, element.y),
|
|
|
|
|
|
|
|
pointFrom<GlobalPoint>(
|
|
|
|
|
|
|
|
element.x + element.width,
|
|
|
|
|
|
|
|
element.y + element.height,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
line(a, b),
|
|
|
|
|
|
|
|
) || center;
|
|
|
|
|
|
|
|
const sign =
|
|
|
|
const sign =
|
|
|
|
vectorDot(vectorFromPoint(center, a), vectorFromPoint(a, b)) < 0 ? -1 : 1;
|
|
|
|
Math.sign(vectorCross(vectorFromPoint(b, a), vectorFromPoint(b, center))) *
|
|
|
|
const signedDist = sign * pointDistance(center, intersect);
|
|
|
|
-1;
|
|
|
|
const sdRatio =
|
|
|
|
const signedDist = sign * pointDistance(center, intersection);
|
|
|
|
|
|
|
|
const signedDistanceRatio =
|
|
|
|
signedDist / (Math.sqrt(element.width ** 2 + element.height ** 2) / 2);
|
|
|
|
signedDist / (Math.sqrt(element.width ** 2 + element.height ** 2) / 2);
|
|
|
|
|
|
|
|
|
|
|
|
return sdRatio;
|
|
|
|
return signedDistanceRatio;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const determineFocusPoint = (
|
|
|
|
const determineFocusPoint = (
|
|
|
@ -1645,22 +1569,55 @@ const determineFocusPoint = (
|
|
|
|
return center;
|
|
|
|
return center;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return pointFromVector(
|
|
|
|
const candidates = [
|
|
|
|
vectorScale(
|
|
|
|
pointFrom<GlobalPoint>(element.x, element.y),
|
|
|
|
vectorRotate(
|
|
|
|
pointFrom<GlobalPoint>(element.x + element.width, element.y),
|
|
|
|
vectorNormalize(vectorFromPoint(adjacentPoint, center)),
|
|
|
|
pointFrom<GlobalPoint>(
|
|
|
|
(Math.PI / 2) as Radians,
|
|
|
|
element.x + element.width,
|
|
|
|
),
|
|
|
|
element.y + element.height,
|
|
|
|
Math.sign(focus) *
|
|
|
|
),
|
|
|
|
Math.min(
|
|
|
|
pointFrom<GlobalPoint>(element.x, element.y + element.height),
|
|
|
|
pointDistance(pointFrom<GlobalPoint>(element.x, element.y), center) *
|
|
|
|
].map((p) =>
|
|
|
|
Math.abs(focus),
|
|
|
|
pointFromVector(
|
|
|
|
element.width / 2,
|
|
|
|
vectorScale(vectorFromPoint(p, center), Math.abs(focus)),
|
|
|
|
element.height / 2,
|
|
|
|
center,
|
|
|
|
),
|
|
|
|
|
|
|
|
),
|
|
|
|
),
|
|
|
|
center,
|
|
|
|
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
const selected = [
|
|
|
|
|
|
|
|
adjacentPoint[1] < candidates[0][1] && // TOP
|
|
|
|
|
|
|
|
(focus > 0
|
|
|
|
|
|
|
|
? adjacentPoint[0] < candidates[1][0]
|
|
|
|
|
|
|
|
: adjacentPoint[0] > candidates[0][0]),
|
|
|
|
|
|
|
|
adjacentPoint[0] > candidates[1][0] && // RIGHT
|
|
|
|
|
|
|
|
(focus > 0
|
|
|
|
|
|
|
|
? adjacentPoint[1] < candidates[2][1]
|
|
|
|
|
|
|
|
: adjacentPoint[1] > candidates[1][1]),
|
|
|
|
|
|
|
|
adjacentPoint[1] > candidates[2][1] && // BOTTOM
|
|
|
|
|
|
|
|
(focus > 0
|
|
|
|
|
|
|
|
? adjacentPoint[0] > candidates[3][0]
|
|
|
|
|
|
|
|
: adjacentPoint[0] < candidates[2][0]),
|
|
|
|
|
|
|
|
adjacentPoint[0] < candidates[3][0] && // LEFT
|
|
|
|
|
|
|
|
(focus > 0
|
|
|
|
|
|
|
|
? adjacentPoint[1] < candidates[3][1]
|
|
|
|
|
|
|
|
: adjacentPoint[1] > candidates[0][1]),
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
const focusPoint = selected[0]
|
|
|
|
|
|
|
|
? focus > 0
|
|
|
|
|
|
|
|
? candidates[1]
|
|
|
|
|
|
|
|
: candidates[0]
|
|
|
|
|
|
|
|
: selected[1]
|
|
|
|
|
|
|
|
? focus > 0
|
|
|
|
|
|
|
|
? candidates[2]
|
|
|
|
|
|
|
|
: candidates[1]
|
|
|
|
|
|
|
|
: selected[2]
|
|
|
|
|
|
|
|
? focus > 0
|
|
|
|
|
|
|
|
? candidates[3]
|
|
|
|
|
|
|
|
: candidates[2]
|
|
|
|
|
|
|
|
: focus > 0
|
|
|
|
|
|
|
|
? candidates[0]
|
|
|
|
|
|
|
|
: candidates[3];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return focusPoint;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export const bindingProperties: Set<BindableProp | BindingProp> = new Set([
|
|
|
|
export const bindingProperties: Set<BindableProp | BindingProp> = new Set([
|
|
|
|