|
|
|
function forceEdgeColors() { |
|
console.log("Forcibly updating edge colors based on connection type"); |
|
|
|
|
|
let nodeInfo = {}; |
|
let typeCounts = { paper: 0, author: 0, organization: 0, unknown: 0 }; |
|
|
|
sigmaInstance.iterNodes(function(node) { |
|
|
|
let nodeType = node.type || 'unknown'; |
|
typeCounts[nodeType] = (typeCounts[nodeType] || 0) + 1; |
|
|
|
nodeInfo[node.id] = { |
|
color: node.color || '#aaa', |
|
type: nodeType |
|
}; |
|
}); |
|
|
|
console.log("Node type counts:", typeCounts); |
|
|
|
|
|
let edgeStats = { |
|
total: 0, |
|
paperAuthor: 0, |
|
paperOrg: 0, |
|
other: 0 |
|
}; |
|
|
|
|
|
sigmaInstance.iterEdges(function(edge) { |
|
edgeStats.total++; |
|
|
|
const sourceNode = nodeInfo[edge.source]; |
|
const targetNode = nodeInfo[edge.target]; |
|
|
|
if (!sourceNode || !targetNode) { |
|
console.log(`Could not find node info for edge ${edge.id}`); |
|
return; |
|
} |
|
|
|
|
|
if (edgeStats.total < 10 && |
|
((sourceNode.type === 'paper' && targetNode.type === 'organization') || |
|
(sourceNode.type === 'organization' && targetNode.type === 'paper'))) { |
|
console.log(`DEBUG Edge ${edge.id}: ${sourceNode.type}(${edge.source}) -> ${targetNode.type}(${edge.target})`); |
|
} |
|
|
|
|
|
if ((sourceNode.type === 'paper' && targetNode.type === 'author') || |
|
(sourceNode.type === 'author' && targetNode.type === 'paper')) { |
|
|
|
edgeStats.paperAuthor++; |
|
|
|
const authorNode = sourceNode.type === 'author' ? sourceNode : targetNode; |
|
edge.color = authorNode.color; |
|
|
|
|
|
if (edgeStats.paperAuthor < 5) { |
|
console.log(`Paper-Author edge ${edge.id}: Using author color ${edge.color}`); |
|
} |
|
} |
|
|
|
else if ((sourceNode.type === 'paper' && targetNode.type === 'organization') || |
|
(sourceNode.type === 'organization' && targetNode.type === 'paper')) { |
|
|
|
edgeStats.paperOrg++; |
|
|
|
const paperNode = sourceNode.type === 'paper' ? sourceNode : targetNode; |
|
const orgNode = sourceNode.type === 'organization' ? sourceNode : targetNode; |
|
|
|
|
|
console.log(`Paper-Org edge ${edge.id}: Paper=${paperNode.type}(${edge.source === paperNode.id ? 'source' : 'target'}) color=${paperNode.color}, Org=${orgNode.type}(${edge.source === orgNode.id ? 'source' : 'target'}) color=${orgNode.color}`); |
|
|
|
|
|
const oldColor = edge.color; |
|
edge.color = paperNode.color; |
|
|
|
console.log(` Set edge ${edge.id} color: ${oldColor} -> ${edge.color}`); |
|
} |
|
|
|
else { |
|
edgeStats.other++; |
|
|
|
edge.color = targetNode.color; |
|
|
|
|
|
if (edgeStats.other < 5) { |
|
console.log(`Other edge ${edge.id}: Using target color ${edge.color}`); |
|
} |
|
} |
|
}); |
|
|
|
|
|
sigmaInstance.refresh(); |
|
|
|
console.log("Edge coloring stats:", edgeStats); |
|
console.log("Edge colors have been updated based on connection types"); |
|
|
|
|
|
setTimeout(() => { |
|
console.log("Verifying edge colors after redraw:"); |
|
let colorCount = {}; |
|
sigmaInstance.iterEdges(function(edge) { |
|
if (!colorCount[edge.color]) { |
|
colorCount[edge.color] = 0; |
|
} |
|
colorCount[edge.color]++; |
|
}); |
|
console.log("Edge color counts:", colorCount); |
|
}, 100); |
|
} |
|
|
|
|
|
function logNodeColors() { |
|
if (!sigmaInstance) return; |
|
|
|
console.log("Current node colors by type:"); |
|
let typeColors = {}; |
|
|
|
|
|
sigmaInstance.iterNodes(function(node) { |
|
if (node.type) { |
|
if (!typeColors[node.type]) { |
|
typeColors[node.type] = { |
|
configColor: (config.nodeTypes && config.nodeTypes[node.type]) |
|
? config.nodeTypes[node.type].color |
|
: (nodeTypes[node.type] ? nodeTypes[node.type].color : 'none'), |
|
nodeCount: 0, |
|
colorCounts: {} |
|
}; |
|
} |
|
|
|
typeColors[node.type].nodeCount++; |
|
|
|
if (!typeColors[node.type].colorCounts[node.color]) { |
|
typeColors[node.type].colorCounts[node.color] = 0; |
|
} |
|
typeColors[node.type].colorCounts[node.color]++; |
|
} |
|
}); |
|
|
|
|
|
for (const type in typeColors) { |
|
console.log(`Node type: ${type}`); |
|
console.log(` Config color: ${typeColors[type].configColor}`); |
|
console.log(` Node count: ${typeColors[type].nodeCount}`); |
|
console.log(' Actual colors used:'); |
|
for (const color in typeColors[type].colorCounts) { |
|
console.log(` ${color}: ${typeColors[type].colorCounts[color]} nodes`); |
|
} |
|
} |
|
} |
|
|
|
|
|
function forceNodeColorsFromConfig() { |
|
console.log("Forcibly applying node colors from config settings"); |
|
|
|
if (!sigmaInstance) { |
|
console.error("Cannot apply node colors, sigma instance not initialized"); |
|
return; |
|
} |
|
|
|
|
|
let configNodeTypes = config.nodeTypes || nodeTypes; |
|
|
|
|
|
sigmaInstance.iterNodes(function(node) { |
|
if (node.type && configNodeTypes[node.type]) { |
|
|
|
const configColor = configNodeTypes[node.type].color; |
|
console.log(`Setting node ${node.id} color to ${configColor} based on type ${node.type}`); |
|
node.color = configColor; |
|
|
|
|
|
if (configNodeTypes[node.type].size) { |
|
node.size = configNodeTypes[node.type].size; |
|
} |
|
} |
|
}); |
|
|
|
|
|
sigmaInstance.refresh(); |
|
|
|
|
|
setTimeout(function() { |
|
forceEdgeColors(); |
|
|
|
logNodeColors(); |
|
}, 100); |
|
|
|
console.log("Node colors have been forcibly applied from config"); |
|
} |
|
|
|
|
|
function debugPaperOrgEdges() { |
|
console.log("Debugging paper-organization edge coloring"); |
|
|
|
|
|
let nodeInfo = {}; |
|
sigmaInstance.iterNodes(function(node) { |
|
nodeInfo[node.id] = { |
|
color: node.color || '#aaa', |
|
type: node.type || 'unknown', |
|
label: node.label || node.id |
|
}; |
|
}); |
|
|
|
|
|
let paperOrgEdges = []; |
|
sigmaInstance.iterEdges(function(edge) { |
|
const sourceNode = nodeInfo[edge.source]; |
|
const targetNode = nodeInfo[edge.target]; |
|
|
|
if (!sourceNode || !targetNode) return; |
|
|
|
|
|
if ((sourceNode.type === 'paper' && targetNode.type === 'organization') || |
|
(sourceNode.type === 'organization' && targetNode.type === 'paper')) { |
|
|
|
const paperNode = sourceNode.type === 'paper' ? sourceNode : targetNode; |
|
const orgNode = sourceNode.type === 'organization' ? sourceNode : targetNode; |
|
|
|
paperOrgEdges.push({ |
|
edgeId: edge.id, |
|
edgeColor: edge.color, |
|
paperNodeId: paperNode.id, |
|
paperNodeLabel: paperNode.label, |
|
paperNodeColor: paperNode.color, |
|
orgNodeId: orgNode.id, |
|
orgNodeLabel: orgNode.label, |
|
orgNodeColor: orgNode.color, |
|
edgeSourceIsOrg: sourceNode.type === 'organization' |
|
}); |
|
} |
|
}); |
|
|
|
console.log(`Found ${paperOrgEdges.length} paper-organization edges`); |
|
console.log("Sample of paper-organization edges:", paperOrgEdges.slice(0, 5)); |
|
|
|
|
|
let correctlyColored = 0; |
|
let incorrectlyColored = 0; |
|
|
|
for (const edge of paperOrgEdges) { |
|
if (edge.edgeColor === edge.paperNodeColor) { |
|
correctlyColored++; |
|
} else { |
|
incorrectlyColored++; |
|
console.log(`Incorrectly colored edge: ${edge.edgeId} (color: ${edge.edgeColor}, should be: ${edge.paperNodeColor})`); |
|
} |
|
} |
|
|
|
console.log(`Edge coloring stats: ${correctlyColored} correct, ${incorrectlyColored} incorrect`); |
|
|
|
return paperOrgEdges; |
|
} |
|
|
|
|
|
function initializeGraph(data) { |
|
graph = data; |
|
console.log("Initializing graph with nodes:", graph.nodes.length, "edges:", graph.edges.length); |
|
|
|
try { |
|
|
|
sigmaInstance = sigma.init(document.getElementById('sigma-canvas')); |
|
|
|
|
|
sigmaInstance.mouseProperties({ |
|
maxRatio: 32, |
|
minRatio: 0.5, |
|
mouseEnabled: true, |
|
mouseInertia: 0.8 |
|
}); |
|
|
|
console.log("Sigma mouse properties configured"); |
|
|
|
|
|
for (let i = 0; i < graph.nodes.length; i++) { |
|
let node = graph.nodes[i]; |
|
sigmaInstance.addNode(node.id, { |
|
label: node.label || node.id, |
|
x: node.x || Math.random() * 100, |
|
y: node.y || Math.random() * 100, |
|
size: node.size || 1, |
|
color: node.color || (node.type && config.nodeTypes && config.nodeTypes[node.type] ? |
|
config.nodeTypes[node.type].color : nodeTypes[node.type]?.color || '#666'), |
|
type: node.type |
|
}); |
|
} |
|
|
|
|
|
for (let i = 0; i < graph.edges.length; i++) { |
|
let edge = graph.edges[i]; |
|
|
|
|
|
let sourceNode = null; |
|
let targetNode = null; |
|
let edgeColor = '#aaa'; |
|
|
|
sigmaInstance.iterNodes(function(node) { |
|
if (node.id === edge.source) { |
|
sourceNode = { |
|
type: node.type || 'unknown', |
|
color: node.color || '#aaa' |
|
}; |
|
} |
|
if (node.id === edge.target) { |
|
targetNode = { |
|
type: node.type || 'unknown', |
|
color: node.color || '#aaa' |
|
}; |
|
} |
|
}); |
|
|
|
if (sourceNode && targetNode) { |
|
|
|
if ((sourceNode.type === 'paper' && targetNode.type === 'author') || |
|
(sourceNode.type === 'author' && targetNode.type === 'paper')) { |
|
const authorNode = sourceNode.type === 'author' ? sourceNode : targetNode; |
|
edgeColor = authorNode.color; |
|
} |
|
|
|
else if ((sourceNode.type === 'paper' && targetNode.type === 'organization') || |
|
(sourceNode.type === 'organization' && targetNode.type === 'paper')) { |
|
const paperNode = sourceNode.type === 'paper' ? sourceNode : targetNode; |
|
edgeColor = paperNode.color; |
|
} |
|
|
|
else { |
|
edgeColor = targetNode.color; |
|
} |
|
} |
|
|
|
sigmaInstance.addEdge(edge.id, edge.source, edge.target, { |
|
size: edge.size || 1, |
|
color: edgeColor |
|
}); |
|
} |
|
|
|
|
|
sigmaInstance.drawingProperties({ |
|
labelThreshold: config.sigma?.drawingProperties?.labelThreshold || 8, |
|
defaultLabelColor: config.sigma?.drawingProperties?.defaultLabelColor || '#000', |
|
defaultLabelSize: config.sigma?.drawingProperties?.defaultLabelSize || 14, |
|
defaultEdgeType: config.sigma?.drawingProperties?.defaultEdgeType || 'curve', |
|
defaultHoverLabelBGColor: config.sigma?.drawingProperties?.defaultHoverLabelBGColor || '#002147', |
|
defaultLabelHoverColor: config.sigma?.drawingProperties?.defaultLabelHoverColor || '#fff', |
|
borderSize: 2, |
|
nodeBorderColor: '#fff', |
|
defaultNodeBorderColor: '#fff', |
|
defaultNodeHoverColor: '#fff', |
|
edgeColor: 'default', |
|
defaultEdgeColor: '#ccc' |
|
}); |
|
|
|
|
|
sigmaInstance.graphProperties({ |
|
minNodeSize: config.sigma?.graphProperties?.minNodeSize || 1, |
|
maxNodeSize: config.sigma?.graphProperties?.maxNodeSize || 8, |
|
minEdgeSize: config.sigma?.graphProperties?.minEdgeSize || 0.5, |
|
maxEdgeSize: config.sigma?.graphProperties?.maxEdgeSize || 2, |
|
sideMargin: 50 |
|
}); |
|
|
|
|
|
sigmaInstance.draw(2, 2, 2, 2); |
|
|
|
|
|
forceNodeColorsFromConfig(); |
|
|
|
|
|
forceEdgeColors(); |
|
|
|
|
|
setTimeout(() => { |
|
console.log("Running edge coloring debug check"); |
|
debugPaperOrgEdges(); |
|
}, 2000); |
|
|
|
console.log("Sigma instance created and configured:", sigmaInstance); |
|
|
|
|
|
applyNodeStyles(); |
|
|
|
|
|
forceEdgeColors(); |
|
|
|
|
|
initFilters(); |
|
|
|
|
|
bindEvents(); |
|
} catch (e) { |
|
console.error("Error initializing sigma instance:", e); |
|
} |
|
} |
|
|
|
|
|
function applyNodeStyles() { |
|
if (!sigmaInstance) return; |
|
try { |
|
|
|
sigmaInstance.iterNodes(function(node) { |
|
|
|
if (node.type && config.nodeTypes && config.nodeTypes[node.type]) { |
|
|
|
node.color = config.nodeTypes[node.type].color; |
|
node.size = config.nodeTypes[node.type].size; |
|
} else if (node.type && nodeTypes[node.type]) { |
|
|
|
node.color = nodeTypes[node.type].color; |
|
node.size = nodeTypes[node.type].size; |
|
} |
|
}); |
|
|
|
|
|
|
|
let nodeInfo = {}; |
|
sigmaInstance.iterNodes(function(node) { |
|
nodeInfo[node.id] = { |
|
color: node.color || '#aaa', |
|
type: node.type || 'unknown' |
|
}; |
|
}); |
|
|
|
|
|
sigmaInstance.iterEdges(function(edge) { |
|
const sourceNode = nodeInfo[edge.source]; |
|
const targetNode = nodeInfo[edge.target]; |
|
|
|
if (!sourceNode || !targetNode) return; |
|
|
|
|
|
if ((sourceNode.type === 'paper' && targetNode.type === 'author') || |
|
(sourceNode.type === 'author' && targetNode.type === 'paper')) { |
|
const authorNode = sourceNode.type === 'author' ? sourceNode : targetNode; |
|
edge.color = authorNode.color; |
|
} |
|
|
|
else if ((sourceNode.type === 'paper' && targetNode.type === 'organization') || |
|
(sourceNode.type === 'organization' && targetNode.type === 'paper')) { |
|
const paperNode = sourceNode.type === 'paper' ? sourceNode : targetNode; |
|
edge.color = paperNode.color; |
|
} |
|
|
|
else { |
|
edge.color = targetNode.color; |
|
} |
|
}); |
|
|
|
sigmaInstance.refresh(); |
|
} catch (e) { |
|
console.error("Error applying node styles:", e); |
|
} |
|
} |
|
|
|
|
|
function colorNodesByAttribute(attribute) { |
|
if (!sigmaInstance) return; |
|
|
|
console.log("Coloring nodes by attribute:", attribute); |
|
|
|
let values = {}; |
|
let valueCount = 0; |
|
|
|
sigmaInstance.iterNodes(function(n) { |
|
let value = n[attribute] || 'unknown'; |
|
if (!values[value]) { |
|
values[value] = true; |
|
valueCount++; |
|
} |
|
}); |
|
|
|
|
|
let valueColors = {}; |
|
let i = 0; |
|
let palette = config.colorPalette || colors; |
|
|
|
for (let value in values) { |
|
valueColors[value] = palette[i % palette.length]; |
|
i++; |
|
} |
|
|
|
|
|
sigmaInstance.iterNodes(function(n) { |
|
let value = n[attribute] || 'unknown'; |
|
n.originalColor = valueColors[value]; |
|
n.color = valueColors[value]; |
|
}); |
|
|
|
|
|
let nodeInfo = {}; |
|
sigmaInstance.iterNodes(function(node) { |
|
nodeInfo[node.id] = { |
|
color: node.color || '#aaa', |
|
type: node.type || 'unknown' |
|
}; |
|
}); |
|
|
|
|
|
sigmaInstance.iterEdges(function(edge) { |
|
const sourceNode = nodeInfo[edge.source]; |
|
const targetNode = nodeInfo[edge.target]; |
|
|
|
if (!sourceNode || !targetNode) return; |
|
|
|
|
|
if ((sourceNode.type === 'paper' && targetNode.type === 'author') || |
|
(sourceNode.type === 'author' && targetNode.type === 'paper')) { |
|
const authorNode = sourceNode.type === 'author' ? sourceNode : targetNode; |
|
edge.originalColor = authorNode.color; |
|
edge.color = authorNode.color; |
|
} |
|
|
|
else if ((sourceNode.type === 'paper' && targetNode.type === 'organization') || |
|
(sourceNode.type === 'organization' && targetNode.type === 'paper')) { |
|
const paperNode = sourceNode.type === 'paper' ? sourceNode : targetNode; |
|
edge.originalColor = paperNode.color; |
|
edge.color = paperNode.color; |
|
} |
|
|
|
else { |
|
edge.originalColor = targetNode.color; |
|
edge.color = targetNode.color; |
|
} |
|
}); |
|
|
|
sigmaInstance.refresh(); |
|
|
|
|
|
updateColorLegend(valueColors); |
|
} |
|
|
|
|
|
function nodeActive(nodeId) { |
|
console.log("nodeActive called with id:", nodeId); |
|
|
|
|
|
var node = null; |
|
sigmaInstance.iterNodes(function(n) { |
|
if (n.id == nodeId) { |
|
node = n; |
|
} |
|
}); |
|
|
|
if (!node) { |
|
console.error("Node not found:", nodeId); |
|
return; |
|
} |
|
|
|
console.log("Node found:", node); |
|
sigmaInstance.detail = true; |
|
selectedNode = node; |
|
|
|
|
|
let nodeInfo = {}; |
|
sigmaInstance.iterNodes(function(n) { |
|
nodeInfo[n.id] = { |
|
color: n.color || '#aaa', |
|
type: n.type || 'unknown' |
|
}; |
|
}); |
|
|
|
|
|
var neighbors = {}; |
|
sigmaInstance.iterEdges(function(e) { |
|
if (e.source == nodeId || e.target == nodeId) { |
|
neighbors[e.source == nodeId ? e.target : e.source] = { |
|
name: e.label || "", |
|
type: e.source == nodeId ? nodeInfo[e.target].type : nodeInfo[e.source].type, |
|
color: e.color |
|
}; |
|
|
|
e.originalColor = e.color; |
|
} |
|
}); |
|
|
|
console.log("Neighbors found:", Object.keys(neighbors).length); |
|
|
|
|
|
sigmaInstance.iterNodes(function(n) { |
|
if (n.id == nodeId) { |
|
n.originalColor = n.color; |
|
n.size = n.size * 1.5; |
|
} else if (neighbors[n.id]) { |
|
n.originalColor = n.color; |
|
} else { |
|
n.originalColor = n.originalColor || n.color; |
|
n.color = greyColor; |
|
} |
|
}); |
|
|
|
|
|
sigmaInstance.iterEdges(function(e) { |
|
if (e.source == nodeId || e.target == nodeId) { |
|
|
|
e.originalColor = e.color; |
|
} else { |
|
e.originalColor = e.originalColor || e.color; |
|
e.color = greyColor; |
|
} |
|
}); |
|
|
|
|
|
sigmaInstance.refresh(); |
|
|
|
|
|
var connectionList = []; |
|
for (var id in neighbors) { |
|
var neighbor = null; |
|
sigmaInstance.iterNodes(function(n) { |
|
if (n.id == id) { |
|
neighbor = n; |
|
} |
|
}); |
|
|
|
if (neighbor) { |
|
connectionList.push('<li><a href="#" data-node-id="' + id + '">' + (neighbor.label || id) + '</a></li>'); |
|
} |
|
} |
|
|
|
|
|
try { |
|
console.log("Displaying attribute pane"); |
|
|
|
$('#attributepane').show().css('display', 'block'); |
|
|
|
|
|
$('.nodeattributes .name').text(node.label || node.id); |
|
|
|
let dataHTML = ''; |
|
for (let attr in node) { |
|
if (attr !== 'id' && attr !== 'x' && attr !== 'y' && attr !== 'size' && attr !== 'color' && |
|
attr !== 'label' && attr !== 'originalColor' && attr !== 'hidden' && |
|
typeof node[attr] !== 'function' && attr !== 'displayX' && attr !== 'displayY' && |
|
attr !== 'displaySize') { |
|
dataHTML += '<div><strong>' + attr + ':</strong> ' + node[attr] + '</div>'; |
|
} |
|
} |
|
|
|
if (dataHTML === '') { |
|
dataHTML = '<div>No additional attributes</div>'; |
|
} |
|
|
|
$('.nodeattributes .data').html(dataHTML); |
|
$('.nodeattributes .link ul').html(connectionList.length ? connectionList.join('') : '<li>No connections</li>'); |
|
|
|
|
|
$('.nodeattributes .link ul li a').click(function(e) { |
|
e.preventDefault(); |
|
var id = $(this).data('node-id'); |
|
nodeActive(id); |
|
}); |
|
|
|
console.log("Attribute pane updated with node details"); |
|
} catch (e) { |
|
console.error("Error updating attribute pane:", e); |
|
} |
|
} |
|
|
|
|
|
function nodeNormal() { |
|
console.log("nodeNormal called"); |
|
if (sigmaInstance) { |
|
sigmaInstance.detail = false; |
|
selectedNode = null; |
|
|
|
|
|
sigmaInstance.iterNodes(function(node) { |
|
node.color = node.originalColor || node.color; |
|
|
|
if (node.type && config.nodeTypes && config.nodeTypes[node.type]) { |
|
node.size = config.nodeTypes[node.type].size; |
|
} else if (node.type && nodeTypes[node.type]) { |
|
node.size = nodeTypes[node.type].size; |
|
} |
|
}); |
|
|
|
|
|
sigmaInstance.iterEdges(function(edge) { |
|
edge.color = edge.originalColor || edge.color; |
|
}); |
|
|
|
|
|
$('#attributepane').css('display', 'none'); |
|
sigmaInstance.refresh(); |
|
|
|
|
|
forceEdgeColors(); |
|
} |
|
} |