diff --git a/public/js/app.js b/public/js/app.js index 5c04bfe..bea3dd4 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -980,25 +980,33 @@ const pts = [start, end].slice().sort((a, b) => a[0] - b[0] || a[1] - b[1]); const isForward = (start[0] === pts[0][0] && start[1] === pts[0][1]); - // Use a deterministic hash of the path to jitter the curves to avoid coincidence with other nearby paths - const pathSeed = pts[0][0] + pts[0][1] * 2 + pts[1][0] * 3 + pts[1][1] * 4; - const curveJitter = (Math.abs(Math.sin(pathSeed)) * 0.12) - 0.06; // Variation of +/- 0.06 + // Geometry metrics for intersection avoidance + const dx = end[0] - start[0]; + const dy = end[1] - start[1]; + const dist = Math.sqrt(dx * dx + dy * dy); + const midLat = (start[1] + end[1]) / 2; - // Calculate curveness: ensure all lines are curved and distinct + // 1. Layering: Longer lines curve more to "loop over" shorter lines + const baseHeight = 0.15 + Math.min(0.45, dist / 400); + + // 2. Directionality: Bias curves toward the poles (North or South) to spread out global traffic + // Eastward (isForward) + North (midLat > 0) -> + curveness (Left/North) + // Westward (!isForward) + North (midLat > 0) -> - curveness (Right/North) + const poleDir = (midLat >= 0 ? 1 : -1); + const dirFactor = poleDir * (isForward ? 1 : -1); + + // 3. Jitter: Deterministic hash to avoid perfect overlap of close paths + const pathSeed = pts[0][0] + pts[0][1] * 2 + pts[1][0] * 3 + pts[1][1] * 4; + const curveJitter = (Math.abs(Math.sin(pathSeed)) * 0.1) - 0.05; + + // Calculate final curveness: layer by distance + spread group + bias toward poles let finalCurve = 0; - if (count === 1) { - // Default curve for single lines with a bit of path-specific variation - finalCurve = 0.2 + curveJitter; - } else { - // Spread overlapping lines: 0.2, -0.2, 0.35, -0.35... - // Use a dynamic step to keep lines within a reasonable arc range - const step = Math.min(0.2, 0.5 / Math.ceil(count / 2)); - const magnitude = 0.2 + Math.floor(i / 2) * step; - const spread = (i % 2 === 0) ? magnitude : -magnitude; - - // Adjust based on direction so A->B and B->A land in different visual arcs (forming an "eye") - finalCurve = (isForward ? spread : -spread) + curveJitter; - } + const step = Math.min(0.2, 0.4 / Math.ceil(count / 2)); + const magnitude = baseHeight + (Math.floor(i / 2) * step); + const spread = (i % 2 === 0) ? magnitude : -magnitude; + + // Combining all factors: dirFactor makes i=0 always point toward its respective pole + finalCurve = (dirFactor * spread) + curveJitter; finalSeries.push({ type: 'lines',