Created
March 7, 2016 20:44
-
-
Save anissen/9c59268eebc352b34a98 to your computer and use it in GitHub Desktop.
Diamond-square algorithm for generating realistic terrain
This file contains 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
class Heightmap { | |
var size :Int; | |
var tiles :Array<Array<Null<Float>>>; | |
var d :Int; | |
public function new() { | |
} | |
public function initialize(size :Int) { | |
this.size = size; // HACK (global) | |
// TODO: Assert that size is (2^n + 1) | |
tiles = create_empty_tiles(size); // HACK (global) | |
var borderWidth = 10; | |
for (y in 0 ... size) { | |
tiles[y][0] = 0.0; | |
tiles[y][size - 1] = 0.0; | |
} | |
for (x in 0 ... size) { | |
tiles[0][x] = 0.0; | |
tiles[size - 1][x] = 0.0; | |
} | |
//tiles[0][0] = -1.0; | |
//tiles[size-1][0] = -1.0; | |
//tiles[0][size-1] = -1.0; | |
//tiles[size-1][size-1] = -1.0; | |
tiles[Math.floor((size-1) / 2)][Math.floor((size-1) / 2)] = 1.0; | |
d = (size - 1); // HACK (global) | |
//diamond_square(); | |
} | |
function create_empty_tiles(size :Int) { | |
return [ for (x in 0 ... size) [ for (y in 0 ... size) null ] ]; | |
} | |
public function tile_value(x :Int, y :Int) { | |
var wrapped_x = (size - 1 + x) % (size - 1); | |
var wrapped_y = (size - 1 + y) % (size - 1); | |
//trace('wrap_x: $wrapped_x, wrap_y: $wrapped_y'); | |
return tiles[wrapped_y][wrapped_x]; | |
} | |
function diamond(x :Int, y :Int, d :Int) { | |
//trace('diamond, $x, $y, $d'); | |
if (tiles[y][x] != null) return; | |
tiles[y][x] = (tile_value(x - d, y) + tile_value(x + d, y) + tile_value(x, y - d) + tile_value(x, y + d)) / 4 + /* func: */ (Math.random() - 0.5) * d; | |
} | |
function square(x :Int, y :Int, d :Int) { | |
//trace('square, $x, $y, $d'); | |
if (tiles[y][x] != null) return; | |
tiles[y][x] = (tile_value(x - d, y - d) + tile_value(x - d, y + d) + tile_value(x + d, y - d) + tile_value(x + d, y + d)) / 4 + /* func: */ (Math.random() - 0.5) * d; | |
} | |
public function diamond_square(/* size, height_function */) { | |
d = Math.floor(d / 2); | |
while (1 <= d) { | |
diamond_square_step(); | |
} | |
} | |
public function diamond_square_step() { | |
if (1 > d) return; | |
{ | |
var x = d; | |
while (x <= size - 1) { | |
var y = d; | |
while (y <= size - 1) { | |
square(x, y, d); | |
y += 2 * d; | |
} | |
x += 2 * d; | |
} | |
} | |
{ | |
var x = d; | |
while (x <= size - 1) { | |
var y = 0; | |
while (y <= size) { | |
diamond(x, y, d); | |
y += 2 * d; | |
} | |
x += 2 * d; | |
} | |
} | |
{ | |
var x = 0; | |
while (x <= size) { | |
var y = d; | |
while (y <= size - 1) { | |
diamond(x, y, d); | |
y += 2 * d; | |
} | |
x += 2 * d; | |
} | |
} | |
d = Math.floor(d / 2); | |
} | |
} | |
class Test { | |
static function main() { | |
var document = js.Browser.document; | |
var canvas = document.createCanvasElement(); | |
canvas.id = "CursorLayer"; | |
canvas.width = 512; | |
canvas.height = 512; | |
canvas.style.border = "1px solid"; | |
var body = document.getElementsByTagName("body")[0]; | |
body.appendChild(canvas); | |
var context = canvas.getContext2d(); | |
var size = 64 + 1; | |
var map = new Heightmap(); | |
map.initialize(size); | |
//map.diamond_square(); | |
//js.Browser.window.setInterval(function() { | |
map.diamond_square(); | |
draw_tiles(map, size, context); | |
//}, 1000); | |
//draw_tiles(context); | |
} | |
static function draw_tiles(map, size, context :js.html.CanvasRenderingContext2D) { | |
var pixelSize = Math.round(512 / size); | |
for (y in 0 ... size) { | |
for (x in 0 ... size) { | |
var value :Float = map.tile_value(x, y); | |
var c = Math.round(value * 255); | |
context.fillStyle = 'rgb($c, $c, $c)'; | |
context.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize); | |
var nc = 255 - c; | |
context.fillStyle = 'rgb($c, $nc, 1)'; | |
context.font = "18px sans-serif"; | |
context.textAlign = 'center'; | |
context.textBaseline = 'middle'; | |
//context.fillText('$value', x * pixelSize + pixelSize / 2, y * pixelSize + pixelSize / 2); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment