(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> |