Use shortest orthogonal relation paths
This commit is contained in:
42
src/App.jsx
42
src/App.jsx
@@ -1752,41 +1752,15 @@ function RelationTreePanel({
|
||||
const startY = from.y + miniNodeHeight;
|
||||
const endY = to.y;
|
||||
const midY = startY + Math.max(30, (endY - startY) / 2);
|
||||
const skipEdges = relations.filter((item) => (levelMap[item.to.id] ?? 0) - (levelMap[item.from.id] ?? 0) > 1);
|
||||
const isRightSkipEdge = fromLevel >= 2 || toCenterX > fromCenterX;
|
||||
const sideSkipEdges = skipEdges.filter((item) => {
|
||||
const skipFrom = miniNodePositions[item.from.id];
|
||||
const skipTo = miniNodePositions[item.to.id];
|
||||
if (!skipFrom || !skipTo) return false;
|
||||
const skipFromCenterX = skipFrom.x + miniNodeWidth / 2;
|
||||
const skipToCenterX = skipTo.x + miniNodeWidth / 2;
|
||||
return (skipToCenterX > skipFromCenterX) === isRightSkipEdge;
|
||||
});
|
||||
const skipFromLevels = [...new Set(sideSkipEdges.map((item) => levelMap[item.from.id] ?? 0))].sort((a, b) => a - b);
|
||||
const leftMostNodeX = Math.min(...Object.values(miniNodePositions).map((position) => position.x));
|
||||
const rightMostNodeX = Math.max(...Object.values(miniNodePositions).map((position) => position.x + miniNodeWidth));
|
||||
const leftOuterLaneX = 16;
|
||||
const leftInnerLaneX = Math.max(leftOuterLaneX + 28, leftMostNodeX - 42);
|
||||
const rightOuterLaneX = miniGraphWidth - 16;
|
||||
const rightInnerLaneX = Math.min(rightOuterLaneX - 28, rightMostNodeX + 42);
|
||||
const getSkipLaneX = (level) => {
|
||||
const levelIndex = Math.max(0, skipFromLevels.indexOf(level));
|
||||
const laneRatio = skipFromLevels.length <= 1 ? 0 : levelIndex / (skipFromLevels.length - 1);
|
||||
return isRightSkipEdge
|
||||
? rightOuterLaneX + (rightInnerLaneX - rightOuterLaneX) * laneRatio
|
||||
: leftOuterLaneX + (leftInnerLaneX - leftOuterLaneX) * laneRatio;
|
||||
};
|
||||
const skipIncoming = sideSkipEdges.filter((item) => item.to.id === relation.to.id);
|
||||
const innermostSkipLevel = Math.max(...skipIncoming.map((item) => levelMap[item.from.id] ?? 0));
|
||||
const isInnermostSkipEdge = fromLevel === innermostSkipLevel;
|
||||
const innermostLaneX = getSkipLaneX(innermostSkipLevel);
|
||||
const laneX = isRightSkipEdge && fromLevel >= 2
|
||||
? Math.min(rightOuterLaneX - 12, from.x + miniNodeWidth + 40)
|
||||
: getSkipLaneX(fromLevel);
|
||||
const isRightSkipEdge = toCenterX > fromCenterX;
|
||||
const laneX = isRightSkipEdge
|
||||
? from.x + miniNodeWidth + 40
|
||||
: from.x - 40;
|
||||
const skipTargetY = to.y + miniNodeHeight / 2;
|
||||
const pathD = isSkipEdge
|
||||
? isRightSkipEdge
|
||||
? `M ${from.x + miniNodeWidth} ${from.y + miniNodeHeight / 2} H ${laneX} V ${to.y - 18} H ${toCenterX} V ${to.y - 8}`
|
||||
: `M ${from.x} ${from.y + miniNodeHeight / 2} H ${laneX} V ${to.y + miniNodeHeight / 2} H ${isInnermostSkipEdge ? to.x - 8 : innermostLaneX}`
|
||||
? `M ${from.x + miniNodeWidth} ${from.y + miniNodeHeight / 2} H ${laneX} V ${skipTargetY} H ${to.x - 8}`
|
||||
: `M ${from.x} ${from.y + miniNodeHeight / 2} H ${laneX} V ${skipTargetY} H ${to.x + miniNodeWidth + 8}`
|
||||
: Math.abs(fromCenterX - toCenterX) < 6
|
||||
? `M ${fromCenterX} ${startY} L ${toCenterX} ${endY - 8}`
|
||||
: `M ${fromCenterX} ${startY} C ${fromCenterX} ${midY}, ${toCenterX} ${midY}, ${toCenterX} ${endY - 8}`;
|
||||
@@ -1799,7 +1773,7 @@ function RelationTreePanel({
|
||||
strokeWidth={isSkipEdge ? '2.2' : '2'}
|
||||
strokeDasharray={isSkipEdge ? '6 5' : undefined}
|
||||
opacity={isSkipEdge ? '0.82' : '1'}
|
||||
markerEnd={isSkipEdge ? (isRightSkipEdge || isInnermostSkipEdge ? 'url(#sidebar-relation-arrow-skip)' : undefined) : 'url(#sidebar-relation-arrow)'}
|
||||
markerEnd={isSkipEdge ? 'url(#sidebar-relation-arrow-skip)' : 'url(#sidebar-relation-arrow)'}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user