feat: Elbow arrow segment fixing & positioning (#8952)

Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
Co-authored-by: David Luzar <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
Márk Tolmács 2025-01-17 18:07:03 +01:00 committed by GitHub
parent 8551823da9
commit 91ebf8b0ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 3282 additions and 1716 deletions

View file

@ -29,7 +29,7 @@ import {
getOmitSidesForDevice,
shouldShowBoundingBox,
} from "../element/transformHandles";
import { arrayToMap, throttleRAF } from "../utils";
import { arrayToMap, invariant, throttleRAF } from "../utils";
import {
DEFAULT_TRANSFORM_HANDLE_SPACING,
FRAME_STYLE,
@ -78,9 +78,32 @@ import type {
InteractiveSceneRenderConfig,
RenderableElementsMap,
} from "../scene/types";
import type { GlobalPoint, LocalPoint, Radians } from "../../math";
import {
pointFrom,
type GlobalPoint,
type LocalPoint,
type Radians,
} from "../../math";
import { getCornerRadius } from "../shapes";
const renderElbowArrowMidPointHighlight = (
context: CanvasRenderingContext2D,
appState: InteractiveCanvasAppState,
) => {
invariant(appState.selectedLinearElement, "selectedLinearElement is null");
const { segmentMidPointHoveredCoords } = appState.selectedLinearElement;
invariant(segmentMidPointHoveredCoords, "midPointCoords is null");
context.save();
context.translate(appState.scrollX, appState.scrollY);
highlightPoint(segmentMidPointHoveredCoords, context, appState);
context.restore();
};
const renderLinearElementPointHighlight = (
context: CanvasRenderingContext2D,
appState: InteractiveCanvasAppState,
@ -490,7 +513,7 @@ const renderLinearPointHandles = (
context.save();
context.translate(appState.scrollX, appState.scrollY);
context.lineWidth = 1 / appState.zoom.value;
const points = LinearElementEditor.getPointsGlobalCoordinates(
const points: GlobalPoint[] = LinearElementEditor.getPointsGlobalCoordinates(
element,
elementsMap,
);
@ -510,55 +533,57 @@ const renderLinearPointHandles = (
renderSingleLinearPoint(context, appState, point, radius, isSelected);
});
//Rendering segment mid points
const midPoints = LinearElementEditor.getEditorMidPoints(
element,
elementsMap,
appState,
).filter((midPoint): midPoint is GlobalPoint => midPoint !== null);
midPoints.forEach((segmentMidPoint) => {
if (
appState?.selectedLinearElement?.segmentMidPointHoveredCoords &&
LinearElementEditor.arePointsEqual(
segmentMidPoint,
appState.selectedLinearElement.segmentMidPointHoveredCoords,
)
) {
// The order of renderingSingleLinearPoint and highLight points is different
// inside vs outside editor as hover states are different,
// in editor when hovered the original point is not visible as hover state fully covers it whereas outside the
// editor original point is visible and hover state is just an outer circle.
if (appState.editingLinearElement) {
// Rendering segment mid points
if (isElbowArrow(element)) {
const fixedSegments =
element.fixedSegments?.map((segment) => segment.index) || [];
points.slice(0, -1).forEach((p, idx) => {
if (
!LinearElementEditor.isSegmentTooShort(
element,
points[idx + 1],
points[idx],
idx,
appState.zoom,
)
) {
renderSingleLinearPoint(
context,
appState,
segmentMidPoint,
radius,
false,
);
highlightPoint(segmentMidPoint, context, appState);
} else {
highlightPoint(segmentMidPoint, context, appState);
renderSingleLinearPoint(
context,
appState,
segmentMidPoint,
radius,
pointFrom<GlobalPoint>(
(p[0] + points[idx + 1][0]) / 2,
(p[1] + points[idx + 1][1]) / 2,
),
POINT_HANDLE_SIZE / 2,
false,
!fixedSegments.includes(idx + 1),
);
}
} else if (appState.editingLinearElement || points.length === 2) {
renderSingleLinearPoint(
context,
appState,
segmentMidPoint,
POINT_HANDLE_SIZE / 2,
false,
true,
);
}
});
});
} else {
const midPoints = LinearElementEditor.getEditorMidPoints(
element,
elementsMap,
appState,
).filter(
(midPoint, idx, midPoints): midPoint is GlobalPoint =>
midPoint !== null &&
!(isElbowArrow(element) && (idx === 0 || idx === midPoints.length - 1)),
);
midPoints.forEach((segmentMidPoint) => {
if (appState.editingLinearElement || points.length === 2) {
renderSingleLinearPoint(
context,
appState,
segmentMidPoint,
POINT_HANDLE_SIZE / 2,
false,
true,
);
}
});
}
context.restore();
};
@ -864,6 +889,12 @@ const _renderInteractiveScene = ({
}
if (
isElbowArrow(selectedElements[0]) &&
appState.selectedLinearElement &&
appState.selectedLinearElement.segmentMidPointHoveredCoords
) {
renderElbowArrowMidPointHighlight(context, appState);
} else if (
appState.selectedLinearElement &&
appState.selectedLinearElement.hoverPointIndex >= 0 &&
!(
@ -875,6 +906,7 @@ const _renderInteractiveScene = ({
) {
renderLinearElementPointHighlight(context, appState, elementsMap);
}
// Paint selected elements
if (!appState.multiElement && !appState.editingLinearElement) {
const showBoundingBox = shouldShowBoundingBox(selectedElements, appState);