Skip to content

Instantly share code, notes, and snippets.

@mforando
Last active December 13, 2019 18:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mforando/3056d5c47e3d46b2f6cd4a89659a5747 to your computer and use it in GitHub Desktop.
Save mforando/3056d5c47e3d46b2f6cd4a89659a5747 to your computer and use it in GitHub Desktop.
Force Layout Mockup
license: mit
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
.delauney{
fill:none;
stroke:rgb(50,50,50);
stroke-opacity:.8;
stroke-width:1px;
filter:url(#glow);
}
</style>
</head>
<body>
<script>
var loadings = [{"Measure":"H-CLEAN","Loading":0.67}
,{"Measure":"H-COMP-1","Loading":0.81}
,{"Measure":"H-COMP-2","Loading":0.75}
,{"Measure":"H-COMP-3","Loading":0.74}
,{"Measure":"H-COMP-5","Loading":0.77}
,{"Measure":"H-COMP-6","Loading":0.7}
,{"Measure":"H-COMP-7","Loading":0.89}
,{"Measure":"H-HSP-RATING","Loading":0.92}
,{"Measure":"H-QUIET","Loading":0.7}
,{"Measure":"H-RECMND","Loading":0.87}
];
loadings = [{"Measure":"MORT-30-AMI","Loading":0.48}
,{"Measure":"MORT-30-CABG","Loading":0.3}
,{"Measure":"MORT-30-COPD","Loading":0.68}
,{"Measure":"MORT-30-HF","Loading":0.72}
,{"Measure":"MORT-30-PN","Loading":0.65}
,{"Measure":"MORT-30-STK","Loading":0.49}
,{"Measure":"PSI-4-SURG-COMP","Loading":0.34}
]
var graph = {}
graph.links = [];
graph.nodes = loadings;
loadings.sort((a,b)=>{return d3.descending(a.Loading, b.Loading);});
//assing IDs
loadings.forEach((d,i)=>{
if(i==0){
d.fx = 0;
d.fy = 0;
}
d.id = i;});
//links
loadings.forEach((source)=>{
loadings.forEach((target)=>{
if(source.Measure != target.Measure){
graph.links.push(
{"source":source.id, "target": target.id, "strength": source.Loading}
)
}
})
})
var width = 150;
var height = 150;
// Feel free to change or delete any of the code you see in this editor!
var svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500)
//Container for the gradients
var defs = svg.append("defs");
//Filter for the outside glow
var filter = defs.append("filter")
.attr("id","glow");
filter.append("feGaussianBlur")
.attr("stdDeviation","1.05")
.attr("result","coloredBlur");
var feMerge = filter.append("feMerge");
feMerge.append("feMergeNode")
.attr("in","coloredBlur");
feMerge.append("feMergeNode")
.attr("in","SourceGraphic");
var chart = svg.append('g')
.attr('id','forceChart')
.attr('transform','translate(' + width/2 + ',' + height/2 + ') '+'rotate(0)');
var radiusScale = d3.scaleLinear()
.range([1,12])
.domain([Math.sqrt(.25),Math.sqrt(1)])
.clamp(true)
var convexHull = chart.append("path")
.attr("class",'hull')
.style('opacity',.5)
.style('stroke-width','30px')
.style('stroke','rgb(230,210,230)')
.style('stroke-linejoin','round')
.style('fill', 'rgb(230,210,230)');
var links = chart.selectAll('line').data(graph.links)
links.enter()
.append("line")
.style('opacity',.15)
.attr("stroke", "rgb(100,100,100)")
var triangle = chart.append("g")
.attr("class", "triangles")
;
var circles = chart.selectAll('circle').data(graph.nodes)
circles.enter()
.append("circle")
.attr("r", (d)=>{return radiusScale(Math.sqrt(d.Loading))})
.attr("fill", "rgb(183,32,51)")
.attr('stroke','black')
.attr('stroke-width','2px');
var voronoi = d3.voronoi();
var sites;
var simulation = d3.forceSimulation()
.nodes(graph.nodes)
.force("link", d3.forceLink().links(graph.links).id(function(d) {return d.id}))
.force("collide",d3.forceCollide(function(d){return radiusScale(Math.sqrt(d.Loading)) + 6 }))
.force("charge", d3.forceManyBody())
// .force("center", d3.forceCenter(width / 2, height / 2))
.alphaDecay(.01)
.on('tick',ticked)
var duration = 35000;
d3.timer(function(d){
var totalCycles = Math.floor(d/duration);
elapsed = d/duration - totalCycles;
d3.select("#forceChart")
.attr('transform','translate(' + width/2 + ',' + height/2 + ') '+'rotate(' + (elapsed*360) + ')');
})
function ticked(){
//calculate convex hull
var hull = d3.polygonHull(graph.nodes.map(function(d) { return [d.x,d.y]; }) );
d3.select('.hull').datum(hull).attr("d", function(d) { return "M" + d.join("L") + "Z"; });
//calculate delauney
sites = graph.nodes.map(function(d) { return [d.x,d.y]; });
var triangles = triangle.selectAll(".delauney")
.data(voronoi.triangles(sites))
triangles.enter()
.append("path")
.attr('class','delauney')
.merge(triangles)
.call(redrawTriangle)
triangles.exit().remove();
d3.select('svg').selectAll('circle')
.attr('cx',(d)=>{return d.x})
.attr('cy',(d)=>{return d.y})
d3.select('svg').selectAll('line')
.attr('x1',(d)=>{return d.source.x})
.attr('x2',(d)=>{return d.target.x})
.attr('y1',(d)=>{return d.source.y})
.attr('y2',(d)=>{return d.target.y})
}
function redrawTriangle(triangle) {
d3.selectAll(".delauney")
.classed("primary", function(d) { return d[0] === sites[0] || d[1] === sites[0] || d[2] === sites[0]; })
.attr("d", function(d) { return "M" + d.join("L") + "Z"; });
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment