Add weighted cross-chord edges: 100x weaker than regular edges

This commit is contained in:
Michael Winter 2026-04-01 17:48:47 +02:00
parent ab7e949f07
commit 7259cd67bd

View file

@ -46,18 +46,23 @@
}); });
}); });
const edgeList = edges.map(e => ({ s: e.source().id(), t: e.target().id() })); const edgeList = edges.map(e => ({
s: e.source().id(),
t: e.target().id(),
isCrossChord: e.data('isCrossChord')
}));
const iter = (i) => { const iter = (i) => {
state.forEach(s => s.fx = 0); state.forEach(s => s.fx = 0);
edgeList.forEach(({s,t})=>{ edgeList.forEach(({s, t, isCrossChord})=>{
const a = state.get(s), b = state.get(t); const a = state.get(s), b = state.get(t);
if(!a || !b) return; if(!a || !b) return;
const dx = b.x - a.x; const dx = b.x - a.x;
const dist = Math.abs(dx) || 0.0001; const dist = Math.abs(dx) || 0.0001;
const dir = dx / dist; const dir = dx / dist;
const spring = opts.linkStrength * (dist - opts.linkDistance); const strength = isCrossChord ? (opts.crossChordStrength || 0.001) : opts.linkStrength;
const spring = strength * (dist - opts.linkDistance);
a.fx += spring * dir; a.fx += spring * dir;
b.fx -= spring * dir; b.fx -= spring * dir;
}); });
@ -350,8 +355,8 @@
{ {
selector: 'edge', selector: 'edge',
style: { style: {
'width': 1.5, 'width': 2.5,
'line-color': '#555555', 'line-color': '#ffffff',
'curve-style': 'straight', 'curve-style': 'straight',
'target-arrow-shape': 'none', 'target-arrow-shape': 'none',
'label': 'data(ratio)', 'label': 'data(ratio)',
@ -364,6 +369,17 @@
'text-background-padding': '2px', 'text-background-padding': '2px',
} }
}, },
{
selector: 'edge[isCrossChord = "true"]',
style: {
'width': 1,
'line-color': '#aaaaaa',
'line-style': 'dashed',
'curve-style': 'straight',
'target-arrow-shape': 'none',
'label': '',
}
},
{ {
selector: ':selected', selector: ':selected',
style: { style: {
@ -479,6 +495,33 @@
// Build elements array for all chords // Build elements array for all chords
let elements = []; let elements = [];
// Collect cross-chord edges (same hs_array between adjacent chords)
const crossChordEdges = [];
for (let chordIdx = 1; chordIdx < allGraphsData.graphs.length; chordIdx++) {
const prevGraph = allGraphsData.graphs[chordIdx - 1];
const currGraph = allGraphsData.graphs[chordIdx];
if (!prevGraph || !prevGraph.nodes || !currGraph || !currGraph.nodes) continue;
currGraph.nodes.forEach(n => {
const prevNode = prevGraph.nodes.find(pn =>
JSON.stringify(pn.hs_array) === JSON.stringify(n.hs_array)
);
if (prevNode) {
const prevNodeId = `c${chordIdx - 1}_${prevNode.id}`;
const currNodeId = `c${chordIdx}_${n.id}`;
crossChordEdges.push({
group: 'edges',
data: {
source: prevNodeId,
target: currNodeId,
ratio: "1/1",
isCrossChord: "true"
},
});
}
});
}
allGraphsData.graphs.forEach((graph, chordIdx) => { allGraphsData.graphs.forEach((graph, chordIdx) => {
if (!graph || !graph.nodes) return; if (!graph || !graph.nodes) return;
@ -527,6 +570,9 @@
if (elements.length === 0) return; if (elements.length === 0) return;
// Add cross-chord edges to elements BEFORE layout (so xforce considers them)
elements.push(...crossChordEdges);
// Add all elements // Add all elements
cy.add(elements); cy.add(elements);
console.log('Added', elements.length, 'elements'); console.log('Added', elements.length, 'elements');
@ -544,16 +590,19 @@
}); });
// Run xforce layout to optimize x positions while keeping y fixed // Run xforce layout to optimize x positions while keeping y fixed
cy.layout({ const layout = cy.layout({
name: 'xforce', name: 'xforce',
linkDistance: 60, linkDistance: 60,
linkStrength: 0.1, linkStrength: 0.1,
crossChordStrength: 0.00005,
charge: -60, charge: -60,
collisionDistance: 35, collisionDistance: 35,
damping: 0.7, damping: 0.7,
iterations: 250, iterations: 250,
bounds: bounds, bounds: bounds,
}).run(); });
layout.run();
// Set canvas size // Set canvas size
cy.style().json()[0].value = graphWidth; cy.style().json()[0].value = graphWidth;