diff --git a/webapp/path_navigator.html b/webapp/path_navigator.html
index efa25d5..20cd2d6 100644
--- a/webapp/path_navigator.html
+++ b/webapp/path_navigator.html
@@ -419,6 +419,33 @@
return label;
}
+ // Calculate edge opacity based on harmonic distance: normalized 0→0.5, 1→1
+ function calcEdgeOpacity(ratio) {
+ const parts = ratio.split('/');
+ if (parts.length === 2) {
+ const n = parseInt(parts[0]);
+ const d = parseInt(parts[1]);
+ const product = n * d;
+ if (product <= 1) return 1; // handle 1/1 edge case
+ const rawOpacity = 1 / Math.log2(product);
+ return 0.5 + (rawOpacity / 2); // normalize: 0→0.5, 1→1
+ }
+ return 1;
+ }
+
+ // Calculate edge width: inverse of harmonic distance (2-5px range)
+ function calcEdgeWidth(ratio) {
+ const parts = ratio.split('/');
+ if (parts.length === 2) {
+ const n = parseInt(parts[0]);
+ const d = parseInt(parts[1]);
+ const product = n * d;
+ if (product <= 1) return 5; // handle 1/1 edge case
+ return (1 / Math.log2(product) * 3) + 2;
+ }
+ return 3.5;
+ }
+
// Multiply two fractions and return as string
function multiplyFractions(frac1, frac2) {
const f1 = parseFraction(frac1);
@@ -659,15 +686,17 @@
{
selector: 'edge',
style: {
- 'width': 2.5,
+ 'width': function(ele) { return calcEdgeWidth(ele.data('ratio')); },
'line-color': '#ffffff',
'curve-style': 'straight',
'target-arrow-shape': 'none',
+ 'line-opacity': function(ele) { return calcEdgeOpacity(ele.data('ratio')); },
'label': 'data(ratio)',
'font-size': '12px',
'color': '#ffffff',
'text-rotation': '0deg',
'text-margin-y': 0,
+ 'text-opacity': 1,
'text-background-color': '#000000',
'text-background-opacity': 0.8,
'text-background-padding': '2px',
@@ -681,6 +710,7 @@
'line-style': 'dashed',
'curve-style': 'straight',
'target-arrow-shape': 'none',
+ 'line-opacity': 1,
'label': '',
}
},