Skip to content

Instantly share code, notes, and snippets.

@anissen
Created March 7, 2016 20:44
Show Gist options
  • Save anissen/9c59268eebc352b34a98 to your computer and use it in GitHub Desktop.
Save anissen/9c59268eebc352b34a98 to your computer and use it in GitHub Desktop.
Diamond-square algorithm for generating realistic terrain
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