(click to reload)
A fork of Mike Bostock's branched random walk. The code has been refactored to allow a pen up/down operation, model was paramaterized further, and three variants with slightly different model settings are shown.
| license: gpl-3.0 |
(click to reload)
A fork of Mike Bostock's branched random walk. The code has been refactored to allow a pen up/down operation, model was paramaterized further, and three variants with slightly different model settings are shown.
| /* this is my general purpose random function | |
| * | |
| * random numbers are always paramaterized between min and max. | |
| * however, the "focus" variable allows an interpolation between | |
| * a uniform and gaussian distribution (normal extends | |
| * "focus" standard deviations from center and a focus of 0 | |
| * is a uniform distribution). | |
| */ | |
| function focusedRandom(min, max, focus, mean) { | |
| if(max === undefined) { | |
| max = min; | |
| min = 0; | |
| } | |
| if(focus === undefined) { | |
| focus = 1.0; | |
| } | |
| if(mean === undefined) { | |
| mean = (min + max) / 2.0; | |
| } | |
| if(focus == 0) { | |
| return d3.randomUniform(min, max)(); | |
| } | |
| else if(focus < 0) { | |
| focus = -1 / focus; | |
| } | |
| sigma = (0.5 * (max - min)) / focus; | |
| val = d3.randomNormal(mean, sigma)(); | |
| if (val > min && val < max) { | |
| return val; | |
| } | |
| return d3.randomUniform(min, max)(); | |
| } |
| <!DOCTYPE html> | |
| <meta charset="utf-8"> | |
| <canvas width="960" height="500"></canvas> | |
| <script src="https://d3js.org/d3.v4.min.js"></script> | |
| <script language="javascript" type="text/javascript" src="focusedRandom.js"></script> | |
| <script> | |
| var canvas = document.querySelector("canvas"), | |
| context = canvas.getContext("2d"), | |
| width = canvas.width, | |
| height = canvas.height, | |
| tail_y = [0.2 * canvas.height, 0.5 * canvas.height, 0.8 * canvas.height], | |
| randomY = d3.randomNormal(0,1.5), | |
| randomPenToUp = d3.randomUniform(-15, 1), | |
| randomPenToDown = d3.randomUniform(-5, 1); | |
| render(); | |
| canvas.onclick = render; | |
| function render() { | |
| context.clearRect(0, 0, width, height); | |
| render_tail(0); | |
| render_tail(1); | |
| render_tail(2); | |
| } | |
| function render_tail(which_tail) { | |
| var length = Math.floor(focusedRandom(80, 1200, 2, 900)); | |
| var tail_length = Math.floor(focusedRandom(60, 400, 3, 200)); | |
| var tail_every = Math.floor(focusedRandom(1, 30, 4, 4)); | |
| var color = d3.scaleSequential(d3.interpolateRainbow).domain([0, length]); | |
| var x_bias = 600 / length; | |
| var local_randomX = d3.randomNormal(x_bias,2); | |
| var x0 = width / 20, | |
| y0 = tail_y[which_tail], | |
| pen_up0 = false, | |
| tail_chunk = Math.floor(tail_length / 20), | |
| mainWalk = randomWalk([[x0, y0, pen_up0, null]], length, local_randomX); | |
| context.lineJoin = "round"; | |
| context.lineCap = "round"; | |
| context.lineWidth = 1.5; | |
| context.strokeStyle = "black"; | |
| renderWalk(mainWalk); | |
| context.globalCompositeOperation = "multiply"; | |
| context.lineWidth = 1; | |
| for (var i = tail_every; i < mainWalk.length; i += tail_every) { | |
| var branchHistory = mainWalk.slice(0, i+1) | |
| for (var j = 0; j < 1; ++j) { | |
| context.strokeStyle = color(i); | |
| branchCopy = branchHistory.slice(); | |
| for (var k = 0, m = 20; k < m; ++k) { | |
| context.globalAlpha = (m - k - 1) / m; | |
| var pieceWalk = randomWalk(branchCopy, tail_chunk, local_randomX), | |
| pieceEnd = pieceWalk[pieceWalk.length - 1]; | |
| renderWalk(pieceWalk); | |
| branchCopy.push(pieceEnd) | |
| } | |
| context.globalAlpha = 1; | |
| } | |
| } | |
| } | |
| function renderWalk(walk) { | |
| var i, n = walk.length; | |
| context.beginPath(); | |
| context.moveTo(walk[0][0], walk[0][1]); | |
| for (i = 0; i < n; ++i) { | |
| // check for pen_up | |
| if(walk[i][2]) { | |
| context.moveTo(walk[i][0], walk[i][1]); | |
| } | |
| else { | |
| context.lineTo(walk[i][0], walk[i][1]); | |
| } | |
| } | |
| context.stroke(); | |
| } | |
| function randomWalk(history, n, local_randomX) { | |
| var points, i, x, y, pen_up, randomPenFn; | |
| points = new Array(n); | |
| points[0] = history[history.length - 1]; | |
| [x, y, pen_up] = points[0]; | |
| for (i = 1; i < n; ++i) { | |
| randomPenFn = pen_up ? randomPenToDown : randomPenToUp; | |
| points[i] = [ | |
| x += local_randomX(), | |
| y += randomY(), | |
| pen_up ^= (randomPenFn() > 0) | |
| ]; | |
| } | |
| return points; | |
| } | |
| </script> |