Built with blockbuilder.org
Last active
August 30, 2017 04:01
-
-
Save ericsoco/1b5d498ddb1bb48fbc9b099e2ed931be to your computer and use it in GitHub Desktop.
contour blob
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: mit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://d3js.org/d3-contour.v1.min.js"></script> | |
<style> | |
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
</style> | |
</head> | |
<body> | |
<div id="app" style="width: 1050px; height: 1500px"></div> | |
<script> | |
var MODE_SVG = 'svg', | |
MODE_CANVAS = 'canvas', | |
MODE = MODE_CANVAS; | |
var SHOW_NODES = false; | |
var BLEND_MULTIPLY = true; | |
var app = void 0, | |
outerwidth = void 0, | |
outerheight = void 0, | |
width = void 0, | |
height = void 0, | |
nodes = void 0, | |
links = void 0; | |
var contourGenerator = void 0, | |
contours = void 0, | |
colorScale = void 0; | |
var graph = void 0, | |
circles = void 0; | |
var canvas = void 0, | |
ctx = void 0, | |
path = void 0; | |
var attractParams = { | |
strength: { | |
base: 16, | |
value: 16, | |
mod: 16, | |
speed: 0.01, | |
counter: 0.5 * Math.PI | |
} | |
}; | |
var collisionParams = { | |
strength: { | |
base: 4, | |
value: 4, | |
mod: 4, | |
speed: 0.01, | |
counter: Math.PI | |
}, | |
radius: { | |
base: 4, | |
value: 4, | |
mod: 4, | |
speed: 0.01, | |
counter: Math.PI | |
} | |
}; | |
var simulation = void 0; | |
function init(params) { | |
initGraph(); | |
}; | |
function initGraph() { | |
app = d3.select('#app').attr('class', 'contour-01'); | |
var margin = { | |
top: 0, | |
right: 0, | |
bottom: 0, | |
left: 0 | |
}; | |
outerWidth = app.node().offsetWidth, | |
outerHeight = app.node().offsetHeight; | |
width = outerWidth - margin.left - margin.right, height = outerHeight - margin.top - margin.bottom; | |
// dummy data | |
var RADIUS_MIN = 8; | |
var RADIUS_VAR = 16; | |
var NUM_NODES = 50; | |
nodes = []; | |
for (var i = 0; i < NUM_NODES; i++) { | |
nodes.push({ | |
// x: (0.3 + 0.4 * Math.random()) * width, | |
// y: (0.3 + 0.4 * Math.random()) * height, | |
// r: RADIUS_MIN + Math.random() * RADIUS_VAR, | |
r: RADIUS_MIN, | |
id: i | |
}); | |
} | |
/* | |
const NODE_LINK_RATIO = 5; | |
const NUM_LINKS = Math.floor(NUM_NODES / NODE_LINK_RATIO); | |
let source, target; | |
links = []; | |
for (let i=0; i<NUM_LINKS; i++) { | |
source = Math.floor(Math.random() * NUM_NODES); | |
target = (source + Math.floor(Math.random() * 10)) % NUM_NODES; | |
links.push({ | |
source, | |
target | |
}); | |
} | |
*/ | |
simulation = d3.forceSimulation(nodes).force('collision', d3.forceCollide().strength(collisionParams.strength.value).radius(function (d) { | |
return 1.5 * d.r; | |
})) | |
/* | |
.force('link', d3.forceLink() | |
.id(d => d.id) | |
.links(links) | |
.distance(30) | |
) | |
*/ | |
.force('attract', d3.forceManyBody().strength(attractParams.strength.value).distanceMax(600)).force('center', d3.forceCenter(width / 2, height / 2)).alphaDecay(0).stop(); | |
//.on('tick', layoutTick); | |
contourGenerator = (0, d3.contourDensity)().x(function (d) { | |
return d.x; | |
}).y(function (d) { | |
return d.y; | |
}).size([width, height]).bandwidth(40).thresholds(16); | |
// .cellSize(64); | |
if (MODE === MODE_CANVAS) { | |
canvas = app.append('canvas').attr('width', outerWidth).attr('height', outerHeight); | |
ctx = canvas.node().getContext('2d'); | |
if (BLEND_MULTIPLY) ctx.globalCompositeOperation = 'multiply'; | |
ctx.translate(margin.left, margin.top); | |
path = d3.geoPath().context(ctx); | |
// colorScale = d3.scaleSequential(d3ScaleChromatic.interpolateYlGnBu); | |
colorScale = d3.scaleSequential(d3.interpolateCubehelixDefault); | |
// colorScale = d3.scaleSequential(d3ScaleChromatic.interpolatePuBu); | |
} else { | |
graph = app.append('svg').attr('width', outerWidth).attr('height', outerHeight).append('g').attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')'); | |
circles = graph.selectAll('circle').data(nodes).enter().append('circle').attr('cx', function (d) { | |
return d.x; | |
}).attr('cy', function (d) { | |
return d.y; | |
}).attr('r', function (d) { | |
return d.r; | |
}); | |
contours = graph.selectAll('path').data(contourGenerator(nodes)).enter().append('path').classed('contour', true).attr('d', d3.geoPath()); | |
} | |
function tick () { | |
simulation.tick(); | |
layoutTick(); | |
} | |
window.setTimeout(function () { | |
window.setInterval(tick, 500); | |
}, 2000); | |
} | |
function layoutTick() { | |
updateParams(collisionParams.strength); | |
updateParams(collisionParams.radius); | |
updateParams(attractParams.strength); | |
simulation.force('collision').strength(collisionParams.strength.value).radius(function (d) { | |
return collisionParams.radius.value * d.r; | |
}); | |
simulation.force('attract').strength(attractParams.strength.value); | |
draw(); | |
} | |
function updateParams(params) { | |
params.counter += params.speed; | |
params.value = params.base + Math.sin(params.counter) * params.mod; | |
} | |
function draw() { | |
if (MODE === MODE_CANVAS) { | |
ctx.clearRect(0, 0, width, height); | |
ctx.translate(outerWidth/2, outerHeight/2); | |
ctx.rotate(0.1 * Math.PI); | |
ctx.translate(-outerWidth/2, -outerHeight/2); | |
contours = contourGenerator(nodes); | |
var numContours = contours.length; | |
// colorScale.domain([0, (BLEND_MULTIPLY ? 1.5 : 1) * numContours]); | |
colorScale.domain([(BLEND_MULTIPLY ? 1.5 : 1) * numContours, 0]); // reverse for cubehelix | |
contours.forEach(function (d, i) { | |
var color = colorScale(i); | |
if (BLEND_MULTIPLY) { | |
color = d3.color(colorScale(i)); | |
color.opacity = 1 - 0.75 * Math.pow(i / numContours, 0.75); | |
} | |
// console.log(i, color); | |
ctx.fillStyle = color; | |
ctx.beginPath(); | |
path(d); | |
ctx.fill(); | |
}); | |
if (SHOW_NODES) { | |
nodes.forEach(function (d) { | |
ctx.strokeStyle = 'rgba(0, 0, 0, 0.85)'; | |
ctx.beginPath(); | |
ctx.arc(d.x, d.y, d.r, 0, 2 * Math.PI); | |
ctx.stroke(); | |
}); | |
} | |
} else { | |
circles.attr('cx', function (d) { | |
return d.x; | |
}).attr('cy', function (d) { | |
return d.y; | |
}).attr('r', function (d) { | |
return d.r; | |
}); | |
contours.data(contourGenerator(nodes)).attr('d', d3.geoPath()); | |
} | |
} | |
init(); | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment