Я пытаюсь сделать мои соседние узлы видимыми, включая ссылки, в то время как другие узлы невидимы, однако я не могу получить правильный список соседних узлов. Я пытаюсь выбрать подключенные узлы в connectedNodes и соседней функции. Однако соседние узлы выбраны неправильно.
var colors = d3.scaleOrdinal(d3.schemeCategory10);
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
node,
link,
ggraph;
var toggle = 0;
//Create an array logging what is connected to what
var linkedByIndex = {};
svg.append('defs').append('marker')
.attrs({'id':'arrowhead',
'viewBox':'-0 -5 10 10',
'refX':43,
'refY':0,
'orient':'auto',
'markerWidth':13,
'markerHeight':13,
'xoverflow':'visible'})
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', '#999')
.style('stroke','none');
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function (d) {return d.id;}).distance(500).strength(1))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("miserables.json", function (error, graph) {
if (error) throw error;
for (i = 0; i < graph.nodes.length; i++) {
linkedByIndex[i + "," + i] = 1;
// console.log(linkedByIndex)
// console.log(i)
};
graph.links.forEach(function (d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
// console.log('array ', linkedByIndex)
});
update(graph.links, graph.nodes);
})
function update(links, nodes) {
link = svg.selectAll(".link")
.data(links)
.enter()
.append("line")
.attr("class", "link")
.attr('marker-end','url(#arrowhead)')
link.append("title")
.text(function (d) {return d.type;});
edgepaths = svg.selectAll(".edgepath")
.data(links)
.enter()
.append('path')
.attrs({
'class': 'edgepath',
'fill-opacity': 0,
'stroke-opacity': 0,
'id': function (d, i) {return 'edgepath' + i}
})
.style("pointer-events", "none");
edgelabels = svg.selectAll(".edgelabel")
.data(links)
.enter()
.append('text')
.style("pointer-events", "none")
.attrs({
'class': 'edgelabel',
'id': function (d, i) {return 'edgelabel' + i},
'font-size': 10,
'fill': '#aaa'
});
edgelabels.append('textPath')
.attr('xlink:href', function (d, i) {return '#edgepath' + i})
.style("text-anchor", "middle")
.style("pointer-events", "none")
.attr("startOffset", "50%")
.text(function (d) {return d.type});
node = svg.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
//.on("end", dragended)
).on("mouseover", function(d) {
// I would like to insert an if statement to do all of these things to the connected nodes
// if(isConnected(d, o)) {
d3.select(this).select("circle").style("stroke-width", 6);
// Figure out the neighboring node id's with brute strength because the graph is small
var nodeNeighbors = links.filter(function(link) {
// Filter the list of links to only those links that have our target
// node as a source or target
return link.source.index === d.index || link.target.index === d.index;})
.map(function(link) {
// Map the list of links to a simple array of the neighboring indices - this is
// technically not required but makes the code below simpler because we can use
// indexOf instead of iterating and searching ourselves.
return link.source.index === d.index ? link.target.index : link.source.index; });
// Reset all circles - we will do this in mouseout also
// svg.selectAll('circle').style('stroke', 'gray');
// now we select the neighboring circles and apply whatever style we want.
// Note that we could also filter a selection of links in this way if we want to
// Highlight those as well
svg.selectAll('circle').filter(function(node) {
// I filter the selection of all circles to only those that hold a node with an
// index in my listg of neighbors
return nodeNeighbors.indexOf(node.index) > -1;
})
.style('stroke', 'purple').style('stroke-width', 6);
d3.select(this).select("circle").style("stroke", "purple");
// d3.select(this).select("circle").style("fill", 'red');
// d3.select(this).select("text").style("font", "20px sans-serif");
// d3.selectAll("rect." + d.location).style("stroke", "orange");
// d3.selectAll("text." + d.location).style("font", "20px sans-serif");
// d3.selectAll("tr." + d.name).style("background-color", "orange");
//}
})
.on("mouseout", function(d) {
// if(isConnected(d, o)) {
svg.selectAll('circle').style('stroke', 'none');
// d3.select(this).select("circle").style("stroke-width", 0);
// d3.select(this).select("circle").style("stroke", "none");
// d3.select(this).select("text").style("font", "12px sans-serif");
// d3.selectAll("rect." + d.location).style("stroke-width", 1.5);
// d3.selectAll("rect." + d.location).style("stroke", "gray");
// d3.selectAll("text." + d.location).style("font", "12px sans-serif");
// d3.selectAll("tr." + d.name).style("background-color", "white");
//}
})
.on('click', connectedNodes);
node.append("circle")
.attr("r", 45)
.style("fill", function (d, i) {return colors(d.group);})
node.append("title")
.text(function (d) {return d.id;});
node.append("text")
.attr("text-anchor", "middle")
.attr("class", "nodeTitle")
.attr("dy", -5)
.text(function (d) {return d.name});
//node subhead
node.append("text")
.attr("text-anchor", "middle")
.attr("class", "subheading")
.attr("dy", 10)
.text(function (d) {return ('Label: ' + d.label)})
//node body text
node.append("text")
.attr("text-anchor", "middle")
.attr("class", "bodyText")
.attr("dy", 25)
.text(function (d) {return ('ID: ' + d.id)});
simulation
.nodes(nodes)
.on("tick", ticked);
simulation.force("link")
.links(links);
}
//This function looks up whether a pair are neighbours
function neighboring(a, b) {
return linkedByIndex[a.index + "," + b.index];
}
function connectedNodes() {
if (toggle == 0) {
//Reduce the opacity of all but the neighbouring nodes
d = d3.select(this).node().__data__;
// console.log('d ' , d)
node.style("opacity", function (o) {
// console.log('d and o ', neighboring(d,o))
return neighboring(d, o) | neighboring(o, d) ? 1 : 0.1;
});
// console.log(node)
link.style("opacity", function (o) {
return d.index==o.source.index | d.index==o.target.index ? 1 : 0.1;
});
//Reduce the op
toggle = 1;
} else {
//Put them back to opacity=1
node.style("opacity", 1);
link.style("opacity", 1);
toggle = 0;
}
}
function ticked() {
link
.attr("x1", function (d) {return d.source.x;})
.attr("y1", function (d) {return d.source.y;})
.attr("x2", function (d) {return d.target.x;})
.attr("y2", function (d) {return d.target.y;});
node
.attr("transform", function (d) {return "translate(" + d.x + ", " + d.y + ")";});
edgepaths.attr('d', function (d) {
return 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y;
});
edgelabels.attr('transform', function (d) {
if (d.target.x < d.source.x) {
var bbox = this.getBBox();
rx = bbox.x + bbox.width / 2;
ry = bbox.y + bbox.height / 2;
return 'rotate(180 ' + rx + ' ' + ry + ')';
}
else {
return 'rotate(0)';
}
});
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart()
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
// function dragended(d) {
// if (!d3.event.active) simulation.alphaTarget(0);
// d.fx = undefined;
// d.fy = undefined;
// }
выборка данных
{
"nodes": [
{
"name": "Peter",
"label": "Person",
"group" : 1,
"id": 1
},
{
"name": "Michael",
"label": "Person",
"group" : 1,
"id": 2
},
{
"name": "Neo4j",
"label": "Database",
"group" : 2,
"id": 3
},
{
"name": "Graph Database",
"label": "Database",
"group" : 2,
"id": 4
},
{
"name": "Bob",
"label": "Person",
"group" : 3,
"id": 5
},
{
"name": "Christine",
"label": "Person",
"group" : 1,
"id": 6
},
{
"name": "Ted",
"label": "Person",
"group" : 1,
"id": 7
}
],
"links": [
{
"source": 1,
"target": 2,
"type": "KNOWS",
"since": 2010
},
{
"source": 2,
"target": 3,
"type": "FOUNDED"
},
{
"source": 3,
"target": 4,
"type": "WORKS_ON"
},
{
"source": 4,
"target": 3,
"type": "IS_A"
},
{
"source": 5,
"target": 6,
"type": "cb"
},
{
"source": 6,
"target": 2,
"type": "duh"
},
{
"source": 7,
"target": 6,
"type": "Bla"
}
]
}