Last active
February 3, 2017 10:05
-
-
Save ralfw/a2545bc61a5fb852959884bc3999a2e8 to your computer and use it in GitHub Desktop.
Diamond-Square Solution
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
using System; | |
using System.Linq; | |
namespace terraingenBlog | |
{ | |
public class TerrainGenerator { | |
public static void Interpolate(Terrain terrain, float offset, float amplitude) { | |
Interpolate(terrain, offset, amplitude, Random_numbers_between_minus_1_and_1()); | |
} | |
public static void Interpolate(Terrain terrain, float offset, float amplitude, Func<float> randomValue) | |
{ | |
var sh = new ShapeHierarchy(terrain.N); | |
var calc = new Calculator(offset, amplitude, randomValue); | |
foreach (var s in sh.Shapes) { | |
var vertex_values = s.Vertexes.Select(c => terrain[c.Y, c.X]); | |
var terrain_value = calc.Process(vertex_values.ToArray()); | |
terrain[s.Center.Y, s.Center.X] = terrain_value; | |
} | |
} | |
static Func<float> Random_numbers_between_minus_1_and_1() { | |
var rnd = new Random(); | |
return () => (float)rnd.Next(-100, 100) / 100.0f; | |
} | |
} | |
} |
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
using System; | |
namespace terraingenBlog | |
{ | |
public class Terrain { | |
private float[,] matrix; | |
public Terrain(int n) { | |
this.N = n; | |
var size = (int)Math.Pow(2,n) + 1; | |
matrix = new float[size, size]; | |
} | |
public Terrain(int n, float leftTop, float rightTop, float rightBottom, float leftBottom) : this(n) { | |
matrix[0, 0] = leftTop; | |
matrix[0, Size - 1] = rightTop; | |
matrix[Size - 1, Size - 1] = rightBottom; | |
matrix[Size - 1, 0] = leftBottom; | |
} | |
public int N { get; } | |
public int Size => matrix.GetLength(0); | |
public float this[int y, int x] { | |
get { return matrix[y, x]; } | |
set { matrix[y, x] = value; } | |
} | |
public float[,] ToArray() => matrix; | |
} | |
} |
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
using System; | |
using System.Linq; | |
namespace terraingenBlog | |
{ | |
class Calculator { | |
readonly float offset; | |
readonly float amplitude; | |
readonly Func<float> randomValue; | |
public Calculator(float offset, float amplitude, Func<float> randomValue) { | |
this.offset = offset; | |
this.amplitude = amplitude; | |
this.randomValue = randomValue; | |
} | |
public float Process(float[] values) { | |
var result = values.Average(); | |
return Add_jitter(result); | |
} | |
float Add_jitter(float value) { | |
return value + this.offset + this.amplitude * this.randomValue(); | |
} | |
} | |
} |
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
using System; | |
using System.Linq; | |
using System.Collections.Generic; | |
namespace terraingenBlog | |
{ | |
class Shape | |
{ | |
public Coordinate Center; | |
public Coordinate[] Vertexes; | |
public override string ToString() | |
{ | |
var vertexes = string.Join(",", Vertexes); | |
return $"{Center}@({vertexes})"; | |
} | |
} | |
struct Coordinate | |
{ | |
public Coordinate(int y, int x) { Y = y; X = x; } | |
public int Y, X; | |
public override string ToString() | |
{ | |
return $"[{Y},{X}]"; | |
} | |
} | |
class ShapeHierarchy { | |
int n; | |
public ShapeHierarchy(int n) { this.n = n; } | |
public IEnumerable<Shape> Shapes { get { | |
var size = (int)Math.Pow(2.0, this.n) + 1; | |
var r = (int)Math.Pow(2.0, (double)(n - 1)); | |
return Enumerate_shapes(size, r); | |
} | |
} | |
IEnumerable<Shape> Enumerate_shapes(int size, int r) { | |
if (r < 1) return new Shape[0]; | |
return Enumerate_squares(size, r) | |
.Concat(Enumerate_diamonds(size, r)) | |
.Concat(Enumerate_shapes(size, r / 2)); | |
} | |
static IEnumerable<Shape> Enumerate_squares(int size, int r) { | |
for (var y = r; y < size; y += 2*r) | |
for (var x = r; x < size; x += 2*r) { | |
yield return new Shape { | |
Center = new Coordinate(y, x), | |
Vertexes = new[]{ | |
new Coordinate(y-r, x-r), // north-west | |
new Coordinate(y-r, x+r), // north-east | |
new Coordinate(y+r, x+r), // south-east | |
new Coordinate(y+r, x-r) // south-west | |
} | |
}; | |
} | |
} | |
static IEnumerable<Shape> Enumerate_diamonds(int size, int r) { | |
for (var y = 0; y < size; y += 2 * r) | |
for (var x = r; x < size; x += 2 * r) { | |
yield return new Shape { | |
Center = new Coordinate(y, x), | |
Vertexes = Enumerate_diamond_vertexes(y,x,r,size).ToArray() | |
}; | |
}; | |
for (var y = r; y < size; y += 2 * r) | |
for (var x = 0; x < size; x += 2 * r) { | |
yield return new Shape { | |
Center = new Coordinate(y, x), | |
Vertexes = Enumerate_diamond_vertexes(y, x, r, size).ToArray() | |
}; | |
}; | |
} | |
static IEnumerable<Coordinate> Enumerate_diamond_vertexes(int y, int x, int r, int size) { | |
int dy, dx; | |
dy = y-r; dx = x; if (Is_valid_coordinate(dy, dx, size)) yield return new Coordinate(dy, dx); // north | |
dy = y; dx = x+r; if (Is_valid_coordinate(dy, dx, size)) yield return new Coordinate(dy, dx); // east | |
dy = y+r; dx = x; if (Is_valid_coordinate(dy, dx, size)) yield return new Coordinate(dy, dx); // south | |
dy = y; dx = x-r; if (Is_valid_coordinate(dy, dx, size)) yield return new Coordinate(dy, dx); // west | |
} | |
static bool Is_valid_coordinate(int y, int x, int size) => y >= 0 && y < size && x >= 0 && x < size; | |
} | |
} |
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
using System; | |
using NUnit.Framework; | |
using System.Linq; | |
namespace terraingenBlog | |
{ | |
[TestFixture] | |
public class test_Acceptance { | |
[Test] | |
public void Run_without_jitter() { | |
var t = new Terrain(2, 1.0f, 2.0f, 3.0f, 4.0f); | |
TerrainGenerator.Interpolate(t, 0.0f, 0.0f, () => 0.0f); | |
Assert_matrix_equality(new float[,] { | |
{1.00f, 1.60f, 1.83f, 2.01f, 2.00f}, | |
{1.82f, 1.96f, 2.13f, 2.21f, 2.24f}, | |
{2.50f, 2.50f, 2.50f, 2.50f, 2.50f}, | |
{3.18f, 3.04f, 2.88f, 2.79f, 2.76f}, | |
{4.00f, 3.40f, 3.17f, 2.99f, 3.00f} | |
}, t.ToArray()); | |
} | |
[Test] | |
public void Run_with_constant_jitter() | |
{ | |
var t = new Terrain(2, 3.0f, 7.0f, 5.0f, 1.0f); | |
TerrainGenerator.Interpolate(t, 3.0f, 1.0f, () => 1.0f); | |
Assert_matrix_equality(new float[,] { | |
{ 3.00f, 12.08f, 10.00f, 13.97f, 7.00f}, | |
{11.42f, 11.25f, 14.54f, 12.92f, 14.19f}, | |
{ 8.00f, 13.42f, 8.00f, 14.92f, 10.67f}, | |
{10.47f, 10.42f, 13.79f, 12.08f, 13.25f}, | |
{ 1.00f, 10.69f, 8.67f, 12.58f, 5.00f} | |
}, t.ToArray()); | |
} | |
[Test] | |
public void RobertCMartin_example() | |
{ | |
var t = new Terrain(2, 0.0f, 0.0f, 0.0f, 0.0f); | |
TerrainGenerator.Interpolate(t, 4.0f, 2.0f, () => 1.0f); | |
Assert_matrix_equality(new float[,] { | |
{ 0.00f, 12.50f, 8.00f, 12.50f, 0.00f}, | |
{12.50f, 11.50f, 15.25f, 11.50f, 12.50f}, | |
{ 8.00f, 15.25f, 6.00f, 15.25f, 8.00f}, | |
{12.50f, 11.50f, 15.25f, 11.50f, 12.50f}, | |
{ 0.00f, 12.50f, 8.00f, 12.50f, 0.00f} | |
}, t.ToArray()); | |
} | |
/* This test helper method is useful to check for same size of matrixes, | |
* but mostly to compare the cell values with a delta. Assert.AreEqual() does not provide | |
* delta comparison for arrays. | |
*/ | |
private void Assert_matrix_equality(float[,] expected, float[,] result) { | |
Assert.IsTrue(expected.GetLength(0) == result.GetLength(0), "Matrix heights differ!"); | |
Assert.IsTrue(expected.GetLength(1) == result.GetLength(1), "Matrix width differ!"); | |
for (var y = 0; y < result.GetLength(0); y++) | |
for (var x = 0; x < result.GetLength(1); x++) | |
Assert.AreEqual(expected[y,x], result[y,x], 0.01, $"Differing values at [{y},{x}]!"); | |
} | |
} | |
} |
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
using System; | |
using NUnit.Framework; | |
using System.Linq; | |
namespace terraingenBlog | |
{ | |
[TestFixture] | |
public class test_Calculator { | |
[Test] | |
public void No_jitter() { | |
var sut = new Calculator(0, 0, () => 0.0f); | |
Assert.AreEqual(2.0f, sut.Process(new[] { 1.0f, 2.0f, 3.0f })); | |
Assert.AreEqual(2.5f, sut.Process(new[] { 1.0f, 2.0f, 3.0f, 4.0f })); | |
} | |
[Test] | |
public void With_jitter() { | |
var sut = new Calculator(6, 2, () => 1.0f); | |
Assert.AreEqual(8.0f, sut.Process(new[] { 0.0f })); | |
sut = new Calculator(6, 2, () => -1.0f); | |
Assert.AreEqual(4.0f, sut.Process(new[] { 0.0f })); | |
} | |
} | |
} |
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
using System; | |
using NUnit.Framework; | |
using System.Linq; | |
using System.Collections.Generic; | |
using System.Diagnostics.Contracts; | |
namespace terraingenBlog | |
{ | |
[TestFixture] | |
public class test_ShapeHierarchy { | |
[Test] | |
public void Size3() { | |
var sut = new ShapeHierarchy(1); | |
var shapes = sut.Shapes.ToArray(); | |
Assert.AreEqual(5, shapes.Length); | |
Assert.AreEqual("[1,1]@([0,0],[0,2],[2,2],[2,0])", shapes[0].ToString()); | |
Assert.AreEqual("[0,1]@([0,2],[1,1],[0,0])", shapes[1].ToString()); | |
Assert.AreEqual("[2,1]@([1,1],[2,2],[2,0])", shapes[2].ToString()); | |
Assert.AreEqual("[1,0]@([0,0],[1,1],[2,0])", shapes[3].ToString()); | |
Assert.AreEqual("[1,2]@([0,2],[2,2],[1,1])", shapes[4].ToString()); | |
} | |
[Test] | |
public void Size5() { | |
var sut = new ShapeHierarchy(2); | |
var shapes = sut.Shapes.ToArray(); | |
Assert.AreEqual(21, shapes.Length); | |
Assert.AreEqual("[2,2]@([0,0],[0,4],[4,4],[4,0])", shapes[0].ToString()); | |
Assert.AreEqual("[2,4]@([0,4],[4,4],[2,2])", shapes[4].ToString()); | |
Assert.AreEqual("[1,1]@([0,0],[0,2],[2,2],[2,0])", shapes[5].ToString()); | |
Assert.AreEqual("[0,1]@([0,2],[1,1],[0,0])", shapes[9].ToString()); | |
} | |
} | |
} |
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
using System; | |
using NUnit.Framework; | |
using System.Linq; | |
namespace terraingenBlog | |
{ | |
[TestFixture] | |
public class test_Terrain { | |
[Test] | |
public void Terrains_are_squares_of_size_2n1() { | |
var t = new Terrain(1); | |
var m = t.ToArray(); | |
Assert.IsTrue(m.GetLength(0) == m.GetLength(1)); | |
Assert.AreEqual(t.Size, m.GetLength(0)); | |
var size = new Terrain(3).Size; | |
Assert.AreEqual(9, size); | |
} | |
[Test] | |
public void Terrains_are_initialized() { | |
var t = new Terrain(3, 1.0f, 2.0f, 3.0f, 4.0f); | |
Assert.AreEqual(1.0f, t[0, 0]); | |
Assert.AreEqual(2.0f, t[0, t.Size-1]); | |
Assert.AreEqual(3.0f, t[t.Size-1, t.Size-1]); | |
Assert.AreEqual(4.0f, t[t.Size-1, 0]); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment