Current section

Files

Jump to
loise src loise-simplex.lfe
Raw

src/loise-simplex.lfe

(defmodule loise-simplex
(export
(options 0)
(1d 2)
(2d 3)
(3d 4)
(point 3) (point 4)
(value-range 0)
(which 3)))
(include-file "include/options.lfe")
(defun value-range () #(-1 1))
(defun options ()
(default-options))
(defun 1d (a opts)
(3d a 0.0 0.0 opts))
(defun 2d (a b opts)
(3d a b 0.0 opts))
(defun 3d (a b c opts)
"Simplex noise is a method for constructing an n-dimensional noise function
comparable to Perlin noise ('classic' noise) but with a lower computational
overhead, especially in larger dimensions. Ken Perlin designed the algorithm
in 2001 to address the limitations of his classic noise function, especially
in higher dimensions."
(let*
(;; opts for re-use
(unskew-factor (loise-opts:unskew-factor opts))
;; skew the input space to determine which simplex cell we're in
(s (* (+ a b c) (loise-opts:skew-factor opts)))
(i (lutil-math:fast-floor (+ a s)))
(j (lutil-math:fast-floor (+ b s)))
(k (lutil-math:fast-floor (+ c s)))
(t (* (+ i j k) unskew-factor))
;; unskew the cell origin back to (x,y,z) space
(X0 (- i t))
(Y0 (- j t))
(Z0 (- k t))
;; the x,y,z distances from the cell origin
(x0 (- a X0))
(y0 (- b Y0))
(z0 (- c Z0))
;; find out which simplex we are in
((list i1 j1 k1 i2 j2 k2) (which x0 y0 z0))
;; A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
;; a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z),
;; and a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in
;; (x,y,z), where c = 1/6.
;;
;; Offsets for second corner in (x,y,z) coords
(x1 (+ (- x0 i1) unskew-factor))
(y1 (+ (- y0 j1) unskew-factor))
(z1 (+ (- z0 k1) unskew-factor))
;; Offsets for third corner in (x,y,z) coords
(x2 (+ (- x0 i2) (* 2.0 unskew-factor)))
(y2 (+ (- y0 j2) (* 2.0 unskew-factor)))
(z2 (+ (- z0 k2) (* 2.0 unskew-factor)))
;; Offsets for last corner in (x,y,z) coords
(x3 (+ (- x0 1.0) (* 3.0 unskew-factor)))
(y3 (+ (- y0 1.0) (* 3.0 unskew-factor)))
(z3 (+ (- z0 1.0) (* 3.0 unskew-factor)))
;; Work out the hashed gradient indices of the four simplex corners
(ii (band i 255))
(jj (band j 255))
(kk (band k 255))
(gi0 (loise-util:get-gradient-index ii jj kk opts))
(gi1 (loise-util:get-gradient-index (+ ii i1) (+ jj j1) (+ kk k1) opts))
(gi2 (loise-util:get-gradient-index (+ ii i2) (+ jj j2) (+ kk k2) opts))
(gi3 (loise-util:get-gradient-index (+ ii 1) (+ jj 1) (+ kk 1) opts))
;; Calculate the contribution from the four corners
(n0 (loise-util:corner-contribution gi0 x0 y0 z0 opts))
(n1 (loise-util:corner-contribution gi1 x1 y1 z1 opts))
(n2 (loise-util:corner-contribution gi2 x2 y2 z2 opts))
(n3 (loise-util:corner-contribution gi3 x3 y3 z3 opts)))
;; Add contributions from each corner to get the final noise value.
;; The result is scaled to stay just inside [-1,1]
;; NOTE: This scaling factor seems to work better than the given one
;; I'm not sure why
(* (loise-opts:simplex-scale opts) (+ n0 n1 n2 n3))))
(defun which (a b c)
"For the 3D case, the simplex shape is a slightly irregular tetrahedron.
This function determines which simplex we are in."
(cond
((and (>= a b) (>= b c)) (list 1 0 0 1 1 0)) ; X Y Z order
((and (>= a b) (>= a c)) (list 1 0 0 1 0 1)) ; X Z Y order
((>= a b) (list 0 0 1 1 0 1)) ; Z X Y order
((< b c) (list 0 0 1 0 1 1)) ; Z Y X order
((< a c) (list 0 1 0 0 1 1)) ; Y Z X order
(else (list 0 1 0 1 1 0)))) ; Y X Z order
(defun point (coords dims multiplier)
(point coords dims multiplier (default-options)))
(defun point
((`(,x) `(,width) multiplier opts)
(1d (* multiplier (/ x width)) opts))
((`(,x ,y) `(,width ,height) multiplier opts)
(2d (* multiplier (/ x width))
(* multiplier (/ y height))
opts))
((`(,x ,y ,z) `(,width ,height ,depth) multiplier opts)
(3d (* multiplier (/ x width))
(* multiplier (/ y height))
(* multiplier (/ z depth))
opts)))