const links = data.links.map(d => Object.create(d));
const nodes = data.nodes.map(d => Object.create(d));
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).distance(10))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("charge", d3.forceManyBody().strength(attraction))
.force("collide", d3.forceCollide().radius(10))
.force("y", d3.forceY().strength(.01));
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
/*
************
Create Markers. Code adapted from:
https://observablehq.com/@xianwu/force-directed-graph-network-graph-with-arrowheads-and-lab
**************
*/
//appending little triangles, path object, as arrowhead
//The <defs> element is used to store graphical objects that will be used at a later time
//The <marker> element defines the graphic that is to be used for drawing arrowheads or polymarkers on a given <path>, <line>, <polyline> or <polygon> element.
svg.append('defs').append('marker')
.attr("id",'arrowhead')
.attr('viewBox','-0 -5 10 10') //the bound of the SVG viewport for the current SVG fragment. defines a coordinate system 10 wide and 10 high starting on (0,-5)
.attr('refX',radius+9) // x coordinate for the reference point of the marker. If circle is bigger, this need to be bigger.
.attr('refY',0)
.attr('orient','auto')
.attr('markerWidth',3)
.attr('markerHeight',3)
.attr('xoverflow','visible')
.append('svg:path')
.attr('d', 'M 0,-5 L 10 ,0 L 0,5')
.attr('fill', '#999')
.style('stroke','none')
.style("opacity", .6);
/*
************
Initialize Links
**************
*/
const link = svg.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.attr("stroke-width", 3)
.attr('marker-end','url(#arrowhead)')
.selectAll("line")
.data(links)
.join("line");
/*
************
Initialize Nodes
**************
*/
const node = svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", radius/20)
.selectAll("circle")
.data(nodes)
.join("circle")
.attr("r", radius) //This variable corresponds to slider above
.attr("fill", color)
.call(drag(simulation));
/*
************
Initialize Text
**************
*/
var texts = svg.selectAll(".texts")
.data(nodes)
.enter()
.append("text")
.attr("font-family", "sans-serif")
.attr("dx", -0.8*radius)
.attr("dy", "0.12em")
.attr("font-size", radius*.9)
.attr("fill", "white")
.text( d => d.pitch )
.call(drag(simulation))
.on("click", clickedtext);
node.append("title")
.text(d => d.id);
/*
************
Animation: Play note and animate when text is clicked
**************
*/
function clickedtext(d) {
if (d3.event.defaultPrevented) return; // dragged
// play(d.data.node.note);
d3.select(this).transition()
.attr("fill", "black")
.attr("r", radius * 2)
.transition()
.attr("r", radius)
.attr("fill", "white");
if(d.id != "start" && d.id != "end"){
play(d.pitch);
}
}
/*
**************
Link Mouseover Functionality.
-should turn selected link blue (only works before mousing over node?)
-show tool tip in top right corner with link name
**************
*/
link.on('mouseover',function (d) {
d3.select(this)
.attr('stroke', 'blue');
mutable highlighted_edge = `{${d.source.id} , ${d.target.id}}`
mutable weight = `{${d.weight}}`
});
link.on('mouseout',function (d) {
d3.select(this)
.attr('stroke', '#999');
d3.select("#tooltip").remove();
});
/*
**************
Describes Simulation
**************
*/
simulation.on("tick", () => {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
// node
// .attr("cx", d => d.x)
// .attr("cy", d => d.y);
node
.attr("cx", function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); })
.attr("cy", function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });
texts
.attr("x", function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); })
.attr("y", function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });
});
/*
**************
Not 100% sure what this does
**************
*/
invalidation.then(() => simulation.stop());
/*
**************
Return function - houses update for random walk
-only activated when 'pnt' changes
**************
*/
return Object.assign(svg.node(), {
update({nodes, links}, c) {
// Make a shallow copy to protect against mutation, while
// recycling old nodes to preserve position and velocity.
const old = new Map(node.data().map(d => [d.id, d]));
nodes = nodes.map(d => Object.assign(old.get(d.id) || {}, d));
links = links.map(d => Object.assign({}, d));
//new variable
const pnt = random_walk[c];
node = node
.data(nodes, d => d.id)
.join(enter => enter.append("circle")
.attr("r", radius)
.attr("fill", d => color(color)))
.style('fill', function (n) {
if(n.id === pnt.id){
return "orange";
}
else{
return "gray";
}
})
.attr("r", function (n) {
if(n.id === pnt.id){
return radius*1.5;
}
else{
return radius;
}
});
link = link
.data(links, d => [d.source, d.target])
.join("line");
simulation.nodes(nodes);
simulation.force("link").links(links);
simulation.alpha(1).restart();
}
});
}
*/