terrain-map
ARCHIVED - repo for blog post http://www.vogt.world/writing/procedural-terrain-generation/
git clone https://git.vogt.world/terrain-map.git
Log | Files | README.md
← Commit log
commit
message
making progress toward using webworkers for long-running algorithms
author
Ben Vogt <[email protected]>
date
2016-05-14 04:44:26
stats
4 file(s) changed, 559 insertions(+), 581 deletions(-)
files
erosion.js
index.html
landmap.js
main.js
   1diff --git a/erosion.js b/erosion.js
   2new file mode 100644
   3index 0000000..e399ab4
   4--- /dev/null
   5+++ b/erosion.js
   6@@ -0,0 +1,16 @@
   7+self.importScripts('landmap.js');
   8+self.addEventListener('message', function(e) {
   9+  var terrain = new LandMap({
  10+    containerId: "container-5"
  11+  });
  12+  terrain.generate(0.75, "standard");
  13+  terrain.complexErosion({
  14+    carryingCapacity: 1.0,
  15+    depositionSpeed: 0.1,
  16+    iterations: 3,
  17+    drops: 2000000,
  18+    one: "standard",
  19+    two: "complexErosion-8000000-3ipd"
  20+  });
  21+  self.postMessage(terrain);
  22+}, false);
  23diff --git a/index.html b/index.html
  24index fbc95f8..3c9e61b 100644
  25--- a/index.html
  26+++ b/index.html
  27@@ -3,6 +3,7 @@
  28     <title>Terrain Maps</title>
  29     <link rel="stylesheet" type="text/css" href="style.css">
  30     <script type="text/javascript" src="https://code.jquery.com/jquery-2.2.3.min.js"></script>
  31+    <script type="text/javascript" src="landmap.js"></script>
  32     <script type="text/javascript" src="main.js"></script>
  33   </head>
  34   <body>
  35diff --git a/landmap.js b/landmap.js
  36new file mode 100644
  37index 0000000..34f9375
  38--- /dev/null
  39+++ b/landmap.js
  40@@ -0,0 +1,538 @@
  41+function indexOfMax(arr) {
  42+  if (arr.length === 0) {
  43+    return -1;
  44+  }
  45+
  46+  var max = arr[0];
  47+  var maxIndex = 0;
  48+
  49+  for (var ind = 1; ind < arr.length; ind++) {
  50+    if (arr[ind] > max) {
  51+      maxIndex = ind;
  52+      max = arr[ind];
  53+    }
  54+  }
  55+  return maxIndex;
  56+}
  57+
  58+function indexOfMin(arr) {
  59+  if (arr.length === 0) {
  60+    return -1;
  61+  }
  62+
  63+  var min = arr[0];
  64+  var minIndex = 0;
  65+
  66+  for (var ind = 1; ind < arr.length; ind++) {
  67+    if (arr[ind] < min) {
  68+      minIndex = ind;
  69+      min = arr[ind];
  70+    }
  71+  }
  72+  return minIndex;
  73+}
  74+
  75+// options can be a serialized LandMap
  76+function LandMap(options) {
  77+  var level = 8;
  78+  this.containerId = options.containerId;
  79+  this.size = Math.pow(2, level) + 1;
  80+  this.max = this.size - 1;
  81+  this.maps = options.maps || {};
  82+}
  83+
  84+LandMap.prototype.get = function(which, x, y) {
  85+  if (x < 0 || x > this.max || y < 0 || y > this.max) {
  86+    return -1;
  87+  } else {
  88+    return this.maps[which][x + this.size * y];
  89+  }
  90+};
  91+
  92+LandMap.prototype.set = function(which, x, y, value) {
  93+  this.maps[which][(x + this.size * y)] = value;
  94+};
  95+
  96+LandMap.prototype.generate = function(deviationAmount, feature) {
  97+  var self = this;
  98+
  99+  if (!(feature in self.maps)) {
 100+    this.maps[feature] = new Array(this.size * this.size);
 101+  }
 102+
 103+  this.set(feature, 0, 0, Math.random() * self.max);
 104+  this.set(feature, this.max, 0, Math.random() * self.max);
 105+  this.set(feature, this.max, this.max, Math.random() * self.max);
 106+  this.set(feature, 0, this.max, Math.random() * self.max);
 107+
 108+  subdivide(this.max);
 109+
 110+  function subdivide(size) {
 111+    var x, y, half = size / 2;
 112+    var scale = deviationAmount * size;
 113+    if (half < 1) return;
 114+
 115+    for (y = half; y < self.max; y += size) {
 116+      for (x = half; x < self.max; x += size) {
 117+        square(feature, x, y, half, Math.random() * scale * 2 - scale);
 118+      }
 119+    }
 120+    for (y = 0; y <= self.max; y += half) {
 121+      for (x = (y + half) % size; x <= self.max; x += size) {
 122+        diamond(feature, x, y, half, Math.random() * scale * 2 - scale);
 123+      }
 124+    }
 125+    subdivide(size / 2);
 126+  }
 127+
 128+  function average(values) {
 129+    var valid = values.filter(function(val) {
 130+      return val !== -1;
 131+    });
 132+    var total = valid.reduce(function(sum, val) {
 133+      return sum + val;
 134+    }, 0);
 135+    return total / valid.length;
 136+  }
 137+
 138+  function square(which, x, y, size, offset) {
 139+    var ave = average([
 140+      self.get(which, x - size, y - size), // upper left
 141+      self.get(which, x + size, y - size), // upper right
 142+      self.get(which, x + size, y + size), // lower right
 143+      self.get(which, x - size, y + size) // lower left
 144+    ]);
 145+    self.set(which, x, y, ave + offset);
 146+  }
 147+
 148+  function diamond(which, x, y, size, offset) {
 149+    var ave = average([
 150+      self.get(which, x, y - size), // top
 151+      self.get(which, x + size, y), // right
 152+      self.get(which, x, y + size), // bottom
 153+      self.get(which, x - size, y) // left
 154+    ]);
 155+    self.set(which, x, y, ave + offset);
 156+  }
 157+};
 158+
 159+LandMap.prototype.smooth = function(amount, featureFrom, featureTo) {
 160+  if (!(featureTo in this.maps)) {
 161+    this.maps[featureTo] = new Array(this.size * this.size);
 162+  }
 163+
 164+  var self = this;
 165+  var MARGIN = amount;
 166+  for (var y = 0; y < this.size; y++) {
 167+    for (var x = 0; x < this.size; x++) {
 168+      var nextValue = self.get(featureFrom, x, y);
 169+      var nextValueCount = 0;
 170+      for (var xRange = Math.max(x - Math.round(MARGIN), 0); xRange < Math.min(x + Math.round(MARGIN), this.size); xRange++) {
 171+        for (var yRange = Math.max(y - Math.round(MARGIN), 0); yRange < Math.min(y + Math.round(MARGIN), this.size); yRange++) {
 172+          if (distanceBetween(x, y, xRange, yRange) <= MARGIN) {
 173+            var value = self.get(featureFrom, xRange, yRange);
 174+            nextValue = nextValue + value;
 175+            nextValueCount++;
 176+          }
 177+        }
 178+      }
 179+      var finalVal = ((nextValue / nextValueCount));
 180+      self.set(featureTo, x, y, finalVal);
 181+    }
 182+  }
 183+
 184+  function distanceBetween(ax, ay, bx, by) {
 185+    return Math.abs(Math.sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by)));
 186+  }
 187+};
 188+
 189+LandMap.prototype.combine = function(one, two, three, result) {
 190+
 191+  function percent(value, max, min) {
 192+    return value / Math.abs((max - min));
 193+  }
 194+
 195+  if (!(result in this.maps)) {
 196+    this.maps[result] = new Array(this.size * this.size);
 197+  }
 198+
 199+  var max = Number.MIN_SAFE_INTEGER;
 200+  var min = Number.MAX_SAFE_INTEGER;
 201+  for (var y = 0; y < this.size; y++) {
 202+    for (var x = 0; x < this.size; x++) {
 203+      var val = this.get(two, x, y);
 204+      if (val !== undefined) {
 205+        max = Math.max(max, val);
 206+        min = Math.min(min, val);
 207+      }
 208+    }
 209+  }
 210+
 211+  for (var y = 0; y < this.size; y++) {
 212+    for (var x = 0; x < this.size; x++) {
 213+      var featureOne = this.get(one, x, y);
 214+      var featureTwo = this.get(two, x, y);
 215+      var featureThree = this.get(three, x, y);
 216+      if (featureOne !== undefined && featureTwo !== undefined && featureThree !== undefined) {
 217+        var featureThreePercent = percent(featureThree, min, max);
 218+        var val = (1 - featureThreePercent) * featureOne + featureThreePercent * featureTwo;
 219+        this.set(result, x, y, val);
 220+      }
 221+    }
 222+  }
 223+};
 224+
 225+LandMap.prototype.grd = function(amount, percent, featureFrom, featureTo) {
 226+  this.maps[featureTo] = new Array(this.size * this.size);
 227+
 228+  for (var y = 0; y < this.size; y++) {
 229+    for (var x = 0; x < this.size; x++) {
 230+      this.set(featureTo, x, y, this.get(featureFrom, x, y));
 231+    }
 232+  }
 233+
 234+  var operationAray = new Array(this.size * this.size);
 235+  var size = this.size
 236+  for (var y = 0; y < this.size; y++) {
 237+    for (var x = 0; x < this.size; x++) {
 238+      operationAray[(x + size * y)] = [0];
 239+    }
 240+  }
 241+
 242+  var MARGIN = amount;
 243+  for (var i = 0; i < MARGIN; i++) {
 244+    operationAray[(x + this.size * y)] = [0];
 245+    var max = Number.MIN_SAFE_INTEGER;
 246+    var min = Number.MAX_SAFE_INTEGER;
 247+    for (var y = 0; y < this.size; y++) {
 248+      for (var x = 0; x < this.size; x++) {
 249+        var val = this.get(featureTo, x, y);
 250+        if (val !== undefined) {
 251+          max = Math.max(max, val);
 252+          min = Math.min(min, val);
 253+        }
 254+      }
 255+    }
 256+
 257+    // iterate through all
 258+    for (var y = MARGIN; y < this.size - MARGIN; y++) {
 259+      for (var x = MARGIN; x < this.size - MARGIN; x++) {
 260+        var neighbors = [
 261+          this.get(featureTo, x - 1, y),
 262+          this.get(featureTo, x + 1, y),
 263+          this.get(featureTo, x, y - 1),
 264+          this.get(featureTo, x, y + 1)
 265+        ];
 266+        operationAray[(x + size * y)].push(this.get(featureTo, x, y));
 267+        var index = indexOfMax(featureTo);
 268+        var thisValue = this.get(featureTo, x, y);
 269+        if (neighbors[index] > thisValue) {
 270+          if (index == 0) {
 271+            // WEST (left)
 272+            operationAray[(x + size * y)].push(this.get(featureTo, x - 1, y) * percent);
 273+            operationAray[((x - 1) + size * y)].push(this.get(featureTo, x - 1, y) * (percent) * (-1));
 274+          } else if (index == 1) {
 275+            // EAST (right)
 276+            operationAray[(x + size * y)].push(this.get(featureTo, x + 1, y) * percent);
 277+            operationAray[((x + 1) + size * y)].push(this.get(featureTo, x + 1, y) * (percent) * (-1));
 278+          } else if (index == 2) {
 279+            // NORTH (up)
 280+            operationAray[(x + size * y)].push(this.get(featureTo, x, y - 1) * percent);
 281+            operationAray[(x + size * (y - 1))].push(this.get(featureTo, x - 1, y) * (percent) * (-1));
 282+          } else if (index == 3) {
 283+            // SOUTH (down)
 284+            operationAray[(x + size * y)].push(this.get(featureTo, x, y + 1) * percent);
 285+            operationAray[(x + size * (y + 1))].push(this.get(featureTo, x, y + 1) * (percent) * (-1));
 286+          }
 287+        }
 288+      }
 289+    }
 290+    //iterate through summing the operationAray, and setting it
 291+    for (var y = MARGIN; y < size - MARGIN; y++) {
 292+      for (var x = MARGIN; x < size - MARGIN; x++) {
 293+        var value = operationAray[(x + size * y)].reduce(function(a, b) {
 294+          return a + b;
 295+        }, 0);
 296+        this.set(featureTo, x, y, value);
 297+        operationAray[(x + size * y)] = [value];
 298+      }
 299+    }
 300+  }
 301+};
 302+
 303+LandMap.prototype.simpleErosion = function(options) {
 304+  var Kq = options.carryingCapacity;
 305+  var Kd = options.depositionSpeed;
 306+  var iterations = options.iterations;
 307+  var drops = options.drops;
 308+  var one = options.one;
 309+  var two = options.two;
 310+
 311+  var HeightMap = new Array(this.size * this.size);
 312+  for (var y = 0; y < this.size; y++) {
 313+    for (var x = 0; x < this.size; x++) {
 314+      HeightMap[(x + this.size * y)] = this.get(one, x, y);
 315+    }
 316+  }
 317+
 318+  var HMAP_SIZE = this.size;
 319+
 320+  function HMAP_INDEX(x, y) {
 321+    var val = (x + HMAP_SIZE * y)
 322+    return val;
 323+  }
 324+
 325+  function HMAP_VALUE(x, y) {
 326+    return HeightMap[(x + HMAP_SIZE * y)];
 327+  }
 328+
 329+  function DEPOSIT_AT(X, Y) {
 330+    var c = 0.0;
 331+    var v = 1.05;
 332+    var maxVelocity = 10.0;
 333+
 334+    // For the number of iterations
 335+    for (var iter = 0; iter < iterations; iter++) {
 336+      v = Math.min(v, maxVelocity); // limiting velocity
 337+      var val = HMAP_VALUE(X, Y);
 338+      var nv = [
 339+        HMAP_VALUE(X, Y - 1), //NORTH
 340+        HMAP_VALUE(X, Y + 1), //SOUTH
 341+        HMAP_VALUE(X + 1, Y), //EAST
 342+        HMAP_VALUE(X - 1, Y) //WEST
 343+      ];
 344+
 345+      var minInd = indexOfMin(nv);
 346+      // if the lowest neighbor is NOT greater than the current value
 347+      if (nv[minInd] < val) {
 348+        //deposit or erode
 349+        var vtc = Kd * v * Math.abs(nv[minInd]); // value to steal is depositionSpeed * velocity * abs(slope);
 350+        // if carrying amount is greater than Kq
 351+        if (c > Kq) {
 352+          //DEPOSIT
 353+          c -= vtc;
 354+          HeightMap[HMAP_INDEX(X, Y)] += vtc;
 355+        } else {
 356+          //ERODE
 357+          // if carrying + value to steal > carrying cap
 358+          if (c + vtc > Kq) {
 359+            var delta = c + vtc - Kq;
 360+            c += delta;
 361+            HeightMap[HMAP_INDEX(X, Y)] -= delta;
 362+          } else {
 363+            c += vtc;
 364+            HeightMap[HMAP_INDEX(X, Y)] -= vtc;
 365+          }
 366+        }
 367+
 368+        // move to next value
 369+        if (minInd == 0) {
 370+          //NORTH
 371+          Y -= 1
 372+        }
 373+        if (minInd == 1) {
 374+          //SOUTH
 375+          Y += 1
 376+        }
 377+        if (minInd == 2) {
 378+          //EAST
 379+          X += 1
 380+        }
 381+        if (minInd == 3) {
 382+          //WEST
 383+          X -= 1
 384+        }
 385+
 386+        // limiting to edge of map
 387+        if (X > this.size - 1) {
 388+          X = this.size;
 389+        }
 390+        if (Y > this.size - 1) {
 391+          Y = this.size;
 392+        }
 393+        if (Y < 0) {
 394+          Y = 0;
 395+        }
 396+        if (X < 0) {
 397+          X = 0;
 398+        }
 399+      }
 400+    }
 401+  }
 402+
 403+  for (var drop = 0; drop < drops; drop++) {
 404+    DEPOSIT_AT(Math.floor(Math.random() * this.size), Math.floor(Math.random() * this.size));
 405+    this.maps[two] = HeightMap;
 406+  }
 407+};
 408+
 409+LandMap.prototype.complexErosion = function(options) {
 410+  var Kq = options.carryingCapacity;
 411+  var Kd = options.depositionSpeed;
 412+  var iterations = options.iterations;
 413+  var drops = options.drops;
 414+  var one = options.one;
 415+  var two = options.two;
 416+
 417+  var HeightMap = new Array(this.size * this.size);
 418+  for (var y = 0; y < this.size; y++) {
 419+    for (var x = 0; x < this.size; x++) {
 420+      HeightMap[(x + this.size * y)] = this.get(one, x, y);
 421+    }
 422+  }
 423+
 424+  var HMAP_SIZE = this.size;
 425+
 426+  function HMAP_INDEX(x, y) {
 427+    var val = (x + HMAP_SIZE * y)
 428+    return val;
 429+  }
 430+
 431+  function HMAP_VALUE(x, y) {
 432+    return HeightMap[(x + HMAP_SIZE * y)];
 433+  }
 434+
 435+  function DEPOSIT_AT(X, Y) {
 436+    var c = 0.0;
 437+    var v = 1.05;
 438+    var minSlope = 1.15;
 439+    var maxVelocity = 10.0;
 440+
 441+    // For the number of iterations
 442+    for (var iter = 0; iter < iterations; iter++) {
 443+      v = Math.min(v, maxVelocity); // limiting velocity
 444+      var val = HMAP_VALUE(X, Y);
 445+      var nv = [
 446+        HMAP_VALUE(X, Y - 1), //NORTH
 447+        HMAP_VALUE(X, Y + 1), //SOUTH
 448+        HMAP_VALUE(X + 1, Y), //EAST
 449+        HMAP_VALUE(X - 1, Y) //WEST
 450+      ];
 451+
 452+      var minInd = indexOfMin(nv);
 453+      // if the lowest neighbor is NOT greater than the current value
 454+      if (nv[minInd] < val) {
 455+        //deposit or erode
 456+        var slope = Math.min(minSlope, val - nv[minInd])
 457+        var vtc = Kd * v * slope; // value to steal is depositionSpeed * velocity * abs(slope);
 458+        // if carrying amount is greater than Kq
 459+        if (c > Kq) {
 460+          //DEPOSIT
 461+          c -= vtc;
 462+          HeightMap[HMAP_INDEX(X, Y)] += vtc;
 463+        } else {
 464+          //ERODE
 465+          // if carrying + value to steal > carrying cap
 466+          if (c + vtc > Kq) {
 467+            var delta = c + vtc - Kq;
 468+            c += delta;
 469+            HeightMap[HMAP_INDEX(X, Y)] -= delta;
 470+          } else {
 471+            c += vtc;
 472+            HeightMap[HMAP_INDEX(X, Y)] -= vtc;
 473+          }
 474+        }
 475+
 476+        // move to next value
 477+        if (minInd == 0) {
 478+          //NORTH
 479+          Y -= 1
 480+        }
 481+        if (minInd == 1) {
 482+          //SOUTH
 483+          Y += 1
 484+        }
 485+        if (minInd == 2) {
 486+          //EAST
 487+          X += 1
 488+        }
 489+        if (minInd == 3) {
 490+          //WEST
 491+          X -= 1
 492+        }
 493+
 494+        // limiting to edge of map
 495+        if (X > this.size - 1) {
 496+          X = this.size;
 497+        }
 498+        if (Y > this.size - 1) {
 499+          Y = this.size;
 500+        }
 501+        if (Y < 0) {
 502+          Y = 0;
 503+        }
 504+        if (X < 0) {
 505+          X = 0;
 506+        }
 507+      }
 508+    }
 509+  }
 510+
 511+  for (var drop = 0; drop < drops; drop++) {
 512+    DEPOSIT_AT(Math.floor(Math.random() * this.size), Math.floor(Math.random() * this.size));
 513+    this.maps[two] = HeightMap;
 514+  }
 515+};
 516+
 517+LandMap.prototype.spinner = function() {
 518+  var spinner = '<img class="spinner" width="24" height="24" src="data:image/gif;base64,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" />';
 519+  document.getElementById(this.containerId).innerHTML = spinner;
 520+}
 521+
 522+LandMap.prototype.draw = function() {
 523+  var html = '<div class="row">';
 524+  var featureCount = 0;
 525+  for (feature in this.maps) {
 526+    html += '<div class="four columns"><strong>' + feature + '</strong><div class="box"><span id="' + this.containerId + feature + '"></span><span id="' + feature + '-description"></span></div></div>';
 527+    featureCount++;
 528+    if (featureCount == 3) {
 529+      html += '</div><div class="row">'
 530+      featureCount = 0;
 531+    }
 532+  }
 533+  document.getElementById(this.containerId).innerHTML = html;
 534+  for (feature in this.maps) {
 535+    var display = document.getElementById("tmp");
 536+    var ctx = display.getContext('2d');
 537+    var width = display.width = 256;
 538+    var height = display.height = 256;
 539+    var self = this;
 540+
 541+    var cellWidth = 1;
 542+
 543+    function drawCell(x, y, inensity) {
 544+      ctx.fillStyle = inensity;
 545+      ctx.fillRect(x * cellWidth, y * cellWidth, cellWidth, cellWidth);
 546+    }
 547+
 548+    // finding max, min
 549+    var max = Number.MIN_SAFE_INTEGER;
 550+    var min = Number.MAX_SAFE_INTEGER;
 551+    for (var y = 0; y < this.size; y++) {
 552+      for (var x = 0; x < this.size; x++) {
 553+        var val = this.get(feature, x, y);
 554+        if (val !== undefined) {
 555+          max = Math.max(max, val);
 556+          min = Math.min(min, val);
 557+        }
 558+      }
 559+    }
 560+    // Drawing each cell
 561+    for (var y = 0; y < this.size; y++) {
 562+      for (var x = 0; x < this.size; x++) {
 563+        var val = this.get(feature, x, y);
 564+        if (val !== undefined) {
 565+          drawCell(x, y, brightness(val, max, min));
 566+        }
 567+      }
 568+    }
 569+
 570+    document.getElementById(this.containerId + feature).innerHTML = '<img src="' + display.toDataURL("image/png") + '" class="u-max-full-width map"/>';
 571+  }
 572+
 573+  function brightness(value, max, min) {
 574+    var delta = Math.abs((max - min));
 575+    var b = Math.floor((value / delta) * 255);
 576+    return 'rgba(' + b + ',' + b + ',' + b + ',1)';
 577+  }
 578+};
 579diff --git a/main.js b/main.js
 580index 5026d9d..63772b2 100644
 581--- a/main.js
 582+++ b/main.js
 583@@ -1,544 +1,6 @@
 584+var worker;
 585 $(document).ready(function() {
 586 
 587-  function indexOfMax(arr) {
 588-    if (arr.length === 0) {
 589-      return -1;
 590-    }
 591-
 592-    var max = arr[0];
 593-    var maxIndex = 0;
 594-
 595-    for (var ind = 1; ind < arr.length; ind++) {
 596-      if (arr[ind] > max) {
 597-        maxIndex = ind;
 598-        max = arr[ind];
 599-      }
 600-    }
 601-    return maxIndex;
 602-  }
 603-
 604-  function indexOfMin(arr) {
 605-    if (arr.length === 0) {
 606-      return -1;
 607-    }
 608-
 609-    var min = arr[0];
 610-    var minIndex = 0;
 611-
 612-    for (var ind = 1; ind < arr.length; ind++) {
 613-      if (arr[ind] < min) {
 614-        minIndex = ind;
 615-        min = arr[ind];
 616-      }
 617-    }
 618-    return minIndex;
 619-  }
 620-
 621-  function LandMap(options) {
 622-    var level = 8;
 623-    this.containerId = options.containerId;
 624-    this.title = options.title;
 625-    this.size = Math.pow(2, level) + 1;
 626-    this.max = this.size - 1;
 627-    this.maps = {};
 628-  }
 629-
 630-  LandMap.prototype.get = function(which, x, y) {
 631-    if (x < 0 || x > this.max || y < 0 || y > this.max) {
 632-      return -1;
 633-    } else {
 634-      return this.maps[which][x + this.size * y];
 635-    }
 636-  };
 637-
 638-  LandMap.prototype.set = function(which, x, y, value) {
 639-    this.maps[which][(x + this.size * y)] = value;
 640-  };
 641-
 642-  LandMap.prototype.generate = function(deviationAmount, feature) {
 643-    var self = this;
 644-
 645-    if (!(feature in self.maps)) {
 646-      this.maps[feature] = new Array(this.size * this.size);
 647-    }
 648-
 649-    this.set(feature, 0, 0, Math.random() * self.max);
 650-    this.set(feature, this.max, 0, Math.random() * self.max);
 651-    this.set(feature, this.max, this.max, Math.random() * self.max);
 652-    this.set(feature, 0, this.max, Math.random() * self.max);
 653-
 654-    subdivide(this.max);
 655-
 656-    function subdivide(size) {
 657-      var x, y, half = size / 2;
 658-      var scale = deviationAmount * size;
 659-      if (half < 1) return;
 660-
 661-      for (y = half; y < self.max; y += size) {
 662-        for (x = half; x < self.max; x += size) {
 663-          square(feature, x, y, half, Math.random() * scale * 2 - scale);
 664-        }
 665-      }
 666-      for (y = 0; y <= self.max; y += half) {
 667-        for (x = (y + half) % size; x <= self.max; x += size) {
 668-          diamond(feature, x, y, half, Math.random() * scale * 2 - scale);
 669-        }
 670-      }
 671-      subdivide(size / 2);
 672-    }
 673-
 674-    function average(values) {
 675-      var valid = values.filter(function(val) {
 676-        return val !== -1;
 677-      });
 678-      var total = valid.reduce(function(sum, val) {
 679-        return sum + val;
 680-      }, 0);
 681-      return total / valid.length;
 682-    }
 683-
 684-    function square(which, x, y, size, offset) {
 685-      var ave = average([
 686-        self.get(which, x - size, y - size), // upper left
 687-        self.get(which, x + size, y - size), // upper right
 688-        self.get(which, x + size, y + size), // lower right
 689-        self.get(which, x - size, y + size) // lower left
 690-      ]);
 691-      self.set(which, x, y, ave + offset);
 692-    }
 693-
 694-    function diamond(which, x, y, size, offset) {
 695-      var ave = average([
 696-        self.get(which, x, y - size), // top
 697-        self.get(which, x + size, y), // right
 698-        self.get(which, x, y + size), // bottom
 699-        self.get(which, x - size, y) // left
 700-      ]);
 701-      self.set(which, x, y, ave + offset);
 702-    }
 703-  };
 704-
 705-  LandMap.prototype.smooth = function(amount, featureFrom, featureTo) {
 706-    if (!(featureTo in this.maps)) {
 707-      this.maps[featureTo] = new Array(this.size * this.size);
 708-    }
 709-
 710-    var self = this;
 711-    var MARGIN = amount;
 712-    for (var y = 0; y < this.size; y++) {
 713-      for (var x = 0; x < this.size; x++) {
 714-        var nextValue = self.get(featureFrom, x, y);
 715-        var nextValueCount = 0;
 716-        for (var xRange = Math.max(x - Math.round(MARGIN), 0); xRange < Math.min(x + Math.round(MARGIN), this.size); xRange++) {
 717-          for (var yRange = Math.max(y - Math.round(MARGIN), 0); yRange < Math.min(y + Math.round(MARGIN), this.size); yRange++) {
 718-            if (distanceBetween(x, y, xRange, yRange) <= MARGIN) {
 719-              var value = self.get(featureFrom, xRange, yRange);
 720-              nextValue = nextValue + value;
 721-              nextValueCount++;
 722-            }
 723-          }
 724-        }
 725-        var finalVal = ((nextValue / nextValueCount));
 726-        self.set(featureTo, x, y, finalVal);
 727-      }
 728-    }
 729-
 730-    function distanceBetween(ax, ay, bx, by) {
 731-      return Math.abs(Math.sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by)));
 732-    }
 733-  };
 734-
 735-  LandMap.prototype.combine = function(one, two, three, result) {
 736-
 737-    function percent(value, max, min) {
 738-      return value / Math.abs((max - min));
 739-    }
 740-
 741-    if (!(result in this.maps)) {
 742-      this.maps[result] = new Array(this.size * this.size);
 743-    }
 744-
 745-    var max = Number.MIN_SAFE_INTEGER;
 746-    var min = Number.MAX_SAFE_INTEGER;
 747-    for (var y = 0; y < this.size; y++) {
 748-      for (var x = 0; x < this.size; x++) {
 749-        var val = this.get(two, x, y);
 750-        if (val !== undefined) {
 751-          max = Math.max(max, val);
 752-          min = Math.min(min, val);
 753-        }
 754-      }
 755-    }
 756-
 757-    for (var y = 0; y < this.size; y++) {
 758-      for (var x = 0; x < this.size; x++) {
 759-        var featureOne = this.get(one, x, y);
 760-        var featureTwo = this.get(two, x, y);
 761-        var featureThree = this.get(three, x, y);
 762-        if (featureOne !== undefined && featureTwo !== undefined && featureThree !== undefined) {
 763-          var featureThreePercent = percent(featureThree, min, max);
 764-          var val = (1 - featureThreePercent) * featureOne + featureThreePercent * featureTwo;
 765-          this.set(result, x, y, val);
 766-        }
 767-      }
 768-    }
 769-  };
 770-
 771-  LandMap.prototype.grd = function(amount, percent, featureFrom, featureTo) {
 772-    this.maps[featureTo] = new Array(this.size * this.size);
 773-
 774-    for (var y = 0; y < this.size; y++) {
 775-      for (var x = 0; x < this.size; x++) {
 776-        this.set(featureTo, x, y, this.get(featureFrom, x, y));
 777-      }
 778-    }
 779-
 780-    var operationAray = new Array(this.size * this.size);
 781-    var size = this.size
 782-    for (var y = 0; y < this.size; y++) {
 783-      for (var x = 0; x < this.size; x++) {
 784-        operationAray[(x + size * y)] = [0];
 785-      }
 786-    }
 787-
 788-    var MARGIN = amount;
 789-    for (var i = 0; i < MARGIN; i++) {
 790-      operationAray[(x + this.size * y)] = [0];
 791-      var max = Number.MIN_SAFE_INTEGER;
 792-      var min = Number.MAX_SAFE_INTEGER;
 793-      for (var y = 0; y < this.size; y++) {
 794-        for (var x = 0; x < this.size; x++) {
 795-          var val = this.get(featureTo, x, y);
 796-          if (val !== undefined) {
 797-            max = Math.max(max, val);
 798-            min = Math.min(min, val);
 799-          }
 800-        }
 801-      }
 802-
 803-      // iterate through all
 804-      for (var y = MARGIN; y < this.size - MARGIN; y++) {
 805-        for (var x = MARGIN; x < this.size - MARGIN; x++) {
 806-          var neighbors = [
 807-            this.get(featureTo, x - 1, y),
 808-            this.get(featureTo, x + 1, y),
 809-            this.get(featureTo, x, y - 1),
 810-            this.get(featureTo, x, y + 1)
 811-          ];
 812-          operationAray[(x + size * y)].push(this.get(featureTo, x, y));
 813-          var index = indexOfMax(featureTo);
 814-          var thisValue = this.get(featureTo, x, y);
 815-          if (neighbors[index] > thisValue) {
 816-            if (index == 0) {
 817-              // WEST (left)
 818-              operationAray[(x + size * y)].push(this.get(featureTo, x - 1, y) * percent);
 819-              operationAray[((x - 1) + size * y)].push(this.get(featureTo, x - 1, y) * (percent) * (-1));
 820-            } else if (index == 1) {
 821-              // EAST (right)
 822-              operationAray[(x + size * y)].push(this.get(featureTo, x + 1, y) * percent);
 823-              operationAray[((x + 1) + size * y)].push(this.get(featureTo, x + 1, y) * (percent) * (-1));
 824-            } else if (index == 2) {
 825-              // NORTH (up)
 826-              operationAray[(x + size * y)].push(this.get(featureTo, x, y - 1) * percent);
 827-              operationAray[(x + size * (y - 1))].push(this.get(featureTo, x - 1, y) * (percent) * (-1));
 828-            } else if (index == 3) {
 829-              // SOUTH (down)
 830-              operationAray[(x + size * y)].push(this.get(featureTo, x, y + 1) * percent);
 831-              operationAray[(x + size * (y + 1))].push(this.get(featureTo, x, y + 1) * (percent) * (-1));
 832-            }
 833-          }
 834-        }
 835-      }
 836-      //iterate through summing the operationAray, and setting it
 837-      for (var y = MARGIN; y < size - MARGIN; y++) {
 838-        for (var x = MARGIN; x < size - MARGIN; x++) {
 839-          var value = operationAray[(x + size * y)].reduce(function(a, b) {
 840-            return a + b;
 841-          }, 0);
 842-          this.set(featureTo, x, y, value);
 843-          operationAray[(x + size * y)] = [value];
 844-        }
 845-      }
 846-    }
 847-  };
 848-
 849-  LandMap.prototype.simpleErosion = function(options) {
 850-    var Kq = options.carryingCapacity;
 851-    var Kd = options.depositionSpeed;
 852-    var iterations = options.iterations;
 853-    var drops = options.drops;
 854-    var one = options.one;
 855-    var two = options.two;
 856-
 857-    var HeightMap = new Array(this.size * this.size);
 858-    for (var y = 0; y < this.size; y++) {
 859-      for (var x = 0; x < this.size; x++) {
 860-        HeightMap[(x + this.size * y)] = this.get(one, x, y);
 861-      }
 862-    }
 863-
 864-    var HMAP_SIZE = this.size;
 865-
 866-    function HMAP_INDEX(x, y) {
 867-      var val = (x + HMAP_SIZE * y)
 868-      return val;
 869-    }
 870-
 871-    function HMAP_VALUE(x, y) {
 872-      return HeightMap[(x + HMAP_SIZE * y)];
 873-    }
 874-
 875-    function DEPOSIT_AT(X, Y) {
 876-      var c = 0.0;
 877-      var v = 1.05;
 878-      var maxVelocity = 10.0;
 879-
 880-      // For the number of iterations
 881-      for (var iter = 0; iter < iterations; iter++) {
 882-        v = Math.min(v, maxVelocity); // limiting velocity
 883-        var val = HMAP_VALUE(X, Y);
 884-        var nv = [
 885-          HMAP_VALUE(X, Y - 1), //NORTH
 886-          HMAP_VALUE(X, Y + 1), //SOUTH
 887-          HMAP_VALUE(X + 1, Y), //EAST
 888-          HMAP_VALUE(X - 1, Y) //WEST
 889-        ];
 890-
 891-        var minInd = indexOfMin(nv);
 892-        // if the lowest neighbor is NOT greater than the current value
 893-        if (nv[minInd] < val) {
 894-          //deposit or erode
 895-          var vtc = Kd * v * Math.abs(nv[minInd]); // value to steal is depositionSpeed * velocity * abs(slope);
 896-          // if carrying amount is greater than Kq
 897-          if (c > Kq) {
 898-            //DEPOSIT
 899-            c -= vtc;
 900-            HeightMap[HMAP_INDEX(X, Y)] += vtc;
 901-          } else {
 902-            //ERODE
 903-            // if carrying + value to steal > carrying cap
 904-            if (c + vtc > Kq) {
 905-              var delta = c + vtc - Kq;
 906-              c += delta;
 907-              HeightMap[HMAP_INDEX(X, Y)] -= delta;
 908-            } else {
 909-              c += vtc;
 910-              HeightMap[HMAP_INDEX(X, Y)] -= vtc;
 911-            }
 912-          }
 913-
 914-          // move to next value
 915-          if (minInd == 0) {
 916-            //NORTH
 917-            Y -= 1
 918-          }
 919-          if (minInd == 1) {
 920-            //SOUTH
 921-            Y += 1
 922-          }
 923-          if (minInd == 2) {
 924-            //EAST
 925-            X += 1
 926-          }
 927-          if (minInd == 3) {
 928-            //WEST
 929-            X -= 1
 930-          }
 931-
 932-          // limiting to edge of map
 933-          if (X > this.size - 1) {
 934-            X = this.size;
 935-          }
 936-          if (Y > this.size - 1) {
 937-            Y = this.size;
 938-          }
 939-          if (Y < 0) {
 940-            Y = 0;
 941-          }
 942-          if (X < 0) {
 943-            X = 0;
 944-          }
 945-        }
 946-      }
 947-    }
 948-
 949-    for (var drop = 0; drop < drops; drop++) {
 950-      DEPOSIT_AT(Math.floor(Math.random() * this.size), Math.floor(Math.random() * this.size));
 951-      this.maps[two] = HeightMap;
 952-    }
 953-  };
 954-
 955-  LandMap.prototype.complexErosion = function(options) {
 956-    var Kq = options.carryingCapacity;
 957-    var Kd = options.depositionSpeed;
 958-    var iterations = options.iterations;
 959-    var drops = options.drops;
 960-    var one = options.one;
 961-    var two = options.two;
 962-
 963-    var HeightMap = new Array(this.size * this.size);
 964-    for (var y = 0; y < this.size; y++) {
 965-      for (var x = 0; x < this.size; x++) {
 966-        HeightMap[(x + this.size * y)] = this.get(one, x, y);
 967-      }
 968-    }
 969-
 970-    var HMAP_SIZE = this.size;
 971-
 972-    function HMAP_INDEX(x, y) {
 973-      var val = (x + HMAP_SIZE * y)
 974-      return val;
 975-    }
 976-
 977-    function HMAP_VALUE(x, y) {
 978-      return HeightMap[(x + HMAP_SIZE * y)];
 979-    }
 980-
 981-    function DEPOSIT_AT(X, Y) {
 982-      var c = 0.0;
 983-      var v = 1.05;
 984-      var minSlope = 1.15;
 985-      var maxVelocity = 10.0;
 986-
 987-      // For the number of iterations
 988-      for (var iter = 0; iter < iterations; iter++) {
 989-        v = Math.min(v, maxVelocity); // limiting velocity
 990-        var val = HMAP_VALUE(X, Y);
 991-        var nv = [
 992-          HMAP_VALUE(X, Y - 1), //NORTH
 993-          HMAP_VALUE(X, Y + 1), //SOUTH
 994-          HMAP_VALUE(X + 1, Y), //EAST
 995-          HMAP_VALUE(X - 1, Y) //WEST
 996-        ];
 997-
 998-        var minInd = indexOfMin(nv);
 999-        // if the lowest neighbor is NOT greater than the current value
1000-        if (nv[minInd] < val) {
1001-          //deposit or erode
1002-          var slope = Math.min(minSlope, val - nv[minInd])
1003-          var vtc = Kd * v * slope; // value to steal is depositionSpeed * velocity * abs(slope);
1004-          // if carrying amount is greater than Kq
1005-          if (c > Kq) {
1006-            //DEPOSIT
1007-            c -= vtc;
1008-            HeightMap[HMAP_INDEX(X, Y)] += vtc;
1009-          } else {
1010-            //ERODE
1011-            // if carrying + value to steal > carrying cap
1012-            if (c + vtc > Kq) {
1013-              var delta = c + vtc - Kq;
1014-              c += delta;
1015-              HeightMap[HMAP_INDEX(X, Y)] -= delta;
1016-            } else {
1017-              c += vtc;
1018-              HeightMap[HMAP_INDEX(X, Y)] -= vtc;
1019-            }
1020-          }
1021-
1022-          // move to next value
1023-          if (minInd == 0) {
1024-            //NORTH
1025-            Y -= 1
1026-          }
1027-          if (minInd == 1) {
1028-            //SOUTH
1029-            Y += 1
1030-          }
1031-          if (minInd == 2) {
1032-            //EAST
1033-            X += 1
1034-          }
1035-          if (minInd == 3) {
1036-            //WEST
1037-            X -= 1
1038-          }
1039-
1040-          // limiting to edge of map
1041-          if (X > this.size - 1) {
1042-            X = this.size;
1043-          }
1044-          if (Y > this.size - 1) {
1045-            Y = this.size;
1046-          }
1047-          if (Y < 0) {
1048-            Y = 0;
1049-          }
1050-          if (X < 0) {
1051-            X = 0;
1052-          }
1053-        }
1054-      }
1055-    }
1056-
1057-    for (var drop = 0; drop < drops; drop++) {
1058-      DEPOSIT_AT(Math.floor(Math.random() * this.size), Math.floor(Math.random() * this.size));
1059-      this.maps[two] = HeightMap;
1060-    }
1061-  };
1062-
1063-  LandMap.prototype.spinner = function() {
1064-    var spinner = '<img class="spinner" width="24" height="24" src="data:image/gif;base64,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" />';
1065-    document.getElementById(this.containerId).innerHTML = spinner;
1066-  }
1067-
1068-  LandMap.prototype.draw = function() {
1069-    var html = '<div class="row">';
1070-    var featureCount = 0;
1071-    for (feature in this.maps) {
1072-      html += '<div class="four columns"><strong>' + feature + '</strong><div class="box"><span id="' + this.containerId + feature + '"></span><span id="' + feature + '-description"></span></div></div>';
1073-      featureCount++;
1074-      if (featureCount == 3) {
1075-        html += '</div><div class="row">'
1076-        featureCount = 0;
1077-      }
1078-    }
1079-    document.getElementById(this.containerId).innerHTML = html;
1080-    for (feature in this.maps) {
1081-      var display = document.getElementById("tmp");
1082-      var ctx = display.getContext('2d');
1083-      var width = display.width = 256;
1084-      var height = display.height = 256;
1085-      var self = this;
1086-
1087-      var cellWidth = 1;
1088-
1089-      function drawCell(x, y, inensity) {
1090-        ctx.fillStyle = inensity;
1091-        ctx.fillRect(x * cellWidth, y * cellWidth, cellWidth, cellWidth);
1092-      }
1093-
1094-      // finding max, min
1095-      var max = Number.MIN_SAFE_INTEGER;
1096-      var min = Number.MAX_SAFE_INTEGER;
1097-      for (var y = 0; y < this.size; y++) {
1098-        for (var x = 0; x < this.size; x++) {
1099-          var val = this.get(feature, x, y);
1100-          if (val !== undefined) {
1101-            max = Math.max(max, val);
1102-            min = Math.min(min, val);
1103-          }
1104-        }
1105-      }
1106-      // Drawing each cell
1107-      for (var y = 0; y < this.size; y++) {
1108-        for (var x = 0; x < this.size; x++) {
1109-          var val = this.get(feature, x, y);
1110-          if (val !== undefined) {
1111-            drawCell(x, y, brightness(val, max, min));
1112-          }
1113-        }
1114-      }
1115-
1116-      document.getElementById(this.containerId + feature).innerHTML = '<img src="' + display.toDataURL("image/png") + '" class="u-max-full-width map"/>';
1117-    }
1118-
1119-    function brightness(value, max, min) {
1120-      var delta = Math.abs((max - min));
1121-      var b = Math.floor((value / delta) * 255);
1122-      return 'rgba(' + b + ',' + b + ',' + b + ',1)';
1123-    }
1124-  };
1125-
1126   $("#standard").click(function() {
1127     var standardMap = new LandMap({
1128       containerId: "container-1"
1129@@ -591,46 +53,14 @@ $(document).ready(function() {
1130   });
1131 
1132   $("#complexErosion").click(function() {
1133-    var terrain = new LandMap({
1134-      containerId: "container-5"
1135-    });
1136-    terrain.spinner();
1137-    setTimeout(function() {
1138-      terrain.generate(0.75, "standard");
1139-      // terrain.complexErosion({
1140-      //   carryingCapacity: 1.5,
1141-      //   depositionSpeed: 0.03,
1142-      //   iterations: 10,
1143-      //   drops: 1000000,
1144-      //   one: "standard",
1145-      //   two: "complexErosion-1000000-10ipd"
1146-      // });
1147-      // terrain.complexErosion({
1148-      //   carryingCapacity: 1.5,
1149-      //   depositionSpeed: 0.03,
1150-      //   iterations: 20,
1151-      //   drops: 1000000,
1152-      //   one: "standard",
1153-      //   two: "complexErosion-1000000-20ipd"
1154-      // });
1155-      // terrain.complexErosion({
1156-      //   carryingCapacity: 1.5,
1157-      //   depositionSpeed: 0.03,
1158-      //   iterations: 3,
1159-      //   drops: 4000000,
1160-      //   one: "standard",
1161-      //   two: "complexErosion-4000000-3ipd"
1162-      // });
1163-      terrain.complexErosion({
1164-        carryingCapacity: 1.0,
1165-        depositionSpeed: 0.1,
1166-        iterations: 3,
1167-        drops: 2000000,
1168-        one: "standard",
1169-        two: "complexErosion-8000000-3ipd"
1170-      });
1171+    var terrain;
1172+    worker = new Worker('erosion.js');
1173+    worker.postMessage(0);
1174+    worker.addEventListener('message', function(e) {
1175+      console.log(e.data.maps)
1176+      terrain = new LandMap(e.data);
1177+      console.log(terrain.maps)
1178       terrain.draw();
1179-    }, 1000);
1180-
1181+    }, false);
1182   });
1183 })