Created
September 14, 2019 17:10
-
-
Save keithcollins/f944a0bd4c0f12f066b5545c8c6a7525 to your computer and use it in GitHub Desktop.
blobs
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
<style> | |
#chart { | |
max-width:940px; | |
margin: 0 auto; | |
} | |
</style> | |
<div id="chart"></div> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://npmcdn.com/regl/dist/regl.min.js"></script> | |
<script> | |
const width = 940; | |
const height = 500; | |
const duration = 2000; | |
const max_radius = 20; | |
const max_distance = 200; | |
const force_strength = 0.03; | |
const fill_color = [.8,.8,.8]; | |
const stroke_color = [0,0,0]; | |
const circle_multiplier = (window.devicePixelRatio > 1) ? 4.0 : 2.0; | |
const centers = [ | |
{x: width*0.25, y:height*0.25}, | |
{x: width-width*0.25, y:height*0.25}, | |
{x: width*0.25, y:height-height*0.25}, | |
{x: width-width*0.25, y:height-height*0.25}, | |
]; | |
const data = d3.range(250).map(n => { | |
const r = Math.round(Math.random() * max_radius); | |
const center_index = Math.floor(Math.random() * 4) + 0; | |
return { | |
r: r, | |
x: centers[center_index].x, | |
y: centers[center_index].y, | |
center_x: centers[center_index].x, | |
center_y: centers[center_index].y, | |
random_x: Math.round(Math.random() * width), | |
random_y: Math.round(Math.random() * height), | |
stroke_size: .08/(r/max_radius), | |
charge: -Math.pow(r, 2.0) * force_strength | |
} | |
}); | |
let simulation = null; | |
let i = 0; | |
const container = d3.select("#chart") | |
.style('height', height + 'px'); | |
const runSimulation = function() { | |
const dur = (i == 0) ? 0 : duration; | |
const pos_x_field = ((i % 2) == 1) ? "center_x" : "random_x"; | |
const pos_y_field = ((i % 2) == 1) ? "center_y" : "random_y"; | |
i++; | |
const timeout_id = setTimeout(()=>{ | |
simulation = d3.forceSimulation(data) | |
.velocityDecay(0.2) | |
.force('x', d3.forceX().strength(force_strength).x(d => d[pos_x_field] )) | |
.force('y', d3.forceY().strength(force_strength).y(d => d[pos_y_field] )) | |
.force('charge', d3.forceManyBody().strength(d => d.charge).distanceMax(max_distance)) | |
.stop(); | |
if (i < 100) runSimulation(); | |
},dur); | |
} | |
runSimulation(); | |
createREGL({ | |
onDone, | |
container: container.node(), | |
extensions: ['oes_standard_derivatives'] | |
}); | |
function onDone(err, regl) { | |
const frameLoop = regl.frame(() => { | |
regl.clear({ | |
color: [1,1,1,0], | |
depth: 1 | |
}); | |
if (simulation) simulation.tick(); | |
drawPoints({ | |
stage_width: width, | |
stage_height: height, | |
fill_color, | |
stroke_color, | |
circle_multiplier | |
}); | |
}); | |
const drawPoints = regl({ | |
blend: { | |
enable: true, | |
func: { | |
srcRGB: 'src alpha', | |
srcAlpha: 'src alpha', | |
dstRGB: 'one minus src alpha', | |
dstAlpha: 'one minus src alpha' | |
} | |
}, | |
depth: { enable: false }, | |
attributes: { | |
position: () => data.map(d => [d.x, d.y]), | |
r: data.map(d => d.r), | |
stroke_size: data.map(d => d.stroke_size) | |
}, | |
uniforms: { | |
stage_width: regl.prop('stage_width'), | |
stage_height: regl.prop('stage_height'), | |
stroke_width: regl.prop('stroke_width'), | |
fill_color: regl.prop('fill_color'), | |
stroke_color: regl.prop('stroke_color'), | |
circle_multiplier: regl.prop('circle_multiplier') | |
}, | |
count: data.length, | |
primitive: 'points', | |
vert: ` | |
precision mediump float; | |
attribute vec2 position; | |
attribute float r; | |
attribute float stroke_size; | |
varying float s_s; | |
uniform float stage_width; | |
uniform float stage_height; | |
uniform float circle_multiplier; | |
vec2 normalizeCoords(vec2 position) { | |
float x = position[0]; | |
float y = position[1]; | |
return vec2( | |
2.0 * ((x / stage_width) - 0.5), | |
-(2.0 * ((y / stage_height) - 0.5)) | |
); | |
} | |
void main () { | |
s_s = stroke_size; | |
gl_PointSize = r*circle_multiplier; | |
gl_Position = vec4(normalizeCoords(position), 0.0, 1.0); | |
}`, | |
frag: ` | |
#extension GL_OES_standard_derivatives : enable | |
precision mediump float; | |
uniform vec3 fill_color; | |
uniform vec3 stroke_color; | |
varying float s_s; | |
void main () { | |
vec2 cxy = 2.0 * gl_PointCoord - 1.0; | |
float dist = dot(cxy, cxy); | |
float delta = fwidth(dist); | |
float alpha = 1.0 - smoothstep(1.0 - delta, 1.0 + delta, dist); | |
float outer_edge_center = 1.0 - s_s; | |
float stroke = 1.0 - smoothstep(outer_edge_center - delta, outer_edge_center + delta, dist); | |
// gl_FragColor = vec4(fill_color,1.0) * alpha; | |
gl_FragColor = vec4( mix(stroke_color, fill_color, stroke), 1.0 ) * alpha; | |
gl_FragColor.rgb *= gl_FragColor.a; | |
}` | |
}); | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment