Blog to follow eventually... There's also a cool way to do this with textures that I will create one day, but hopefully this gives you an idea of how to play around with particles in regl. Code is similar to my canvas example.
-
-
Save tafsiri/dba04b04ae949760f96f97a2fba23ba6 to your computer and use it in GitHub Desktop.
Animate 100,000 points with regl
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 | |
height: 720 | |
border: no |
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
/** | |
* Given a set of points, lay them out in a phyllotaxis layout. | |
* Mutates the `points` passed in by updating the x and y values. | |
* | |
* @param {Object[]} points The array of points to update. Will get `x` and `y` set. | |
* @param {Number} pointWidth The size in pixels of the point's width. Should also include margin. | |
* @param {Number} xOffset The x offset to apply to all points | |
* @param {Number} yOffset The y offset to apply to all points | |
* | |
* @return {Object[]} points with modified x and y | |
*/ | |
function phyllotaxisLayout(points, pointWidth, xOffset = 0, yOffset = 0, iOffset = 0) { | |
// theta determines the spiral of the layout | |
const theta = Math.PI * (3 - Math.sqrt(5)); | |
const pointRadius = pointWidth / 2; | |
points.forEach((point, i) => { | |
const index = (i + iOffset) % points.length; | |
const phylloX = pointRadius * Math.sqrt(index) * Math.cos(index * theta); | |
const phylloY = pointRadius * Math.sqrt(index) * Math.sin(index * theta); | |
point.x = xOffset + phylloX - pointRadius; | |
point.y = yOffset + phylloY - pointRadius; | |
}); | |
return points; | |
} | |
/** | |
* Given a set of points, lay them out in a grid. | |
* Mutates the `points` passed in by updating the x and y values. | |
* | |
* @param {Object[]} points The array of points to update. Will get `x` and `y` set. | |
* @param {Number} pointWidth The size in pixels of the point's width. Should also include margin. | |
* @param {Number} gridWidth The width of the grid of points | |
* | |
* @return {Object[]} points with modified x and y | |
*/ | |
function gridLayout(points, pointWidth, gridWidth) { | |
const pointHeight = pointWidth; | |
const pointsPerRow = Math.floor(gridWidth / pointWidth); | |
const numRows = points.length / pointsPerRow; | |
points.forEach((point, i) => { | |
point.x = pointWidth * (i % pointsPerRow); | |
point.y = pointHeight * Math.floor(i / pointsPerRow); | |
}); | |
return points; | |
} | |
/** | |
* Given a set of points, lay them out randomly. | |
* Mutates the `points` passed in by updating the x and y values. | |
* | |
* @param {Object[]} points The array of points to update. Will get `x` and `y` set. | |
* @param {Number} pointWidth The size in pixels of the point's width. Should also include margin. | |
* @param {Number} width The width of the area to place them in | |
* @param {Number} height The height of the area to place them in | |
* | |
* @return {Object[]} points with modified x and y | |
*/ | |
function randomLayout(points, pointWidth, width, height) { | |
points.forEach((point, i) => { | |
point.x = Math.random() * (width - pointWidth); | |
point.y = Math.random() * (height - pointWidth); | |
}); | |
return points; | |
} | |
/** | |
* Given a set of points, lay them out in a sine wave. | |
* Mutates the `points` passed in by updating the x and y values. | |
* | |
* @param {Object[]} points The array of points to update. Will get `x` and `y` set. | |
* @param {Number} pointWidth The size in pixels of the point's width. Should also include margin. | |
* @param {Number} width The width of the area to place them in | |
* @param {Number} height The height of the area to place them in | |
* | |
* @return {Object[]} points with modified x and y | |
*/ | |
function sineLayout(points, pointWidth, width, height) { | |
const amplitude = 0.3 * (height / 2); | |
const yOffset = height / 2; | |
const periods = 3; | |
const yScale = d3.scaleLinear() | |
.domain([0, points.length - 1]) | |
.range([0, periods * 2 * Math.PI]); | |
points.forEach((point, i) => { | |
point.x = (i / points.length) * (width - pointWidth); | |
point.y = amplitude * Math.sin(yScale(i)) + yOffset; | |
}); | |
return points; | |
} | |
/** | |
* Given a set of points, lay them out in a spiral. | |
* Mutates the `points` passed in by updating the x and y values. | |
* | |
* @param {Object[]} points The array of points to update. Will get `x` and `y` set. | |
* @param {Number} pointWidth The size in pixels of the point's width. Should also include margin. | |
* @param {Number} width The width of the area to place them in | |
* @param {Number} height The height of the area to place them in | |
* | |
* @return {Object[]} points with modified x and y | |
*/ | |
function spiralLayout(points, pointWidth, width, height) { | |
const amplitude = 0.3 * (height / 2); | |
const xOffset = width / 2; | |
const yOffset = height / 2; | |
const periods = 20; | |
const rScale = d3.scaleLinear() | |
.domain([0, points.length -1]) | |
.range([0, Math.min(width / 2, height / 2) - pointWidth]); | |
const thetaScale = d3.scaleLinear() | |
.domain([0, points.length - 1]) | |
.range([0, periods * 2 * Math.PI]); | |
points.forEach((point, i) => { | |
point.x = rScale(i) * Math.cos(thetaScale(i)) + xOffset | |
point.y = rScale(i) * Math.sin(thetaScale(i)) + yOffset; | |
}); | |
return points; | |
} | |
/** | |
* Generate an object array of `numPoints` length with unique IDs | |
* and assigned colors | |
*/ | |
function createPoints(numPoints, pointWidth, width, height) { | |
const colorScale = d3.scaleSequential(d3.interpolateViridis) | |
.domain([numPoints - 1, 0]); | |
const points = d3.range(numPoints).map(id => ({ | |
id, | |
color: colorScale(id), | |
})); | |
return randomLayout(points, pointWidth, width, height); | |
} |
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
function main(t,n){function o(t){var n=d3.scaleLinear().domain([0,1]).range([.4,1]);return function(o){var i=d3.rgb(t(n(o)));return[i.r/255,i.g/255,i.b/255]}}function i(t){var o=n({frag:"\n\t\t precision highp float;\n\t\t\tvarying vec3 fragColor;\n\n\t\t\tvoid main() {\n\t\t\t\tfloat x = gl_PointCoord.x;\n\t\t\t\tfloat y = gl_PointCoord.y;\n\n\t\t\t\t// Measure distance from center\n\t\t\t\tvec2 uv = vec2(x, y) * 2. - 1.;\n\t\t\t\tfloat point_dist = length(uv);\n\n\t\t\t\tif (point_dist > 1.000) {\n\t\t\t\t\tdiscard;\n\t\t\t\t}\n\t\t\t\tgl_FragColor = vec4(fragColor, 1.0);\n\t\t\t}\n\t\t\t",vert:"\n\t\t\tattribute vec2 positionStart;\n\t\t\tattribute vec2 positionEnd;\n\t\t\tattribute float index;\n\t\t\tattribute vec3 colorStart;\n\t\t\tattribute vec3 colorEnd;\n\n\t\t\tvarying vec3 fragColor;\n\n\t\t\tuniform float pointWidth;\n\t\t\tuniform float stageWidth;\n\t\t\tuniform float stageHeight;\n\t\t\tuniform float elapsed;\n\t\t\tuniform float duration;\n\t\t\tuniform float delayByIndex;\n\t\t\tvoid main() {\n\t\t\t\tgl_PointSize = pointWidth;\n\n\t\t\t\tfloat delay = delayByIndex * index;\n\t float t;\n\n\t // drawing without animation, so show end state immediately\n\t if (duration == 0.0) {\n\t t = 1.0;\n\n\t // still delaying before animating\n\t } else if (elapsed < delay) {\n\t t = 0.0;\n\t } else {\n\t t = 2.0 * ((elapsed - delay) / duration);\n\n\t // cubic easing (cubicInOut) -- note there are glslify things for this toPhyllotaxis\n\t // this is copied from d3.\n\t t = (t <= 1.0 ? t * t * t : (t -= 2.0) * t * t + 2.0) / 2.0;\n\n\t if (t > 1.0) {\n\t t = 1.0;\n\t }\n\t }\n\n\t\t\t\t// interpolate position\n\t float x = mix(positionStart[0], positionEnd[0], t);\n\t float y = mix(positionStart[1], positionEnd[1], t);\n\n\t // interpolate color\n\t fragColor = mix(colorStart, colorEnd, t);\n\n\t\t\t\t// scale to normalized device coordinates (-1, -1) to (1, 1)\n\t gl_Position = vec4(\n\t\t 2.0 * ((x / stageWidth) - 0.5),\n\t\t // invert y since we think [0,0] is bottom left in pixel space (needed for d3.zoom)\n\t\t -(2.0 * ((y / stageHeight) - 0.5)),\n\t\t 0.0,\n\t\t 1.0);\n\t\t\t}\n\t\t\t",attributes:{positionStart:t.map(function(t){return[t.sx,t.sy]}),positionEnd:t.map(function(t){return[t.tx,t.ty]}),colorStart:t.map(function(t){return t.colorStart}),colorEnd:t.map(function(t){return t.colorEnd}),index:d3.range(t.length)},uniforms:{pointWidth:n.prop("pointWidth"),stageWidth:n.prop("stageWidth"),stageHeight:n.prop("stageHeight"),delayByIndex:n.prop("delayByIndex"),duration:n.prop("duration"),elapsed:function(t,n){var o=t.time,i=n.startTime;return void 0===i&&(i=0),1e3*(o-i)}},count:t.length,primitive:"points"});return o}function e(t,o){console.log("animating with new layout"),o.forEach(function(t){t.sx=t.tx,t.sy=t.ty,t.colorStart=t.colorEnd}),t(o);var r=S[b];o.forEach(function(t,n){t.tx=t.x,t.ty=t.y,t.colorEnd=r(n/o.length)});var d=i(o);a=n.frame(function(t){var i=t.time;null===E&&(E=i),n.clear({color:[0,0,0,1],depth:1}),d({pointWidth:l,stageWidth:s,stageHeight:c,duration:u,delayByIndex:f,startTime:E}),i-E>g/1e3&&(console.log("done animating, moving to next layout"),a.cancel(),x=(x+1)%v.length,E=null,b=(b+1)%S.length,e(v[x],o))})}var a,r=1e4,l=20,d=1,s=window.innerWidth,c=window.innerHeight,u=1500,f=500/r,g=u+f*r,p=function(t){return phyllotaxisLayout(t,l+d,s/2,c/2)},h=function(t){return gridLayout(t,l+d,s)},m=function(t){return sineLayout(t,l+d,s,c)},y=function(t){return spiralLayout(t,l+d,s,c)},v=[p,h,m,y],x=0,E=null,S=[d3.scaleSequential(d3.interpolateViridis),d3.scaleSequential(d3.interpolateMagma),d3.scaleSequential(d3.interpolateInferno),d3.scaleSequential(d3.interpolateCool)].map(o),b=0,w=createPoints(r,l,s,c);window.points=w,w.forEach(function(t,n){t.tx=s/2,t.ty=c/2,t.colorEnd=S[b](n/w.length)}),e(v[x],w)}regl({extensions:["OES_texture_float"],onDone:main}); | |
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNjcmlwdC5qcyJdLCJuYW1lcyI6WyJtYWluIiwiZXJyIiwicmVnbCIsIndyYXBDb2xvclNjYWxlIiwic2NhbGUiLCJjb25zdCIsInRTY2FsZSIsImQzIiwic2NhbGVMaW5lYXIiLCJkb21haW4iLCJyYW5nZSIsInQiLCJyZ2IiLCJyIiwiZyIsImIiLCJjcmVhdGVEcmF3UG9pbnRzIiwicG9pbnRzIiwiZHJhd1BvaW50cyIsImZyYWciLCJ2ZXJ0IiwiYXR0cmlidXRlcyIsInBvc2l0aW9uU3RhcnQiLCJtYXAiLCJkIiwic3giLCJzeSIsInBvc2l0aW9uRW5kIiwidHgiLCJ0eSIsImNvbG9yU3RhcnQiLCJjb2xvckVuZCIsImluZGV4IiwibGVuZ3RoIiwidW5pZm9ybXMiLCJwb2ludFdpZHRoIiwicHJvcCIsInN0YWdlV2lkdGgiLCJzdGFnZUhlaWdodCIsImRlbGF5QnlJbmRleCIsImR1cmF0aW9uIiwiZWxhcHNlZCIsInJlZiIsInJlZiQxIiwidGltZSIsInN0YXJ0VGltZSIsInByaW1pdGl2ZSIsImFuaW1hdGUiLCJsYXlvdXQiLCJjb25zb2xlIiwibG9nIiwiZm9yRWFjaCIsImNvbG9yU2NhbGUiLCJjb2xvclNjYWxlcyIsImN1cnJlbnRDb2xvclNjYWxlIiwiaSIsIngiLCJ5IiwiZnJhbWVMb29wIiwiZnJhbWUiLCJjbGVhciIsImNvbG9yIiwiZGVwdGgiLCJ3aWR0aCIsImhlaWdodCIsIm1heER1cmF0aW9uIiwiY2FuY2VsIiwiY3VycmVudExheW91dCIsImxheW91dHMiLCJudW1Qb2ludHMiLCJwb2ludE1hcmdpbiIsIndpbmRvdyIsImlubmVyV2lkdGgiLCJpbm5lckhlaWdodCIsInRvUGh5bGxvdGF4aXMiLCJwaHlsbG90YXhpc0xheW91dCIsInRvR3JpZCIsImdyaWRMYXlvdXQiLCJ0b1NpbmUiLCJzaW5lTGF5b3V0IiwidG9TcGlyYWwiLCJzcGlyYWxMYXlvdXQiLCJzY2FsZVNlcXVlbnRpYWwiLCJpbnRlcnBvbGF0ZVZpcmlkaXMiLCJpbnRlcnBvbGF0ZU1hZ21hIiwiaW50ZXJwb2xhdGVJbmZlcm5vIiwiaW50ZXJwb2xhdGVDb29sIiwiY3JlYXRlUG9pbnRzIiwiZXh0ZW5zaW9ucyIsIm9uRG9uZSJdLCJtYXBwaW5ncyI6IkFBQUEsUUFBU0EsTUFBS0MsRUFBS0MsR0F1QmxCLFFBQVNDLEdBQWVDLEdBQ3ZCQyxHQUFNQyxHQUFXQyxHQUFDQyxjQUFjQyxRQUFTLEVBQUksSUFBRUMsT0FBTyxHQUFPLEdBQzdELE9BQU8sVUFBQUMsR0FDTk4sR0FBTU8sR0FBUUwsR0FBQ0ssSUFBSVIsRUFBTUUsRUFBU0ssSUFDbEMsUUFBUUMsRUFBSUMsRUFBSSxJQUFLRCxFQUFJRSxFQUFJLElBQUtGLEVBQUlHLEVBQUksTUFhNUMsUUFBU0MsR0FBaUJDLEdBQ3pCWixHQUFNYSxHQUFhaEIsR0FDbEJpQixLQUFNLDhaQW1CTkMsS0FBTSxpcERBeUROQyxZQWtFQ0MsY0FBQUwsRUFBQU0sSUFBWSxTQUFBQyxHQUFBLE9BQUFBLEVBQUFDLEdBQUFELEVBQUFFLE1BQ1pDLFlBQUFWLEVBQVNNLElBQUEsU0FBQUMsR0FBQSxPQUFBQSxFQUFBSSxHQUFBSixFQUFBSyxNQUNUQyxXQUFFYixFQUFBTSxJQUFBLFNBQUFDLEdBQUEsTUFBQUEsR0FBQU0sYUFoRUZDLFNBQVVkLEVBQU9NLElBQUksU0FBQUMsR0FBRSxNQUFHQSxHQUFFTyxXQWtFN0JDLE1BQVF6QixHQUFHRyxNQUFBTyxFQUFhZ0IsU0E5RHhCQyxVQWtFQ0MsV0FBQWpDLEVBQWlCa0MsS0FBQSxjQUNqQkMsV0FBWW5DLEVBQUtrQyxLQUFBLGNBQ2pCRSxZQUFBcEMsRUFBaUJrQyxLQUFJLGVBQ3JCRyxhQUFRckMsRUFBUWtDLEtBQUEsZ0JBQ2hCSSxTQUFBdEMsRUFBQWtDLEtBQUEsWUE5REdLLFFBQVMsU0FBQ0MsRUFBVUMsTUFBUkMsR0FBSUYsRUFBQUUseUNBQWtCLEdBQTZCLEtBQXBCQSxFQUFPQyxLQW9FeER4QyxNQUFNWSxFQUFTZ0IsT0FDZmEsVUFBYSxVQUdaLE9BQU81QixHQUlSLFFBQVE2QixHQUFRQyxFQUFBL0IsR0FDaEJnQyxRQUFBQyxJQUFBLDZCQS9EQ2pDLEVBQU9rQyxRQUFRLFNBQUEzQixHQUNkQSxFQUFFQyxHQUFLRCxFQUFFSSxHQWtFWkosRUFBS0UsR0FBQUYsRUFBQUssR0FoRUZMLEVBQUVNLFdBQWFOLEVBQUVPLFdBSWxCaUIsRUFBTy9CLEVBb0VQWixJQUFDK0MsR0FBQUMsRUFBQUMsRUFoRURyQyxHQUFPa0MsUUFBUSxTQUFDM0IsRUFBRytCLEdBQ2xCL0IsRUFBRUksR0FBS0osRUFBRWdDLEVBQ1RoQyxFQUFFSyxHQUFLTCxFQUFFaUMsRUFDVGpDLEVBQUVPLFNBQVdxQixFQUFXRyxFQUFJdEMsRUFBT2dCLFNBSXBDNUIsSUFBTWEsR0FBYUYsRUFBaUJDLEVBRXBDeUMsR0FBWXhELEVBQUt5RCxNQUFNLFNBQUNqQixNQUFFRSxHQUFJRixFQUFBRSxJQUNYLFFBQWRDLElBQ0hBLEVBQVlELEdBR2IxQyxFQUFLMEQsT0FFSkMsT0FBUSxFQUFHLEVBQUcsRUFBRyxHQUNqQkMsTUFBTyxJQUdSNUMsR0FDQ2lCLFdBQUFBLEVBQ0FFLFdBQVkwQixFQUNaekIsWUFBYTBCLEVBQ2J4QixTQUFBQSxFQUNBRCxhQUFBQSxFQUNBTSxVQUFBQSxJQUdHRCxFQUFPQyxFQUFhb0IsRUFBYyxNQUNyQ2hCLFFBQVFDLElBQUkseUNBQ1pRLEVBQVVRLFNBRVZDLEdBQWlCQSxFQUFnQixHQUFLQyxFQUFRbkMsT0FDOUNZLEVBQVksS0FDWlMsR0FBcUJBLEVBQW9CLEdBQUtELEVBQVlwQixPQUMxRGMsRUFBUXFCLEVBQVFELEdBQWdCbEQsTUFsTW5DWixHQWtCSXFELEdBbEJFVyxFQUFZLElBQ1psQyxFQUFlLEdBQ2ZtQyxFQUFnQixFQUNoQlAsRUFBUVEsT0FBT0MsV0FDZlIsRUFBU08sT0FBT0UsWUFDaEJqQyxFQUFXLEtBQ1hELEVBQWUsSUFBTThCLEVBQ3JCSixFQUFjekIsRUFBV0QsRUFBZThCLEVBRXhDSyxFQUFnQixTQUFBekQsR0FBQyxNQUFBMEQsbUJBQVcxRCxFQUFBa0IsRUFBMEJtQyxFQUFhUCxFQUFBLEVBQVdDLEVBQU8sSUFDckZZLEVBQVMsU0FBQTNELEdBQUMsTUFBQTRELFlBQVE1RCxFQUFHa0IsRUFBbUJtQyxFQUFhUCxJQUNyRGUsRUFBUyxTQUFBN0QsR0FBQyxNQUFBOEQsWUFBUTlELEVBQUdrQixFQUFtQm1DLEVBQWFQLEVBQUFDLElBQ3JEZ0IsRUFBVyxTQUFBL0QsR0FBQyxNQUFBZ0UsY0FBV2hFLEVBQUFrQixFQUFxQm1DLEVBQWFQLEVBQUFDLElBRXpESSxHQUFXTSxFQUFlRSxFQUFRRSxFQUFRRSxHQUM1Q2IsRUFBZ0IsRUFDaEJ0QixFQUFZLEtBY1ZRLEdBQ0w5QyxHQUFHMkUsZ0JBQWdCM0UsR0FBRzRFLG9CQUN0QjVFLEdBQUcyRSxnQkFBZ0IzRSxHQUFHNkUsa0JBQ3RCN0UsR0FBRzJFLGdCQUFnQjNFLEdBQUc4RSxvQkFDdEI5RSxHQUFHMkUsZ0JBQWdCM0UsR0FBRytFLGtCQUNyQi9ELElBQUlwQixHQUNGbUQsRUFBb0IsRUFxS2xCckMsRUFBU3NFLGFBQWFsQixFQUFXbEMsRUFBWTRCLEVBQU9DLEVBQzFETyxRQUFPdEQsT0FBU0EsRUFDaEJBLEVBQU9rQyxRQUFRLFNBQUMzQixFQUFHK0IsR0FDbEIvQixFQUFFSSxHQUFLbUMsRUFBUSxFQUNmdkMsRUFBRUssR0FBS21DLEVBQVMsRUFDaEJ4QyxFQUFFTyxTQUFXc0IsRUFBWUMsR0FBbUJDLEVBQUl0QyxFQUFPZ0IsVUFHeERjLEVBQVFxQixFQUFRRCxHQUFnQmxELEdBS2pDZixNQUVFc0YsWUFDRSxxQkFJRkMsT0FBUXpGIiwiZmlsZSI6InNjcmlwdC5qcyIsInNvdXJjZXNDb250ZW50IjpbImZ1bmN0aW9uIG1haW4oZXJyLCByZWdsKSB7XG5cdGNvbnN0IG51bVBvaW50cyA9IDEwMDAwO1xuXHRjb25zdCBwb2ludFdpZHRoID0gMjA7XG5cdGNvbnN0IHBvaW50TWFyZ2luID0gMTtcblx0Y29uc3Qgd2lkdGggPSB3aW5kb3cuaW5uZXJXaWR0aDtcblx0Y29uc3QgaGVpZ2h0ID0gd2luZG93LmlubmVySGVpZ2h0O1xuXHRjb25zdCBkdXJhdGlvbiA9IDE1MDA7XG5cdGNvbnN0IGRlbGF5QnlJbmRleCA9IDUwMCAvIG51bVBvaW50cztcblx0Y29uc3QgbWF4RHVyYXRpb24gPSBkdXJhdGlvbiArIGRlbGF5QnlJbmRleCAqIG51bVBvaW50czsgLy8gaW5jbHVkZSBtYXggZGVsYXkgaW4gaGVyZVxuXG5cdGNvbnN0IHRvUGh5bGxvdGF4aXMgPSAocG9pbnRzKSA9PiBwaHlsbG90YXhpc0xheW91dChwb2ludHMsIHBvaW50V2lkdGggKyBwb2ludE1hcmdpbiwgd2lkdGggLyAyLCBoZWlnaHQgLyAyKTtcblx0Y29uc3QgdG9HcmlkID0gKHBvaW50cykgPT4gZ3JpZExheW91dChwb2ludHMsIHBvaW50V2lkdGggKyBwb2ludE1hcmdpbiwgd2lkdGgpO1xuXHRjb25zdCB0b1NpbmUgPSAocG9pbnRzKSA9PiBzaW5lTGF5b3V0KHBvaW50cywgcG9pbnRXaWR0aCArIHBvaW50TWFyZ2luLCB3aWR0aCwgaGVpZ2h0KTtcblx0Y29uc3QgdG9TcGlyYWwgPSAocG9pbnRzKSA9PiBzcGlyYWxMYXlvdXQocG9pbnRzLCBwb2ludFdpZHRoICsgcG9pbnRNYXJnaW4sIHdpZHRoLCBoZWlnaHQpO1xuXG5cdGNvbnN0IGxheW91dHMgPSBbdG9QaHlsbG90YXhpcywgdG9HcmlkLCB0b1NpbmUsIHRvU3BpcmFsXTtcblx0bGV0IGN1cnJlbnRMYXlvdXQgPSAwO1xuXHRsZXQgc3RhcnRUaW1lID0gbnVsbDsgLy8gaW4gc2Vjb25kc1xuXHQvLyBzdGFydCBhbmltYXRpb24gbG9vcCAobm90ZTogdGltZSBpcyBpbiBzZWNvbmRzKVxuXHRsZXQgZnJhbWVMb29wO1xuXG5cdC8vIHdyYXAgZDMgY29sb3Igc2NhbGVzIHNvIHRoZXkgcHJvZHVjZSB2ZWMzcyB3aXRoIHZhbHVlcyAwLTFcblx0Ly8gYWxzbyBsaW1pdCB0aGUgdCB2YWx1ZSB0byByZW1vdmUgZGFya2VzdCBjb2xvclxuXHRmdW5jdGlvbiB3cmFwQ29sb3JTY2FsZShzY2FsZSkge1xuXHRcdGNvbnN0IHRTY2FsZSA9IGQzLnNjYWxlTGluZWFyKCkuZG9tYWluKFswLCAxXSkucmFuZ2UoWzAuNCwgMV0pO1xuXHRcdHJldHVybiB0ID0+IHtcblx0XHRcdGNvbnN0IHJnYiA9IGQzLnJnYihzY2FsZSh0U2NhbGUodCkpKTtcblx0XHRcdHJldHVybiBbcmdiLnIgLyAyNTUsIHJnYi5nIC8gMjU1LCByZ2IuYiAvIDI1NV07XG5cdFx0fTtcblx0fVxuXG5cdGNvbnN0IGNvbG9yU2NhbGVzID0gW1xuXHRcdGQzLnNjYWxlU2VxdWVudGlhbChkMy5pbnRlcnBvbGF0ZVZpcmlkaXMpLFxuXHRcdGQzLnNjYWxlU2VxdWVudGlhbChkMy5pbnRlcnBvbGF0ZU1hZ21hKSxcblx0XHRkMy5zY2FsZVNlcXVlbnRpYWwoZDMuaW50ZXJwb2xhdGVJbmZlcm5vKSxcblx0XHRkMy5zY2FsZVNlcXVlbnRpYWwoZDMuaW50ZXJwb2xhdGVDb29sKSxcblx0XS5tYXAod3JhcENvbG9yU2NhbGUpO1xuXHRsZXQgY3VycmVudENvbG9yU2NhbGUgPSAwO1xuXG5cdC8vIGZ1bmN0aW9uIHRvIGNvbXBpbGUgYSBkcmF3IHBvaW50cyByZWdsIGZ1bmNcblx0ZnVuY3Rpb24gY3JlYXRlRHJhd1BvaW50cyhwb2ludHMpIHtcblx0XHRjb25zdCBkcmF3UG9pbnRzID0gcmVnbCh7XG5cdFx0XHRmcmFnOiBgXG5cdFx0ICBwcmVjaXNpb24gaGlnaHAgZmxvYXQ7XG5cdFx0XHR2YXJ5aW5nIHZlYzMgZnJhZ0NvbG9yO1xuXG5cdFx0XHR2b2lkIG1haW4oKSB7XG5cdFx0XHRcdGZsb2F0IHggPSBnbF9Qb2ludENvb3JkLng7XG5cdFx0XHRcdGZsb2F0IHkgPSBnbF9Qb2ludENvb3JkLnk7XG5cblx0XHRcdFx0Ly8gTWVhc3VyZSBkaXN0YW5jZSBmcm9tIGNlbnRlclxuXHRcdFx0XHR2ZWMyIHV2ID0gdmVjMih4LCB5KSAqIDIuIC0gMS47XG5cdFx0XHRcdGZsb2F0IHBvaW50X2Rpc3QgPSBsZW5ndGgodXYpO1xuXG5cdFx0XHRcdGlmIChwb2ludF9kaXN0ID4gMS4wMDApIHtcblx0XHRcdFx0XHRkaXNjYXJkO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGdsX0ZyYWdDb2xvciA9IHZlYzQoZnJhZ0NvbG9yLCAxLjApO1xuXHRcdFx0fVxuXHRcdFx0YCxcblxuXHRcdFx0dmVydDogYFxuXHRcdFx0YXR0cmlidXRlIHZlYzIgcG9zaXRpb25TdGFydDtcblx0XHRcdGF0dHJpYnV0ZSB2ZWMyIHBvc2l0aW9uRW5kO1xuXHRcdFx0YXR0cmlidXRlIGZsb2F0IGluZGV4O1xuXHRcdFx0YXR0cmlidXRlIHZlYzMgY29sb3JTdGFydDtcblx0XHRcdGF0dHJpYnV0ZSB2ZWMzIGNvbG9yRW5kO1xuXG5cdFx0XHR2YXJ5aW5nIHZlYzMgZnJhZ0NvbG9yO1xuXG5cdFx0XHR1bmlmb3JtIGZsb2F0IHBvaW50V2lkdGg7XG5cdFx0XHR1bmlmb3JtIGZsb2F0IHN0YWdlV2lkdGg7XG5cdFx0XHR1bmlmb3JtIGZsb2F0IHN0YWdlSGVpZ2h0O1xuXHRcdFx0dW5pZm9ybSBmbG9hdCBlbGFwc2VkO1xuXHRcdFx0dW5pZm9ybSBmbG9hdCBkdXJhdGlvbjtcblx0XHRcdHVuaWZvcm0gZmxvYXQgZGVsYXlCeUluZGV4O1xuXHRcdFx0dm9pZCBtYWluKCkge1xuXHRcdFx0XHRnbF9Qb2ludFNpemUgPSBwb2ludFdpZHRoO1xuXG5cdFx0XHRcdGZsb2F0IGRlbGF5ID0gZGVsYXlCeUluZGV4ICogaW5kZXg7XG5cdCAgICAgIGZsb2F0IHQ7XG5cblx0ICAgICAgLy8gZHJhd2luZyB3aXRob3V0IGFuaW1hdGlvbiwgc28gc2hvdyBlbmQgc3RhdGUgaW1tZWRpYXRlbHlcblx0ICAgICAgaWYgKGR1cmF0aW9uID09IDAuMCkge1xuXHQgICAgICAgIHQgPSAxLjA7XG5cblx0ICAgICAgLy8gc3RpbGwgZGVsYXlpbmcgYmVmb3JlIGFuaW1hdGluZ1xuXHQgICAgICB9IGVsc2UgaWYgKGVsYXBzZWQgPCBkZWxheSkge1xuXHQgICAgICAgIHQgPSAwLjA7XG5cdCAgICAgIH0gZWxzZSB7XG5cdCAgICAgICAgdCA9IDIuMCAqICgoZWxhcHNlZCAtIGRlbGF5KSAvIGR1cmF0aW9uKTtcblxuXHQgICAgICAgIC8vIGN1YmljIGVhc2luZyAoY3ViaWNJbk91dCkgLS0gbm90ZSB0aGVyZSBhcmUgZ2xzbGlmeSB0aGluZ3MgZm9yIHRoaXMgdG9QaHlsbG90YXhpc1xuXHQgICAgICAgIC8vIHRoaXMgaXMgY29waWVkIGZyb20gZDMuXG5cdCAgICAgICAgdCA9ICh0IDw9IDEuMCA/IHQgKiB0ICogdCA6ICh0IC09IDIuMCkgKiB0ICogdCArIDIuMCkgLyAyLjA7XG5cblx0ICAgICAgICBpZiAodCA+IDEuMCkge1xuXHQgICAgICAgICAgdCA9IDEuMDtcblx0ICAgICAgICB9XG5cdCAgICAgIH1cblxuXHRcdFx0XHQvLyBpbnRlcnBvbGF0ZSBwb3NpdGlvblxuXHQgICAgICBmbG9hdCB4ID0gbWl4KHBvc2l0aW9uU3RhcnRbMF0sIHBvc2l0aW9uRW5kWzBdLCB0KTtcblx0ICAgICAgZmxvYXQgeSA9IG1peChwb3NpdGlvblN0YXJ0WzFdLCBwb3NpdGlvbkVuZFsxXSwgdCk7XG5cblx0ICAgICAgLy8gaW50ZXJwb2xhdGUgY29sb3Jcblx0ICAgICAgZnJhZ0NvbG9yID0gbWl4KGNvbG9yU3RhcnQsIGNvbG9yRW5kLCB0KTtcblxuXHRcdFx0XHQvLyBzY2FsZSB0byBub3JtYWxpemVkIGRldmljZSBjb29yZGluYXRlcyAoLTEsIC0xKSB0byAoMSwgMSlcblx0ICAgICAgZ2xfUG9zaXRpb24gPSB2ZWM0KFxuXHRcdCAgICAgIDIuMCAqICgoeCAvIHN0YWdlV2lkdGgpIC0gMC41KSxcblx0XHQgICAgICAvLyBpbnZlcnQgeSBzaW5jZSB3ZSB0aGluayBbMCwwXSBpcyBib3R0b20gbGVmdCBpbiBwaXhlbCBzcGFjZSAobmVlZGVkIGZvciBkMy56b29tKVxuXHRcdCAgICAgIC0oMi4wICogKCh5IC8gc3RhZ2VIZWlnaHQpIC0gMC41KSksXG5cdFx0ICAgICAgMC4wLFxuXHRcdCAgICAgIDEuMCk7XG5cdFx0XHR9XG5cdFx0XHRgLFxuXG5cdFx0XHRhdHRyaWJ1dGVzOiB7XG5cdFx0XHRcdHBvc2l0aW9uU3RhcnQ6IHBvaW50cy5tYXAoZCA9PiBbZC5zeCwgZC5zeV0pLFxuXHRcdFx0XHRwb3NpdGlvbkVuZDogcG9pbnRzLm1hcChkID0+IFtkLnR4LCBkLnR5XSksXG5cdFx0XHRcdGNvbG9yU3RhcnQ6IHBvaW50cy5tYXAoZCA9PiBkLmNvbG9yU3RhcnQpLFxuXHRcdFx0XHRjb2xvckVuZDogcG9pbnRzLm1hcChkID0+IGQuY29sb3JFbmQpLFxuXHRcdFx0XHRpbmRleDogZDMucmFuZ2UocG9pbnRzLmxlbmd0aCksXG5cdFx0XHR9LFxuXG5cdFx0XHR1bmlmb3Jtczoge1xuXHRcdFx0XHRwb2ludFdpZHRoOiByZWdsLnByb3AoJ3BvaW50V2lkdGgnKSxcblx0XHRcdFx0c3RhZ2VXaWR0aDogcmVnbC5wcm9wKCdzdGFnZVdpZHRoJyksXG5cdFx0XHRcdHN0YWdlSGVpZ2h0OiByZWdsLnByb3AoJ3N0YWdlSGVpZ2h0JyksXG5cdFx0XHRcdGRlbGF5QnlJbmRleDogcmVnbC5wcm9wKCdkZWxheUJ5SW5kZXgnKSxcblx0ICAgICAgZHVyYXRpb246IHJlZ2wucHJvcCgnZHVyYXRpb24nKSxcblxuXHQgICAgICAvLyB0aW1lIGluIG1pbGxpc2Vjb25kcyBzaW5jZSB0aGUgcHJvcCBzdGFydFRpbWUgKGkuZS4gdGltZSBlbGFwc2VkKVxuXHQgICAgICBlbGFwc2VkOiAoeyB0aW1lIH0sIHsgc3RhcnRUaW1lID0gMCB9KSA9PiAodGltZSAtIHN0YXJ0VGltZSkgKiAxMDAwLFxuXHRcdFx0fSxcblxuXHRcdFx0Y291bnQ6IHBvaW50cy5sZW5ndGgsXG5cdFx0XHRwcmltaXRpdmU6ICdwb2ludHMnLFxuXHRcdH0pO1xuXG5cdFx0cmV0dXJuIGRyYXdQb2ludHM7XG5cdH1cblxuXG5cdGZ1bmN0aW9uIGFuaW1hdGUobGF5b3V0LCBwb2ludHMpIHtcblx0XHRjb25zb2xlLmxvZygnYW5pbWF0aW5nIHdpdGggbmV3IGxheW91dCcpO1xuXHRcdC8vIG1ha2UgcHJldmlvdXMgZW5kIHRoZSBuZXcgYmVnaW5uaW5nXG5cdFx0cG9pbnRzLmZvckVhY2goZCA9PiB7XG5cdFx0XHRkLnN4ID0gZC50eDtcblx0XHRcdGQuc3kgPSBkLnR5O1xuXHRcdFx0ZC5jb2xvclN0YXJ0ID0gZC5jb2xvckVuZDtcblx0XHR9KTtcblxuXHRcdC8vIGxheW91dCBwb2ludHNcblx0XHRsYXlvdXQocG9pbnRzKTtcblxuXHRcdC8vIGNvcHkgbGF5b3V0IHggeSB0byBlbmQgcG9zaXRpb25zXG5cdFx0Y29uc3QgY29sb3JTY2FsZSA9IGNvbG9yU2NhbGVzW2N1cnJlbnRDb2xvclNjYWxlXTtcblx0XHRwb2ludHMuZm9yRWFjaCgoZCwgaSkgPT4ge1xuXHRcdFx0ZC50eCA9IGQueDtcblx0XHRcdGQudHkgPSBkLnk7XG5cdFx0XHRkLmNvbG9yRW5kID0gY29sb3JTY2FsZShpIC8gcG9pbnRzLmxlbmd0aClcblx0XHR9KTtcblxuXHRcdC8vIGNyZWF0ZSB0aGUgcmVnbCBmdW5jdGlvbiB3aXRoIHRoZSBuZXcgc3RhcnQgYW5kIGVuZCBwb2ludHNcblx0XHRjb25zdCBkcmF3UG9pbnRzID0gY3JlYXRlRHJhd1BvaW50cyhwb2ludHMpO1xuXG5cdFx0ZnJhbWVMb29wID0gcmVnbC5mcmFtZSgoeyB0aW1lIH0pID0+IHtcblx0XHRcdGlmIChzdGFydFRpbWUgPT09IG51bGwpIHtcblx0XHRcdFx0c3RhcnRUaW1lID0gdGltZTtcblx0XHRcdH1cblxuXHRcdFx0cmVnbC5jbGVhcih7XG5cdFx0XHRcdC8vIGJhY2tncm91bmQgY29sb3IgKGJsYWNrKVxuXHRcdFx0XHRjb2xvcjogWzAsIDAsIDAsIDFdLFxuXHRcdFx0XHRkZXB0aDogMSxcblx0XHRcdH0pO1xuXG5cdFx0XHRkcmF3UG9pbnRzKHtcblx0XHRcdFx0cG9pbnRXaWR0aCxcblx0XHRcdFx0c3RhZ2VXaWR0aDogd2lkdGgsXG5cdFx0XHRcdHN0YWdlSGVpZ2h0OiBoZWlnaHQsXG5cdFx0XHRcdGR1cmF0aW9uLFxuXHRcdFx0XHRkZWxheUJ5SW5kZXgsXG5cdFx0XHRcdHN0YXJ0VGltZSxcblx0XHRcdH0pO1xuXG5cdFx0XHRpZiAodGltZSAtIHN0YXJ0VGltZSA+IChtYXhEdXJhdGlvbiAvIDEwMDApKSB7XG5cdFx0XHRcdGNvbnNvbGUubG9nKCdkb25lIGFuaW1hdGluZywgbW92aW5nIHRvIG5leHQgbGF5b3V0Jyk7XG5cdFx0XHRcdGZyYW1lTG9vcC5jYW5jZWwoKTtcblxuXHRcdFx0XHRjdXJyZW50TGF5b3V0ID0gKGN1cnJlbnRMYXlvdXQgKyAxKSAlIGxheW91dHMubGVuZ3RoO1xuXHRcdFx0XHRzdGFydFRpbWUgPSBudWxsO1xuXHRcdFx0XHRjdXJyZW50Q29sb3JTY2FsZSA9IChjdXJyZW50Q29sb3JTY2FsZSArIDEpICUgY29sb3JTY2FsZXMubGVuZ3RoO1xuXHRcdFx0XHRhbmltYXRlKGxheW91dHNbY3VycmVudExheW91dF0sIHBvaW50cyk7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdH1cblxuXG5cdC8vIGNyZWF0ZSBpbml0aWFsIHNldCBvZiBwb2ludHNcblx0Y29uc3QgcG9pbnRzID0gY3JlYXRlUG9pbnRzKG51bVBvaW50cywgcG9pbnRXaWR0aCwgd2lkdGgsIGhlaWdodCk7XG5cdHdpbmRvdy5wb2ludHMgPSBwb2ludHM7XG5cdHBvaW50cy5mb3JFYWNoKChkLCBpKSA9PiB7XG5cdFx0ZC50eCA9IHdpZHRoIC8gMjtcblx0XHRkLnR5ID0gaGVpZ2h0IC8gMjtcblx0XHRkLmNvbG9yRW5kID0gY29sb3JTY2FsZXNbY3VycmVudENvbG9yU2NhbGVdKGkgLyBwb2ludHMubGVuZ3RoKTtcblx0fSk7XG5cblx0YW5pbWF0ZShsYXlvdXRzW2N1cnJlbnRMYXlvdXRdLCBwb2ludHMpO1xufVxuXG5cbi8vIGluaXRpYWxpemUgcmVnbFxucmVnbCh7XG5cdC8vIGVuYWJsZSB0aGUgdGV4dHVyZSBmbG9hdCBleHRlbnNpb24gdG8gc3RvcmUgcG9zaXRpb25zIGluIGJ1ZmZlcnNcbiAgZXh0ZW5zaW9uczogW1xuICAgICdPRVNfdGV4dHVyZV9mbG9hdCcsXG4gIF0sXG5cbiAgLy8gY2FsbGJhY2sgd2hlbiByZWdsIGlzIGluaXRpYWxpemVkXG4gIG9uRG9uZTogbWFpblxufSk7XG4iXX0= |
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
function phyllotaxisLayout(points,pointWidth,xOffset,yOffset,iOffset){if(xOffset===void 0)xOffset=0;if(yOffset===void 0)yOffset=0;if(iOffset===void 0)iOffset=0;var theta=Math.PI*(3-Math.sqrt(5));var pointRadius=pointWidth/2;points.forEach(function(point,i){var index=(i+iOffset)%points.length;var phylloX=pointRadius*Math.sqrt(index)*Math.cos(index*theta);var phylloY=pointRadius*Math.sqrt(index)*Math.sin(index*theta);point.x=xOffset+phylloX-pointRadius;point.y=yOffset+phylloY-pointRadius});return points}function gridLayout(points,pointWidth,gridWidth){var pointHeight=pointWidth;var pointsPerRow=Math.floor(gridWidth/pointWidth);var numRows=points.length/pointsPerRow;points.forEach(function(point,i){point.x=pointWidth*(i%pointsPerRow);point.y=pointHeight*Math.floor(i/pointsPerRow)});return points}function randomLayout(points,pointWidth,width,height){points.forEach(function(point,i){point.x=Math.random()*(width-pointWidth);point.y=Math.random()*(height-pointWidth)});return points}function sineLayout(points,pointWidth,width,height){var amplitude=.3*(height/2);var yOffset=height/2;var periods=3;var yScale=d3.scaleLinear().domain([0,points.length-1]).range([0,periods*2*Math.PI]);points.forEach(function(point,i){point.x=i/points.length*(width-pointWidth);point.y=amplitude*Math.sin(yScale(i))+yOffset});return points}function spiralLayout(points,pointWidth,width,height){var amplitude=.3*(height/2);var xOffset=width/2;var yOffset=height/2;var periods=20;var rScale=d3.scaleLinear().domain([0,points.length-1]).range([0,Math.min(width/2,height/2)-pointWidth]);var thetaScale=d3.scaleLinear().domain([0,points.length-1]).range([0,periods*2*Math.PI]);points.forEach(function(point,i){point.x=rScale(i)*Math.cos(thetaScale(i))+xOffset;point.y=rScale(i)*Math.sin(thetaScale(i))+yOffset});return points}function createPoints(numPoints,pointWidth,width,height){var colorScale=d3.scaleSequential(d3.interpolateViridis).domain([numPoints-1,0]);var points=d3.range(numPoints).map(function(id){return{id:id,color:colorScale(id)}});return randomLayout(points,pointWidth,width,height)} | |
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImNvbW1vbi5qcyJdLCJuYW1lcyI6WyJwaHlsbG90YXhpc0xheW91dCIsInBvaW50cyIsInBvaW50V2lkdGgiLCJ4T2Zmc2V0IiwieU9mZnNldCIsImlPZmZzZXQiLCJjb25zdCIsInRoZXRhIiwiTWF0aCIsIlBJIiwic3FydCIsInBvaW50UmFkaXVzIiwiZm9yRWFjaCIsInBvaW50IiwiaSIsImluZGV4IiwibGVuZ3RoIiwicGh5bGxvWCIsImNvcyIsInBoeWxsb1kiLCJzaW4iLCJ4IiwieSIsImdyaWRMYXlvdXQiLCJncmlkV2lkdGgiLCJwb2ludEhlaWdodCIsInBvaW50c1BlclJvdyIsImZsb29yIiwibnVtUm93cyIsInJhbmRvbUxheW91dCIsIndpZHRoIiwiaGVpZ2h0IiwicmFuZG9tIiwic2luZUxheW91dCIsImFtcGxpdHVkZSIsInBlcmlvZHMiLCJ5U2NhbGUiLCJkMyIsInNjYWxlTGluZWFyIiwiZG9tYWluIiwicmFuZ2UiLCJzcGlyYWxMYXlvdXQiLCJyU2NhbGUiLCJtaW4iLCJ0aGV0YVNjYWxlIiwiY3JlYXRlUG9pbnRzIiwibnVtUG9pbnRzIiwiY29sb3JTY2FsZSIsInNjYWxlU2VxdWVudGlhbCIsImludGVycG9sYXRlVmlyaWRpcyIsIm1hcCIsImlkIiwiY29sb3IiXSwibWFwcGluZ3MiOiJBQVdBLFFBQVNBLG1CQUFrQkMsT0FBUUMsV0FBWUMsUUFBYUMsUUFBYUMscUNBQWhCLDhCQUFhLDhCQUFhLENBRWpGQyxJQUFNQyxPQUFRQyxLQUFLQyxJQUFNLEVBQUlELEtBQUtFLEtBQUssR0FFdkNKLElBQU1LLGFBQWNULFdBQWEsQ0FFakNELFFBQU9XLFFBQVEsU0FBQ0MsTUFBT0MsR0FDckJSLEdBQU1TLFFBQVNELEVBQUlULFNBQVdKLE9BQU9lLE1BQ3JDVixJQUFNVyxTQUFVTixZQUFjSCxLQUFLRSxLQUFLSyxPQUFTUCxLQUFLVSxJQUFJSCxNQUFRUixNQUNsRUQsSUFBTWEsU0FBVVIsWUFBY0gsS0FBS0UsS0FBS0ssT0FBU1AsS0FBS1ksSUFBSUwsTUFBUVIsTUFFbEVNLE9BQU1RLEVBQUlsQixRQUFVYyxRQUFVTixXQUM5QkUsT0FBTVMsRUFBSWxCLFFBQVVlLFFBQVVSLGFBR2hDLE9BQU9WLFFBYVQsUUFBU3NCLFlBQVd0QixPQUFRQyxXQUFZc0IsV0FDdENsQixHQUFNbUIsYUFBY3ZCLFVBQ3BCSSxJQUFNb0IsY0FBZWxCLEtBQUttQixNQUFNSCxVQUFZdEIsV0FDNUNJLElBQU1zQixTQUFVM0IsT0FBT2UsT0FBU1UsWUFFaEN6QixRQUFPVyxRQUFRLFNBQUNDLE1BQU9DLEdBQ3JCRCxNQUFNUSxFQUFJbkIsWUFBY1ksRUFBSVksYUFDNUJiLE9BQU1TLEVBQUlHLFlBQWNqQixLQUFLbUIsTUFBTWIsRUFBSVksZUFHekMsT0FBT3pCLFFBY1QsUUFBUzRCLGNBQWE1QixPQUFRQyxXQUFZNEIsTUFBT0MsUUFDL0M5QixPQUFPVyxRQUFRLFNBQUNDLE1BQU9DLEdBQ3JCRCxNQUFNUSxFQUFJYixLQUFLd0IsVUFBWUYsTUFBUTVCLFdBQ25DVyxPQUFNUyxFQUFJZCxLQUFLd0IsVUFBWUQsT0FBUzdCLGFBR3RDLE9BQU9ELFFBY1QsUUFBU2dDLFlBQVdoQyxPQUFRQyxXQUFZNEIsTUFBT0MsUUFDN0N6QixHQUFNNEIsV0FBWSxJQUFPSCxPQUFTLEVBQ2xDekIsSUFBTUYsU0FBVTJCLE9BQVMsQ0FDekJ6QixJQUFNNkIsU0FBVSxDQUNoQjdCLElBQU04QixRQUFTQyxHQUFHQyxjQUNmQyxRQUFRLEVBQUd0QyxPQUFPZSxPQUFTLElBQzNCd0IsT0FBTyxFQUFHTCxRQUFVLEVBQUkzQixLQUFLQyxJQUVoQ1IsUUFBT1csUUFBUSxTQUFDQyxNQUFPQyxHQUNyQkQsTUFBTVEsRUFBS1AsRUFBSWIsT0FBT2UsUUFBV2MsTUFBUTVCLFdBQ3pDVyxPQUFNUyxFQUFJWSxVQUFZMUIsS0FBS1ksSUFBSWdCLE9BQU90QixJQUFNVixTQUc5QyxPQUFPSCxRQWNULFFBQVN3QyxjQUFheEMsT0FBUUMsV0FBWTRCLE1BQU9DLFFBQy9DekIsR0FBTTRCLFdBQVksSUFBT0gsT0FBUyxFQUNsQ3pCLElBQU1ILFNBQVUyQixNQUFRLENBQ3hCeEIsSUFBTUYsU0FBVTJCLE9BQVMsQ0FDekJ6QixJQUFNNkIsU0FBVSxFQUVoQjdCLElBQU1vQyxRQUFTTCxHQUFHQyxjQUNmQyxRQUFRLEVBQUd0QyxPQUFPZSxPQUFRLElBQzFCd0IsT0FBTyxFQUFHaEMsS0FBS21DLElBQUliLE1BQVEsRUFBR0MsT0FBUyxHQUFLN0IsWUFFL0NJLElBQU1zQyxZQUFhUCxHQUFHQyxjQUNuQkMsUUFBUSxFQUFHdEMsT0FBT2UsT0FBUyxJQUMzQndCLE9BQU8sRUFBR0wsUUFBVSxFQUFJM0IsS0FBS0MsSUFFaENSLFFBQU9XLFFBQVEsU0FBQ0MsTUFBT0MsR0FDckJELE1BQU1RLEVBQUlxQixPQUFPNUIsR0FBS04sS0FBS1UsSUFBSTBCLFdBQVc5QixJQUFNWCxPQUNoRFUsT0FBTVMsRUFBSW9CLE9BQU81QixHQUFLTixLQUFLWSxJQUFJd0IsV0FBVzlCLElBQU1WLFNBR2xELE9BQU9ILFFBVVQsUUFBUzRDLGNBQWFDLFVBQVc1QyxXQUFZNEIsTUFBT0MsUUFDbER6QixHQUFNeUMsWUFBYVYsR0FBR1csZ0JBQWdCWCxHQUFHWSxvQkFDdENWLFFBQVFPLFVBQVksRUFBRyxHQUUxQnhDLElBQU1MLFFBQVNvQyxHQUFHRyxNQUFNTSxXQUFXSSxJQUFJLFNBQUFDLElBQUcsT0FDeENBLEdBQUFBLEdBQ0FDLE1BQU9MLFdBQVdJLE1BR3BCLE9BQU90QixjQUFhNUIsT0FBUUMsV0FBWTRCLE1BQU9DIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBHaXZlbiBhIHNldCBvZiBwb2ludHMsIGxheSB0aGVtIG91dCBpbiBhIHBoeWxsb3RheGlzIGxheW91dC5cbiAqIE11dGF0ZXMgdGhlIGBwb2ludHNgIHBhc3NlZCBpbiBieSB1cGRhdGluZyB0aGUgeCBhbmQgeSB2YWx1ZXMuXG4gKlxuICogQHBhcmFtIHtPYmplY3RbXX0gcG9pbnRzIFRoZSBhcnJheSBvZiBwb2ludHMgdG8gdXBkYXRlLiBXaWxsIGdldCBgeGAgYW5kIGB5YCBzZXQuXG4gKiBAcGFyYW0ge051bWJlcn0gcG9pbnRXaWR0aCBUaGUgc2l6ZSBpbiBwaXhlbHMgb2YgdGhlIHBvaW50J3Mgd2lkdGguIFNob3VsZCBhbHNvIGluY2x1ZGUgbWFyZ2luLlxuICogQHBhcmFtIHtOdW1iZXJ9IHhPZmZzZXQgVGhlIHggb2Zmc2V0IHRvIGFwcGx5IHRvIGFsbCBwb2ludHNcbiAqIEBwYXJhbSB7TnVtYmVyfSB5T2Zmc2V0IFRoZSB5IG9mZnNldCB0byBhcHBseSB0byBhbGwgcG9pbnRzXG4gKlxuICogQHJldHVybiB7T2JqZWN0W119IHBvaW50cyB3aXRoIG1vZGlmaWVkIHggYW5kIHlcbiAqL1xuZnVuY3Rpb24gcGh5bGxvdGF4aXNMYXlvdXQocG9pbnRzLCBwb2ludFdpZHRoLCB4T2Zmc2V0ID0gMCwgeU9mZnNldCA9IDAsIGlPZmZzZXQgPSAwKSB7XG4gIC8vIHRoZXRhIGRldGVybWluZXMgdGhlIHNwaXJhbCBvZiB0aGUgbGF5b3V0XG4gIGNvbnN0IHRoZXRhID0gTWF0aC5QSSAqICgzIC0gTWF0aC5zcXJ0KDUpKTtcblxuICBjb25zdCBwb2ludFJhZGl1cyA9IHBvaW50V2lkdGggLyAyO1xuXG4gIHBvaW50cy5mb3JFYWNoKChwb2ludCwgaSkgPT4ge1xuICAgIGNvbnN0IGluZGV4ID0gKGkgKyBpT2Zmc2V0KSAlIHBvaW50cy5sZW5ndGg7XG4gICAgY29uc3QgcGh5bGxvWCA9IHBvaW50UmFkaXVzICogTWF0aC5zcXJ0KGluZGV4KSAqIE1hdGguY29zKGluZGV4ICogdGhldGEpO1xuICAgIGNvbnN0IHBoeWxsb1kgPSBwb2ludFJhZGl1cyAqIE1hdGguc3FydChpbmRleCkgKiBNYXRoLnNpbihpbmRleCAqIHRoZXRhKTtcblxuICAgIHBvaW50LnggPSB4T2Zmc2V0ICsgcGh5bGxvWCAtIHBvaW50UmFkaXVzO1xuICAgIHBvaW50LnkgPSB5T2Zmc2V0ICsgcGh5bGxvWSAtIHBvaW50UmFkaXVzO1xuICB9KTtcblxuICByZXR1cm4gcG9pbnRzO1xufVxuXG4vKipcbiAqIEdpdmVuIGEgc2V0IG9mIHBvaW50cywgbGF5IHRoZW0gb3V0IGluIGEgZ3JpZC5cbiAqIE11dGF0ZXMgdGhlIGBwb2ludHNgIHBhc3NlZCBpbiBieSB1cGRhdGluZyB0aGUgeCBhbmQgeSB2YWx1ZXMuXG4gKlxuICogQHBhcmFtIHtPYmplY3RbXX0gcG9pbnRzIFRoZSBhcnJheSBvZiBwb2ludHMgdG8gdXBkYXRlLiBXaWxsIGdldCBgeGAgYW5kIGB5YCBzZXQuXG4gKiBAcGFyYW0ge051bWJlcn0gcG9pbnRXaWR0aCBUaGUgc2l6ZSBpbiBwaXhlbHMgb2YgdGhlIHBvaW50J3Mgd2lkdGguIFNob3VsZCBhbHNvIGluY2x1ZGUgbWFyZ2luLlxuICogQHBhcmFtIHtOdW1iZXJ9IGdyaWRXaWR0aCBUaGUgd2lkdGggb2YgdGhlIGdyaWQgb2YgcG9pbnRzXG4gKlxuICogQHJldHVybiB7T2JqZWN0W119IHBvaW50cyB3aXRoIG1vZGlmaWVkIHggYW5kIHlcbiAqL1xuZnVuY3Rpb24gZ3JpZExheW91dChwb2ludHMsIHBvaW50V2lkdGgsIGdyaWRXaWR0aCkge1xuICBjb25zdCBwb2ludEhlaWdodCA9IHBvaW50V2lkdGg7XG4gIGNvbnN0IHBvaW50c1BlclJvdyA9IE1hdGguZmxvb3IoZ3JpZFdpZHRoIC8gcG9pbnRXaWR0aCk7XG4gIGNvbnN0IG51bVJvd3MgPSBwb2ludHMubGVuZ3RoIC8gcG9pbnRzUGVyUm93O1xuXG4gIHBvaW50cy5mb3JFYWNoKChwb2ludCwgaSkgPT4ge1xuICAgIHBvaW50LnggPSBwb2ludFdpZHRoICogKGkgJSBwb2ludHNQZXJSb3cpO1xuICAgIHBvaW50LnkgPSBwb2ludEhlaWdodCAqIE1hdGguZmxvb3IoaSAvIHBvaW50c1BlclJvdyk7XG4gIH0pO1xuXG4gIHJldHVybiBwb2ludHM7XG59XG5cbi8qKlxuICogR2l2ZW4gYSBzZXQgb2YgcG9pbnRzLCBsYXkgdGhlbSBvdXQgcmFuZG9tbHkuXG4gKiBNdXRhdGVzIHRoZSBgcG9pbnRzYCBwYXNzZWQgaW4gYnkgdXBkYXRpbmcgdGhlIHggYW5kIHkgdmFsdWVzLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0W119IHBvaW50cyBUaGUgYXJyYXkgb2YgcG9pbnRzIHRvIHVwZGF0ZS4gV2lsbCBnZXQgYHhgIGFuZCBgeWAgc2V0LlxuICogQHBhcmFtIHtOdW1iZXJ9IHBvaW50V2lkdGggVGhlIHNpemUgaW4gcGl4ZWxzIG9mIHRoZSBwb2ludCdzIHdpZHRoLiBTaG91bGQgYWxzbyBpbmNsdWRlIG1hcmdpbi5cbiAqIEBwYXJhbSB7TnVtYmVyfSB3aWR0aCBUaGUgd2lkdGggb2YgdGhlIGFyZWEgdG8gcGxhY2UgdGhlbSBpblxuICogQHBhcmFtIHtOdW1iZXJ9IGhlaWdodCBUaGUgaGVpZ2h0IG9mIHRoZSBhcmVhIHRvIHBsYWNlIHRoZW0gaW5cbiAqXG4gKiBAcmV0dXJuIHtPYmplY3RbXX0gcG9pbnRzIHdpdGggbW9kaWZpZWQgeCBhbmQgeVxuICovXG5mdW5jdGlvbiByYW5kb21MYXlvdXQocG9pbnRzLCBwb2ludFdpZHRoLCB3aWR0aCwgaGVpZ2h0KSB7XG4gIHBvaW50cy5mb3JFYWNoKChwb2ludCwgaSkgPT4ge1xuICAgIHBvaW50LnggPSBNYXRoLnJhbmRvbSgpICogKHdpZHRoIC0gcG9pbnRXaWR0aCk7XG4gICAgcG9pbnQueSA9IE1hdGgucmFuZG9tKCkgKiAoaGVpZ2h0IC0gcG9pbnRXaWR0aCk7XG4gIH0pO1xuXG4gIHJldHVybiBwb2ludHM7XG59XG5cbi8qKlxuICogR2l2ZW4gYSBzZXQgb2YgcG9pbnRzLCBsYXkgdGhlbSBvdXQgaW4gYSBzaW5lIHdhdmUuXG4gKiBNdXRhdGVzIHRoZSBgcG9pbnRzYCBwYXNzZWQgaW4gYnkgdXBkYXRpbmcgdGhlIHggYW5kIHkgdmFsdWVzLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0W119IHBvaW50cyBUaGUgYXJyYXkgb2YgcG9pbnRzIHRvIHVwZGF0ZS4gV2lsbCBnZXQgYHhgIGFuZCBgeWAgc2V0LlxuICogQHBhcmFtIHtOdW1iZXJ9IHBvaW50V2lkdGggVGhlIHNpemUgaW4gcGl4ZWxzIG9mIHRoZSBwb2ludCdzIHdpZHRoLiBTaG91bGQgYWxzbyBpbmNsdWRlIG1hcmdpbi5cbiAqIEBwYXJhbSB7TnVtYmVyfSB3aWR0aCBUaGUgd2lkdGggb2YgdGhlIGFyZWEgdG8gcGxhY2UgdGhlbSBpblxuICogQHBhcmFtIHtOdW1iZXJ9IGhlaWdodCBUaGUgaGVpZ2h0IG9mIHRoZSBhcmVhIHRvIHBsYWNlIHRoZW0gaW5cbiAqXG4gKiBAcmV0dXJuIHtPYmplY3RbXX0gcG9pbnRzIHdpdGggbW9kaWZpZWQgeCBhbmQgeVxuICovXG5mdW5jdGlvbiBzaW5lTGF5b3V0KHBvaW50cywgcG9pbnRXaWR0aCwgd2lkdGgsIGhlaWdodCkge1xuICBjb25zdCBhbXBsaXR1ZGUgPSAwLjMgKiAoaGVpZ2h0IC8gMik7XG4gIGNvbnN0IHlPZmZzZXQgPSBoZWlnaHQgLyAyO1xuICBjb25zdCBwZXJpb2RzID0gMztcbiAgY29uc3QgeVNjYWxlID0gZDMuc2NhbGVMaW5lYXIoKVxuICAgIC5kb21haW4oWzAsIHBvaW50cy5sZW5ndGggLSAxXSlcbiAgICAucmFuZ2UoWzAsIHBlcmlvZHMgKiAyICogTWF0aC5QSV0pO1xuXG4gIHBvaW50cy5mb3JFYWNoKChwb2ludCwgaSkgPT4ge1xuICAgIHBvaW50LnggPSAoaSAvIHBvaW50cy5sZW5ndGgpICogKHdpZHRoIC0gcG9pbnRXaWR0aCk7XG4gICAgcG9pbnQueSA9IGFtcGxpdHVkZSAqIE1hdGguc2luKHlTY2FsZShpKSkgKyB5T2Zmc2V0O1xuICB9KTtcblxuICByZXR1cm4gcG9pbnRzO1xufVxuXG4vKipcbiAqIEdpdmVuIGEgc2V0IG9mIHBvaW50cywgbGF5IHRoZW0gb3V0IGluIGEgc3BpcmFsLlxuICogTXV0YXRlcyB0aGUgYHBvaW50c2AgcGFzc2VkIGluIGJ5IHVwZGF0aW5nIHRoZSB4IGFuZCB5IHZhbHVlcy5cbiAqXG4gKiBAcGFyYW0ge09iamVjdFtdfSBwb2ludHMgVGhlIGFycmF5IG9mIHBvaW50cyB0byB1cGRhdGUuIFdpbGwgZ2V0IGB4YCBhbmQgYHlgIHNldC5cbiAqIEBwYXJhbSB7TnVtYmVyfSBwb2ludFdpZHRoIFRoZSBzaXplIGluIHBpeGVscyBvZiB0aGUgcG9pbnQncyB3aWR0aC4gU2hvdWxkIGFsc28gaW5jbHVkZSBtYXJnaW4uXG4gKiBAcGFyYW0ge051bWJlcn0gd2lkdGggVGhlIHdpZHRoIG9mIHRoZSBhcmVhIHRvIHBsYWNlIHRoZW0gaW5cbiAqIEBwYXJhbSB7TnVtYmVyfSBoZWlnaHQgVGhlIGhlaWdodCBvZiB0aGUgYXJlYSB0byBwbGFjZSB0aGVtIGluXG4gKlxuICogQHJldHVybiB7T2JqZWN0W119IHBvaW50cyB3aXRoIG1vZGlmaWVkIHggYW5kIHlcbiAqL1xuZnVuY3Rpb24gc3BpcmFsTGF5b3V0KHBvaW50cywgcG9pbnRXaWR0aCwgd2lkdGgsIGhlaWdodCkge1xuICBjb25zdCBhbXBsaXR1ZGUgPSAwLjMgKiAoaGVpZ2h0IC8gMik7XG4gIGNvbnN0IHhPZmZzZXQgPSB3aWR0aCAvIDI7XG4gIGNvbnN0IHlPZmZzZXQgPSBoZWlnaHQgLyAyO1xuICBjb25zdCBwZXJpb2RzID0gMjA7XG5cbiAgY29uc3QgclNjYWxlID0gZDMuc2NhbGVMaW5lYXIoKVxuICAgIC5kb21haW4oWzAsIHBvaW50cy5sZW5ndGggLTFdKVxuICAgIC5yYW5nZShbMCwgTWF0aC5taW4od2lkdGggLyAyLCBoZWlnaHQgLyAyKSAtIHBvaW50V2lkdGhdKTtcblxuICBjb25zdCB0aGV0YVNjYWxlID0gZDMuc2NhbGVMaW5lYXIoKVxuICAgIC5kb21haW4oWzAsIHBvaW50cy5sZW5ndGggLSAxXSlcbiAgICAucmFuZ2UoWzAsIHBlcmlvZHMgKiAyICogTWF0aC5QSV0pO1xuXG4gIHBvaW50cy5mb3JFYWNoKChwb2ludCwgaSkgPT4ge1xuICAgIHBvaW50LnggPSByU2NhbGUoaSkgKiBNYXRoLmNvcyh0aGV0YVNjYWxlKGkpKSArIHhPZmZzZXRcbiAgICBwb2ludC55ID0gclNjYWxlKGkpICogTWF0aC5zaW4odGhldGFTY2FsZShpKSkgKyB5T2Zmc2V0O1xuICB9KTtcblxuICByZXR1cm4gcG9pbnRzO1xufVxuXG5cblxuXG4vKipcbiAqIEdlbmVyYXRlIGFuIG9iamVjdCBhcnJheSBvZiBgbnVtUG9pbnRzYCBsZW5ndGggd2l0aCB1bmlxdWUgSURzXG4gKiBhbmQgYXNzaWduZWQgY29sb3JzXG4gKi9cbmZ1bmN0aW9uIGNyZWF0ZVBvaW50cyhudW1Qb2ludHMsIHBvaW50V2lkdGgsIHdpZHRoLCBoZWlnaHQpIHtcbiAgY29uc3QgY29sb3JTY2FsZSA9IGQzLnNjYWxlU2VxdWVudGlhbChkMy5pbnRlcnBvbGF0ZVZpcmlkaXMpXG4gICAgLmRvbWFpbihbbnVtUG9pbnRzIC0gMSwgMF0pO1xuXG4gIGNvbnN0IHBvaW50cyA9IGQzLnJhbmdlKG51bVBvaW50cykubWFwKGlkID0+ICh7XG4gICAgaWQsXG4gICAgY29sb3I6IGNvbG9yU2NhbGUoaWQpLFxuICB9KSk7XG5cbiAgcmV0dXJuIHJhbmRvbUxheW91dChwb2ludHMsIHBvaW50V2lkdGgsIHdpZHRoLCBoZWlnaHQpO1xufVxuIl19 |
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> | |
<title>Animate 100,000 points with regl</title> | |
<body> | |
<script src="https://wzrd.in/standalone/[email protected]"></script> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="dist_common.js"></script> | |
<script src="dist.js"></script> | |
</body> |
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
function main(err, regl) { | |
const numPoints = 10000; | |
const pointWidth = 20; | |
const pointMargin = 1; | |
const width = window.innerWidth; | |
const height = window.innerHeight; | |
const duration = 1500; | |
const delayByIndex = 500 / numPoints; | |
const maxDuration = duration + delayByIndex * numPoints; // include max delay in here | |
const toPhyllotaxis = (points) => phyllotaxisLayout(points, pointWidth + pointMargin, width / 2, height / 2); | |
const toGrid = (points) => gridLayout(points, pointWidth + pointMargin, width); | |
const toSine = (points) => sineLayout(points, pointWidth + pointMargin, width, height); | |
const toSpiral = (points) => spiralLayout(points, pointWidth + pointMargin, width, height); | |
const layouts = [toPhyllotaxis, toGrid, toSine, toSpiral]; | |
let currentLayout = 0; | |
let startTime = null; // in seconds | |
// start animation loop (note: time is in seconds) | |
let frameLoop; | |
// wrap d3 color scales so they produce vec3s with values 0-1 | |
// also limit the t value to remove darkest color | |
function wrapColorScale(scale) { | |
const tScale = d3.scaleLinear().domain([0, 1]).range([0.4, 1]); | |
return t => { | |
const rgb = d3.rgb(scale(tScale(t))); | |
return [rgb.r / 255, rgb.g / 255, rgb.b / 255]; | |
}; | |
} | |
const colorScales = [ | |
d3.scaleSequential(d3.interpolateViridis), | |
d3.scaleSequential(d3.interpolateMagma), | |
d3.scaleSequential(d3.interpolateInferno), | |
d3.scaleSequential(d3.interpolateCool), | |
].map(wrapColorScale); | |
let currentColorScale = 0; | |
// function to compile a draw points regl func | |
function createDrawPoints(points) { | |
const drawPoints = regl({ | |
frag: ` | |
precision highp float; | |
varying vec3 fragColor; | |
void main() { | |
float x = gl_PointCoord.x; | |
float y = gl_PointCoord.y; | |
// Measure distance from center | |
vec2 uv = vec2(x, y) * 2. - 1.; | |
float point_dist = length(uv); | |
if (point_dist > 1.000) { | |
discard; | |
} | |
gl_FragColor = vec4(fragColor, 1.0); | |
} | |
`, | |
vert: ` | |
attribute vec2 positionStart; | |
attribute vec2 positionEnd; | |
attribute float index; | |
attribute vec3 colorStart; | |
attribute vec3 colorEnd; | |
varying vec3 fragColor; | |
uniform float pointWidth; | |
uniform float stageWidth; | |
uniform float stageHeight; | |
uniform float elapsed; | |
uniform float duration; | |
uniform float delayByIndex; | |
void main() { | |
gl_PointSize = pointWidth; | |
float delay = delayByIndex * index; | |
float t; | |
// drawing without animation, so show end state immediately | |
if (duration == 0.0) { | |
t = 1.0; | |
// still delaying before animating | |
} else if (elapsed < delay) { | |
t = 0.0; | |
} else { | |
t = 2.0 * ((elapsed - delay) / duration); | |
// cubic easing (cubicInOut) -- note there are glslify things for this toPhyllotaxis | |
// this is copied from d3. | |
t = (t <= 1.0 ? t * t * t : (t -= 2.0) * t * t + 2.0) / 2.0; | |
if (t > 1.0) { | |
t = 1.0; | |
} | |
} | |
// interpolate position | |
float x = mix(positionStart[0], positionEnd[0], t); | |
float y = mix(positionStart[1], positionEnd[1], t); | |
// interpolate color | |
fragColor = mix(colorStart, colorEnd, t); | |
// scale to normalized device coordinates (-1, -1) to (1, 1) | |
gl_Position = vec4( | |
2.0 * ((x / stageWidth) - 0.5), | |
// invert y since we think [0,0] is bottom left in pixel space (needed for d3.zoom) | |
-(2.0 * ((y / stageHeight) - 0.5)), | |
0.0, | |
1.0); | |
} | |
`, | |
attributes: { | |
positionStart: points.map(d => [d.sx, d.sy]), | |
positionEnd: points.map(d => [d.tx, d.ty]), | |
colorStart: points.map(d => d.colorStart), | |
colorEnd: points.map(d => d.colorEnd), | |
index: d3.range(points.length), | |
}, | |
uniforms: { | |
pointWidth: regl.prop('pointWidth'), | |
stageWidth: regl.prop('stageWidth'), | |
stageHeight: regl.prop('stageHeight'), | |
delayByIndex: regl.prop('delayByIndex'), | |
duration: regl.prop('duration'), | |
// time in milliseconds since the prop startTime (i.e. time elapsed) | |
elapsed: ({ time }, { startTime = 0 }) => (time - startTime) * 1000, | |
}, | |
count: points.length, | |
primitive: 'points', | |
}); | |
return drawPoints; | |
} | |
function animate(layout, points) { | |
console.log('animating with new layout'); | |
// make previous end the new beginning | |
points.forEach(d => { | |
d.sx = d.tx; | |
d.sy = d.ty; | |
d.colorStart = d.colorEnd; | |
}); | |
// layout points | |
layout(points); | |
// copy layout x y to end positions | |
const colorScale = colorScales[currentColorScale]; | |
points.forEach((d, i) => { | |
d.tx = d.x; | |
d.ty = d.y; | |
d.colorEnd = colorScale(i / points.length) | |
}); | |
// create the regl function with the new start and end points | |
const drawPoints = createDrawPoints(points); | |
frameLoop = regl.frame(({ time }) => { | |
if (startTime === null) { | |
startTime = time; | |
} | |
regl.clear({ | |
// background color (black) | |
color: [0, 0, 0, 1], | |
depth: 1, | |
}); | |
drawPoints({ | |
pointWidth, | |
stageWidth: width, | |
stageHeight: height, | |
duration, | |
delayByIndex, | |
startTime, | |
}); | |
if (time - startTime > (maxDuration / 1000)) { | |
console.log('done animating, moving to next layout'); | |
frameLoop.cancel(); | |
currentLayout = (currentLayout + 1) % layouts.length; | |
startTime = null; | |
currentColorScale = (currentColorScale + 1) % colorScales.length; | |
animate(layouts[currentLayout], points); | |
} | |
}); | |
} | |
// create initial set of points | |
const points = createPoints(numPoints, pointWidth, width, height); | |
window.points = points; | |
points.forEach((d, i) => { | |
d.tx = width / 2; | |
d.ty = height / 2; | |
d.colorEnd = colorScales[currentColorScale](i / points.length); | |
}); | |
animate(layouts[currentLayout], points); | |
} | |
// initialize regl | |
regl({ | |
// enable the texture float extension to store positions in buffers | |
extensions: [ | |
'OES_texture_float', | |
], | |
// callback when regl is initialized | |
onDone: main | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment