├── README ├── clean.bash ├── compile.bash ├── run.bash ├── test.c ├── test.cr ├── test.cs ├── test.d ├── test.fs ├── test.go ├── test.java ├── test.jl ├── test.js ├── test.lua ├── test.nim ├── test.p6 ├── test.py ├── test.rs ├── test.tcl └── testffi.lua /README: -------------------------------------------------------------------------------- 1 | Perlin noise benchmark. 2 | 3 | Runs perlin noise 256x256 image generation 100 times. 4 | 5 | Currently the frontend is just a bunch of shell scripts to compile/run compiled 6 | versions of it. You'll have to run scripted versions manually with the 7 | interpreter of your choice. 8 | 9 | Example output of the run on my machine (intel i5-3470, 64 bit) and the versions I use: 10 | 11 | clang version 3.8.1 (tags/RELEASE_381/final) 12 | gcc (GCC) 6.1.1 20160802 13 | Mono C# compiler version 4.4.1.0 14 | Mono JIT compiler version 4.4.1 (Nightly 4.4.1.0/4747417 Thu Jul 14 18:06:32 UTC 2016) 15 | F# Compiler for F# 4.0 (Open Source Edition) 16 | DMD64 D Compiler v2.071.1 17 | LDC - the LLVM D compiler (0.15.1): 18 | based on DMD v2.066.1 and LLVM 3.5.1 19 | gdc (GCC) 6.1.1 20160501 20 | go version go1.7.1 linux/amd64 21 | gccgo (GCC) 6.1.1 20160802 22 | rustc 1.0.0-dev (built 2015-05-17) 23 | Nim Compiler Version 0.11.2 (2015-05-05) [Linux: amd64] 24 | Crystal 0.7.1 (Пн май 11 00:47:29 UTC 2015) 25 | java version "1.8.0_66" 26 | Java(TM) SE Runtime Environment (build 1.8.0_66-b17) 27 | Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode) 28 | 29 | === clang -O3: 30 | 0,162385900 seconds time elapsed 31 | 32 | === gcc -O3: 33 | 0,089993071 seconds time elapsed 34 | 35 | === mono C#: 36 | 0,520457137 seconds time elapsed 37 | 38 | === mono F#: 39 | 0,553722808 seconds time elapsed 40 | 41 | === D (dmd): 42 | 0,314917796 seconds time elapsed 43 | 44 | === D (ldc2): 45 | 0,100076327 seconds time elapsed 46 | 47 | === D (gdc): 48 | 0,076248784 seconds time elapsed 49 | 50 | === Go gc: 51 | 0,287522697 seconds time elapsed 52 | 53 | === Go gccgo -O3: 54 | 0,259701515 seconds time elapsed 55 | 56 | === Rust: 57 | 0,074902613 seconds time elapsed 58 | 59 | === Nim (gcc): 60 | 0,160059764 seconds time elapsed 61 | 62 | === Nim (clang): 63 | 0,108235264 seconds time elapsed 64 | 65 | === Crystal: 66 | 0,071615525 seconds time elapsed 67 | 68 | === Java: 69 | 0,473815480 seconds time elapsed 70 | -------------------------------------------------------------------------------- /clean.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf *.o bin_* nimcache .crystal *.class 4 | -------------------------------------------------------------------------------- /compile.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | gcc -std=c99 -msse3 -mfpmath=sse -O3 -o bin_test_c_gcc test.c -lm 3 | clang -std=c99 -march=native -msse3 -mfpmath=sse -O3 -o bin_test_c_clang test.c -lm 4 | dmd -ofbin_test_d_dmd -O -boundscheck=off -inline -release test.d 5 | ldc2 -O -ofbin_test_d_ldc test.d -release -mcpu=native -inline -boundscheck=off 6 | gdc -Ofast -o bin_test_d_gdc test.d -frelease -finline -march=native -fno-bounds-check 7 | gccgo -O3 -g -o bin_test_go_gccgo test.go 8 | go build -o bin_test_go_gc test.go 9 | rustc -O -o bin_test_rs test.rs 10 | mcs -out:bin_test_cs test.cs 11 | fsharpc -o bin_test_fs.exe -O test.fs 12 | nim c -d:release --cc:gcc -o:bin_test_nim_gcc test.nim 13 | nim c -d:release --cc:clang -o:bin_test_nim_clang test.nim 14 | crystal build -o bin_test_cr --release test.cr 15 | javac test.java 16 | -------------------------------------------------------------------------------- /run.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo -e "=== clang -O3:" 4 | perf stat -r 10 ./bin_test_c_clang 2>&1 > /dev/null | grep time 5 | echo -e "\n=== gcc -O3:" 6 | perf stat -r 10 ./bin_test_c_gcc 2>&1 > /dev/null | grep time 7 | echo -e "\n=== mono C#:" 8 | perf stat -r 10 mono -O=float32 bin_test_cs 2>&1 > /dev/null | grep time 9 | echo -e "\n=== mono F#:" 10 | perf stat -r 10 mono -O=float32 bin_test_fs.exe 2>&1 > /dev/null | grep time 11 | echo -e "\n=== D (dmd):" 12 | perf stat -r 10 ./bin_test_d_dmd 2>&1 > /dev/null | grep time 13 | echo -e "\n=== D (ldc2):" 14 | perf stat -r 10 ./bin_test_d_ldc 2>&1 > /dev/null | grep time 15 | echo -e "\n=== D (gdc):" 16 | perf stat -r 10 ./bin_test_d_gdc 2>&1 > /dev/null | grep time 17 | echo -e "\n=== Go gc:" 18 | perf stat -r 10 ./bin_test_go_gc 2>&1 > /dev/null | grep time 19 | echo -e "\n=== Go gccgo -O3:" 20 | perf stat -r 10 ./bin_test_go_gccgo 2>&1 > /dev/null | grep time 21 | echo -e "\n=== Rust:" 22 | perf stat -r 10 ./bin_test_rs 2>&1 >/dev/null | grep time 23 | echo -e "\n=== Nim (gcc):" 24 | perf stat -r 10 ./bin_test_nim_gcc 2>&1 > /dev/null | grep time 25 | echo -e "\n=== Nim (clang):" 26 | perf stat -r 10 ./bin_test_nim_clang 2>&1 > /dev/null | grep time 27 | echo -e "\n=== Crystal:" 28 | perf stat -r 10 ./bin_test_cr 2>&1 > /dev/null | grep time 29 | echo -e "\n=== Java:" 30 | perf stat -r 10 java -cp . test 2>&1 > /dev/null | grep time 31 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define M_PI 3.1415926535f 7 | 8 | typedef struct { 9 | float x, y; 10 | } Vec2; 11 | 12 | static inline float lerp(float a, float b, float v) 13 | { 14 | return a * (1 - v) + b * v; 15 | } 16 | 17 | static inline float smooth(float v) { 18 | return v * v * (3 - 2 * v); 19 | } 20 | 21 | static inline Vec2 random_gradient() 22 | { 23 | const float v = (float)rand() / RAND_MAX * M_PI * 2.0f; 24 | return (Vec2){cosf(v), sinf(v)}; 25 | } 26 | 27 | static inline float gradient(Vec2 orig, Vec2 grad, Vec2 p) 28 | { 29 | Vec2 sp = {p.x - orig.x, p.y - orig.y}; 30 | return grad.x * sp.x + grad.y * sp.y; 31 | } 32 | 33 | typedef struct { 34 | Vec2 rgradients[256]; 35 | int permutations[256]; 36 | Vec2 gradients[4]; 37 | Vec2 origins[4]; 38 | } Noise2DContext; 39 | 40 | static inline Vec2 get_gradient(Noise2DContext *ctx, int x, int y) { 41 | int idx = ctx->permutations[x & 255] + ctx->permutations[y & 255]; 42 | return ctx->rgradients[idx & 255]; 43 | } 44 | 45 | static inline void get_gradients(Noise2DContext *ctx, float x, float y) { 46 | float x0f = floorf(x); 47 | float y0f = floorf(y); 48 | int x0 = x0f; 49 | int y0 = y0f; 50 | int x1 = x0 + 1; 51 | int y1 = y0 + 1; 52 | 53 | ctx->gradients[0] = get_gradient(ctx, x0, y0); 54 | ctx->gradients[1] = get_gradient(ctx, x1, y0); 55 | ctx->gradients[2] = get_gradient(ctx, x0, y1); 56 | ctx->gradients[3] = get_gradient(ctx, x1, y1); 57 | 58 | ctx->origins[0] = (Vec2){x0f + 0.0f, y0f + 0.0f}; 59 | ctx->origins[1] = (Vec2){x0f + 1.0f, y0f + 0.0f}; 60 | ctx->origins[2] = (Vec2){x0f + 0.0f, y0f + 1.0f}; 61 | ctx->origins[3] = (Vec2){x0f + 1.0f, y0f + 1.0f}; 62 | } 63 | 64 | 65 | static float noise2d_get(Noise2DContext *ctx, float x, float y) 66 | { 67 | Vec2 p = {x, y}; 68 | get_gradients(ctx, x, y); 69 | float v0 = gradient(ctx->origins[0], ctx->gradients[0], p); 70 | float v1 = gradient(ctx->origins[1], ctx->gradients[1], p); 71 | float v2 = gradient(ctx->origins[2], ctx->gradients[2], p); 72 | float v3 = gradient(ctx->origins[3], ctx->gradients[3], p); 73 | 74 | float fx = smooth(x - ctx->origins[0].x); 75 | float vx0 = lerp(v0, v1, fx); 76 | float vx1 = lerp(v2, v3, fx); 77 | float fy = smooth(y - ctx->origins[0].y); 78 | return lerp(vx0, vx1, fy); 79 | } 80 | 81 | static void init_noise2d(Noise2DContext *ctx) 82 | { 83 | for (int i = 0; i < 256; i++) 84 | ctx->rgradients[i] = random_gradient(); 85 | 86 | for (int i = 0; i < 256; i++) { 87 | int j = rand() % (i+1); 88 | ctx->permutations[i] = ctx->permutations[j]; 89 | ctx->permutations[j] = i; 90 | } 91 | } 92 | 93 | int main(int argc, char **argv) 94 | { 95 | srand(time(NULL)); 96 | 97 | const char *symbols[] = {" ", "░", "▒", "▓", "█", "█"}; 98 | float *pixels = malloc(sizeof(float) * 256 * 256); 99 | 100 | Noise2DContext n2d; 101 | init_noise2d(&n2d); 102 | 103 | for (int i = 0; i < 100; i++) { 104 | for (int y = 0; y < 256; y++) { 105 | for (int x = 0; x < 256; x++) { 106 | float v = noise2d_get(&n2d, x * 0.1f, y * 0.1f) 107 | * 0.5f + 0.5f; 108 | pixels[y*256+x] = v; 109 | } 110 | } 111 | } 112 | 113 | for (int y = 0; y < 256; y++) { 114 | for (int x = 0; x < 256; x++) { 115 | int idx = pixels[y*256+x] / 0.2f; 116 | printf("%s", symbols[idx]); 117 | } 118 | printf("\n"); 119 | } 120 | 121 | return 0; 122 | } 123 | -------------------------------------------------------------------------------- /test.cr: -------------------------------------------------------------------------------- 1 | record Vec2, x, y 2 | 3 | def lerp(a, b, v) 4 | a * (1.0 - v) + b * v 5 | end 6 | 7 | def smooth(v) 8 | v * v * (3.0 - 2.0 * v) 9 | end 10 | 11 | def random_gradient 12 | v = rand * Math::PI * 2.0 13 | Vec2.new(Math.cos(v), Math.sin(v)) 14 | end 15 | 16 | def gradient(orig, grad, p) 17 | sp = Vec2.new(p.x - orig.x, p.y - orig.y) 18 | grad.x * sp.x + grad.y * sp.y 19 | end 20 | 21 | struct Noise2DContext 22 | def initialize 23 | @rgradients = StaticArray(Vec2, 256).new { random_gradient } 24 | @permutations = StaticArray(Int32, 256).new { |i | i }.shuffle! 25 | end 26 | 27 | def get_gradient(x, y) 28 | idx = @permutations[x & 255] + @permutations[y & 255] 29 | @rgradients[idx & 255] 30 | end 31 | 32 | def get_gradients(x, y) 33 | x0f = x.floor 34 | y0f = y.floor 35 | x0 = x0f.to_i 36 | y0 = y0f.to_i 37 | x1 = x0 + 1 38 | y1 = y0 + 1 39 | 40 | { 41 | { 42 | get_gradient(x0, y0), 43 | get_gradient(x1, y0), 44 | get_gradient(x0, y1), 45 | get_gradient(x1, y1), 46 | }, 47 | { 48 | Vec2.new(x0f + 0.0, y0f + 0.0), 49 | Vec2.new(x0f + 1.0, y0f + 0.0), 50 | Vec2.new(x0f + 0.0, y0f + 1.0), 51 | Vec2.new(x0f + 1.0, y0f + 1.0), 52 | } 53 | } 54 | end 55 | 56 | def get(x, y) 57 | p = Vec2.new(x, y) 58 | gradients, origins = get_gradients(x, y) 59 | v0 = gradient(origins[0], gradients[0], p) 60 | v1 = gradient(origins[1], gradients[1], p) 61 | v2 = gradient(origins[2], gradients[2], p) 62 | v3 = gradient(origins[3], gradients[3], p) 63 | fx = smooth(x - origins[0].x) 64 | vx0 = lerp(v0, v1, fx) 65 | vx1 = lerp(v2, v3, fx) 66 | fy = smooth(y - origins[0].y) 67 | lerp(vx0, vx1, fy) 68 | end 69 | end 70 | 71 | symbols = [' ', '░', '▒', '▓', '█', '█'] 72 | pixels = Array.new(256) { Array.new(256, 0.0) } 73 | 74 | n2d = Noise2DContext.new 75 | 76 | 100.times do |i| 77 | 256.times do |y| 78 | 256.times do |x| 79 | v = n2d.get(x * 0.1, (y + (i * 128)) * 0.1) * 0.5 + 0.5 80 | pixels[y][x] = v 81 | end 82 | end 83 | end 84 | 85 | 256.times do |y| 86 | 256.times do |x| 87 | v = pixels[y][x] 88 | print(symbols[(v / 0.2).to_i]) 89 | end 90 | puts 91 | end 92 | -------------------------------------------------------------------------------- /test.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | 4 | struct Vec2 { 5 | public float x; 6 | public float y; 7 | } 8 | 9 | class Noise2DContext { 10 | Vec2[] rgradients; 11 | int[] permutations; 12 | Vec2[] gradients; 13 | Vec2[] origins; 14 | 15 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 16 | internal static float lerp(float a, float b, float v) { 17 | return a * (1 - v) + b * v; 18 | } 19 | 20 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 21 | internal static float smooth(float v) { 22 | return v * v * (3 - 2 * v); 23 | } 24 | 25 | internal static Vec2 random_gradient(Random rnd) { 26 | var v = rnd.NextDouble() * Math.PI * 2.0; 27 | return new Vec2 { x = (float)Math.Cos(v), y = (float)Math.Sin(v) }; 28 | } 29 | 30 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 31 | internal static float gradient(Vec2 orig, Vec2 grad, Vec2 p) { 32 | return grad.x * (p.x - orig.x) + grad.y * (p.y - orig.y); 33 | } 34 | 35 | public Noise2DContext(int seed) { 36 | Random rnd = new Random(seed); 37 | rgradients = new Vec2[256]; 38 | permutations = new int[256]; 39 | for (int i = 0; i < 256; i++) { 40 | rgradients[i] = random_gradient(rnd); 41 | } 42 | for (int i = 0; i < 256; i++) { 43 | int j = rnd.Next(i + 1); 44 | permutations[i] = permutations[j]; 45 | permutations[j] = i; 46 | } 47 | 48 | gradients = new Vec2[4]; 49 | origins = new Vec2[4]; 50 | } 51 | 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | internal Vec2 get_gradient(int x, int y) { 54 | int idx = permutations[x & 255] + permutations[y & 255]; 55 | return rgradients[idx & 255]; 56 | } 57 | 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | internal void get_gradients(float x, float y) { 60 | float x0f = (float)Math.Floor(x); 61 | float y0f = (float)Math.Floor(y); 62 | int x0 = (int)x0f; 63 | int y0 = (int)y0f; 64 | int x1 = x0 + 1; 65 | int y1 = y0 + 1; 66 | 67 | gradients[0] = get_gradient(x0, y0); 68 | gradients[1] = get_gradient(x1, y0); 69 | gradients[2] = get_gradient(x0, y1); 70 | gradients[3] = get_gradient(x1, y1); 71 | 72 | origins[0].x = x0f + 0; 73 | origins[0].y = y0f + 0; 74 | origins[1].x = x0f + 1; 75 | origins[1].y = y0f + 0; 76 | origins[2].x = x0f + 0; 77 | origins[2].y = y0f + 1; 78 | origins[3].x = x0f + 1; 79 | origins[3].y = y0f + 1; 80 | } 81 | 82 | public float get(float x, float y) { 83 | Vec2 p = new Vec2 { x = x, y = y }; 84 | get_gradients(x, y); 85 | float v0 = gradient(origins[0], gradients[0], p); 86 | float v1 = gradient(origins[1], gradients[1], p); 87 | float v2 = gradient(origins[2], gradients[2], p); 88 | float v3 = gradient(origins[3], gradients[3], p); 89 | 90 | float fx = smooth(x - origins[0].x); 91 | float vx0 = lerp(v0, v1, fx); 92 | float vx1 = lerp(v2, v3, fx); 93 | float fy = smooth(y - origins[0].y); 94 | return lerp(vx0, vx1, fy); 95 | } 96 | } 97 | 98 | class Application { 99 | static readonly char[] symbols = { ' ', '░', '▒', '▓', '█', '█' }; 100 | 101 | public static void Main(string[] args) { 102 | var n2d = new Noise2DContext((int)DateTime.Now.Ticks); 103 | float[] pixels = new float[256 * 256]; 104 | 105 | for (int i = 0; i < 100; i++) { 106 | for (int y = 0; y < 256; y++) { 107 | for (int x = 0; x < 256; x++) { 108 | float v = n2d.get(x * 0.1f, y * 0.1f) * 0.5f + 0.5f; 109 | pixels[y * 256 + x] = v; 110 | } 111 | } 112 | } 113 | 114 | for (int y = 0; y < 256; y++) { 115 | for (int x = 0; x < 256; x++) { 116 | int idx = (int)(pixels[y * 256 + x] / 0.2f); 117 | Console.Write(symbols[idx]); 118 | } 119 | Console.WriteLine(); 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /test.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | import std.random; 3 | import std.math; 4 | 5 | struct Vec2 { 6 | float x, y; 7 | } 8 | 9 | alias floor = core.stdc.math.floor; 10 | 11 | static float lerp(immutable float a, immutable float b, immutable float v) pure nothrow 12 | { 13 | return a * (1 - v) + b * v; 14 | } 15 | 16 | static float smooth(immutable float v) pure nothrow 17 | { 18 | return v * v * (3 - 2 * v); 19 | } 20 | 21 | static Vec2 random_gradient(Random)(ref Random r) 22 | { 23 | immutable v = uniform(0.0f, cast(float)PI * 2.0f, r); 24 | return Vec2(cos(v), sin(v)); 25 | } 26 | 27 | static float gradient(immutable Vec2 orig, immutable Vec2 grad, immutable Vec2 p) pure nothrow 28 | { 29 | immutable sp = Vec2(p.x - orig.x, p.y - orig.y); 30 | return grad.x * sp.x + grad.y * sp.y; 31 | } 32 | 33 | struct Noise2DContext { 34 | Vec2[256] rgradients; 35 | uint[256] permutations; 36 | 37 | private: 38 | Vec2 get_gradient(immutable int x, immutable int y) pure nothrow 39 | { 40 | immutable idx = permutations[x & 255] + permutations[y & 255]; 41 | return rgradients[idx & 255]; 42 | } 43 | 44 | Vec2[8] get_gradients(immutable float x, immutable float y) nothrow 45 | { 46 | float x0f = floor(x); 47 | float y0f = floor(y); 48 | int x0 = cast(int)x; 49 | int y0 = cast(int)y; 50 | int x1 = x0 + 1; 51 | int y1 = y0 + 1; 52 | 53 | return cast(Vec2[8]) [get_gradient(x0, y0), 54 | get_gradient(x1, y0), 55 | get_gradient(x0, y1), 56 | get_gradient(x1, y1), 57 | Vec2(x0f + 0.0f, y0f + 0.0f), 58 | Vec2(x0f + 1.0f, y0f + 0.0f), 59 | Vec2(x0f + 0.0f, y0f + 1.0f), 60 | Vec2(x0f + 1.0f, y0f + 1.0f)]; 61 | } 62 | 63 | public: 64 | static Noise2DContext opCall(uint seed) 65 | { 66 | Noise2DContext ret; 67 | auto rnd = Random(seed); 68 | foreach (ref elem; ret.rgradients) 69 | elem = random_gradient(rnd); 70 | 71 | foreach (i; 0 .. ret.permutations.length) { 72 | uint j = uniform(0, cast(uint)i+1, rnd); 73 | ret.permutations[i] = ret.permutations[j]; 74 | ret.permutations[j] = cast(uint)i; 75 | } 76 | 77 | return ret; 78 | } 79 | 80 | float get(immutable float x, immutable float y) nothrow 81 | { 82 | immutable p = Vec2(x, y); 83 | 84 | immutable vecs = get_gradients(x, y); 85 | immutable v0 = gradient(vecs[4], vecs[0], p); 86 | immutable v1 = gradient(vecs[5], vecs[1], p); 87 | immutable v2 = gradient(vecs[6], vecs[2], p); 88 | immutable v3 = gradient(vecs[7], vecs[3], p); 89 | 90 | immutable fx = smooth(x - vecs[4].x); 91 | immutable vx0 = lerp(v0, v1, fx); 92 | immutable vx1 = lerp(v2, v3, fx); 93 | immutable fy = smooth(y - vecs[4].y); 94 | return lerp(vx0, vx1, fy); 95 | } 96 | } 97 | 98 | void main() 99 | { 100 | immutable symbols = [" ", "░", "▒", "▓", "█", "█"]; 101 | auto pixels = new float[256*256]; 102 | 103 | auto n2d = Noise2DContext(0); 104 | foreach (immutable i; 0..100) { 105 | foreach (immutable y; 0..256) { 106 | foreach (immutable x; 0..256) { 107 | immutable v = n2d.get(x * 0.1f, y * 0.1f) * 108 | 0.5f + 0.5f; 109 | pixels[y*256+x] = v; 110 | } 111 | } 112 | } 113 | 114 | foreach (immutable y; 0..256) { 115 | foreach (immutable x; 0..256) { 116 | write(symbols[cast(int)(pixels[y*256+x] / 0.2f)]); 117 | } 118 | writeln(); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /test.fs: -------------------------------------------------------------------------------- 1 | open System 2 | 3 | type Vec2 = struct 4 | val X : single 5 | val Y : single 6 | new(x, y) = { X = x; Y = y } 7 | end 8 | 9 | let inline lerp a b v = a * (1.0f - v) + b * v 10 | let inline smooth v = v * v * (3.0f - 2.0f * v) 11 | let inline gradient (o : Vec2) (g : Vec2) (p : Vec2) = g.X * (p.X - o.X) + g.Y * (p.Y - o.Y) 12 | 13 | let random_gradient (rnd : Random) = 14 | let v = rnd.NextDouble() * Math.PI * 2.0 15 | Vec2(single (Math.Cos v), single (Math.Sin v)) 16 | 17 | // is there a better way to ignore function argument? 18 | let random_vectors n (rnd : Random) = Array.init n (fun _ -> random_gradient rnd) 19 | 20 | // is there a better way to generate permutations? 21 | let random_permutations n (rnd : Random) = 22 | let perm = Array.init n id 23 | for i = 0 to n - 1 do 24 | let j = rnd.Next(i + 1) 25 | perm.[i] <- perm.[j] 26 | perm.[j] <- i 27 | perm 28 | 29 | type Noise2DContext(seed) = 30 | let rgradients, permutations = 31 | let rnd = new Random(seed) 32 | (random_vectors 256 rnd, random_permutations 256 rnd) 33 | 34 | let gradients = Array.zeroCreate 4 35 | let origins = Array.zeroCreate 4 36 | 37 | member inline private this.get_gradient x y = 38 | let idx = permutations.[x &&& 255] + permutations.[y &&& 255] 39 | rgradients.[idx &&& 255] 40 | 41 | member inline private this.get_gradients_and_origins x y = 42 | let x0f = single (Math.Floor(double x)) 43 | let y0f = single (Math.Floor(double y)) 44 | let x0 = int x0f 45 | let y0 = int y0f 46 | let x1 = x0 + 1 47 | let y1 = y0 + 1 48 | gradients.[0] <- this.get_gradient x0 y0 49 | gradients.[1] <- this.get_gradient x1 y0 50 | gradients.[2] <- this.get_gradient x0 y1 51 | gradients.[3] <- this.get_gradient x1 y1 52 | origins.[0] <- Vec2(x0f + 0.0f, y0f + 0.0f) 53 | origins.[1] <- Vec2(x0f + 1.0f, y0f + 0.0f) 54 | origins.[2] <- Vec2(x0f + 0.0f, y0f + 1.0f) 55 | origins.[3] <- Vec2(x0f + 1.0f, y0f + 1.0f) 56 | 57 | member this.Get (x : single) (y : single) = 58 | let p = Vec2(x, y) 59 | this.get_gradients_and_origins x y 60 | let v0 = gradient origins.[0] gradients.[0] p 61 | let v1 = gradient origins.[1] gradients.[1] p 62 | let v2 = gradient origins.[2] gradients.[2] p 63 | let v3 = gradient origins.[3] gradients.[3] p 64 | let fx = smooth (x - origins.[0].X) 65 | let vx0 = lerp v0 v1 fx 66 | let vx1 = lerp v2 v3 fx 67 | let fy = smooth (y - origins.[0].Y) 68 | lerp vx0 vx1 fy 69 | 70 | let symbols = [| ' '; '░'; '▒'; '▓'; '█'; '█' |] 71 | let n2d = new Noise2DContext(int DateTime.Now.Ticks) 72 | let pixels = Array.zeroCreate (256 * 256) 73 | 74 | for i = 1 to 100 do 75 | for y = 0 to 255 do 76 | for x = 0 to 255 do 77 | let v = n2d.Get (single x * 0.1f) (single y * 0.1f) * 0.5f + 0.5f 78 | pixels.[y * 256 + x] <- v 79 | for y = 0 to 255 do 80 | for x = 0 to 255 do 81 | let idx = int (pixels.[y * 256 + x] / 0.2f) 82 | Console.Write symbols.[idx] 83 | Console.WriteLine() 84 | -------------------------------------------------------------------------------- /test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "math" 7 | "bufio" 8 | "os" 9 | ) 10 | 11 | const PI = 3.1415926535 12 | 13 | type Vec2 struct { 14 | X, Y float32 15 | } 16 | 17 | func lerp(a, b, v float32) float32 { 18 | return a * (1 - v) + b * v 19 | } 20 | 21 | func smooth(v float32) float32 { 22 | return v * v * (3 - 2 * v) 23 | } 24 | 25 | func random_gradient(r *rand.Rand) Vec2 { 26 | v := r.Float64() * PI * 2 27 | return Vec2{ 28 | float32(math.Cos(v)), 29 | float32(math.Sin(v)), 30 | } 31 | } 32 | 33 | func gradient(orig, grad, p Vec2) float32 { 34 | sp := Vec2{p.X - orig.X, p.Y - orig.Y} 35 | return grad.X * sp.X + grad.Y * sp.Y 36 | } 37 | 38 | type Noise2DContext struct { 39 | rgradients [256]Vec2 40 | permutations [256]int 41 | gradients [4]Vec2 42 | origins [4]Vec2 43 | } 44 | 45 | func NewNoise2DContext(seed int) *Noise2DContext { 46 | rnd := rand.New(rand.NewSource(int64(seed))) 47 | 48 | n2d := new(Noise2DContext) 49 | copy(n2d.permutations[:], rand.Perm(256)) 50 | for i := range n2d.rgradients { 51 | n2d.rgradients[i] = random_gradient(rnd) 52 | } 53 | 54 | return n2d 55 | } 56 | 57 | func (n2d *Noise2DContext) get_gradient(x, y int) Vec2 { 58 | idx := n2d.permutations[x & 255] + n2d.permutations[y & 255] 59 | return n2d.rgradients[idx & 255] 60 | } 61 | 62 | func (n2d *Noise2DContext) get_gradients(x, y float32) { 63 | x0f := math.Floor(float64(x)) 64 | y0f := math.Floor(float64(y)) 65 | x0 := int(x0f) 66 | y0 := int(y0f) 67 | x1 := x0 + 1 68 | y1 := y0 + 1 69 | 70 | n2d.gradients[0] = n2d.get_gradient(x0, y0) 71 | n2d.gradients[1] = n2d.get_gradient(x1, y0) 72 | n2d.gradients[2] = n2d.get_gradient(x0, y1) 73 | n2d.gradients[3] = n2d.get_gradient(x1, y1) 74 | 75 | n2d.origins[0] = Vec2{float32(x0f + 0.0), float32(y0f + 0.0)} 76 | n2d.origins[1] = Vec2{float32(x0f + 1.0), float32(y0f + 0.0)} 77 | n2d.origins[2] = Vec2{float32(x0f + 0.0), float32(y0f + 1.0)} 78 | n2d.origins[3] = Vec2{float32(x0f + 1.0), float32(y0f + 1.0)} 79 | } 80 | 81 | func (n2d *Noise2DContext) Get(x, y float32) float32 { 82 | p := Vec2{x, y} 83 | n2d.get_gradients(x, y) 84 | v0 := gradient(n2d.origins[0], n2d.gradients[0], p) 85 | v1 := gradient(n2d.origins[1], n2d.gradients[1], p) 86 | v2 := gradient(n2d.origins[2], n2d.gradients[2], p) 87 | v3 := gradient(n2d.origins[3], n2d.gradients[3], p) 88 | fx := smooth(x - n2d.origins[0].X) 89 | vx0 := lerp(v0, v1, fx) 90 | vx1 := lerp(v2, v3, fx) 91 | fy := smooth(y - n2d.origins[0].Y) 92 | return lerp(vx0, vx1, fy) 93 | } 94 | 95 | func main() { 96 | symbols := []string{" ", "░", "▒", "▓", "█", "█"} 97 | pixels := make([]float32, 256*256) 98 | n2d := NewNoise2DContext(0) 99 | for i := 0; i < 100; i++ { 100 | for y := 0; y < 256; y++ { 101 | for x := 0; x < 256; x++ { 102 | v := n2d.Get(float32(x) * 0.1, 103 | float32(y) * 0.1) 104 | v = v * 0.5 + 0.5 105 | pixels[y*256+x] = v 106 | } 107 | } 108 | } 109 | 110 | out := bufio.NewWriter(os.Stdout) 111 | for y := 0; y < 256; y++ { 112 | for x := 0; x < 256; x++ { 113 | fmt.Fprint(out, symbols[int(pixels[y*256+x] / 0.2)]) 114 | } 115 | fmt.Fprintln(out) 116 | } 117 | out.Flush() 118 | } 119 | -------------------------------------------------------------------------------- /test.java: -------------------------------------------------------------------------------- 1 | import java.util.Random; 2 | 3 | public class test { 4 | public static class Vec2 { 5 | public Vec2(float x, float y) { 6 | this.x = x; 7 | this.y = y; 8 | } 9 | 10 | public float x; 11 | public float y; 12 | } 13 | 14 | private static class Noise2DContext { 15 | Vec2[] rgradients; 16 | int[] permutations; 17 | Vec2[] gradients; 18 | Vec2[] origins; 19 | 20 | private static final float lerp(float a, float b, float v) { 21 | return a * (1 - v) + b * v; 22 | } 23 | 24 | private static final float smooth(float v) { 25 | return v * v * (3 - 2 * v); 26 | } 27 | 28 | Vec2 random_gradient(Random rnd) { 29 | double v = rnd.nextDouble() * Math.PI * 2.0; 30 | return new Vec2((float) Math.cos(v), (float) Math.sin(v)); 31 | } 32 | 33 | float gradient(Vec2 orig, Vec2 grad, Vec2 p) { 34 | Vec2 sp = new Vec2(p.x - orig.x, p.y - orig.y); 35 | return grad.x * sp.x + grad.y * sp.y; 36 | } 37 | 38 | public Noise2DContext(int seed) { 39 | Random rnd = new Random(seed); 40 | rgradients = new Vec2[256]; 41 | permutations = new int[256]; 42 | for (int i = 0; i < 256; i++) { 43 | rgradients[i] = random_gradient(rnd); 44 | } 45 | for (int i = 0; i < 256; i++) { 46 | int j = rnd.nextInt(i + 1); 47 | permutations[i] = permutations[j]; 48 | permutations[j] = i; 49 | } 50 | 51 | gradients = new Vec2[4]; 52 | origins = new Vec2[4]; 53 | } 54 | 55 | Vec2 get_gradient(int x, int y) { 56 | int idx = permutations[x & 255] + permutations[y & 255]; 57 | return rgradients[idx & 255]; 58 | } 59 | 60 | void get_gradients(float x, float y) { 61 | float x0f = (float) Math.floor(x); 62 | float y0f = (float) Math.floor(y); 63 | int x0 = (int) x0f; 64 | int y0 = (int) y0f; 65 | int x1 = x0 + 1; 66 | int y1 = y0 + 1; 67 | 68 | gradients[0] = get_gradient(x0, y0); 69 | gradients[1] = get_gradient(x1, y0); 70 | gradients[2] = get_gradient(x0, y1); 71 | gradients[3] = get_gradient(x1, y1); 72 | 73 | origins[0] = new Vec2(x0f + 0, y0f + 0); 74 | origins[1] = new Vec2(x0f + 1, y0f + 0); 75 | origins[2] = new Vec2(x0f + 0, y0f + 1); 76 | origins[3] = new Vec2(x0f + 1, y0f + 1); 77 | } 78 | 79 | public float get(float x, float y) { 80 | Vec2 p = new Vec2(x, y); 81 | get_gradients(x, y); 82 | float v0 = gradient(origins[0], gradients[0], p); 83 | float v1 = gradient(origins[1], gradients[1], p); 84 | float v2 = gradient(origins[2], gradients[2], p); 85 | float v3 = gradient(origins[3], gradients[3], p); 86 | 87 | float fx = smooth(x - origins[0].x); 88 | float vx0 = lerp(v0, v1, fx); 89 | float vx1 = lerp(v2, v3, fx); 90 | float fy = smooth(y - origins[0].y); 91 | return lerp(vx0, vx1, fy); 92 | } 93 | } 94 | 95 | static char[] symbols = { ' ', '░', '▒', '▓', '█', '█' }; 96 | 97 | public test() { 98 | Noise2DContext n2d = new Noise2DContext((int) System.currentTimeMillis()); 99 | float[] pixels = new float[256 * 256]; 100 | 101 | for (int i = 0; i < 100; i++) { 102 | for (int y = 0; y < 256; y++) { 103 | for (int x = 0; x < 256; x++) { 104 | float v = n2d.get(x * 0.1f, y * 0.1f) * 0.5f + 0.5f; 105 | pixels[y * 256 + x] = v; 106 | } 107 | } 108 | } 109 | 110 | for (int y = 0; y < 256; y++) { 111 | for (int x = 0; x < 256; x++) { 112 | int idx = (int) (pixels[y * 256 + x] / 0.2f); 113 | System.out.print(symbols[idx]); 114 | } 115 | System.out.println(); 116 | } 117 | } 118 | 119 | public static void main(String[] args) { 120 | new test(); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /test.jl: -------------------------------------------------------------------------------- 1 | #Perlin line noise test in Julia 2 | const RAND_MAX = 0x7fff 3 | const symbols = [ " ", "░", "▒", "▓", "█", "█" ] 4 | 5 | type Vec2 6 | x::Float32 7 | y::Float32 8 | end 9 | 10 | function Vec2() 11 | Vec2(0,0) 12 | end 13 | 14 | type Noise2DContext 15 | rgradients::Vector{Vec2} 16 | permutations::Vector{Int64} 17 | gradients::Vector{Vec2} 18 | origins::Vector{Vec2} 19 | end 20 | 21 | function Noise2DContext() 22 | Noise2DContext( 23 | Array(Vec2,256), #rgradients 24 | Array(Int64,256), #permutations 25 | Array(Vec2,4), 26 | Array(Vec2,4) 27 | ) 28 | end 29 | 30 | lerp(a::Float32,b::Float32,v::Float32) = float32(a * (1.0 - v) + b * v)::Float32 31 | 32 | smooth(v::Float32) = float32(v * v * (3.0 - 2.0*v))::Float32 33 | 34 | function random_gradient() 35 | v = rand()*pi*2.0 36 | Vec2(cos(v),sin(v)) 37 | end 38 | 39 | function gradient(orig::Vec2,grad::Vec2,p::Vec2) 40 | sp = Vec2((p.x - orig.x),(p.y - orig.y)) 41 | grad.x * sp.x + grad.y * sp.y 42 | end 43 | 44 | function get_gradient(ctx::Noise2DContext, x::Int64, y::Int64) 45 | idx = ctx.permutations[x&255+1] + ctx.permutations[y&255+1] 46 | ctx.rgradients[idx&255+1] 47 | end 48 | 49 | function get_gradient(ctx::Noise2DContext,x::Float32,y::Float32) 50 | x0f = floor(x) 51 | y0f = floor(y) 52 | x0 = int(x0f) 53 | y0 = int(y0f) 54 | y = int(y0f) 55 | x1 = x0 + 1 56 | y1 = y0 + 1 57 | 58 | ctx.gradients[1] = get_gradient(ctx, x0, y0) 59 | ctx.gradients[2] = get_gradient(ctx, x1, y0) 60 | ctx.gradients[3] = get_gradient(ctx, x0, y1) 61 | ctx.gradients[4] = get_gradient(ctx, x1, y1) 62 | ctx.origins[1] = Vec2( (x0f + 0.0), (y0f + 0.0)) 63 | ctx.origins[2] = Vec2( (x0f + 1.0), (y0f + 0.0)) 64 | ctx.origins[3] = Vec2( (x0f + 0.0), (y0f + 1.0)) 65 | ctx.origins[4] = Vec2( (x0f + 1.0), (y0f + 1.0)) 66 | ctx.gradients 67 | end 68 | 69 | function noise2d_get(ctx:: Noise2DContext, x::Float32, y::Float32) 70 | p = Vec2(x,y) 71 | get_gradient(ctx,x,y) 72 | v0 = gradient(ctx.origins[1], ctx.gradients[1], p) 73 | v1 = gradient(ctx.origins[2], ctx.gradients[2], p) 74 | v2 = gradient(ctx.origins[3], ctx.gradients[3], p) 75 | v3 = gradient(ctx.origins[4], ctx.gradients[4], p) 76 | fx = smooth(x - ctx.origins[1].x) 77 | vx0 = lerp(v0, v1, fx) 78 | vx1 = lerp(v2, v3, fx) 79 | fy = smooth(y - ctx.origins[1].y) 80 | lerp(vx0,vx1,fy) 81 | end 82 | 83 | function init_noise2d(ctx::Noise2DContext) 84 | for i in 1:256 85 | ctx.rgradients[i] = random_gradient() 86 | end 87 | for i in 1:256 88 | ctx.permutations[i] = i 89 | end 90 | shuffle!(ctx.permutations) 91 | end 92 | 93 | function main() 94 | n2d = Noise2DContext() 95 | init_noise2d(n2d) 96 | pixels = Array(Float32,256,256) 97 | for i in 1:100 98 | for y in 1:256 99 | for x in 1:256 100 | v = noise2d_get(n2d, float32(x*0.1), float32(y*0.1))*0.5 + 0.5 101 | pixels[y,x] = v 102 | end 103 | end 104 | end 105 | 106 | for y in 1:256 107 | for x in 1:256 108 | write(STDOUT,symbols[int(pixels[y,x]/0.2)]) 109 | end 110 | write(STDOUT,"\n") 111 | end 112 | end 113 | 114 | @time main() 115 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const lerp = (a, b, v) => a * (1 - v) + b * v; 2 | const smooth = v => v * v * (3 - 2 * v); 3 | const gradient = (orig, grad, p) => grad.x * (p.x - orig.x) + grad.y * (p.y - orig.y); 4 | 5 | function random_gradient() { 6 | const v = Math.random() * Math.PI * 2; 7 | return {x: Math.cos(v), y: Math.sin(v)}; 8 | } 9 | 10 | class Noise2D { 11 | constructor() { 12 | this.rgradients = new Array(256); 13 | this.permutations = new Array(256); 14 | this.gradients = new Array(4); 15 | this.origins = new Array(4); 16 | 17 | for (let i = 0; i < 256; i++) { 18 | this.rgradients[i] = random_gradient(); 19 | } 20 | for (let i = 0; i < 256; i++) { 21 | const j = Math.floor(Math.random() * i); 22 | this.permutations[i] = this.permutations[j]; 23 | this.permutations[j] = i; 24 | } 25 | } 26 | 27 | get_gradient(x, y) { 28 | const idx = this.permutations[x & 255] + this.permutations[y & 255]; 29 | return this.rgradients[idx & 255]; 30 | } 31 | 32 | get_gradients_and_origins(x, y) { 33 | const x0 = Math.floor(x); 34 | const y0 = Math.floor(y); 35 | const x1 = x0 + 1; 36 | const y1 = y0 + 1; 37 | 38 | this.gradients[0] = this.get_gradient(x0, y0); 39 | this.gradients[1] = this.get_gradient(x1, y0); 40 | this.gradients[2] = this.get_gradient(x0, y1); 41 | this.gradients[3] = this.get_gradient(x1, y1); 42 | this.origins[0] = {x: x0 + 0, y: y0 + 0}; 43 | this.origins[1] = {x: x0 + 1, y: y0 + 0}; 44 | this.origins[2] = {x: x0 + 0, y: y0 + 1}; 45 | this.origins[3] = {x: x0 + 1, y: y0 + 1}; 46 | } 47 | 48 | get(x, y) { 49 | const p = {x, y}; 50 | this.get_gradients_and_origins(x, y); 51 | const v1 = gradient(this.origins[0], this.gradients[0], p); 52 | const v2 = gradient(this.origins[1], this.gradients[1], p); 53 | const v3 = gradient(this.origins[2], this.gradients[2], p); 54 | const v4 = gradient(this.origins[3], this.gradients[3], p); 55 | const fx = smooth(x - this.origins[0].x); 56 | const vx1 = lerp(v1, v2, fx); 57 | const vx2 = lerp(v3, v4, fx); 58 | const fy = smooth(y - this.origins[0].y); 59 | return lerp(vx1, vx2, fy); 60 | } 61 | } 62 | 63 | const symbols = [' ', '░', '▒', '▓', '█', '█']; 64 | const pixels = new Array(256*256); 65 | const n2d = new Noise2D(); 66 | 67 | for (let i = 0; i < 100; i++) { 68 | for (let y = 0; y < 256; y++) { 69 | for (let x = 0; x < 256; x++) { 70 | const v = n2d.get(x * 0.1, y * 0.1) * 0.5 + 0.5; 71 | pixels[y*256+x] = v; 72 | } 73 | } 74 | } 75 | 76 | let output = ''; 77 | for (let y = 0; y < 256; y++) { 78 | for (let x = 0; x < 256; x++) { 79 | const idx = Math.floor(pixels[y*256+x] / 0.2); 80 | output += symbols[idx]; 81 | } 82 | output += '\n'; 83 | } 84 | process.stdout.write(output); 85 | -------------------------------------------------------------------------------- /test.lua: -------------------------------------------------------------------------------- 1 | local band = nil 2 | if jit ~= nil then -- luajit has a different bitops module name 3 | band = bit.band 4 | else 5 | band = bit32.band 6 | end 7 | 8 | local function lerp(a, b, v) 9 | return a * (1 - v) + b * v 10 | end 11 | 12 | local function smooth(v) 13 | return v * v * (3 - 2 * v) 14 | end 15 | 16 | local function random_gradient() 17 | local v = math.random() * math.pi * 2.0 18 | return {x = math.cos(v), y = math.sin(v)} 19 | end 20 | 21 | local function gradient(orig, grad, p) 22 | return grad.x * (p.x - orig.x) + grad.y * (p.y - orig.y) 23 | end 24 | 25 | local Noise2D = {} 26 | Noise2D.__index = Noise2D 27 | 28 | function Noise2D.new(seed) 29 | math.randomseed(seed) 30 | local rgradients = {} 31 | for i = 1, 256 do 32 | rgradients[i] = random_gradient() 33 | end 34 | local permutations = {} 35 | for i = 1, 256 do 36 | local j = math.random(i) 37 | permutations[i] = permutations[j] 38 | permutations[j] = i 39 | end 40 | return setmetatable({ 41 | rgradients = rgradients, 42 | permutations = permutations, 43 | gradients = {}, 44 | origins = {}, 45 | }, Noise2D) 46 | end 47 | 48 | function Noise2D:get_gradient(x, y) 49 | x = band(x, 255)+1 50 | y = band(y, 255)+1 51 | local idx = self.permutations[x] + self.permutations[y] 52 | return self.rgradients[band(idx, 255)+1] 53 | end 54 | 55 | function Noise2D:get_gradients_and_origins(x, y) 56 | local x0 = math.floor(x) 57 | local y0 = math.floor(y) 58 | local x1 = x0 + 1 59 | local y1 = y0 + 1 60 | 61 | self.gradients[1] = self:get_gradient(x0, y0) 62 | self.gradients[2] = self:get_gradient(x1, y0) 63 | self.gradients[3] = self:get_gradient(x0, y1) 64 | self.gradients[4] = self:get_gradient(x1, y1) 65 | self.origins[1] = {x = x0 + 0, y = y0 + 0} 66 | self.origins[2] = {x = x0 + 1, y = y0 + 0} 67 | self.origins[3] = {x = x0 + 0, y = y0 + 1} 68 | self.origins[4] = {x = x0 + 1, y = y0 + 1} 69 | end 70 | 71 | function Noise2D:get(x, y) 72 | local p = {x = x, y = y} 73 | self:get_gradients_and_origins(x, y) 74 | local v1 = gradient(self.origins[1], self.gradients[1], p) 75 | local v2 = gradient(self.origins[2], self.gradients[2], p) 76 | local v3 = gradient(self.origins[3], self.gradients[3], p) 77 | local v4 = gradient(self.origins[4], self.gradients[4], p) 78 | local fx = smooth(x - self.origins[1].x) 79 | local vx1 = lerp(v1, v2, fx) 80 | local vx2 = lerp(v3, v4, fx) 81 | local fy = smooth(y - self.origins[1].y) 82 | return lerp(vx1, vx2, fy) 83 | end 84 | 85 | local symbols = {' ', '░', '▒', '▓', '█', '█'} 86 | local pixels = {} 87 | for i = 1, 256*256 do 88 | pixels[i] = "" 89 | end 90 | 91 | local n2d = Noise2D.new(os.clock()) 92 | for i = 1, 100 do 93 | for y = 1, 256 do 94 | y = y - 1 95 | for x = 1, 256 do 96 | x = x - 1 97 | local v = n2d:get(x * 0.1, y * 0.1) * 0.5 + 0.5 98 | pixels[(y*256+x)+1] = v 99 | end 100 | end 101 | end 102 | 103 | for y = 1, 256 do 104 | y = y - 1 105 | for x = 1, 256 do 106 | x = x - 1 107 | local idx = pixels[(y*256+x)+1] / 0.2 108 | io.write(symbols[math.floor(idx)+1]) 109 | end 110 | print("") 111 | end 112 | -------------------------------------------------------------------------------- /test.nim: -------------------------------------------------------------------------------- 1 | # Imports 2 | import math 3 | 4 | # Constants 5 | const RAND_MAX = 0x7fff 6 | 7 | # Types 8 | type 9 | TVec2 = object 10 | x, y: float 11 | 12 | TNoise2DContext = object 13 | rgradients: array[0..255, TVec2] 14 | permutations: array[0..255, int] 15 | gradients, origins: array[0..4, TVec2] 16 | 17 | 18 | # Procedures 19 | proc lerp(a, b, v: float): float = 20 | a * (1 - v) + b * v 21 | 22 | proc smooth(v: float): float = 23 | v * v * (3 - 2 * v) 24 | 25 | proc random_gradient: TVec2 = 26 | let v = random(2 * Pi) 27 | TVec2(x: cos(v), y: sin(v)) 28 | 29 | proc gradient(orig, grad, p: TVec2): float = 30 | let sp = TVec2(x: p.x - orig.x, y: p.y - orig.y) 31 | grad.x * sp.x + grad.y * sp.y 32 | 33 | proc get_gradient(ctx: TNoise2DContext, x, y: int): TVec2 = 34 | let idx = ctx.permutations[x and 255] + ctx.permutations[y and 255]; 35 | ctx.rgradients[idx and 255] 36 | 37 | proc get_gradients(ctx: var TNoise2DContext, x, y: float) = 38 | let 39 | x0f = floor(x) 40 | y0f = floor(y) 41 | x0 = x0f.int 42 | y0 = y0f.int 43 | x1 = x0 + 1 44 | y1 = y0 + 1 45 | 46 | ctx.gradients[0] = get_gradient(ctx, x0, y0) 47 | ctx.gradients[1] = get_gradient(ctx, x1, y0) 48 | ctx.gradients[2] = get_gradient(ctx, x0, y1) 49 | ctx.gradients[3] = get_gradient(ctx, x1, y1) 50 | 51 | ctx.origins[0] = TVec2(x: x0f + 0.0, y: y0f + 0.0) 52 | ctx.origins[1] = TVec2(x: x0f + 1.0, y: y0f + 0.0) 53 | ctx.origins[2] = TVec2(x: x0f + 0.0, y: y0f + 1.0) 54 | ctx.origins[3] = TVec2(x: x0f + 1.0, y: y0f + 1.0) 55 | 56 | proc noise2d_get(ctx: var TNoise2DContext, x, y: float): float = 57 | let p = TVec2(x: x, y: y) 58 | 59 | get_gradients(ctx, x, y) 60 | 61 | let 62 | v0 = gradient(ctx.origins[0], ctx.gradients[0], p) 63 | v1 = gradient(ctx.origins[1], ctx.gradients[1], p) 64 | v2 = gradient(ctx.origins[2], ctx.gradients[2], p) 65 | v3 = gradient(ctx.origins[3], ctx.gradients[3], p) 66 | 67 | fx = smooth(x - ctx.origins[0].x) 68 | vx0 = lerp(v0, v1, fx) 69 | vx1 = lerp(v2, v3, fx) 70 | fy = smooth(y - ctx.origins[0].y) 71 | 72 | lerp(vx0, vx1, fy) 73 | 74 | proc init_noise2d(ctx: var TNoise2DContext) = 75 | for i in 0..255: 76 | ctx.rgradients[i] = random_gradient() 77 | 78 | for i in 0..255: 79 | let j = random(RAND_MAX) mod (i + 1) 80 | ctx.permutations[i] = ctx.permutations[j] 81 | ctx.permutations[j] = i 82 | 83 | 84 | block main: 85 | randomize() 86 | 87 | const symbols = [ " ", "░", "▒", "▓", "█", "█" ] 88 | 89 | var pixels: array[256*256, float] 90 | 91 | var n2d = TNoise2DContext() 92 | init_noise2d(n2d) 93 | 94 | for i in 0..99: 95 | for y in 0..255: 96 | for x in 0..255: 97 | let v = noise2d_get(n2d, x.float * 0.1, y.float * 0.1) * 0.5 + 0.5 98 | pixels[y*256+x] = v 99 | 100 | for y in 0..255: 101 | for x in 0..255: 102 | stdout.write(symbols[int(pixels[y*256+x] / 0.2)]) 103 | 104 | stdout.write("\L") 105 | -------------------------------------------------------------------------------- /test.p6: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl6 2 | 3 | class Vec2 { 4 | has $.x; 5 | has $.y; 6 | } 7 | 8 | sub lerp($a, $b, $v) { 9 | $a * (1 - $v) + $b * $v 10 | } 11 | 12 | sub smooth($v) { 13 | $v * $v * (3 - 2 * $v) 14 | } 15 | 16 | sub random_gradient { 17 | my $v = rand * pi * 2; 18 | Vec2.new(x => cos($v), y => sin($v)) 19 | } 20 | 21 | sub gradient($orig, $grad, $p) { 22 | my $sp = Vec2.new(x => $p.x - $orig.x, y => $p.y - $orig.y); 23 | $grad.x * $sp.x + $grad.y * $sp.y 24 | } 25 | 26 | class Noise2DContext { 27 | has @.rgradients; 28 | has @.permutations; 29 | has @.gradients; 30 | has @.origins; 31 | 32 | method new($seed) { 33 | srand($seed); 34 | 35 | my @rgradients; 36 | for 0..255 { 37 | @rgradients.push(random_gradient); 38 | } 39 | 40 | my @permutations; 41 | for 0..255 -> $i { 42 | @permutations.push($i); 43 | } 44 | @permutations = @permutations.pick(*); 45 | 46 | self.bless(:@permutations, :@rgradients, 47 | gradients => [Any, Any, Any, Any], 48 | origins => [Any, Any, Any, Any]); 49 | } 50 | 51 | method get_gradient($x, $y) { 52 | my $idx = @!permutations[$x +& 255] + @!permutations[$y +& 255]; 53 | @!rgradients[$idx +& 255] 54 | } 55 | 56 | method get_gradients($x, $y) { 57 | my $x0f = floor $x; 58 | my $y0f = floor $y; 59 | my $x0 = $x0f.Int; 60 | my $y0 = $y0f.Int; 61 | my $x1 = $x0 + 1; 62 | my $y1 = $y0 + 1; 63 | 64 | @!gradients[0] = self.get_gradient($x0, $y0); 65 | @!gradients[1] = self.get_gradient($x1, $y0); 66 | @!gradients[2] = self.get_gradient($x0, $y1); 67 | @!gradients[3] = self.get_gradient($x1, $y1); 68 | 69 | @!origins[0] = Vec2.new(x => $x0f + 0.0, y => $y0f + 0.0); 70 | @!origins[1] = Vec2.new(x => $x0f + 1.0, y => $y0f + 0.0); 71 | @!origins[2] = Vec2.new(x => $x0f + 0.0, y => $y0f + 1.0); 72 | @!origins[3] = Vec2.new(x => $x0f + 1.0, y => $y0f + 1.0); 73 | } 74 | 75 | method get($x, $y) { 76 | my $p = Vec2.new(:$x, :$y); 77 | self.get_gradients($x, $y); 78 | my $v0 = gradient(@!origins[0], @!gradients[0], $p); 79 | my $v1 = gradient(@!origins[1], @!gradients[1], $p); 80 | my $v2 = gradient(@!origins[2], @!gradients[2], $p); 81 | my $v3 = gradient(@!origins[3], @!gradients[3], $p); 82 | 83 | my $fx = smooth($x - @!origins[0].x); 84 | my $vx0 = lerp($v0, $v1, $fx); 85 | my $vx1 = lerp($v2, $v3, $fx); 86 | my $fy = smooth($y - @!origins[0].y); 87 | lerp($vx0, $vx1, $fy) 88 | } 89 | } 90 | 91 | my @symbols = [' ', '░', '▒', '▓', '█', '█']; 92 | my @pixels = map {0}, (0..65535); 93 | my $n2d = Noise2DContext.new(0); 94 | for 0..255 -> $y { 95 | for 0..255 -> $x { 96 | my $v = $n2d.get($x * 0.1, $y * 0.1) * 0.5 + 0.5; 97 | @pixels[$y*256+$x] = @symbols[$v / 0.2] 98 | } 99 | } 100 | 101 | for 0..255 -> $y { 102 | for 0..255 -> $x { 103 | print @pixels[$y*256+$x] 104 | } 105 | print "\n" 106 | } 107 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | from random import Random 4 | import math 5 | import sys 6 | import time 7 | 8 | def lerp(a, b, v): 9 | return a * (1 - v) + b * v 10 | 11 | def smooth(v): 12 | return v * v * (3 - 2 * v) 13 | 14 | def random_gradient(r): 15 | v = r.random() * math.pi * 2.0 16 | return Vec2(math.cos(v), math.sin(v)) 17 | 18 | def gradient(orig, grad, p): 19 | sp = Vec2(p.x - orig.x, p.y - orig.y) 20 | return grad.x * sp.x + grad.y * sp.y 21 | 22 | class Vec2(object): 23 | __slots__ = ('x', 'y') 24 | 25 | def __init__(self, x, y): 26 | self.x = x 27 | self.y = y 28 | 29 | class Noise2DContext(object): 30 | __slots__ = ('rgradients', 'permutations', 'gradients', 'origins') 31 | 32 | def __init__(self, seed): 33 | self.rgradients = [] 34 | self.permutations = [] 35 | self.gradients = [None, None, None, None] 36 | self.origins = [None, None, None, None] 37 | 38 | r = Random(seed) 39 | for i in xrange(256): 40 | self.rgradients.append(random_gradient(r)) 41 | 42 | for i in xrange(256): 43 | self.permutations.append(i) 44 | r.shuffle(self.permutations) 45 | 46 | def get_gradient(self, x, y): 47 | idx = self.permutations[x & 255] + self.permutations[y & 255] 48 | return self.rgradients[idx & 255] 49 | 50 | def get_gradients(self, x, y): 51 | x0f = math.floor(x) 52 | y0f = math.floor(y) 53 | x0 = int(x0f) 54 | y0 = int(y0f) 55 | x1 = x0 + 1 56 | y1 = y0 + 1 57 | 58 | self.gradients[0] = self.get_gradient(x0, y0) 59 | self.gradients[1] = self.get_gradient(x1, y0) 60 | self.gradients[2] = self.get_gradient(x0, y1) 61 | self.gradients[3] = self.get_gradient(x1, y1) 62 | 63 | self.origins[0] = Vec2(x0f + 0.0, y0f + 0.0) 64 | self.origins[1] = Vec2(x0f + 1.0, y0f + 0.0) 65 | self.origins[2] = Vec2(x0f + 0.0, y0f + 1.0) 66 | self.origins[3] = Vec2(x0f + 1.0, y0f + 1.0) 67 | 68 | def get(self, x, y): 69 | p = Vec2(x, y) 70 | self.get_gradients(x, y) 71 | v0 = gradient(self.origins[0], self.gradients[0], p) 72 | v1 = gradient(self.origins[1], self.gradients[1], p) 73 | v2 = gradient(self.origins[2], self.gradients[2], p) 74 | v3 = gradient(self.origins[3], self.gradients[3], p) 75 | 76 | fx = smooth(x - self.origins[0].x) 77 | vx0 = lerp(v0, v1, fx) 78 | vx1 = lerp(v2, v3, fx) 79 | fy = smooth(y - self.origins[0].y) 80 | return lerp(vx0, vx1, fy) 81 | 82 | 83 | symbols = [' ', '░', '▒', '▓', '█', '█'] 84 | 85 | pixels = [['' for i in xrange(256)] for i in xrange(256)] 86 | 87 | n2d = Noise2DContext(time.time()) 88 | for i in xrange(100): 89 | for y in xrange(256): 90 | for x in xrange(256): 91 | v = n2d.get(x * 0.1, (y + (i * 128)) * 0.1) * 0.5 + 0.5 92 | s = symbols[int(v / 0.2)] 93 | pixels[y][x] = s 94 | 95 | for y in xrange(256): 96 | for x in xrange(256): 97 | sys.stdout.write(pixels[y][x]) 98 | sys.stdout.write('\n') 99 | -------------------------------------------------------------------------------- /test.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Rust Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | // Multi-language Perlin noise benchmark. 12 | // See https://github.com/nsf/pnoise for timings and alternative implementations. 13 | // ignore-lexer-test FIXME #15679 14 | 15 | #![feature(rand)] 16 | 17 | use std::f32::consts::PI; 18 | use std::__rand::{Rng, thread_rng}; 19 | 20 | #[derive(Copy, Clone)] 21 | struct Vec2 { 22 | x: f32, 23 | y: f32, 24 | } 25 | 26 | fn lerp(a: f32, b: f32, v: f32) -> f32 { a * (1.0 - v) + b * v } 27 | 28 | fn smooth(v: f32) -> f32 { v * v * (3.0 - 2.0 * v) } 29 | 30 | fn random_gradient(r: &mut R) -> Vec2 { 31 | let v = PI * 2.0 * r.gen::(); 32 | Vec2 { x: v.cos(), y: v.sin() } 33 | } 34 | 35 | fn gradient(orig: Vec2, grad: Vec2, p: Vec2) -> f32 { 36 | (p.x - orig.x) * grad.x + (p.y - orig.y) * grad.y 37 | } 38 | 39 | struct Noise2DContext { 40 | rgradients: [Vec2; 256], 41 | permutations: [i32; 256], 42 | } 43 | 44 | impl Noise2DContext { 45 | fn new() -> Noise2DContext { 46 | let mut rng = thread_rng(); 47 | 48 | let mut rgradients = [Vec2 { x: 0.0, y: 0.0 }; 256]; 49 | for x in &mut rgradients[..] { 50 | *x = random_gradient(&mut rng); 51 | } 52 | 53 | let mut permutations = [0; 256]; 54 | for (i, x) in permutations.iter_mut().enumerate() { 55 | *x = i as i32; 56 | } 57 | rng.shuffle(&mut permutations); 58 | 59 | Noise2DContext { rgradients: rgradients, permutations: permutations } 60 | } 61 | 62 | fn get_gradient(&self, x: i32, y: i32) -> Vec2 { 63 | let idx = self.permutations[(x & 255) as usize] + 64 | self.permutations[(y & 255) as usize]; 65 | self.rgradients[(idx & 255) as usize] 66 | } 67 | 68 | fn get_gradients(&self, x: f32, y: f32) -> ([Vec2; 4], [Vec2; 4]) { 69 | let x0f = x.floor(); 70 | let y0f = y.floor(); 71 | let x1f = x0f + 1.0; 72 | let y1f = y0f + 1.0; 73 | 74 | let x0 = x0f as i32; 75 | let y0 = y0f as i32; 76 | let x1 = x0 + 1; 77 | let y1 = y0 + 1; 78 | 79 | ([self.get_gradient(x0, y0), self.get_gradient(x1, y0), 80 | self.get_gradient(x0, y1), self.get_gradient(x1, y1)], 81 | [Vec2 { x: x0f, y: y0f }, Vec2 { x: x1f, y: y0f }, 82 | Vec2 { x: x0f, y: y1f }, Vec2 { x: x1f, y: y1f }]) 83 | } 84 | 85 | fn get(&self, x: f32, y: f32) -> f32 { 86 | let p = Vec2 {x: x, y: y}; 87 | let (gradients, origins) = self.get_gradients(x, y); 88 | 89 | let v0 = gradient(origins[0], gradients[0], p); 90 | let v1 = gradient(origins[1], gradients[1], p); 91 | let v2 = gradient(origins[2], gradients[2], p); 92 | let v3 = gradient(origins[3], gradients[3], p); 93 | 94 | let fx = smooth(x - origins[0].x); 95 | let vx0 = lerp(v0, v1, fx); 96 | let vx1 = lerp(v2, v3, fx); 97 | let fy = smooth(y - origins[0].y); 98 | 99 | lerp(vx0, vx1, fy) 100 | } 101 | } 102 | 103 | fn main() { 104 | let symbols = [' ', '░', '▒', '▓', '█', '█']; 105 | let mut pixels = [0f32; 256*256]; 106 | let n2d = Noise2DContext::new(); 107 | 108 | for _ in 0..100 { 109 | for y in 0..256 { 110 | for x in 0..256 { 111 | let v = n2d.get(x as f32 * 0.1, y as f32 * 0.1); 112 | pixels[y*256+x] = v * 0.5 + 0.5; 113 | } 114 | } 115 | } 116 | 117 | for y in 0..256 { 118 | for x in 0..256 { 119 | let idx = (pixels[y*256+x] / 0.2) as usize; 120 | print!("{}", symbols[idx]); 121 | } 122 | print!("\n"); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /test.tcl: -------------------------------------------------------------------------------- 1 | set PI 3.1415926535 2 | 3 | proc lerp {a b v} { 4 | return [expr {$a * (1 - $v) + $b * $v}] 5 | } 6 | 7 | proc smooth {v} { 8 | return [expr {$v * $v * (3 - 2 * $v)}] 9 | } 10 | 11 | proc random_gradient {} { 12 | global PI 13 | set v [expr {rand() * $PI * 2}] 14 | return [list [expr {cos($v)}] [expr {sin($v)}]] 15 | } 16 | 17 | proc gradient {orig grad p} { 18 | set sp [list [expr {[lindex $p 0] - [lindex $orig 0]}]\ 19 | [expr {[lindex $p 1] - [lindex $orig 1]}]] 20 | return [expr {[lindex $grad 0] * [lindex $sp 0] +\ 21 | [lindex $grad 1] * [lindex $sp 1]}] 22 | } 23 | 24 | proc n2d_new {seed} { 25 | expr {srand($seed)} 26 | 27 | set permutations [lrepeat 256 0] 28 | set rgradients [lrepeat 256 0] 29 | 30 | for {set i 0} {$i < 256} {incr i} { 31 | lset rgradients $i [random_gradient] 32 | } 33 | 34 | for {set i 0} {$i < 256} {incr i} { 35 | set j [expr {round(rand() * 65536) % ($i + 1)}] 36 | lset permutations $i [lindex $permutations $j] 37 | lset permutations $j $i 38 | } 39 | 40 | set n2d(permutations) $permutations 41 | set n2d(rgradients) $rgradients 42 | return [array get n2d] 43 | } 44 | 45 | proc n2d_get_gradient {ctx x y} { 46 | upvar $ctx n2d 47 | set idx [expr {[lindex $n2d(permutations) [expr {$x & 255}]] +\ 48 | [lindex $n2d(permutations) [expr {$y & 255}]]}] 49 | return [lindex $n2d(rgradients) [expr {$idx & 255}]] 50 | } 51 | 52 | proc n2d_get_gradients {ctx x y} { 53 | upvar $ctx n2d 54 | set x0f [expr {floor($x)}] 55 | set y0f [expr {floor($y)}] 56 | set x0 [expr {round($x0f)}] 57 | set y0 [expr {round($y0f)}] 58 | set x1 [expr {$x0 + 1}] 59 | set y1 [expr {$y0 + 1}] 60 | 61 | set n2d(gradients) [list [n2d_get_gradient n2d $x0 $y0]\ 62 | [n2d_get_gradient n2d $x1 $y0]\ 63 | [n2d_get_gradient n2d $x0 $y1]\ 64 | [n2d_get_gradient n2d $x1 $y1]] 65 | 66 | set n2d(origins) [list [list [expr {$x0f + 0}] [expr {$y0f + 0}]]\ 67 | [list [expr {$x0f + 1}] [expr {$y0f + 0}]]\ 68 | [list [expr {$x0f + 0}] [expr {$y0f + 1}]]\ 69 | [list [expr {$x0f + 1}] [expr {$y0f + 1}]]] 70 | } 71 | 72 | proc n2d_get {ctx x y} { 73 | upvar $ctx n2d 74 | set p [list $x $y] 75 | n2d_get_gradients n2d $x $y 76 | set v0 [gradient [lindex $n2d(origins) 0] [lindex $n2d(gradients) 0] $p] 77 | set v1 [gradient [lindex $n2d(origins) 1] [lindex $n2d(gradients) 1] $p] 78 | set v2 [gradient [lindex $n2d(origins) 2] [lindex $n2d(gradients) 2] $p] 79 | set v3 [gradient [lindex $n2d(origins) 3] [lindex $n2d(gradients) 3] $p] 80 | set fx [smooth [expr {$x - [lindex [lindex $n2d(origins) 0] 0]}]] 81 | set vx0 [lerp $v0 $v1 $fx] 82 | set vx1 [lerp $v2 $v3 $fx] 83 | set fy [smooth [expr {$y - [lindex [lindex $n2d(origins) 0] 1]}]] 84 | return [lerp $vx0 $vx1 $fy] 85 | } 86 | 87 | proc main {} { 88 | set symbols {{ } {░} {▒} {▓} {█} {█}} 89 | set pixels [lrepeat [expr 256*256] 0] 90 | array set n2d [n2d_new 0] 91 | 92 | for {set i 0} {$i < 100} {incr i} { 93 | for {set y 0} {$y < 256} {incr y} { 94 | for {set x 0} {$x < 256} {incr x} { 95 | set v [n2d_get n2d [expr {$x * 0.1}] [expr {$y * 0.1}]] 96 | set v [expr {$v * 0.5 + 0.5}] 97 | lset pixels [expr {$y*256+$x}] $v 98 | } 99 | } 100 | } 101 | 102 | for {set y 0} {$y < 256} {incr y} { 103 | for {set x 0} {$x < 256} {incr x} { 104 | set p [lindex $pixels [expr {$y*256+$x}]] 105 | puts -nonewline [lindex $symbols [expr {round($p / 0.2)}]] 106 | } 107 | puts {} 108 | } 109 | } 110 | 111 | main 112 | -------------------------------------------------------------------------------- /testffi.lua: -------------------------------------------------------------------------------- 1 | -- This version uses ffi structs capability offered by luajit and shows that 2 | -- using them leads to near C performance in some cases. Despite the name 3 | -- "ffi", there is no C code involved. But in a way it's interesting that JIT 4 | -- compilers may offer such functionality. 5 | 6 | local ffi = require("ffi") 7 | ffi.cdef [[ 8 | typedef struct { float x, y; } vec3; 9 | ]] 10 | 11 | local band = bit.band -- use luajit bitops always 12 | 13 | local function lerp(a, b, v) 14 | return a * (1 - v) + b * v 15 | end 16 | 17 | local function smooth(v) 18 | return v * v * (3 - 2 * v) 19 | end 20 | 21 | local function random_gradient() 22 | local v = math.random() * math.pi * 2.0 23 | return ffi.new("vec3", math.cos(v), math.sin(v)) 24 | end 25 | 26 | local function gradient(orig, grad, p) 27 | return grad.x * (p.x - orig.x) + grad.y * (p.y - orig.y) 28 | end 29 | 30 | local Noise2D = {} 31 | Noise2D.__index = Noise2D 32 | 33 | function Noise2D.new(seed) 34 | math.randomseed(seed) 35 | local rgradients = ffi.new("vec3[256]") 36 | for i = 0, 255 do 37 | rgradients[i] = random_gradient() 38 | end 39 | local permutations = ffi.new("int[256]") 40 | for i = 0, 255 do 41 | local j = math.random(i) 42 | permutations[i] = permutations[j] 43 | permutations[j] = i 44 | end 45 | return setmetatable({ 46 | rgradients = rgradients, 47 | permutations = permutations, 48 | gradients = ffi.new("vec3[4]"), 49 | origins = ffi.new("vec3[4]"), 50 | }, Noise2D) 51 | end 52 | 53 | function Noise2D:get_gradient(x, y) 54 | x = band(x, 255) 55 | y = band(y, 255) 56 | local idx = self.permutations[x] + self.permutations[y] 57 | return self.rgradients[band(idx, 255)] 58 | end 59 | 60 | function Noise2D:get_gradients_and_origins(x, y) 61 | local x0 = math.floor(x) 62 | local y0 = math.floor(y) 63 | local x1 = x0 + 1 64 | local y1 = y0 + 1 65 | 66 | self.gradients[0] = self:get_gradient(x0, y0) 67 | self.gradients[1] = self:get_gradient(x1, y0) 68 | self.gradients[2] = self:get_gradient(x0, y1) 69 | self.gradients[3] = self:get_gradient(x1, y1) 70 | self.origins[0] = ffi.new("vec3", x0 + 0, y0 + 0) 71 | self.origins[1] = ffi.new("vec3", x0 + 1, y0 + 0) 72 | self.origins[2] = ffi.new("vec3", x0 + 0, y0 + 1) 73 | self.origins[3] = ffi.new("vec3", x0 + 1, y0 + 1) 74 | end 75 | 76 | function Noise2D:get(x, y) 77 | local p = ffi.new("vec3", x, y) 78 | self:get_gradients_and_origins(x, y) 79 | local v1 = gradient(self.origins[0], self.gradients[0], p) 80 | local v2 = gradient(self.origins[1], self.gradients[1], p) 81 | local v3 = gradient(self.origins[2], self.gradients[2], p) 82 | local v4 = gradient(self.origins[3], self.gradients[3], p) 83 | local fx = smooth(x - self.origins[0].x) 84 | local vx1 = lerp(v1, v2, fx) 85 | local vx2 = lerp(v3, v4, fx) 86 | local fy = smooth(y - self.origins[0].y) 87 | return lerp(vx1, vx2, fy) 88 | end 89 | 90 | local symbols = {' ', '░', '▒', '▓', '█', '█'} 91 | local pixels = ffi.new("float[?]", 256*256) 92 | 93 | local n2d = Noise2D.new(os.clock()) 94 | for i = 1, 100 do 95 | for y = 0, 255 do 96 | for x = 0, 255 do 97 | local v = n2d:get(x * 0.1, y * 0.1) * 0.5 + 0.5 98 | pixels[y*256+x] = v 99 | end 100 | end 101 | end 102 | 103 | for y = 0, 255 do 104 | for x = 0, 255 do 105 | local idx = pixels[y*256+x] / 0.2 106 | io.write(symbols[math.floor(idx)+1]) 107 | end 108 | print("") 109 | end 110 | --------------------------------------------------------------------------------