/************************************************************************
 * noises.h - various noise-based patterns
 *
 * Entropy and BMRT are:
 * (c) Copyright 2001 Exluna, Inc.  Portions (c) 1990-2000 Larry Gritz.
 * All rights reserved.
 *
 * $Revision: 1.8 $    $Date: 2001/07/27 21:28:28 $
 *
 ************************************************************************/


#ifndef NOISES_H
#define NOISES_H 1


#include "filterwidth.h"    /* Needed for filterwidth and friends */

#ifndef PATTERNS_H
#include "patterns.h"    /* Needed for filteredabs */
#endif




/* Signed noise -- the original Perlin kind with range (-1,1) We often 
 * prefer signed noise to regular noise mostly because its average is zero.
 */
float snoise (float p)    { return 2 * (float noise(p)) - 1; }
float snoise (float x, y) { return 2 * (float noise(x,y)) - 1; }
float snoise (point p)    { return 2 * (float noise(p)) - 1; }
float snoise (vector p)   { return 2 * (float noise(p)) - 1; }

/* Vector valued signed noise */
vector vsnoise (float p)    { return 2 * (vector noise(p)) - 1; }
vector vsnoise (float x, y) { return 2 * (vector noise(x,y)) - 1; }
vector vsnoise (point p)    { return 2 * (vector noise(p)) - 1; }
vector vsnoise (vector p)   { return 2 * (vector noise(p)) - 1; }

/* Signed periodic noise */
float spnoise (float p; uniform float per) { return 2*pnoise(p,per) - 1; }
float spnoise (float x, y; uniform float px, py) {
    return 2*pnoise(x,y,px,py) - 1;
}
float spnoise (point p; uniform point per) { return 2*pnoise(p,per) - 1; }
float spnoise (vector p; uniform vector per) { return 2*pnoise(p,per) - 1; }



/* If we know the filter size, we can crudely antialias snoise by fading
 * to its average value at approximately the Nyquist limit.
 */
float filteredsnoise (float p; float width) {
    return snoise(p) * (1-smoothstep (0.2,0.75,width));
}
float filteredsnoise (point p; float width) {
    return snoise(p) * (1-smoothstep (0.2,0.75,width));
}
float filteredsnoise (vector p; float width) {
    return snoise(p) * (1-smoothstep (0.2,0.75,width));
}

vector filteredvsnoise (float p; float width) {
    return vsnoise(p) * (1-smoothstep (0.2,0.75,width));
}
vector filteredvsnoise (point p; float width) {
    return vsnoise(p) * (1-smoothstep (0.2,0.75,width));
}
vector filteredvsnoise (vector p; float width) {
    return vsnoise(p) * (1-smoothstep (0.2,0.75,width));
}

float filteredspnoise (float p; uniform float per; float width) {
    return spnoise(p, per) * (1-smoothstep (0.2,0.75,width));
}
float filteredspnoise (point p; uniform point per; float width) {
    return spnoise(p, per) * (1-smoothstep (0.2,0.75,width));
}




/* fractional Brownian motion
 * Inputs: 
 *    p, filtwidth   position and approximate inter-pixel spacing
 *    octaves        max # of octaves to calculate
 *    lacunarity     frequency spacing between successive octaves
 *    gain           scaling factor between successive octaves
 */
float fBm (point p; float filtwidth;
           uniform float octaves, lacunarity, gain)
{
    uniform float amp = 1;
    varying point pp = p;
    varying float sum = 0, fw = filtwidth;
    uniform float i;

    for (i = 0;  i < octaves;  i += 1) {
	sum += amp * filteredsnoise (pp, fw);
	amp *= gain;  pp *= lacunarity;  fw *= lacunarity;
    }
    return sum;
}


/* Simplified version for easy use (careful -- it takes derivatives) */
float fBm (point p) { return fBm (p, filterwidth(p), 4, 2, 0.5); }





/* A vector-valued antialiased fBm. */
vector
vfBm (point p; float filtwidth;
      uniform float octaves, lacunarity, gain)
{
    uniform float amp = 1;
    varying point pp = p;
    varying vector sum = 0;
    varying float fw = filtwidth;
    uniform float i;

    for (i = 0;  i < octaves;  i += 1) {
	sum += amp * filteredvsnoise (pp, fw);
	amp *= gain;  pp *= lacunarity;  fw *= lacunarity;
    }
    return sum;
}


/* Simplified version for easy use (careful -- it takes derivatives) */
vector fBm (point p) { return vfBm (p, filterwidth(p), 4, 2, 0.5); }



/* The stuff that Ken Musgrave calls "VLNoise" */
#define VLNoise(Pt,scale) (snoise(vsnoise(Pt)*scale+Pt))
#define filteredVLNoise(Pt,fwidth,scale) \
            (filteredsnoise(filteredvsnoise(Pt,fwidth)*scale+Pt,fwidth))


float
VLfBm (point p; float filtwidth;
       uniform float octaves, lacunarity, gain, scale)
{
    uniform float amp = 1;
    varying point pp = p;
    varying float sum = 0;
    varying float fw = filtwidth;
    uniform float i;

    for (i = 0;  i < octaves;  i += 1) {
#pragma nolint
	sum += amp * filteredVLNoise (pp, fw, scale);
	amp *= gain;  pp *= lacunarity;  fw *= lacunarity;
    }
    return sum;
}


/* Typical use of vfBm: */
#define VLfBm_default(p)      VLfBm (p, filterwidth(p), 4, 2, 0.5, 1.0)




/* Antialiased turbulence.  Watch out -- the abs() call introduces infinite
 * frequency content, which makes our antialiasing efforts much trickier!
 */
float turbulence (point p; float filtwidth;
                  uniform float octaves, lacunarity, gain)
{
    extern float du, dv;   /* Needed for filterwidth macro */
    uniform float amp = 1;
    varying point pp = p;
    varying float sum = 0, fw = filtwidth;
    uniform float i;

    for (i = 0;  i < octaves;  i += 1) {
	float n = filteredsnoise (pp, fw);
	sum += amp * filteredabs (n, fw);
	amp *= gain;  pp *= lacunarity;  fw *= lacunarity;
    }
    return sum;
}


/* Typical use of turbulence: */
#define turbulence_default(p)  turbulence (p, filterwidth(p), 4, 2, 0.5)




/***************************************************************************
 * Voronoi cell noise (a.k.a. Worley noise) functions
 *
 * These functions assume that space is filled with "features" (points
 * of interest).  There are interesting patterns we can make by
 * figuring out which feature we are closest to, or to what extent
 * we're on the boundary between two features.  Several varieties of
 * these computations are below, categorized by the dimension of their
 * domains, and the number of close features they are interested in.
 *
 * All these functions have similar inputs:
 *   P      - position to test (for 3-D varieties; 2-D varieties use ss,tt)
 *   jitter - how much to jitter the cell center positions (1 is typical,
 *             smaller values make a more regular pattern, larger values
 *             make a more jagged pattern; use jitter >1 at your risk!).
 * And outputs:
 *   f_n    - distance to the nth nearest feature (f1 is closest, f2 is
 *            the distance to the 2nd closest, etc.)
 *   pos_n  - the position of the nth nearest feature.  For 2-D varieties,
 *            these are instead spos_n and tpos_n.
 ***************************************************************************/

/* Voronoi cell noise (a.k.a. Worley noise) -- 3-D, 1-feature version. */
void
voronoi_f1 (point P;
	    float jitter;
	    output float f1;
	    output point pos1;
    )
{
    point thiscell = point (floor(xcomp(P))+0.5, floor(ycomp(P))+0.5,
			    floor(zcomp(P))+0.5);
    f1 = 1000;
    uniform float i, j, k;
    for (i = -1;  i <= 1;  i += 1) {
        for (j = -1;  j <= 1;  j += 1) {
            for (k = -1;  k <= 1;  k += 1) {
		point testcell = thiscell + vector(i,j,k);
                point pos = testcell + 
		            jitter * (vector cellnoise (testcell) - 0.5);
		vector offset = pos - P;
                float dist = offset . offset; /* actually dist^2 */
                if (dist < f1) {
                    f1 = dist;  pos1 = pos;
                }
            }
	}
    }
    f1 = sqrt(f1);
}


/* Voronoi cell noise (a.k.a. Worley noise) -- 3-D, 2-feature version. */
void
voronoi_f1f2 (point P;
	      float jitter;
	      output float f1;  output point pos1;
	      output float f2;  output point pos2;
    )
{
    point thiscell = point (floor(xcomp(P))+0.5, floor(ycomp(P))+0.5,
			    floor(zcomp(P))+0.5);
    f1 = f2 = 1000;
    uniform float i, j, k;
    for (i = -1;  i <= 1;  i += 1) {
        for (j = -1;  j <= 1;  j += 1) {
            for (k = -1;  k <= 1;  k += 1) {
		point testcell = thiscell + vector(i,j,k);
                point pos = testcell + 
		            jitter * (vector cellnoise (testcell) - 0.5);
		vector offset = pos - P;
                float dist = offset . offset; /* actually dist^2 */
                if (dist < f1) {
                    f2 = f1;  pos2 = pos1;
                    f1 = dist;  pos1 = pos;
                } else if (dist < f2) {
                    f2 = dist;  pos2 = pos;
		}
            }
	}
    }
    f1 = sqrt(f1);  f2 = sqrt(f2);
}


/* Voronoi cell noise (a.k.a. Worley noise) -- 2-D, 1-feature version. */
void
voronoi_f1 (float ss, tt;
	    float jitter;
	    output float f1;
	    output float spos1, tpos1;
    )
{
    float sthiscell = floor(ss)+0.5, tthiscell = floor(tt)+0.5;
    f1 = 1000;
    uniform float i, j;
    for (i = -1;  i <= 1;  i += 1) {
	float stestcell = sthiscell + i;
        for (j = -1;  j <= 1;  j += 1) {
	    float ttestcell = tthiscell + j;
	    float spos = stestcell +
		     jitter * (float cellnoise(stestcell, ttestcell) - 0.5);
	    float tpos = ttestcell +
		 jitter * (float cellnoise(stestcell+23, ttestcell-87) - 0.5);
	    float soffset = spos - ss;
	    float toffset = tpos - tt;
	    float dist = soffset*soffset + toffset*toffset;
	    if (dist < f1) {
		f1 = dist;
		spos1 = spos;  tpos1 = tpos;
	    }
	}
    }
    f1 = sqrt(f1);
}


/* Voronoi cell noise (a.k.a. Worley noise) -- 2-D, 2-feature version. */
void
voronoi_f1f2 (float ss, tt;
	      float jitter;
	      output float f1;
	      output float spos1, tpos1;
	      output float f2;
	      output float spos2, tpos2;
    )
{
    float sthiscell = floor(ss)+0.5, tthiscell = floor(tt)+0.5;
    f1 = f2 = 1000;
    uniform float i, j;
    for (i = -1;  i <= 1;  i += 1) {
	float stestcell = sthiscell + i;
        for (j = -1;  j <= 1;  j += 1) {
	    float ttestcell = tthiscell + j;
	    float spos = stestcell +
		   jitter * (cellnoise(stestcell, ttestcell) - 0.5);
	    float tpos = ttestcell +
		   jitter * (cellnoise(stestcell+23, ttestcell-87) - 0.5);
	    float soffset = spos - ss;
	    float toffset = tpos - tt;
	    float dist = soffset*soffset + toffset*toffset;
	    if (dist < f1) {
		f2 = f1;  spos2 = spos1;  tpos2 = tpos1;
		f1 = dist;  spos1 = spos;  tpos1 = tpos;
	    } else if (dist < f2) {
		f2 = dist;
		spos2 = spos;  tpos2 = tpos;
	    }
	}
    }
    f1 = sqrt(f1);  f2 = sqrt(f2);
}


#endif /* NOISES_H */
