mirror of
https://github.com/OFFIS-ESC/constellation-analyzer
synced 2026-03-13 12:08:46 +00:00
Improve label staggering and remove parallel badge
- Simplify label positioning formula: symmetric 10% stagger per offset unit - Remove parallel edge badge (X relations) - not needed since all labels show - Labels now closer to center with consistent formula - Better balance between separation and readability Example for 3 edges: - offset -1: label at t=0.4 (toward source) - offset 0: label at t=0.5 (center) - offset +1: label at t=0.6 (toward target) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
fdef63e8bd
commit
ce7f6c2eed
3 changed files with 19 additions and 46 deletions
|
|
@ -96,18 +96,25 @@ const CustomEdge = ({
|
||||||
// Create cubic bezier path using custom control points
|
// Create cubic bezier path using custom control points
|
||||||
const edgePath = `M ${params.sx},${params.sy} C ${params.sourceControlX},${params.sourceControlY} ${params.targetControlX},${params.targetControlY} ${params.tx},${params.ty}`;
|
const edgePath = `M ${params.sx},${params.sy} C ${params.sourceControlX},${params.sourceControlY} ${params.targetControlX},${params.targetControlY} ${params.tx},${params.ty}`;
|
||||||
|
|
||||||
// Calculate label position at midpoint of the bezier curve (t=0.5)
|
// Smart label positioning: vary position along curve based on offset
|
||||||
const t = 0.5;
|
// This staggers labels for parallel edges to reduce overlap
|
||||||
|
// Symmetric staggering: negative offsets toward source, positive toward target
|
||||||
|
const labelPositionOffset = offsetMultiplier * 0.1; // 10% stagger per offset unit
|
||||||
|
const t = 0.5 + labelPositionOffset;
|
||||||
|
|
||||||
|
// Clamp t to reasonable range to keep labels on the visible part of the curve
|
||||||
|
const clampedT = Math.max(0.3, Math.min(0.7, t));
|
||||||
|
|
||||||
const labelX =
|
const labelX =
|
||||||
Math.pow(1 - t, 3) * params.sx +
|
Math.pow(1 - clampedT, 3) * params.sx +
|
||||||
3 * Math.pow(1 - t, 2) * t * params.sourceControlX +
|
3 * Math.pow(1 - clampedT, 2) * clampedT * params.sourceControlX +
|
||||||
3 * (1 - t) * Math.pow(t, 2) * params.targetControlX +
|
3 * (1 - clampedT) * Math.pow(clampedT, 2) * params.targetControlX +
|
||||||
Math.pow(t, 3) * params.tx;
|
Math.pow(clampedT, 3) * params.tx;
|
||||||
const labelY =
|
const labelY =
|
||||||
Math.pow(1 - t, 3) * params.sy +
|
Math.pow(1 - clampedT, 3) * params.sy +
|
||||||
3 * Math.pow(1 - t, 2) * t * params.sourceControlY +
|
3 * Math.pow(1 - clampedT, 2) * clampedT * params.sourceControlY +
|
||||||
3 * (1 - t) * Math.pow(t, 2) * params.targetControlY +
|
3 * (1 - clampedT) * Math.pow(clampedT, 2) * params.targetControlY +
|
||||||
Math.pow(t, 3) * params.ty;
|
Math.pow(clampedT, 3) * params.ty;
|
||||||
|
|
||||||
return { edgePath, labelX, labelY };
|
return { edgePath, labelX, labelY };
|
||||||
}, [sourceNode, targetNode, sourceShape, targetShape, sourceX, sourceY, targetX, targetY, data]);
|
}, [sourceNode, targetNode, sourceShape, targetShape, sourceX, sourceY, targetX, targetY, data]);
|
||||||
|
|
@ -117,10 +124,6 @@ const CustomEdge = ({
|
||||||
// Check if this is an aggregated edge
|
// Check if this is an aggregated edge
|
||||||
const isAggregated = !!(data as { aggregatedCount?: number })?.aggregatedCount;
|
const isAggregated = !!(data as { aggregatedCount?: number })?.aggregatedCount;
|
||||||
|
|
||||||
// Check if this edge is part of a large parallel group (4+ edges)
|
|
||||||
const parallelGroupSize = (data as { parallelGroupSize?: number })?.parallelGroupSize || 0;
|
|
||||||
const showParallelBadge = parallelGroupSize >= 4;
|
|
||||||
|
|
||||||
// Find the edge type configuration
|
// Find the edge type configuration
|
||||||
const edgeTypeConfig = edgeTypes.find((et) => et.id === data?.type);
|
const edgeTypeConfig = edgeTypes.find((et) => et.id === data?.type);
|
||||||
|
|
||||||
|
|
@ -232,8 +235,8 @@ const CustomEdge = ({
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Edge label - show custom or type default, plus labels, plus aggregation count, plus parallel badge */}
|
{/* Edge label - show custom or type default, plus labels, plus aggregation count */}
|
||||||
{(displayLabel || (data?.labels && data.labels.length > 0) || (data as { aggregatedCount?: number })?.aggregatedCount || showParallelBadge) && (
|
{(displayLabel || (data?.labels && data.labels.length > 0) || (data as { aggregatedCount?: number })?.aggregatedCount) && (
|
||||||
<EdgeLabelRenderer>
|
<EdgeLabelRenderer>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -275,16 +278,6 @@ const CustomEdge = ({
|
||||||
{(data as { aggregatedCount?: number }).aggregatedCount} relations
|
{(data as { aggregatedCount?: number }).aggregatedCount} relations
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* Parallel edge badge for 4+ parallel edges (show only on center edge) */}
|
|
||||||
{showParallelBadge && Math.abs((data as { offsetMultiplier?: number })?.offsetMultiplier || 0) < 0.1 && (
|
|
||||||
<div
|
|
||||||
className="mt-1 px-2 py-0.5 rounded-full text-xs font-semibold text-white"
|
|
||||||
style={{ backgroundColor: '#6b7280' }}
|
|
||||||
title={`${parallelGroupSize} parallel relations between these nodes`}
|
|
||||||
>
|
|
||||||
{parallelGroupSize} relations
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</EdgeLabelRenderer>
|
</EdgeLabelRenderer>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -432,19 +432,11 @@ const GraphEditor = ({ presentationMode = false, onNodeSelect, onEdgeSelect, onG
|
||||||
) {
|
) {
|
||||||
const currentViewport = getCurrentViewport();
|
const currentViewport = getCurrentViewport();
|
||||||
saveViewport(prevDocumentIdRef.current, currentViewport);
|
saveViewport(prevDocumentIdRef.current, currentViewport);
|
||||||
console.log(
|
|
||||||
`Saved viewport for document: ${prevDocumentIdRef.current}`,
|
|
||||||
currentViewport,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore viewport for the new document
|
// Restore viewport for the new document
|
||||||
const savedViewport = getViewport(activeDocumentId);
|
const savedViewport = getViewport(activeDocumentId);
|
||||||
if (savedViewport) {
|
if (savedViewport) {
|
||||||
console.log(
|
|
||||||
`Restoring viewport for document: ${activeDocumentId}`,
|
|
||||||
savedViewport,
|
|
||||||
);
|
|
||||||
setViewport(savedViewport, { duration: 0 });
|
setViewport(savedViewport, { duration: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -574,18 +574,6 @@ export function getFloatingEdgeParams(
|
||||||
const targetControlX = targetIntersection.x + Math.cos(targetIntersection.angle) * controlPointDistance + perpOffset.x;
|
const targetControlX = targetIntersection.x + Math.cos(targetIntersection.angle) * controlPointDistance + perpOffset.x;
|
||||||
const targetControlY = targetIntersection.y + Math.sin(targetIntersection.angle) * controlPointDistance + perpOffset.y;
|
const targetControlY = targetIntersection.y + Math.sin(targetIntersection.angle) * controlPointDistance + perpOffset.y;
|
||||||
|
|
||||||
// Debug: Log control point offsets
|
|
||||||
if (offsetMultiplier !== 0) {
|
|
||||||
console.log('🎯 Edge path with offset:', {
|
|
||||||
offsetMultiplier,
|
|
||||||
perpOffset,
|
|
||||||
controlPointDistance,
|
|
||||||
endpointOffset: sourceEndpointOffset,
|
|
||||||
sourceControl: { x: sourceControlX, y: sourceControlY },
|
|
||||||
targetControl: { x: targetControlX, y: targetControlY },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sx: sourceIntersection.x + sourceEndpointOffset.x,
|
sx: sourceIntersection.x + sourceEndpointOffset.x,
|
||||||
sy: sourceIntersection.y + sourceEndpointOffset.y,
|
sy: sourceIntersection.y + sourceEndpointOffset.y,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue