Draw Polygon C Christmas Tree Graphics
Javascript
Quasirealistic fully 3D procedural fir tree generator.
Featuring: extensive configuration with even more configuration options present in code; a zigzagy trunk; branching branches; growth animation; rotation of a fully grown tree.
Not featuring: jQuery, Underscore.js or any other library; hardware dependency - only canvas support is required; messy code (at least that was the intention)
Live page: http://fiddle.jshell.net/honnza/NMva7/show/
Edit page: http://jsfiddle.net/honnza/NMva7/
screenshot:
HTML:
<canvas id=c width=200 height=300 style="display:none"></canvas> <div id=config></div>
Javascript:
var TAU = 2*Math.PI, deg = TAU/360, TRUNK_VIEW = {lineWidth:3, strokeStyle: "brown", zIndex: 1}, BRANCH_VIEW = {lineWidth:1, strokeStyle: "green", zIndex: 2}, TRUNK_SPACING = 1.5, TRUNK_BIAS_STR = -0.5, TRUNK_SLOPE = 0.25, BRANCH_LEN = 1, BRANCH_P = 0.01, MIN_SLOPE = -5*deg, MAX_SLOPE = 20*deg, INIT_SLOPE = 10*deg, MAX_D_SLOPE = 5*deg, DIR_KEEP_BIAS = 10, GROWTH_MSPF = 10, //ms per frame GROWTH_TPF = 10, //ticks per frame ROTATE_MSPF = 10, ROTATE_RPF = 1*deg; //radians per frame var configurables = [ // {key: "TRUNK_SPACING", name: "Branch spacing", widget: "number", // description: "Distance between main branches on the trunk, in pixels"}, {key: "TRUNK_BIAS_STR", name: "Branch distribution", widget: "number", description: "Angular distribution between nearby branches. High values tend towards one-sided trees, highly negative values tend towards planar trees. Zero means that branches grow in independent directions."}, {key: "TRUNK_SLOPE", name: "Trunk slope", widget: "number", description: "Amount of horizontal movement of the trunk while growing"}, {key: "BRANCH_P", name: "Branch frequency", widget: "number", description: "Branch frequency - if =1/100, a single twig will branch off approximately once per 100 px"}, {key: "MIN_SLOPE", name: "Minimum slope", widget: "number", scale: deg, description: "Minimum slope of a branch, in degrees"}, {key: "MAX_SLOPE", name: "Maximum slope", widget: "number", scale: deg, description: "Maximum slope of a branch, in degrees"}, {key: "INIT_SLOPE", name: "Initial slope", widget: "number", scale: deg, description: "Angle at which branches leave the trunk"}, {key: "DIR_KEEP_BIAS", name: "Directional inertia", widget: "number", description: "Tendency of twigs to keep their horizontal direction"}, {get: function(){return maxY}, set: setCanvasSize, name: "Tree height", widget:"number"} ]; var config = document.getElementById("config"), canvas = document.getElementById("c"), maxX = canvas.width/2, maxY = canvas.height, canvasRatio = canvas.width / canvas.height, c; function setCanvasSize(height){ if(height === 'undefined') return maxY; maxY = canvas.height = height; canvas.width = height * canvasRatio; maxX = canvas.width/2; x} var nodes = [{x:0, y:0, z:0, dir:'up', isEnd:true}], buds = [nodes[0]], branches = [], branch, trunkDirBias = {x:0, z:0}; //////////////////////////////////////////////////////////////////////////////// configurables.forEach(function(el){ var widget; switch(el.widget){ case 'number': widget = document.createElement("input"); if(el.key){ widget.value = window[el.key] / (el.scale||1); el.set = function(value){window[el.key]=value * (el.scale||1)}; }else{ widget.value = el.get(); } widget.onblur = function(){ el.set(+widget.value); }; break; default: throw "unknown widget type"; } var p = document.createElement("p"); p.textContent = el.name + ": "; p.appendChild(widget); p.title = el.description; config.appendChild(p); }); var button = document.createElement("input"); button.type = "button"; button.value = "grow"; button.onclick = function(){ button.value = "stop"; button.onclick = function(){clearInterval(interval)}; config.style.display="none"; canvas.style.display=""; c=canvas.getContext("2d"); c.translate(maxX, maxY); c.scale(1, -1); interval = setInterval(grow, GROWTH_MSPF); } document.body.appendChild(button); function grow(){ for(var tick=0; tick<GROWTH_TPF; tick++){ var budId = 0 | Math.random() * buds.length, nodeFrom = buds[budId], nodeTo, branch, dir, slope, bias if(nodeFrom.dir === 'up' && nodeFrom.isEnd){ nodeFrom.isEnd = false; rndArg = Math.random()*TAU; nodeTo = { x:nodeFrom.x + TRUNK_SPACING * TRUNK_SLOPE * Math.sin(rndArg), y:nodeFrom.y + TRUNK_SPACING, z:nodeFrom.z + TRUNK_SPACING * TRUNK_SLOPE * Math.cos(rndArg), dir:'up', isEnd:true} if(nodeTo.y > maxY){ console.log("end"); clearInterval(interval); rotateInit(); return; } nodes.push(nodeTo); buds.push(nodeTo); branch = {from: nodeFrom, to: nodeTo, view: TRUNK_VIEW}; branches.push(branch); renderBranch(branch); }else{ //bud is not a trunk top if(!(nodeFrom.dir !== 'up' && Math.random() < BRANCH_P)){ buds.splice(buds.indexOf(nodeFrom), 1) } nodeFrom.isEnd = false; if(nodeFrom.dir === 'up'){ bias = {x:trunkDirBias.x * TRUNK_BIAS_STR, z:trunkDirBias.z * TRUNK_BIAS_STR}; slope = INIT_SLOPE; }else{ bias = {x:nodeFrom.dir.x * DIR_KEEP_BIAS, z:nodeFrom.dir.z * DIR_KEEP_BIAS}; slope = nodeFrom.slope; } var rndLen = Math.random(), rndArg = Math.random()*TAU; dir = {x: rndLen * Math.sin(rndArg) + bias.x, z: rndLen * Math.cos(rndArg) + bias.z}; var uvFix = 1/Math.sqrt(dir.x*dir.x + dir.z*dir.z); dir = {x:dir.x*uvFix, z:dir.z*uvFix}; if(nodeFrom.dir === "up") trunkDirBias = dir; slope += MAX_D_SLOPE * (2*Math.random() - 1); if(slope > MAX_SLOPE) slope = MAX_SLOPE; if(slope < MIN_SLOPE) slope = MIN_SLOPE; var length = BRANCH_LEN * Math.random(); nodeTo = { x: nodeFrom.x + length * Math.cos(slope) * dir.x, y: nodeFrom.y + length * Math.sin(slope), z: nodeFrom.z + length * Math.cos(slope) * dir.z, dir: dir, slope: slope, isEnd: true } //if(Math.abs(nodeTo.x)/maxX + nodeTo.y/maxY > 1) return; nodes.push(nodeTo); buds.push(nodeTo); branch = {from: nodeFrom, to: nodeTo, view: BRANCH_VIEW}; branches.push(branch); renderBranch(branch); }// end if-is-trunk }// end for-tick }//end func-grow function rotateInit(){ branches.sort(function(a,b){ return (a.view.zIndex-b.view.zIndex); }); interval = setInterval(rotate, ROTATE_MSPF); } var time = 0; var view = {x:1, z:0} function rotate(){ time++; view = {x: Math.cos(time * ROTATE_RPF), z: Math.sin(time * ROTATE_RPF)}; c.fillStyle = "white" c.fillRect(-maxX, 0, 2*maxX, maxY); branches.forEach(renderBranch); c.stroke(); c.beginPath(); } var prevView = null; function renderBranch(branch){ if(branch.view !== prevView){ c.stroke(); for(k in branch.view) c[k] = branch.view[k]; c.beginPath(); prevView = branch.view; } c.moveTo(view.x * branch.from.x + view.z * branch.from.z, branch.from.y); c.lineTo(view.x * branch.to.x + view.z * branch.to.z, branch.to.y); }
Source: https://codegolf.stackexchange.com/questions/15860/make-a-scalable-christmas-tree
0 Response to "Draw Polygon C Christmas Tree Graphics"
Post a Comment