Created
May 27, 2012 14:56
-
-
Save freshtonic/2814588 to your computer and use it in GitHub Desktop.
Fractal mountains vista
This file contains hidden or 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> | |
<html> | |
<head> | |
<script type='text/javascript' src='/javascript/jquery-1.7.2-min.js'></script> | |
<script type='text/javascript'> | |
(function(){ | |
var mountains = null; | |
var canvas = null; | |
var context = null; | |
var mountainGap = null; | |
var noiseImage = null; | |
/* Wraps context-modifying operations in a save() & restore() call */ | |
var preserveContext = function(op) { | |
try { | |
context.save(); | |
op(); | |
} finally { | |
context.restore(); | |
} | |
}; | |
var jitter = 0.1; | |
var jitterDirection = function() { | |
return Math.random() < 0.5 ? -1 : 1; | |
}; | |
/* | |
Creates a fractal mountain of *segmentCount* line segments. | |
The algorithm works by adding a new point to a line where the new | |
height is chosen as the average of the adjacent heights, plus some | |
(+ve or -ve) jitter, the amount of which is a function of the distance | |
between the adjacent points. | |
The x-coordinate of the new point is always half way between the points. | |
*/ | |
var generateFractalMountain = function(segmentCount) { | |
var segmentWidth = canvas.width / segmentCount; | |
var mountain = [{x:0,y:100 + jitterDirection() * 50}, {x:segmentWidth * segmentCount, y:100 + jitterDirection() * 50}]; | |
var distance = function(p1,p2) { | |
var xdistance = p2.x - p1.x; | |
var ydistance = Math.abs(p2.y - p1.y); | |
return Math.sqrt(xdistance*xdistance + ydistance*ydistance); | |
}; | |
var split = function(p1Index, p2Index) { | |
var p1 = mountain[p1Index]; | |
var p2 = mountain[p2Index]; | |
if (Math.abs(p2.x - p1.x) >= (2 * segmentWidth)) { | |
var newX = (p1.x + p2.x) / 2; | |
var newY = ((p1.y + p2.y) / 2) + distance(p1,p2) * jitterDirection() * jitter; | |
var newPoint = {x:newX, y:newY}; | |
var newIndex = p2Index; | |
p2Index+=1; | |
mountain.splice(newIndex, 0, newPoint); | |
// FIXME: data structure is wrong, should be using a tree. | |
// While the following operation seems to complete in a reasonable time | |
// frame on my machine, its probably O(N^2). | |
split(mountain.indexOf(p1), mountain.indexOf(newPoint)); | |
split(mountain.indexOf(newPoint), mountain.indexOf(p2)); | |
} | |
}; | |
split(0, 1); | |
return mountain; | |
}; | |
/* | |
Creates a new mountain that is the result of interpolation between | |
two other mountains. | |
The new mountain is the average of the first two, with some added jitter | |
so reduce the smoothing that occurs due to averaging. | |
*/ | |
var interpolate = function(mountain1, mountain2) { | |
var newMountain = new Array(mountain1.length); | |
for (var i = 0; i < newMountain.length; i++) { | |
newMountain[i] = { | |
x: mountain1[i].x, | |
y: ((mountain1[i].y + mountain2[i].y) / 2) + jitterDirection() * 0.3 | |
}; | |
} | |
return newMountain; | |
}; | |
var generateMountains = function(mountainCount) { | |
var mountains = new Array(mountainCount); | |
mountains[0] = generateFractalMountain(1024); | |
for (var i = 1; i < mountains.length; i++) { | |
mountains[i] = interpolate(generateFractalMountain(1024), mountains[i-1]); | |
} | |
return mountains; | |
}; | |
var drawSky = function() { | |
preserveContext(function(){ | |
var gradient = context.createRadialGradient(75,75,600,75,75,1500); | |
//gradient.addColorStop(0, "#8ED6FF"); // light blue | |
gradient.addColorStop(0, "#9EE6FF"); // light blue | |
gradient.addColorStop(1, "#004CB3"); // dark blue | |
context.fillStyle = gradient; | |
context.fillRect(0, 0, canvas.width, canvas.height); | |
}); | |
}; | |
var drawMountain = function(mountain, offset) { | |
preserveContext(function(){ | |
context.fillStyle = "rgba(0, 33, 0, 0.2)" | |
context.beginPath(); | |
context.moveTo(mountain[0].x, offset + mountain[0].y); | |
for (var i = 1; i < mountain.length; i++) { | |
context.lineTo(mountain[i].x, offset + mountain[i].y); | |
} | |
context.lineTo(mountain[mountain.length - 1].x,canvas.height); | |
context.lineTo(0,canvas.height); | |
context.lineTo(0,mountain[0].y); | |
context.closePath(); | |
context.fill(); | |
}); | |
}; | |
var drawMountains = function() { | |
var numMountains = mountains.length; | |
var offset = 100; | |
for (var i = 0; i < mountains.length; i++) { | |
drawMountain(mountains[i], offset); | |
offset += mountainGap; | |
} | |
}; | |
var drawSun = function() { | |
preserveContext(function() { | |
var gradient = context.createRadialGradient(75,75,30,75,75,50); | |
gradient.addColorStop(0, 'rgba(255,255,255,0.7)'); | |
gradient.addColorStop(0.2, 'rgba(244,242,1,0.8)'); | |
gradient.addColorStop(1, 'rgba(255,255,255,0)'); | |
context.fillStyle = gradient; | |
context.beginPath(); | |
context.arc(75, 75, 70, 0, Math.PI*2, true); | |
context.closePath(); | |
context.fill(); | |
}); | |
}; | |
var drawNoise = function() { | |
preserveContext(function() | |
var pattern = context.createPattern(noiseImage, "repeat"); | |
context.fillStyle = pattern; | |
context.globalAlpha = 0.05; | |
context.fillRect(0, 0, canvas.width, canvas.height); | |
}); | |
}; | |
var draw = function() { | |
drawSky(); | |
drawMountains(); | |
drawSun(); | |
drawNoise(); | |
}; | |
var setCanvasDimensions = function() { | |
console.log("Setting canvas dimensions to: " + $(window).width() + ", " + $(window).height()); | |
$('#background').attr('width', $(window).width()); | |
$('#background').attr('height', $(window).height()); | |
}; | |
var init = function() { | |
noiseImage = new Image(); | |
noiseImage.src = 'images/noise.png'; | |
noiseImage.onload = function(){ | |
draw(); | |
} | |
canvas = canvas || document.getElementById('background'); | |
setCanvasDimensions(); | |
mountains = generateMountains(5); | |
context = context || canvas.getContext("2d"); | |
mountainGap = canvas.height / (mountains.length + 1); | |
}; | |
$(document).ready(init); | |
})(); | |
</script> | |
<style tyoe='text/css'> | |
body { | |
margin:0; | |
padding:0; | |
height:100%; | |
} | |
</style> | |
<title>Vista of Fractal Mountains</title> | |
</head> | |
<body> | |
<canvas id='background'></canvas> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment