Skip to content

Instantly share code, notes, and snippets.

@nsf
Last active March 4, 2019 14:38
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save nsf/1170424 to your computer and use it in GitHub Desktop.
Save nsf/1170424 to your computer and use it in GitHub Desktop.
Perlin noise benchmark
#!/bin/bash
rm -rf *.o *.[568] test_*
#!/bin/bash
gcc -std=c99 -march=native -msse3 -mfpmath=sse -O3 -o test_c test.c -lm
dmd -oftest_d -O -noboundscheck -inline -release test.d
go build -o test_go test.go
rustc --opt-level 3 -o test_rs test.rs
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#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);
Noise2DContext n2d;
init_noise2d(&n2d);
for (int i = 0; i < 100; 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;
}
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) {
var sp = new Vec2 { x = p.x - orig.x, y = 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.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] = new Vec2 { x = x0f + 0, y = y0f + 0 };
origins[1] = new Vec2 { x = x0f + 1, y = y0f + 0 };
origins[2] = new Vec2 { x = x0f + 0, y = y0f + 1 };
origins[3] = new Vec2 { x = x0f + 1, 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];
for (int i = 0; i < 100; 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();
}
}
}
import std.stdio;
import std.random;
import std.math;
struct Vec2 {
float x, y;
}
float lerp(float a, float b, float v)
{
return a * (1 - v) + b * v;
}
float smooth(float v)
{
return v * v * (3 - 2 * v);
}
Vec2 random_gradient(Random)(ref Random r)
{
auto v = uniform(0.0f, cast(float)PI * 2.0f, r);
return Vec2(cos(v), sin(v));
}
float gradient(Vec2 orig, Vec2 grad, Vec2 p)
{
auto sp = Vec2(p.x - orig.x, p.y - orig.y);
return grad.x * sp.x + grad.y * sp.y;
}
class Noise2DContext {
Vec2[256] rgradients;
uint[256] permutations;
Vec2[4] gradients;
Vec2[4] origins;
private:
Vec2 get_gradient(int x, int y)
{
auto idx = permutations[x & 255] + permutations[y & 255];
return rgradients[idx & 255];
}
void get_gradients(float x, float y)
{
float x0f = floor(x);
float y0f = floor(y);
int x0 = cast(int)x0f;
int y0 = cast(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] = Vec2(x0f + 0.0f, y0f + 0.0f);
origins[1] = Vec2(x0f + 1.0f, y0f + 0.0f);
origins[2] = Vec2(x0f + 0.0f, y0f + 1.0f);
origins[3] = Vec2(x0f + 1.0f, y0f + 1.0f);
}
public:
this(uint seed)
{
auto rnd = Random(seed);
foreach (ref elem; rgradients)
elem = random_gradient(rnd);
foreach (i; 0 .. permutations.length) {
uint j = uniform(0, i+1, rnd);
permutations[i] = permutations[j];
permutations[j] = i;
}
}
float get(float x, float y)
{
auto p = Vec2(x, y);
get_gradients(x, y);
auto v0 = gradient(origins[0], gradients[0], p);
auto v1 = gradient(origins[1], gradients[1], p);
auto v2 = gradient(origins[2], gradients[2], p);
auto v3 = gradient(origins[3], gradients[3], p);
auto fx = smooth(x - origins[0].x);
auto vx0 = lerp(v0, v1, fx);
auto vx1 = lerp(v2, v3, fx);
auto fy = smooth(y - origins[0].y);
return lerp(vx0, vx1, fy);
}
}
void main()
{
immutable symbols = [" ", "░", "▒", "▓", "█", "█"];
auto pixels = new float[256*256];
auto n2d = new Noise2DContext(0);
foreach (i; 0..100) {
foreach (y; 0..256) {
foreach (x; 0..256) {
auto v = n2d.get(x * 0.1f, y * 0.1f) *
0.5f + 0.5f;
pixels[y*256+x] = v;
}
}
}
foreach (y; 0..256) {
foreach (x; 0..256) {
write(symbols[cast(int)(pixels[y*256+x] / 0.2f)]);
}
writeln();
}
}
package main
import (
"fmt"
"math/rand"
"math"
"bufio"
"os"
)
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 []Vec2
permutations []int
gradients [4]Vec2
origins [4]Vec2
}
func NewNoise2DContext(seed int) *Noise2DContext {
rnd := rand.New(rand.NewSource(int64(seed)))
n2d := new(Noise2DContext)
n2d.rgradients = make([]Vec2, 256)
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)
for i := 0; i < 100; 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()
}
#!/usr/bin/env lua
local band = nil
if jit ~= nil then -- luajit has a different bitops module name
band = bit.band
else
band = bit32.band
end
local function lerp(a, b, v)
return a * (1 - v) + b * v
end
local function smooth(v)
return v * v * (3 - 2 * v)
end
local function random_gradient()
local v = math.random() * math.pi * 2.0
return {x = math.cos(v), y = math.sin(v)}
end
local function gradient(orig, grad, p)
return grad.x * (p.x - orig.x) + grad.y * (p.y - orig.y)
end
local Noise2D = {}
Noise2D.__index = Noise2D
function Noise2D.new(seed)
math.randomseed(seed)
local rgradients = {}
for i = 1, 256 do
rgradients[i] = random_gradient()
end
local permutations = {}
for i = 1, 256 do
local j = math.random(i)
permutations[i] = permutations[j]
permutations[j] = i
end
return setmetatable({
rgradients = rgradients,
permutations = permutations,
gradients = {},
origins = {},
}, Noise2D)
end
function Noise2D:get_gradient(x, y)
x = band(x, 255)+1
y = band(y, 255)+1
local idx = self.permutations[x] + self.permutations[y]
return self.rgradients[band(idx, 255)+1]
end
function Noise2D:get_gradients_and_origins(x, y)
local x0 = math.floor(x)
local y0 = math.floor(y)
local x1 = x0 + 1
local y1 = y0 + 1
self.gradients[1] = self:get_gradient(x0, y0)
self.gradients[2] = self:get_gradient(x1, y0)
self.gradients[3] = self:get_gradient(x0, y1)
self.gradients[4] = self:get_gradient(x1, y1)
self.origins[1] = {x = x0 + 0, y = y0 + 0}
self.origins[2] = {x = x0 + 1, y = y0 + 0}
self.origins[3] = {x = x0 + 0, y = y0 + 1}
self.origins[4] = {x = x0 + 1, y = y0 + 1}
end
function Noise2D:get(x, y)
local p = {x = x, y = y}
self:get_gradients_and_origins(x, y)
local v1 = gradient(self.origins[1], self.gradients[1], p)
local v2 = gradient(self.origins[2], self.gradients[2], p)
local v3 = gradient(self.origins[3], self.gradients[3], p)
local v4 = gradient(self.origins[4], self.gradients[4], p)
local fx = smooth(x - self.origins[1].x)
local vx1 = lerp(v1, v2, fx)
local vx2 = lerp(v3, v4, fx)
local fy = smooth(y - self.origins[1].y)
return lerp(vx1, vx2, fy)
end
local symbols = {' ', '░', '▒', '▓', '█', '█'}
local pixels = {}
for i = 1, 256*256 do
pixels[i] = ""
end
local n2d = Noise2D.new(os.clock())
for i = 1, 100 do
for y = 1, 256 do
y = y - 1
for x = 1, 256 do
x = x - 1
local v = n2d:get(x * 0.1, y * 0.1) * 0.5 + 0.5
pixels[(y*256+x)+1] = v
end
end
end
for y = 1, 256 do
y = y - 1
for x = 1, 256 do
x = x - 1
local idx = pixels[(y*256+x)+1] / 0.2
io.write(symbols[math.floor(idx)+1])
end
print("")
end
#!/usr/bin/pypy
# encoding: utf-8
from random import Random
import math
import sys
import time
def lerp(a, b, v):
return a * (1 - v) + b * v
def smooth(v):
return v * v * (3 - 2 * v)
def random_gradient(r):
v = r.random() * math.pi * 2.0
return Vec2(math.cos(v), math.sin(v))
def gradient(orig, grad, p):
sp = Vec2(p.x - orig.x, p.y - orig.y)
return grad.x * sp.x + grad.y * sp.y
class Vec2(object):
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
class Noise2DContext(object):
__slots__ = ('rgradients', 'permutations', 'gradients', 'origins')
def __init__(self, seed):
self.rgradients = []
self.permutations = []
self.gradients = [None, None, None, None]
self.origins = [None, None, None, None]
r = Random(seed)
for i in xrange(256):
self.rgradients.append(random_gradient(r))
for i in xrange(256):
self.permutations.append(i)
r.shuffle(self.permutations)
def get_gradient(self, x, y):
idx = self.permutations[x & 255] + self.permutations[y & 255]
return self.rgradients[idx & 255]
def get_gradients(self, x, y):
x0f = math.floor(x)
y0f = math.floor(y)
x0 = int(x0f)
y0 = int(y0f)
x1 = x0 + 1
y1 = y0 + 1
self.gradients[0] = self.get_gradient(x0, y0)
self.gradients[1] = self.get_gradient(x1, y0)
self.gradients[2] = self.get_gradient(x0, y1)
self.gradients[3] = self.get_gradient(x1, y1)
self.origins[0] = Vec2(x0f + 0.0, y0f + 0.0)
self.origins[1] = Vec2(x0f + 1.0, y0f + 0.0)
self.origins[2] = Vec2(x0f + 0.0, y0f + 1.0)
self.origins[3] = Vec2(x0f + 1.0, y0f + 1.0)
def get(self, x, y):
p = Vec2(x, y)
self.get_gradients(x, y)
v0 = gradient(self.origins[0], self.gradients[0], p)
v1 = gradient(self.origins[1], self.gradients[1], p)
v2 = gradient(self.origins[2], self.gradients[2], p)
v3 = gradient(self.origins[3], self.gradients[3], p)
fx = smooth(x - self.origins[0].x)
vx0 = lerp(v0, v1, fx)
vx1 = lerp(v2, v3, fx)
fy = smooth(y - self.origins[0].y)
return lerp(vx0, vx1, fy)
symbols = [' ', '░', '▒', '▓', '█', '█']
pixels = [['' for i in xrange(256)] for i in xrange(256)]
n2d = Noise2DContext(time.time())
for i in xrange(100):
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')
struct Vec2 {
x: f32,
y: f32,
}
fn lerp(a: f32, b: f32, v: f32) -> f32 {
a * (1f32 - v) + b * v
}
fn smooth(v: f32) -> f32 {
v * v * (3f32 - 2f32 * v)
}
fn random_gradient(r: rand::Rng) -> Vec2 {
let v = r.gen_float() * float::consts::pi * 2.0;
Vec2{
x: float::cos(v) as f32,
y: float::sin(v) as f32,
}
}
fn gradient(orig: Vec2, grad: Vec2, p: Vec2) -> f32 {
let sp = Vec2{x: p.x - orig.x, y: p.y - orig.y};
grad.x * sp.x + grad.y + sp.y
}
struct Noise2DContext {
rgradients: ~[Vec2],
permutations: ~[int],
}
fn Noise2DContext() -> ~Noise2DContext {
let r = rand::Rng();
let rgradients = do vec::from_fn(256) |_i| { random_gradient(r) };
let mut permutations = do vec::from_fn(256) |i| { i as int };
r.shuffle_mut(permutations);
~Noise2DContext{
rgradients: move rgradients,
permutations: move permutations,
}
}
impl Noise2DContext {
fn get_gradient(x: int, y: int) -> Vec2 {
let idx = self.permutations[x & 255] + self.permutations[y & 255];
self.rgradients[idx & 255]
}
fn get_gradients(gradients: &[mut Vec2 * 4], origins: &[mut Vec2 * 4], x: f32, y: f32) {
let x0f = float::floor(x as libc::c_double) as f32;
let y0f = float::floor(y as libc::c_double) as f32;
let x0 = x0f as int;
let y0 = y0f as int;
let x1 = x0 + 1;
let y1 = y0 + 1;
gradients[0] = self.get_gradient(x0, y0);
gradients[1] = self.get_gradient(x1, y0);
gradients[2] = self.get_gradient(x0, y1);
gradients[3] = self.get_gradient(x1, y1);
origins[0] = Vec2{x: x0f + 0f32, y: y0f + 0f32};
origins[1] = Vec2{x: x0f + 1f32, y: y0f + 0f32};
origins[2] = Vec2{x: x0f + 0f32, y: y0f + 1f32};
origins[3] = Vec2{x: x0f + 1f32, y: y0f + 1f32};
}
fn get(x: f32, y: f32) -> f32 {
let p = Vec2{x: x, y: y};
let gradients: [mut Vec2 * 4] = [mut
Vec2{x:0f32, y:0f32},
Vec2{x:0f32, y:0f32},
Vec2{x:0f32, y:0f32},
Vec2{x:0f32, y:0f32},
];
let origins: [mut Vec2 * 4] = [mut
Vec2{x:0f32, y:0f32},
Vec2{x:0f32, y:0f32},
Vec2{x:0f32, y:0f32},
Vec2{x:0f32, y:0f32},
];
self.get_gradients(&gradients, &origins, 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 pixels = vec::to_mut(vec::from_elem(256*256, 0f32));
let n2d = Noise2DContext();
for int::range(0, 100) |_i| {
for int::range(0, 256) |y| {
for int::range(0, 256) |x| {
let v = n2d.get(
x as f32 * 0.1f32,
y as f32 * 0.1f32
) * 0.5f32 + 0.5f32;
pixels[y*256+x] = v;
};
};
};
for int::range(0, 256) |y| {
for int::range(0, 256) |x| {
io::print(symbols[pixels[y*256+x] / 0.2f32 as int]);
}
io::println("");
}
}
#!/usr/bin/tclsh
set PI 3.1415926535
proc lerp {a b v} {
return [expr {$a * (1 - $v) + $b * $v}]
}
proc smooth {v} {
return [expr {$v * $v * (3 - 2 * $v)}]
}
proc random_gradient {} {
global PI
set v [expr {rand() * $PI * 2}]
return [list [expr {cos($v)}] [expr {sin($v)}]]
}
proc gradient {orig grad p} {
set sp [list [expr {[lindex $p 0] - [lindex $orig 0]}]\
[expr {[lindex $p 1] - [lindex $orig 1]}]]
return [expr {[lindex $grad 0] * [lindex $sp 0] +\
[lindex $grad 1] * [lindex $sp 1]}]
}
proc n2d_new {seed} {
expr {srand($seed)}
set permutations [lrepeat 256 0]
set rgradients [lrepeat 256 0]
for {set i 0} {$i < 256} {incr i} {
lset rgradients $i [random_gradient]
}
for {set i 0} {$i < 256} {incr i} {
set j [expr {round(rand() * 65536) % ($i + 1)}]
lset permutations $i [lindex $permutations $j]
lset permutations $j $i
}
set n2d(permutations) $permutations
set n2d(rgradients) $rgradients
return [array get n2d]
}
proc n2d_get_gradient {ctx x y} {
upvar $ctx n2d
set idx [expr {[lindex $n2d(permutations) [expr {$x & 255}]] +\
[lindex $n2d(permutations) [expr {$y & 255}]]}]
return [lindex $n2d(rgradients) [expr {$idx & 255}]]
}
proc n2d_get_gradients {ctx x y} {
upvar $ctx n2d
set x0f [expr {floor($x)}]
set y0f [expr {floor($y)}]
set x0 [expr {round($x0f)}]
set y0 [expr {round($y0f)}]
set x1 [expr {$x0 + 1}]
set y1 [expr {$y0 + 1}]
set n2d(gradients) [list [n2d_get_gradient n2d $x0 $y0]\
[n2d_get_gradient n2d $x1 $y0]\
[n2d_get_gradient n2d $x0 $y1]\
[n2d_get_gradient n2d $x1 $y1]]
set n2d(origins) [list [list [expr {$x0f + 0}] [expr {$y0f + 0}]]\
[list [expr {$x0f + 1}] [expr {$y0f + 0}]]\
[list [expr {$x0f + 0}] [expr {$y0f + 1}]]\
[list [expr {$x0f + 1}] [expr {$y0f + 1}]]]
}
proc n2d_get {ctx x y} {
upvar $ctx n2d
set p [list $x $y]
n2d_get_gradients n2d $x $y
set v0 [gradient [lindex $n2d(origins) 0] [lindex $n2d(gradients) 0] $p]
set v1 [gradient [lindex $n2d(origins) 1] [lindex $n2d(gradients) 1] $p]
set v2 [gradient [lindex $n2d(origins) 2] [lindex $n2d(gradients) 2] $p]
set v3 [gradient [lindex $n2d(origins) 3] [lindex $n2d(gradients) 3] $p]
set fx [smooth [expr {$x - [lindex [lindex $n2d(origins) 0] 0]}]]
set vx0 [lerp $v0 $v1 $fx]
set vx1 [lerp $v2 $v3 $fx]
set fy [smooth [expr {$y - [lindex [lindex $n2d(origins) 0] 1]}]]
return [lerp $vx0 $vx1 $fy]
}
proc main {} {
set symbols {{ } {░} {▒} {▓} {█} {█}}
set pixels [lrepeat [expr 256*256] 0]
array set n2d [n2d_new 0]
for {set i 0} {$i < 100} {incr i} {
for {set y 0} {$y < 256} {incr y} {
for {set x 0} {$x < 256} {incr x} {
set v [n2d_get n2d [expr {$x * 0.1}] [expr {$y * 0.1}]]
set v [expr {$v * 0.5 + 0.5}]
lset pixels [expr {$y*256+$x}] $v
}
}
}
for {set y 0} {$y < 256} {incr y} {
for {set x 0} {$x < 256} {incr x} {
set p [lindex $pixels [expr {$y*256+$x}]]
puts -nonewline [lindex $symbols [expr {round($p / 0.2)}]]
}
puts {}
}
}
main
#!/usr/bin/env luajit
local ffi = require("ffi")
ffi.cdef [[
typedef struct { float x, y; } vec3;
]]
local band = bit.band -- use luajit bitops always
local function lerp(a, b, v)
return a * (1 - v) + b * v
end
local function smooth(v)
return v * v * (3 - 2 * v)
end
local function random_gradient()
local v = math.random() * math.pi * 2.0
return ffi.new("vec3", math.cos(v), math.sin(v))
end
local function gradient(orig, grad, p)
return grad.x * (p.x - orig.x) + grad.y * (p.y - orig.y)
end
local Noise2D = {}
Noise2D.__index = Noise2D
function Noise2D.new(seed)
math.randomseed(seed)
local rgradients = ffi.new("vec3[256]")
for i = 0, 255 do
rgradients[i] = random_gradient()
end
local permutations = ffi.new("int[256]")
for i = 0, 255 do
local j = math.random(i)
permutations[i] = permutations[j]
permutations[j] = i
end
return setmetatable({
rgradients = rgradients,
permutations = permutations,
gradients = ffi.new("vec3[4]"),
origins = ffi.new("vec3[4]"),
}, Noise2D)
end
function Noise2D:get_gradient(x, y)
x = band(x, 255)
y = band(y, 255)
local idx = self.permutations[x] + self.permutations[y]
return self.rgradients[band(idx, 255)]
end
function Noise2D:get_gradients_and_origins(x, y)
local x0 = math.floor(x)
local y0 = math.floor(y)
local x1 = x0 + 1
local y1 = y0 + 1
self.gradients[0] = self:get_gradient(x0, y0)
self.gradients[1] = self:get_gradient(x1, y0)
self.gradients[2] = self:get_gradient(x0, y1)
self.gradients[3] = self:get_gradient(x1, y1)
self.origins[0] = ffi.new("vec3", x0 + 0, y0 + 0)
self.origins[1] = ffi.new("vec3", x0 + 1, y0 + 0)
self.origins[2] = ffi.new("vec3", x0 + 0, y0 + 1)
self.origins[3] = ffi.new("vec3", x0 + 1, y0 + 1)
end
function Noise2D:get(x, y)
local p = ffi.new("vec3", x, y)
self:get_gradients_and_origins(x, y)
local v1 = gradient(self.origins[0], self.gradients[0], p)
local v2 = gradient(self.origins[1], self.gradients[1], p)
local v3 = gradient(self.origins[2], self.gradients[2], p)
local v4 = gradient(self.origins[3], self.gradients[3], p)
local fx = smooth(x - self.origins[0].x)
local vx1 = lerp(v1, v2, fx)
local vx2 = lerp(v3, v4, fx)
local fy = smooth(y - self.origins[0].y)
return lerp(vx1, vx2, fy)
end
local symbols = {' ', '░', '▒', '▓', '█', '█'}
local pixels = ffi.new("float[?]", 256*256)
local n2d = Noise2D.new(os.clock())
for i = 1, 100 do
for y = 0, 255 do
for x = 0, 255 do
local v = n2d:get(x * 0.1, y * 0.1) * 0.5 + 0.5
pixels[y*256+x] = v
end
end
end
for y = 0, 255 do
for x = 0, 255 do
local idx = pixels[y*256+x] / 0.2
io.write(symbols[math.floor(idx)+1])
end
print("")
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment