From b1c6c721064243c6b838a15ddb82a4f8f7a3f044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Chaloupka?= Date: Sun, 26 Apr 2015 17:47:56 +0200 Subject: [PATCH 1/2] Added perlin noise generator micro benchmark --- perlin/Cargo.toml | 8 +++ perlin/README.md | 9 ++++ perlin/build.sh | 14 +++++ perlin/clean.sh | 10 ++++ perlin/run.sh | 32 ++++++++++++ perlin/test.c | 124 +++++++++++++++++++++++++++++++++++++++++++ perlin/test.cr | 93 +++++++++++++++++++++++++++++++++ perlin/test.cs | 118 +++++++++++++++++++++++++++++++++++++++++ perlin/test.d | 123 +++++++++++++++++++++++++++++++++++++++++++ perlin/test.go | 125 ++++++++++++++++++++++++++++++++++++++++++++ perlin/test.java | 124 +++++++++++++++++++++++++++++++++++++++++++ perlin/test.jl | 118 +++++++++++++++++++++++++++++++++++++++++ perlin/test.nim | 108 ++++++++++++++++++++++++++++++++++++++ perlin/test.py | 104 +++++++++++++++++++++++++++++++++++++ perlin/test.rs | 130 ++++++++++++++++++++++++++++++++++++++++++++++ 15 files changed, 1240 insertions(+) create mode 100644 perlin/Cargo.toml create mode 100644 perlin/README.md create mode 100755 perlin/build.sh create mode 100755 perlin/clean.sh create mode 100755 perlin/run.sh create mode 100644 perlin/test.c create mode 100644 perlin/test.cr create mode 100644 perlin/test.cs create mode 100644 perlin/test.d create mode 100644 perlin/test.go create mode 100644 perlin/test.java create mode 100644 perlin/test.jl create mode 100644 perlin/test.nim create mode 100644 perlin/test.py create mode 100644 perlin/test.rs diff --git a/perlin/Cargo.toml b/perlin/Cargo.toml new file mode 100644 index 00000000..0159fa0e --- /dev/null +++ b/perlin/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rust perlin" +version = "0.0.1" +[[bin]] +name = "bin_test_rs" +path = "test.rs" +[dependencies] +rand = "*" diff --git a/perlin/README.md b/perlin/README.md new file mode 100644 index 00000000..41a9d32a --- /dev/null +++ b/perlin/README.md @@ -0,0 +1,9 @@ +Perlin noise benchmark. + +Runs perlin noise 256x256 image generation 1000 times. + +Original benchmark https://github.com/nsf/pnoise. + +To compile all: `sh build.sh` + +To run all: `sh run.sh` diff --git a/perlin/build.sh b/perlin/build.sh new file mode 100755 index 00000000..dfdac337 --- /dev/null +++ b/perlin/build.sh @@ -0,0 +1,14 @@ +#!/bin/sh +gcc -std=c99 -march=native -msse3 -mfpmath=sse -O3 -o bin_test_c_gcc test.c -lm +clang -std=c99 -march=native -msse3 -mfpmath=sse -O3 -o bin_test_c_clang test.c -lm +dmd -ofbin_test_d_dmd -O -boundscheck=off -inline -release test.d +ldc2 -O5 -ofbin_test_d_ldc test.d -release -mcpu=native -inline -boundscheck=off +gdc -Ofast -o bin_test_d_gdc test.d -frelease -finline -march=native -fno-bounds-check +gccgo -O3 -g -o bin_test_go_gccgo test.go +go build -o bin_test_go_gc test.go +cargo build --release +mcs -out:bin_test_cs test.cs +nim c -d:release --cc:gcc -o:bin_test_nim_gcc test.nim +nim c -d:release --cc:clang -o:bin_test_nim_clang test.nim +crystal build -o bin_test_cr --release test.cr +javac test.java diff --git a/perlin/clean.sh b/perlin/clean.sh new file mode 100755 index 00000000..b1004904 --- /dev/null +++ b/perlin/clean.sh @@ -0,0 +1,10 @@ +#!/bin/sh +rm base64_* +rm *.class +rm *.o +rm *.exe +rm *.lock +rm -rf bin_* +rm -rf .crystal +rm -rf nimcache +rm -rf target diff --git a/perlin/run.sh b/perlin/run.sh new file mode 100755 index 00000000..d1e5e543 --- /dev/null +++ b/perlin/run.sh @@ -0,0 +1,32 @@ +#!/bin/sh +readonly NUM=1000 +echo C +../xtime.rb ./bin_test_c_clang $NUM 2>&1 > /dev/null +echo Cpp +../xtime.rb ./bin_test_c_gcc $NUM 2>&1 > /dev/null +echo Mono +../xtime.rb mono ./bin_test_cs $NUM 2>&1 > /dev/null +echo D +../xtime.rb ./bin_test_d_dmd $NUM 2>&1 > /dev/null +echo D Ldc +../xtime.rb ./bin_test_d_ldc $NUM 2>&1 > /dev/null +echo D Gdc +../xtime.rb ./bin_test_d_gdc $NUM 2>&1 > /dev/null +echo Go +../xtime.rb ./bin_test_go_gc $NUM 2>&1 > /dev/null +echo Go gcc +../xtime.rb ./bin_test_go_gccgo $NUM 2>&1 > /dev/null +echo Rust +../xtime.rb target/release/bin_test_rs $NUM 2>&1 >/dev/null +echo Nim gcc +../xtime.rb ./bin_test_nim_gcc $NUM 2>&1 > /dev/null +echo Nim clang +../xtime.rb ./bin_test_nim_clang $NUM 2>&1 > /dev/null +echo Crystal +../xtime.rb ./bin_test_cr $NUM 2>&1 > /dev/null +echo Java +../xtime.rb java -cp . test $NUM 2>&1 > /dev/null +echo Python Pypy +../xtime.rb pypy test.py $NUM 2>&1 > /dev/null +echo Python +../xtime.rb python test.py $NUM 2>&1 > /dev/null diff --git a/perlin/test.c b/perlin/test.c new file mode 100644 index 00000000..c9c5c5f1 --- /dev/null +++ b/perlin/test.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include + +#define M_PI 3.1415926535f + +typedef struct { + float x, y; +} Vec2; + +static inline float lerp(float a, float b, float v) +{ + return a * (1 - v) + b * v; +} + +static inline float smooth(float v) { + return v * v * (3 - 2 * v); +} + +static inline Vec2 random_gradient() +{ + const float v = (float)rand() / RAND_MAX * M_PI * 2.0f; + return (Vec2){cosf(v), sinf(v)}; +} + +static inline float gradient(Vec2 orig, Vec2 grad, Vec2 p) +{ + Vec2 sp = {p.x - orig.x, p.y - orig.y}; + return grad.x * sp.x + grad.y * sp.y; +} + +typedef struct { + Vec2 rgradients[256]; + int permutations[256]; + Vec2 gradients[4]; + Vec2 origins[4]; +} Noise2DContext; + +static inline Vec2 get_gradient(Noise2DContext *ctx, int x, int y) { + int idx = ctx->permutations[x & 255] + ctx->permutations[y & 255]; + return ctx->rgradients[idx & 255]; +} + +static inline void get_gradients(Noise2DContext *ctx, float x, float y) { + float x0f = floorf(x); + float y0f = floorf(y); + int x0 = x0f; + int y0 = y0f; + int x1 = x0 + 1; + int y1 = y0 + 1; + + ctx->gradients[0] = get_gradient(ctx, x0, y0); + ctx->gradients[1] = get_gradient(ctx, x1, y0); + ctx->gradients[2] = get_gradient(ctx, x0, y1); + ctx->gradients[3] = get_gradient(ctx, x1, y1); + + ctx->origins[0] = (Vec2){x0f + 0.0f, y0f + 0.0f}; + ctx->origins[1] = (Vec2){x0f + 1.0f, y0f + 0.0f}; + ctx->origins[2] = (Vec2){x0f + 0.0f, y0f + 1.0f}; + ctx->origins[3] = (Vec2){x0f + 1.0f, y0f + 1.0f}; +} + + +static float noise2d_get(Noise2DContext *ctx, float x, float y) +{ + Vec2 p = {x, y}; + get_gradients(ctx, x, y); + float v0 = gradient(ctx->origins[0], ctx->gradients[0], p); + float v1 = gradient(ctx->origins[1], ctx->gradients[1], p); + float v2 = gradient(ctx->origins[2], ctx->gradients[2], p); + float v3 = gradient(ctx->origins[3], ctx->gradients[3], p); + + float fx = smooth(x - ctx->origins[0].x); + float vx0 = lerp(v0, v1, fx); + float vx1 = lerp(v2, v3, fx); + float fy = smooth(y - ctx->origins[0].y); + return lerp(vx0, vx1, fy); +} + +static void init_noise2d(Noise2DContext *ctx) +{ + for (int i = 0; i < 256; i++) + ctx->rgradients[i] = random_gradient(); + + for (int i = 0; i < 256; i++) { + int j = rand() % (i+1); + ctx->permutations[i] = ctx->permutations[j]; + ctx->permutations[j] = i; + } +} + +int main(int argc, char **argv) +{ + srand(time(NULL)); + + const char *symbols[] = {" ", "░", "▒", "▓", "█", "█"}; + float *pixels = malloc(sizeof(float) * 256 * 256); + int n = 1; + if (argc > 1) n = atoi(argv[1]); + + Noise2DContext n2d; + init_noise2d(&n2d); + + for (int i = 0; i < n; i++) { + for (int y = 0; y < 256; y++) { + for (int x = 0; x < 256; x++) { + float v = noise2d_get(&n2d, x * 0.1f, y * 0.1f) + * 0.5f + 0.5f; + pixels[y*256+x] = v; + } + } + } + + for (int y = 0; y < 256; y++) { + for (int x = 0; x < 256; x++) { + int idx = pixels[y*256+x] / 0.2f; + printf("%s", symbols[idx]); + } + printf("\n"); + } + + return 0; +} diff --git a/perlin/test.cr b/perlin/test.cr new file mode 100644 index 00000000..5ba66dc5 --- /dev/null +++ b/perlin/test.cr @@ -0,0 +1,93 @@ +record Vec2, x, y + +def lerp(a, b, v) + a * (1.0 - v) + b * v +end + +def smooth(v) + v * v * (3.0 - 2.0 * v) +end + +def random_gradient + v = rand * Math::PI * 2.0 + Vec2.new(Math.cos(v), Math.sin(v)) +end + +def gradient(orig, grad, p) + sp = Vec2.new(p.x - orig.x, p.y - orig.y) + grad.x * sp.x + grad.y * sp.y +end + +struct Noise2DContext + def initialize + @rgradients = StaticArray(Vec2, 256).new { random_gradient } + @permutations = StaticArray(Int32, 256).new { |i | i }.shuffle! + end + + def get_gradient(x, y) + idx = @permutations[x & 255] + @permutations[y & 255] + @rgradients[idx & 255] + end + + def get_gradients(x, y) + x0f = x.floor + y0f = y.floor + x0 = x0f.to_i + y0 = y0f.to_i + x1 = x0 + 1 + y1 = y0 + 1 + + { + { + get_gradient(x0, y0), + get_gradient(x1, y0), + get_gradient(x0, y1), + get_gradient(x1, y1), + }, + { + Vec2.new(x0f + 0.0, y0f + 0.0), + Vec2.new(x0f + 1.0, y0f + 0.0), + Vec2.new(x0f + 0.0, y0f + 1.0), + Vec2.new(x0f + 1.0, y0f + 1.0), + } + } + end + + def get(x, y) + p = Vec2.new(x, y) + gradients, origins = get_gradients(x, y) + v0 = gradient(origins[0], gradients[0], p) + v1 = gradient(origins[1], gradients[1], p) + v2 = gradient(origins[2], gradients[2], p) + v3 = gradient(origins[3], gradients[3], p) + fx = smooth(x - origins[0].x) + vx0 = lerp(v0, v1, fx) + vx1 = lerp(v2, v3, fx) + fy = smooth(y - origins[0].y) + lerp(vx0, vx1, fy) + end +end + +symbols = [' ', '░', '▒', '▓', '█', '█'] +pixels = Array.new(256) { Array.new(256, 0.0) } + +n2d = Noise2DContext.new + +n = (ARGV[0]? || 1).to_i + +n.times do |i| + 256.times do |y| + 256.times do |x| + v = n2d.get(x * 0.1, (y + (i * 128)) * 0.1) * 0.5 + 0.5 + pixels[y][x] = v + end + end +end + +256.times do |y| + 256.times do |x| + v = pixels[y][x] + print(symbols[(v / 0.2).to_i]) + end + puts +end diff --git a/perlin/test.cs b/perlin/test.cs new file mode 100644 index 00000000..2de3208c --- /dev/null +++ b/perlin/test.cs @@ -0,0 +1,118 @@ +using System; + +struct Vec2 { + public float x; + public float y; +} + +class Noise2DContext { + Vec2[] rgradients; + int[] permutations; + Vec2[] gradients; + Vec2[] origins; + + internal static float lerp(float a, float b, float v) { + return a * (1 - v) + b * v; + } + + internal static float smooth(float v) { + return v * v * (3 - 2 * v); + } + + internal static Vec2 random_gradient(Random rnd) { + var v = rnd.NextDouble() * Math.PI * 2.0; + return new Vec2 { x = (float)Math.Cos(v), y = (float)Math.Sin(v) }; + } + + internal static float gradient(Vec2 orig, Vec2 grad, Vec2 p) { + return grad.x * (p.x - orig.x) + grad.y * (p.y - orig.y); + } + + public Noise2DContext(int seed) { + Random rnd = new Random(seed); + rgradients = new Vec2[256]; + permutations = new int[256]; + for (int i = 0; i < 256; i++) { + rgradients[i] = random_gradient(rnd); + } + for (int i = 0; i < 256; i++) { + int j = rnd.Next(i + 1); + permutations[i] = permutations[j]; + permutations[j] = i; + } + + gradients = new Vec2[4]; + origins = new Vec2[4]; + } + + internal Vec2 get_gradient(int x, int y) { + int idx = permutations[x & 255] + permutations[y & 255]; + return rgradients[idx & 255]; + } + + internal void get_gradients(float x, float y) { + float x0f = (float)Math.Floor(x); + float y0f = (float)Math.Floor(y); + int x0 = (int)x0f; + int y0 = (int)y0f; + int x1 = x0 + 1; + int y1 = y0 + 1; + + gradients[0] = get_gradient(x0, y0); + gradients[1] = get_gradient(x1, y0); + gradients[2] = get_gradient(x0, y1); + gradients[3] = get_gradient(x1, y1); + + origins[0].x = x0f + 0; + origins[0].y = y0f + 0; + origins[1].x = x0f + 1; + origins[1].y = y0f + 0; + origins[2].x = x0f + 0; + origins[2].y = y0f + 1; + origins[3].x = x0f + 1; + origins[3].y = y0f + 1; + } + + public float get(float x, float y) { + Vec2 p = new Vec2 { x = x, y = y }; + get_gradients(x, y); + float v0 = gradient(origins[0], gradients[0], p); + float v1 = gradient(origins[1], gradients[1], p); + float v2 = gradient(origins[2], gradients[2], p); + float v3 = gradient(origins[3], gradients[3], p); + + float fx = smooth(x - origins[0].x); + float vx0 = lerp(v0, v1, fx); + float vx1 = lerp(v2, v3, fx); + float fy = smooth(y - origins[0].y); + return lerp(vx0, vx1, fy); + } +} + +class Application { + static readonly char[] symbols = { ' ', '░', '▒', '▓', '█', '█' }; + + public static void Main(string[] args) { + var n2d = new Noise2DContext((int)DateTime.Now.Ticks); + float[] pixels = new float[256 * 256]; + + var n = args.Length >= 1 ? int.Parse(args[0]) : 1; + + for (int i = 0; i < n; i++) { + for (int y = 0; y < 256; y++) { + for (int x = 0; x < 256; x++) { + float v = n2d.get(x * 0.1f, y * 0.1f) * 0.5f + 0.5f; + pixels[y * 256 + x] = v; + } + } + } + + for (int y = 0; y < 256; y++) { + for (int x = 0; x < 256; x++) { + int idx = (int)(pixels[y * 256 + x] / 0.2f); + Console.Write(symbols[idx]); + } + Console.WriteLine(); + } + } +} diff --git a/perlin/test.d b/perlin/test.d new file mode 100644 index 00000000..6daf455a --- /dev/null +++ b/perlin/test.d @@ -0,0 +1,123 @@ +import std.stdio; +import std.random; +import std.math; +import std.conv; + +struct Vec2 { + float x, y; +} + +alias floor = core.stdc.math.floor; + +static float lerp(immutable float a, immutable float b, immutable float v) pure nothrow +{ + return a * (1 - v) + b * v; +} + +static float smooth(immutable float v) pure nothrow +{ + return v * v * (3 - 2 * v); +} + +static Vec2 random_gradient(Random)(ref Random r) +{ + immutable v = uniform(0.0f, cast(float)PI * 2.0f, r); + return Vec2(cos(v), sin(v)); +} + +static float gradient(immutable Vec2 orig, immutable Vec2 grad, immutable Vec2 p) pure nothrow +{ + immutable sp = Vec2(p.x - orig.x, p.y - orig.y); + return grad.x * sp.x + grad.y * sp.y; +} + +struct Noise2DContext { + Vec2[256] rgradients; + uint[256] permutations; + +private: + Vec2 get_gradient(immutable int x, immutable int y) pure nothrow + { + immutable idx = permutations[x & 255] + permutations[y & 255]; + return rgradients[idx & 255]; + } + + Vec2[8] get_gradients(immutable float x, immutable float y) nothrow + { + float x0f = floor(x); + float y0f = floor(y); + int x0 = cast(int)x; + int y0 = cast(int)y; + int x1 = x0 + 1; + int y1 = y0 + 1; + + return cast(Vec2[8]) [get_gradient(x0, y0), + get_gradient(x1, y0), + get_gradient(x0, y1), + get_gradient(x1, y1), + Vec2(x0f + 0.0f, y0f + 0.0f), + Vec2(x0f + 1.0f, y0f + 0.0f), + Vec2(x0f + 0.0f, y0f + 1.0f), + Vec2(x0f + 1.0f, y0f + 1.0f)]; + } + +public: + static Noise2DContext opCall(uint seed) + { + Noise2DContext ret; + auto rnd = Random(seed); + foreach (ref elem; ret.rgradients) + elem = random_gradient(rnd); + + foreach (i; 0 .. ret.permutations.length) { + uint j = uniform(0, cast(uint)i+1, rnd); + ret.permutations[i] = ret.permutations[j]; + ret.permutations[j] = cast(uint)i; + } + + return ret; + } + + float get(immutable float x, immutable float y) nothrow + { + immutable p = Vec2(x, y); + + immutable vecs = get_gradients(x, y); + immutable v0 = gradient(vecs[4], vecs[0], p); + immutable v1 = gradient(vecs[5], vecs[1], p); + immutable v2 = gradient(vecs[6], vecs[2], p); + immutable v3 = gradient(vecs[7], vecs[3], p); + + immutable fx = smooth(x - vecs[4].x); + immutable vx0 = lerp(v0, v1, fx); + immutable vx1 = lerp(v2, v3, fx); + immutable fy = smooth(y - vecs[4].y); + return lerp(vx0, vx1, fy); + } +} + +void main(string[] args) +{ + immutable symbols = [" ", "░", "▒", "▓", "█", "█"]; + auto pixels = new float[256*256]; + + int n = args.length > 1 ? to!int(args[1]) : 1; + + auto n2d = Noise2DContext(0); + foreach (immutable i; 0..n) { + foreach (immutable y; 0..256) { + foreach (immutable x; 0..256) { + immutable v = n2d.get(x * 0.1f, y * 0.1f) * + 0.5f + 0.5f; + pixels[y*256+x] = v; + } + } + } + + foreach (immutable y; 0..256) { + foreach (immutable x; 0..256) { + write(symbols[cast(int)(pixels[y*256+x] / 0.2f)]); + } + writeln(); + } +} diff --git a/perlin/test.go b/perlin/test.go new file mode 100644 index 00000000..a331f121 --- /dev/null +++ b/perlin/test.go @@ -0,0 +1,125 @@ +package main + +import ( + "fmt" + "math/rand" + "math" + "bufio" + "os" + "flag" + "strconv" +) + +const PI = 3.1415926535 + +type Vec2 struct { + X, Y float32 +} + +func lerp(a, b, v float32) float32 { + return a * (1 - v) + b * v +} + +func smooth(v float32) float32 { + return v * v * (3 - 2 * v) +} + +func random_gradient(r *rand.Rand) Vec2 { + v := r.Float64() * PI * 2 + return Vec2{ + float32(math.Cos(v)), + float32(math.Sin(v)), + } +} + +func gradient(orig, grad, p Vec2) float32 { + sp := Vec2{p.X - orig.X, p.Y - orig.Y} + return grad.X * sp.X + grad.Y * sp.Y +} + +type Noise2DContext struct { + rgradients [256]Vec2 + permutations [256]int + gradients [4]Vec2 + origins [4]Vec2 +} + +func NewNoise2DContext(seed int) *Noise2DContext { + rnd := rand.New(rand.NewSource(int64(seed))) + + n2d := new(Noise2DContext) + copy(n2d.permutations[:], rand.Perm(256)) + for i := range n2d.rgradients { + n2d.rgradients[i] = random_gradient(rnd) + } + + return n2d +} + +func (n2d *Noise2DContext) get_gradient(x, y int) Vec2 { + idx := n2d.permutations[x & 255] + n2d.permutations[y & 255] + return n2d.rgradients[idx & 255] +} + +func (n2d *Noise2DContext) get_gradients(x, y float32) { + x0f := math.Floor(float64(x)) + y0f := math.Floor(float64(y)) + x0 := int(x0f) + y0 := int(y0f) + x1 := x0 + 1 + y1 := y0 + 1 + + n2d.gradients[0] = n2d.get_gradient(x0, y0) + n2d.gradients[1] = n2d.get_gradient(x1, y0) + n2d.gradients[2] = n2d.get_gradient(x0, y1) + n2d.gradients[3] = n2d.get_gradient(x1, y1) + + n2d.origins[0] = Vec2{float32(x0f + 0.0), float32(y0f + 0.0)} + n2d.origins[1] = Vec2{float32(x0f + 1.0), float32(y0f + 0.0)} + n2d.origins[2] = Vec2{float32(x0f + 0.0), float32(y0f + 1.0)} + n2d.origins[3] = Vec2{float32(x0f + 1.0), float32(y0f + 1.0)} +} + +func (n2d *Noise2DContext) Get(x, y float32) float32 { + p := Vec2{x, y} + n2d.get_gradients(x, y) + v0 := gradient(n2d.origins[0], n2d.gradients[0], p) + v1 := gradient(n2d.origins[1], n2d.gradients[1], p) + v2 := gradient(n2d.origins[2], n2d.gradients[2], p) + v3 := gradient(n2d.origins[3], n2d.gradients[3], p) + fx := smooth(x - n2d.origins[0].X) + vx0 := lerp(v0, v1, fx) + vx1 := lerp(v2, v3, fx) + fy := smooth(y - n2d.origins[0].Y) + return lerp(vx0, vx1, fy) +} + +func main() { + symbols := []string{" ", "░", "▒", "▓", "█", "█"} + pixels := make([]float32, 256*256) + n2d := NewNoise2DContext(0) + + n := int(1) + flag.Parse() + if flag.NArg() > 0 { n,_ = strconv.Atoi(flag.Arg(0)) } + + for i := 0; i < n; i++ { + for y := 0; y < 256; y++ { + for x := 0; x < 256; x++ { + v := n2d.Get(float32(x) * 0.1, + float32(y) * 0.1) + v = v * 0.5 + 0.5 + pixels[y*256+x] = v + } + } + } + + out := bufio.NewWriter(os.Stdout) + for y := 0; y < 256; y++ { + for x := 0; x < 256; x++ { + fmt.Fprint(out, symbols[int(pixels[y*256+x] / 0.2)]) + } + fmt.Fprintln(out) + } + out.Flush() +} diff --git a/perlin/test.java b/perlin/test.java new file mode 100644 index 00000000..608bab36 --- /dev/null +++ b/perlin/test.java @@ -0,0 +1,124 @@ +import java.util.Random; + +public class test { + public static class Vec2 { + public Vec2(float x, float y) { + this.x = x; + this.y = y; + } + + public float x; + public float y; + } + + private static class Noise2DContext { + Vec2[] rgradients; + int[] permutations; + Vec2[] gradients; + Vec2[] origins; + + private static final float lerp(float a, float b, float v) { + return a * (1 - v) + b * v; + } + + private static final float smooth(float v) { + return v * v * (3 - 2 * v); + } + + Vec2 random_gradient(Random rnd) { + double v = rnd.nextDouble() * Math.PI * 2.0; + return new Vec2((float) Math.cos(v), (float) Math.sin(v)); + } + + float gradient(Vec2 orig, Vec2 grad, Vec2 p) { + Vec2 sp = new Vec2(p.x - orig.x, p.y - orig.y); + return grad.x * sp.x + grad.y * sp.y; + } + + public Noise2DContext(int seed) { + Random rnd = new Random(seed); + rgradients = new Vec2[256]; + permutations = new int[256]; + for (int i = 0; i < 256; i++) { + rgradients[i] = random_gradient(rnd); + } + for (int i = 0; i < 256; i++) { + int j = rnd.nextInt(i + 1); + permutations[i] = permutations[j]; + permutations[j] = i; + } + + gradients = new Vec2[4]; + origins = new Vec2[4]; + } + + Vec2 get_gradient(int x, int y) { + int idx = permutations[x & 255] + permutations[y & 255]; + return rgradients[idx & 255]; + } + + void get_gradients(float x, float y) { + float x0f = (float) Math.floor(x); + float y0f = (float) Math.floor(y); + int x0 = (int) x0f; + int y0 = (int) y0f; + int x1 = x0 + 1; + int y1 = y0 + 1; + + gradients[0] = get_gradient(x0, y0); + gradients[1] = get_gradient(x1, y0); + gradients[2] = get_gradient(x0, y1); + gradients[3] = get_gradient(x1, y1); + + origins[0] = new Vec2(x0f + 0, y0f + 0); + origins[1] = new Vec2(x0f + 1, y0f + 0); + origins[2] = new Vec2(x0f + 0, y0f + 1); + origins[3] = new Vec2(x0f + 1, y0f + 1); + } + + public float get(float x, float y) { + Vec2 p = new Vec2(x, y); + get_gradients(x, y); + float v0 = gradient(origins[0], gradients[0], p); + float v1 = gradient(origins[1], gradients[1], p); + float v2 = gradient(origins[2], gradients[2], p); + float v3 = gradient(origins[3], gradients[3], p); + + float fx = smooth(x - origins[0].x); + float vx0 = lerp(v0, v1, fx); + float vx1 = lerp(v2, v3, fx); + float fy = smooth(y - origins[0].y); + return lerp(vx0, vx1, fy); + } + } + + static char[] symbols = { ' ', '░', '▒', '▓', '█', '█' }; + + public test(int n) { + Noise2DContext n2d = new Noise2DContext((int) System.currentTimeMillis()); + float[] pixels = new float[256 * 256]; + + for (int i = 0; i < n; i++) { + for (int y = 0; y < 256; y++) { + for (int x = 0; x < 256; x++) { + float v = n2d.get(x * 0.1f, y * 0.1f) * 0.5f + 0.5f; + pixels[y * 256 + x] = v; + } + } + } + + for (int y = 0; y < 256; y++) { + for (int x = 0; x < 256; x++) { + int idx = (int) (pixels[y * 256 + x] / 0.2f); + System.out.print(symbols[idx]); + } + System.out.println(); + } + } + + public static void main(String[] args) { + int n = 1; + if (args.length >= 1) n = Integer.parseInt(args[0]); + new test(n); + } +} diff --git a/perlin/test.jl b/perlin/test.jl new file mode 100644 index 00000000..ebc53925 --- /dev/null +++ b/perlin/test.jl @@ -0,0 +1,118 @@ +#Perlin line noise test in Julia +const RAND_MAX = 0x7fff +const symbols = [ " ", "░", "▒", "▓", "█", "█" ] + +type Vec2 + x::Float32 + y::Float32 +end + +function Vec2() + Vec2(0,0) +end + +type Noise2DContext + rgradients::Vector{Vec2} + permutations::Vector{Int64} + gradients::Vector{Vec2} + origins::Vector{Vec2} +end + +function Noise2DContext() + Noise2DContext( + Array(Vec2,256), #rgradients + Array(Int64,256), #permutations + Array(Vec2,4), + Array(Vec2,4) + ) +end + +lerp(a::Float32,b::Float32,v::Float32) = float32(a * (1.0 - v) + b * v)::Float32 + +smooth(v::Float32) = float32(v * v * (3.0 - 2.0*v))::Float32 + +function random_gradient() + v = rand()*pi*2.0 + Vec2(cos(v),sin(v)) +end + +function gradient(orig::Vec2,grad::Vec2,p::Vec2) + sp = Vec2((p.x - orig.x),(p.y - orig.y)) + grad.x * sp.x + grad.y * sp.y +end + +function get_gradient(ctx::Noise2DContext, x::Int64, y::Int64) + idx = ctx.permutations[x&255+1] + ctx.permutations[y&255+1] + ctx.rgradients[idx&255+1] +end + +function get_gradient(ctx::Noise2DContext,x::Float32,y::Float32) + x0f = floor(x) + y0f = floor(y) + x0 = int(x0f) + y0 = int(y0f) + y = int(y0f) + x1 = x0 + 1 + y1 = y0 + 1 + + ctx.gradients[1] = get_gradient(ctx, x0, y0) + ctx.gradients[2] = get_gradient(ctx, x1, y0) + ctx.gradients[3] = get_gradient(ctx, x0, y1) + ctx.gradients[4] = get_gradient(ctx, x1, y1) + ctx.origins[1] = Vec2( (x0f + 0.0), (y0f + 0.0)) + ctx.origins[2] = Vec2( (x0f + 1.0), (y0f + 0.0)) + ctx.origins[3] = Vec2( (x0f + 0.0), (y0f + 1.0)) + ctx.origins[4] = Vec2( (x0f + 1.0), (y0f + 1.0)) + ctx.gradients +end + +function noise2d_get(ctx:: Noise2DContext, x::Float32, y::Float32) + p = Vec2(x,y) + get_gradient(ctx,x,y) + v0 = gradient(ctx.origins[1], ctx.gradients[1], p) + v1 = gradient(ctx.origins[2], ctx.gradients[2], p) + v2 = gradient(ctx.origins[3], ctx.gradients[3], p) + v3 = gradient(ctx.origins[4], ctx.gradients[4], p) + fx = smooth(x - ctx.origins[1].x) + vx0 = lerp(v0, v1, fx) + vx1 = lerp(v2, v3, fx) + fy = smooth(y - ctx.origins[1].y) + lerp(vx0,vx1,fy) +end + +function init_noise2d(ctx::Noise2DContext) + for i in 1:256 + ctx.rgradients[i] = random_gradient() + end + for i in 1:256 + ctx.permutations[i] = i + end + shuffle!(ctx.permutations) +end + +function main() + n2d = Noise2DContext() + init_noise2d(n2d) + pixels = Array(Float32,256,256) + n = 1 + if length(ARGS) >= 1 + n = int(ARGS[1]) + end + for i in 1:n + for y in 1:256 + for x in 1:256 + v = noise2d_get(n2d, float32(x*0.1), float32(y*0.1))*0.5 + 0.5 + pixels[y,x] = v + end + end + end + + for y in 1:256 + for x in 1:256 + write(STDOUT,symbols[int(pixels[y,x]/0.2)]) + end + write(STDOUT,"\n") + end +end + +@time main() diff --git a/perlin/test.nim b/perlin/test.nim new file mode 100644 index 00000000..62fd3896 --- /dev/null +++ b/perlin/test.nim @@ -0,0 +1,108 @@ +# Imports +import math + +# Constants +const RAND_MAX = 0x7fff + +# Types +type + TVec2 = object + x, y: float + + TNoise2DContext = object + rgradients: array[0..255, TVec2] + permutations: array[0..255, int] + gradients, origins: array[0..4, TVec2] + + +# Procedures +proc lerp(a, b, v: float): float = + a * (1 - v) + b * v + +proc smooth(v: float): float = + v * v * (3 - 2 * v) + +proc random_gradient: TVec2 = + let v = random(2 * Pi) + TVec2(x: cos(v), y: sin(v)) + +proc gradient(orig, grad, p: TVec2): float = + let sp = TVec2(x: p.x - orig.x, y: p.y - orig.y) + grad.x * sp.x + grad.y * sp.y + +proc get_gradient(ctx: TNoise2DContext, x, y: int): TVec2 = + let idx = ctx.permutations[x and 255] + ctx.permutations[y and 255]; + ctx.rgradients[idx and 255] + +proc get_gradients(ctx: var TNoise2DContext, x, y: float) = + let + x0f = floor(x) + y0f = floor(y) + x0 = x0f.int + y0 = y0f.int + x1 = x0 + 1 + y1 = y0 + 1 + + ctx.gradients[0] = get_gradient(ctx, x0, y0) + ctx.gradients[1] = get_gradient(ctx, x1, y0) + ctx.gradients[2] = get_gradient(ctx, x0, y1) + ctx.gradients[3] = get_gradient(ctx, x1, y1) + + ctx.origins[0] = TVec2(x: x0f + 0.0, y: y0f + 0.0) + ctx.origins[1] = TVec2(x: x0f + 1.0, y: y0f + 0.0) + ctx.origins[2] = TVec2(x: x0f + 0.0, y: y0f + 1.0) + ctx.origins[3] = TVec2(x: x0f + 1.0, y: y0f + 1.0) + +proc noise2d_get(ctx: var TNoise2DContext, x, y: float): float = + let p = TVec2(x: x, y: y) + + get_gradients(ctx, x, y) + + let + v0 = gradient(ctx.origins[0], ctx.gradients[0], p) + v1 = gradient(ctx.origins[1], ctx.gradients[1], p) + v2 = gradient(ctx.origins[2], ctx.gradients[2], p) + v3 = gradient(ctx.origins[3], ctx.gradients[3], p) + + fx = smooth(x - ctx.origins[0].x) + vx0 = lerp(v0, v1, fx) + vx1 = lerp(v2, v3, fx) + fy = smooth(y - ctx.origins[0].y) + + lerp(vx0, vx1, fy) + +proc init_noise2d(ctx: var TNoise2DContext) = + for i in 0..255: + ctx.rgradients[i] = random_gradient() + + for i in 0..255: + let j = random(RAND_MAX) mod (i + 1) + ctx.permutations[i] = ctx.permutations[j] + ctx.permutations[j] = i + + +block main: + randomize() + + const symbols = [ " ", "░", "▒", "▓", "█", "█" ] + + var pixels: array[256*256, float] + + var n2d = TNoise2DContext() + init_noise2d(n2d) + + var n = 1 + if paramCount() > 0: + n = parseInt(paramStr(1)) + + for i in 0.. 1: + n = int(sys.argv[1]) + + for i in xrange(n): + for y in xrange(256): + for x in xrange(256): + v = n2d.get(x * 0.1, (y + (i * 128)) * 0.1) * 0.5 + 0.5 + s = symbols[int(v / 0.2)] + pixels[y][x] = s + + for y in xrange(256): + for x in xrange(256): + sys.stdout.write(pixels[y][x]) + sys.stdout.write('\n') + +if __name__ == "__main__": + main(sys.argv) diff --git a/perlin/test.rs b/perlin/test.rs new file mode 100644 index 00000000..54ee655a --- /dev/null +++ b/perlin/test.rs @@ -0,0 +1,130 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Multi-language Perlin noise benchmark. +// See https://github.com/nsf/pnoise for timings and alternative implementations. +// ignore-lexer-test FIXME #15679 + +extern crate rand; + +use std::f32::consts::PI; +use rand::Rng; +use std::env; + +#[derive(Copy, Clone)] +struct Vec2 { + x: f32, + y: f32, +} + +fn lerp(a: f32, b: f32, v: f32) -> f32 { a * (1.0 - v) + b * v } + +fn smooth(v: f32) -> f32 { v * v * (3.0 - 2.0 * v) } + +fn random_gradient(r: &mut R) -> Vec2 { + let v = PI * 2.0 * r.gen::(); + Vec2 { x: v.cos(), y: v.sin() } +} + +fn gradient(orig: Vec2, grad: Vec2, p: Vec2) -> f32 { + (p.x - orig.x) * grad.x + (p.y - orig.y) * grad.y +} + +struct Noise2DContext { + rgradients: [Vec2; 256], + permutations: [i32; 256], +} + +impl Noise2DContext { + fn new() -> Noise2DContext { + let mut rng = rand::thread_rng(); + + let mut rgradients = [Vec2 { x: 0.0, y: 0.0 }; 256]; + for x in &mut rgradients[..] { + *x = random_gradient(&mut rng); + } + + let mut permutations = [0; 256]; + for (i, x) in permutations.iter_mut().enumerate() { + *x = i as i32; + } + rng.shuffle(&mut permutations); + + Noise2DContext { rgradients: rgradients, permutations: permutations } + } + + fn get_gradient(&self, x: i32, y: i32) -> Vec2 { + let idx = self.permutations[(x & 255) as usize] + + self.permutations[(y & 255) as usize]; + self.rgradients[(idx & 255) as usize] + } + + fn get_gradients(&self, x: f32, y: f32) -> ([Vec2; 4], [Vec2; 4]) { + let x0f = x.floor(); + let y0f = y.floor(); + let x1f = x0f + 1.0; + let y1f = y0f + 1.0; + + let x0 = x0f as i32; + let y0 = y0f as i32; + let x1 = x0 + 1; + let y1 = y0 + 1; + + ([self.get_gradient(x0, y0), self.get_gradient(x1, y0), + self.get_gradient(x0, y1), self.get_gradient(x1, y1)], + [Vec2 { x: x0f, y: y0f }, Vec2 { x: x1f, y: y0f }, + Vec2 { x: x0f, y: y1f }, Vec2 { x: x1f, y: y1f }]) + } + + fn get(&self, x: f32, y: f32) -> f32 { + let p = Vec2 {x: x, y: y}; + let (gradients, origins) = self.get_gradients(x, y); + + let v0 = gradient(origins[0], gradients[0], p); + let v1 = gradient(origins[1], gradients[1], p); + let v2 = gradient(origins[2], gradients[2], p); + let v3 = gradient(origins[3], gradients[3], p); + + let fx = smooth(x - origins[0].x); + let vx0 = lerp(v0, v1, fx); + let vx1 = lerp(v2, v3, fx); + let fy = smooth(y - origins[0].y); + + lerp(vx0, vx1, fy) + } +} + +fn main() { + let symbols = [' ', '░', '▒', '▓', '█', '█']; + let mut pixels = [0f32; 256*256]; + let n2d = Noise2DContext::new(); + let mut n = 1; + if env::args().len() > 1 { + let arg1 = env::args().nth(1).unwrap(); + n = ::std::str::FromStr::from_str(&arg1).unwrap(); + } + + for _ in 0..n { + for y in 0..256 { + for x in 0..256 { + let v = n2d.get(x as f32 * 0.1, y as f32 * 0.1); + pixels[y*256+x] = v * 0.5 + 0.5; + } + } + } + + for y in 0..256 { + for x in 0..256 { + let idx = (pixels[y*256+x] / 0.2) as usize; + print!("{}", symbols[idx]); + } + print!("\n"); + } +} From 1675c31f65361e38e78b8d2981c1b780ef40f90f Mon Sep 17 00:00:00 2001 From: Akzhan Abdulin Date: Sun, 11 Jun 2017 18:10:48 +0300 Subject: [PATCH 2/2] Update Crystal Perlin test to Crystal 0.22.0 syntax. --- perlin/test.cr | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/perlin/test.cr b/perlin/test.cr index 5ba66dc5..5a064877 100644 --- a/perlin/test.cr +++ b/perlin/test.cr @@ -1,4 +1,4 @@ -record Vec2, x, y +record Vec2, x : Float64, y : Float64 def lerp(a, b, v) a * (1.0 - v) + b * v @@ -10,7 +10,7 @@ end def random_gradient v = rand * Math::PI * 2.0 - Vec2.new(Math.cos(v), Math.sin(v)) + Vec2.new Math.cos(v), Math.sin(v) end def gradient(orig, grad, p) @@ -19,9 +19,11 @@ def gradient(orig, grad, p) end struct Noise2DContext + @permutations : StaticArray(Int32, 256) + def initialize @rgradients = StaticArray(Vec2, 256).new { random_gradient } - @permutations = StaticArray(Int32, 256).new { |i | i }.shuffle! + @permutations = typeof(@permutations).new { |i| i }.shuffle! end def get_gradient(x, y) @@ -49,7 +51,7 @@ struct Noise2DContext Vec2.new(x0f + 1.0, y0f + 0.0), Vec2.new(x0f + 0.0, y0f + 1.0), Vec2.new(x0f + 1.0, y0f + 1.0), - } + }, } end