Created
January 15, 2016 18:17
-
-
Save mgold/5a9165d4559de3427e08 to your computer and use it in GitHub Desktop.
Perlin noise in Elm (requires mgold/elm-random-pcg)
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
import Graphics.Collage as C | |
import Color exposing (grayscale) | |
import Random.PCG as PCG | |
import Perlin | |
grid n = | |
let side = [0..n-1] | |
in List.concatMap (\x -> List.map (\y -> (x,y)) side) side | |
seed = PCG.initialSeed2 628 31853 | |
noise = Perlin.octaves 4 seed | |
size = 150 | |
pixel (i,j) = | |
let | |
x = toFloat i / 4 | |
y = toFloat j / 4 | |
val = noise (x,y) |> clamp 0 1 | |
in | |
C.square 1 |> C.filled (grayscale val) |> C.move (toFloat i, toFloat j) | |
main = | |
C.collage size size | |
[C.group (List.map pixel (grid size)) |> C.move (-size/2, -size/2)] |
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
module Perlin (noise, octaves) where | |
{-| | |
Technically, this is an implementation of [Improved Noise](http://mrl.nyu.edu/~perlin/paper445.pdf), a refinement on the | |
original Perlin noise, but not Simplex Noise. | |
-} | |
import Random.PCG as PCG | |
type alias Gradient = (Float, Float) | |
spiral : (Int, Int) -> Int | |
spiral (x,y) = | |
let d = x+y | |
in d*(d+1)//2 + x | |
lookup : PCG.Seed -> (Int, Int) -> Gradient | |
lookup seed pos = | |
let seed1 = PCG.fastForward (spiral pos) seed | |
in PCG.generate generateGradient seed1 |> fst | |
generateGradient : PCG.Generator Gradient | |
generateGradient = | |
PCG.map | |
(\theta -> fromPolar (1, theta)) | |
(PCG.float 0 (2*pi)) | |
dot : (Float, Float) -> (Float, Float) -> Float | |
dot (a, b) (c, d) = a*c + b*d | |
type alias Noise = (Float, Float) -> Float | |
noise : PCG.Seed -> Noise | |
noise seed (x,y) = | |
let | |
xi = floor x -- integer part of x | |
xf = x - toFloat xi -- fractional/floating part of x | |
yi = floor y | |
yf = y - toFloat yi | |
-- lookup four points on square around gradient | |
gxx = lookup seed |> curry | |
gbl = gxx xi yi -- gradient at bottom left | |
gbr = gxx (xi+1) yi -- gradient at bottom right, and so on | |
gtl = gxx xi (yi+1) | |
gtr = gxx (xi+1) (yi+1) | |
-- get dot product of gradient points with distance to them (except maybe negative??) | |
dbl = dot gbl (xf , yf) -- dot bottom left | |
dbr = dot gbr (xf-1, yf) | |
dtl = dot gtl (xf , yf-1) | |
dtr = dot gtr (xf-1, yf-1) | |
-- apply nonlinear interpolation | |
fadeX = fade xf | |
fadeY = fade yf | |
nx1 = dbl*(1-fadeX) + dbr*fadeX | |
nx2 = dtl*(1-fadeX) + dtr*fadeX | |
in | |
nx1*(1-fadeY) + nx2*fadeY + 0.4 | |
-- nonlinear interpolation: 6t^5 - 15t^4 + 10t^3 | |
fade : Float -> Float | |
fade t = t * t * t * (t * (t * 6 - 15) + 10) | |
octaves : Int -> PCG.Seed -> Noise | |
octaves n seed = | |
let | |
seeds = generateNSeeds n seed | |
multipliers = List.map (\m -> 2^m) [0..n-1] | |
octave m seed (x,y) = | |
noise seed (x/m, y/m) * m | |
normalizer = List.sum multipliers | |
noiseFuncs = List.map2 octave multipliers seeds | |
in | |
\pos -> List.sum (List.map (\f -> f pos) noiseFuncs) / normalizer | |
generateNSeeds : Int -> PCG.Seed -> List PCG.Seed | |
generateNSeeds n seed = | |
let helper seeds = | |
if List.length seeds >= n then | |
List.take n seeds |> Debug.log "seeds" | |
else | |
List.concatMap (\seed -> let (x,y) = PCG.split seed in [x,y]) seeds |> helper | |
in | |
helper [seed] | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is very cool! Is it available as an Elm package?