Created
February 14, 2015 17:24
-
-
Save andrewroberts/b71fb499d11d9d2f25d1 to your computer and use it in GitHub Desktop.
Generates terrain values for a 257x257 Google sheet
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
function onOpen() { | |
SpreadsheetApp | |
.getUi() | |
.createMenu('Terrain') | |
.addItem('Generate terrain map', 'createTerrain') | |
.addToUi(); | |
} | |
// From https://github.com/hunterloftis/playfuljs-demos/blob/gh-pages/terrain/index.html | |
var MAP_SIZE = 8; | |
var ROUGHNESS = 0.7; | |
function createTerrain() { | |
var terrain = new Terrain(MAP_SIZE); // Map array size pow(2, MAP_SIZE) | |
terrain.generate(ROUGHNESS); // Roughness 0 - 1 | |
terrain.draw(); | |
} // createTerrain() | |
/** | |
* Terrain class with an array pow(2,detail) elements long, each with a | |
* height value. So map should be pow(2, N) + 1. So a 65x65 array would | |
* be pow(2, 6) + 1, so MAP_SIZE = 6. | |
*/ | |
function Terrain(detail) { | |
this.size = Math.pow(2, detail) + 1; | |
this.max = this.size - 1; | |
this.map = new Float32Array(this.size * this.size); | |
} | |
Terrain.prototype.get = function(x, y) { | |
if (x < 0 || x > this.max || y < 0 || y > this.max) return -1; | |
return this.map[x + this.size * y]; | |
}; | |
Terrain.prototype.set = function(x, y, val) { | |
this.map[x + this.size * y] = val; | |
}; | |
Terrain.prototype.generate = function(roughness) { | |
var self = this; | |
this.set(0, 0, self.max); | |
this.set(this.max, 0, self.max / 2); | |
this.set(this.max, this.max, 0); | |
this.set(0, this.max, self.max / 2); | |
divide(this.max); | |
function divide(size) { | |
var x, y, half = size / 2; | |
var scale = roughness * size; | |
if (half < 1) return; | |
for (y = half; y < self.max; y += size) { | |
for (x = half; x < self.max; x += size) { | |
square(x, y, half, Math.random() * scale * 2 - scale); | |
} | |
} | |
for (y = 0; y <= self.max; y += half) { | |
for (x = (y + half) % size; x <= self.max; x += size) { | |
diamond(x, y, half, Math.random() * scale * 2 - scale); | |
} | |
} | |
divide(size / 2); | |
} | |
function average(values) { | |
var valid = values.filter(function(val) { return val !== -1; }); | |
var total = valid.reduce(function(sum, val) { return sum + val; }, 0); | |
return total / valid.length; | |
} | |
function square(x, y, size, offset) { | |
var ave = average([ | |
self.get(x - size, y - size), // upper left | |
self.get(x + size, y - size), // upper right | |
self.get(x + size, y + size), // lower right | |
self.get(x - size, y + size) // lower left | |
]); | |
self.set(x, y, ave + offset); | |
} | |
function diamond(x, y, size, offset) { | |
var ave = average([ | |
self.get(x, y - size), // top | |
self.get(x + size, y), // right | |
self.get(x, y + size), // bottom | |
self.get(x - size, y) // left | |
]); | |
self.set(x, y, ave + offset); | |
} | |
}; | |
Terrain.prototype.draw = function() { | |
var self = this; | |
var sheet = SpreadsheetApp.getActiveSheet(); | |
var rowIndex; | |
var numberOfRows = sheet.getMaxRows(); | |
var columnIndex; | |
var numberOfColumns = sheet.getMaxColumns(); | |
var newRow = []; | |
var data = []; | |
var height; | |
for (rowIndex = 0; rowIndex < numberOfRows; rowIndex++) { | |
for (columnIndex = 0; columnIndex < numberOfColumns; columnIndex++) { | |
height = this.map[(rowIndex * numberOfRows) + columnIndex]; | |
newRow.push(height); | |
} | |
data.push(newRow.slice()); | |
newRow.length = 0; | |
} | |
var range = sheet.getRange(1, 1, numberOfRows, numberOfColumns); | |
range.setValues(data); | |
}; | |
function Float32Array(size) { | |
this.array = []; | |
var index; | |
for (index = 0; index < size; index++) { | |
this.array.push(0); | |
} | |
return this.array; | |
} // Float32Array() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment