Tree generator

by jcubed111

HTML

<script src="https://cdn.jsdelivr.net/lodash/4.16.4/lodash.min.js"></script>
<canvas id='canvas' width='500' height='500'></canvas>

CSS

body{
    background:#EEE;
}
canvas{
    width:500px;
    height:500px;
    background:#FFF;
    border:1px solid #CCC;
}

JavaScript

"use strict";

var ctx = document.getElementById('canvas').getContext('2d');
ctx.scale(1, -1);
ctx.translate(0, -500);

var nodes = [];
nodes = [[250, 50]];
for(var i=1; i<_.random(14, 18); i++) {
	nodes.push([0, 0]);
}

var dist = 30;

function generate() {
	var angle = _.random(45, 135);
	
	var periods = [
		_.random(10, 14, true),
		_.random(10, 14, true),
	];
	var offsets = [
		_.random(0, periods[0], true),
		_.random(0, periods[1], true),
	];
	var factors = [
		1,
		0.5,
	];
	
	var slant = _.random(-30, 30, true);
	
	var minAmp = 40;
	var maxAmp = 130;
	
	for(var i=1; i<nodes.length; i++) {
		var prev = nodes[i-1];
		var cur = nodes[i];
		
		var components = _.map(periods, (p, j) => {
			return Math.sin((i - offsets[j])/periods[j] * 2 * Math.PI) * factors[j];
		});
		components.push(slant / 180 * Math.PI);
		
		var sinFac = _.sum(components);
		sinFac /= _.sum(factors);
		
		var ampFac = 1 - 2 * Math.abs(i / nodes.length - 0.5);
		var amp = minAmp + ampFac * (maxAmp - minAmp)
				   
		angle = 90 + sinFac * amp;
		
		prev.sinFac = sinFac;
		prev.amp = amp;
		
		cur[0] = 30 * Math.cos(angle / 180 * Math.PI) + prev[0];
		cur[1] = 30 * Math.sin(angle / 180 * Math.PI) + prev[1];
	}
}


function render() {
	ctx.beginPath();
	nodes.forEach(n => {
		ctx.lineTo(n[0], n[1]);
	});
	ctx.strokeStyle = '#000';
	ctx.stroke();
	
	ctx.beginPath();
	ctx.moveTo(0, 250);
	ctx.lineTo(500, 250);
	ctx.strokeStyle = '#ccc';
	ctx.stroke();
	
	ctx.beginPath();
	nodes.forEach((n, i) => {
		ctx.lineTo(i*10, 250 + n.sinFac * n.amp);
	});
	ctx.strokeStyle = '#f00';
	ctx.stroke();
	
	// amp
	ctx.beginPath();
	nodes.forEach((n, i) => {
		ctx.lineTo(i*10, 250 + n.amp);
	});
	ctx.strokeStyle = '#f0f';
	ctx.stroke();
	ctx.beginPath();
	nodes.forEach((n, i) => {
		ctx.lineTo(i*10, 250 - n.amp);
	});
	ctx.strokeStyle = '#f0f';
	ctx.stroke();
}


generate();
render();