Last active
August 13, 2016 14:55
-
-
Save sgeos/e61390c41878a94ddc0a7a7406535780 to your computer and use it in GitHub Desktop.
Simplex noise reference implementation in Elixir.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Reference: | |
# http://www.csee.umbc.edu/~olano/s2002c36/ch02.pdf | |
# https://www.google.com/patents/US6867776 | |
# A complete implementation of a function returning a value that | |
# conforms to the new method is given below. | |
# Translated from Java class definition to Elixir module. | |
defmodule SimplexNoiseReference do | |
use Bitwise | |
# kernel summation radius squared | |
# 0.5 for no discontinuities | |
# 0.6 in original code | |
@r2 0.6 | |
# bit pattern table | |
@t { 0x15, 0x38, 0x32, 0x2c, 0x0d, 0x13, 0x07, 0x2a } | |
# Noise Function Alias | |
def noise(x, y, z), do: noise {x, y, z} | |
def noise(xyz) when is_tuple(xyz) do | |
size = tuple_size(xyz) | |
max = size - 1 | |
# 1.0 / 3.0 is the 3D skew factor | |
s = xyz | |
|> Tuple.to_list | |
|> Enum.reduce(0, &(&1+&2)) | |
s = s / (size * 1.0) | |
ijk = 0..max | |
|> Enum.map(&((elem(xyz,&1) + s) |> Float.floor |> trunc)) | |
|> List.to_tuple | |
# 1.0 / 6.0 is the 3D unskew factor | |
s = ijk | |
|> Tuple.to_list | |
|> Enum.reduce(0, &(&1+&2)) | |
s = s / (size * 2.0) | |
uvw = 0..max | |
|> Enum.map(&(elem(xyz,&1)-elem(ijk,&1)+s)) | |
|> List.to_tuple | |
hi = hi(uvw) | |
lo = lo(uvw) | |
{result, _a} = [hi, 3-hi-lo, lo, 0] | |
|> Enum.reduce({0.0, Tuple.duplicate(0,size)}, | |
fn index, {total, a} -> | |
{result, a} = k(ijk, uvw, a, index) | |
{total+result, a} | |
end) | |
result | |
end | |
def k(ijk, uvw, a, index) do | |
size = tuple_size(ijk) | |
max = size - 1 | |
s = a | |
|> Tuple.to_list | |
|> Enum.reduce(0, &(&1+&2)) | |
s = s / (size * 2.0) | |
xyz = 0..max | |
|> Enum.map(&(elem(uvw,&1)-elem(a,&1)+s)) | |
|> List.to_tuple | |
{x,y,z} = xyz | |
t = xyz | |
|> Tuple.to_list | |
|> Enum.reduce(@r2, &(&2-&1*&1)) | |
result = if t < 0 do | |
0 | |
else | |
h = 0..max | |
|> Enum.map(&(elem(ijk,&1)+elem(a,&1))) | |
|> List.to_tuple | |
|> shuffle | |
[b1, b2, b3, b4, b5] = 1..5 | |
|> Enum.map(&(if 1<&1 do b(h,&1) else h &&& 0x3 end)) | |
[p, q, r] = [b3==b5, b4==b5, (b4^^^b3)!=b5] | |
|> Enum.map(&(if &1 do -1.0 else 1.0 end)) | |
pqr = case b1 do | |
0 -> {p*z, q*x, r*y} | |
1 -> {p*x, q*y, r*z} | |
_ -> {p*y, q*z, r*x} # 2==b1 | |
end | |
{p, q, r} = pqr | |
c = case {b1, b2} do | |
{0, _} -> q+r | |
{_, 0} -> q | |
_ -> r # b2==1 | |
end | |
8*t*t*t*t * (p+c) | |
end | |
a = a |> put_elem(index, elem(a,index) + 1) | |
{result, a} | |
end | |
def hi({u, v, w}) when w <= u and v <= u, do: 0 | |
def hi({u, v, w}) when u < w and v < w, do: 2 | |
def hi(_uvw), do: 1 | |
# not the same as hi({w,v,u}) | |
def lo({u, v, w}) when u < w and u < v, do: 0 | |
def lo({u, v, w}) when w <= u and w <= v, do: 2 | |
def lo(_uvw), do: 1 | |
def shuffle(t) when is_tuple(t) do | |
0..7 | |
|> Enum.reduce(0, fn bit, acc -> acc + b(t, bit) end) | |
end | |
# probably does not scale beyond 3 dimensions | |
def b(t, b_in) when is_tuple(t) do | |
max = tuple_size(t) - 1 | |
index = 0..max | |
|> Enum.map(&({t |> elem(&1), max - &1})) | |
|> Enum.reduce(0, fn {value, shift}, acc -> (b(value, b_in) <<< shift) + acc end) | |
@t | |
|> elem(index) | |
end | |
def b(n_in, b_in), do: n_in >>> b_in &&& 0x1 | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment