[net.graphics] 3D fractional Brownian Motion

fournier@Navajo.ARPA (07/31/85)

 /* computes approximations to 3d fractional Brownian motion */
 /* try as input: 4 0.65 1.0 4157633211        */
 /* to watch results, easiest is to try 64*64*64, then load the resulting 
 */
 /* array into a frame buffer as 64 64x64 frames. Then use the hardware */
 /* pan/scroll/zoom feature (if your frame buffer  does not have it, junk 
 */
 /* it) to "slice" through the cube. You will get a nice animation */
 /* of clouds or fire, depending on the colour map you use. You might */
 /* have to rescale the numbers you write out to fit your frame buffer */
#include <stdio.h>
#define TYPES 7
#define SIZE 65
 /* At the beginning there are 8 CORNERS */
 /* Then are computed 8 types of points. The first type is CENTER,
    computed from */
 /* the four CORNERS. The next three are FACE CENTERS, computed from two
    CENTERS */
 /* and four CORNERS. There are three kinds of FACE CENTERS, in order */
 /* in the XY plane, in the XZ plane and in the YZ plane. */
 /* Last are EDGE CENTERS, computed from two CORNERS and four FACE
    CENTERS. */
 /* There are three kinds of EDGE CENTERS, in order Z//, X// and Y//  */
float   fbm[SIZE][SIZE][SIZE];
int     start[TYPES][3] = {
    1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0
};
int     neigh[TYPES] = {
    8, 6, 6, 6, 6, 6, 6
};
int     offset[TYPES][8][3] = {
    1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, -1, -1,
    0, 0, 1, 0, 0, -1, 1, 1, 0, 1, -1, 0, -1, 1, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0,
    0, 1, 0, 0, -1, 0, 1, 0, 1, 1, 0, -1, -1, 0, 1, -1, 0, -1, 0, 0, 0, 0, 0, 0,
    1, 0, 0, -1, 0, 0, 0, 1, 1, 0, 1, -1, 0, -1, 1, 0, -1, -1, 0, 0, 0, 0, 0, 0,
    0, 0, 1, 0, 0, -1, 0, -1, 0, 0, 1, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0,
    1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 1, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0,
    0, 1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0
};
 /* the last point in the loops is U-start for each coordinate */
 /* the steps are 2*dist in every case */
 /* the orders of the points is important, since they use some of the
    precedings */
float   weight[TYPES][8] = {
    1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
    1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
    1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
    1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
    1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
    1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
    1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0
};
 /* for the time being, the weights are all one. They should be related to
    the distance */
int     fudge[3] = {
    2514521, 891653, 8765142
};

/* fudge(s) are coefficients to relate the pseudo-random seeds to the indices
*/
main () {
    float   h,
            scale,
            std,
            ratio;
    int     level,
            seed,
            max,
            clevel,
            type,
            i,
            j,
            k,
            dist;
    int     inseed;
    float   gauss (), expect ();
    int     scrseed ();
    int     scaledvalue;
    double  pow ();
    int     fd;
    printf (" Give maximum level, h, scale and seed: ");
    scanf ("%d %f %f %d", &level, &h, &scale, &inseed);
    if (level < 0 || level > 6) {
	fprintf (stderr, " Level (%d) out of range.\n", level);
	exit ();
    };
    if (h > 1.0 || h < 0.0) {
	fprintf (stderr, " h (%f) out of range.\n", h);
	exit ();
    };
    if (scale < 0.0) {
	fprintf (stderr, " Scale (%f) out of range.\n", scale);
	exit ();
    };
 /* determine max */
    max = 1 << level;
    std = scale;
    dist = max;
    ratio = pow (2.0, -h);
 /* compute corners (all eight of them) */
    seed = scrseed (0, 0, 0, inseed, max);
    fbm[0][0][0] = gauss (0.0, std, &seed);
    seed = scrseed (max, 0, 0, inseed, max);
    fbm[max][0][0] = gauss (0.0, std, &seed);
    seed = scrseed (0, max, 0, inseed, max);
    fbm[0][max][0] = gauss (0.0, std, &seed);
    seed = scrseed (0, 0, max, inseed, max);
    fbm[0][0][max] = gauss (0.0, std, &seed);
    seed = scrseed (max, max, 0, inseed, max);
    fbm[max][max][0] = gauss (0.0, std, &seed);
    seed = scrseed (0, max, max, inseed, max);
    fbm[0][max][max] = gauss (0.0, std, &seed);
    seed = scrseed (max, 0, max, inseed, max);
    fbm[max][0][max] = gauss (0.0, std, &seed);
    seed = scrseed (max, max, max, inseed, max);
    fbm[max][max][max] = gauss (0.0, std, &seed);
 /* compute all others */
    for (clevel = 1; clevel <= level; clevel++) {
	std = std * ratio;
	dist = dist / 2;
	for (type = 0; type < TYPES; type++) {
	    for (i = start[type][0] * dist; i <= max - start[type][0] * dist; i += 2 * dist) {
		for (j = start[type][1] * dist; j <= max - start[type][1] * dist; j += 2 * dist) {
		    for (k = start[type][2] * dist; k <= max - start[type][2] * dist; k += 2 * dist) {
		    /* compute value */
			seed = scrseed (i, j, k, inseed, max);
			fbm[i][j][k] = gauss (expect (type, i, j, k, dist, max), std, &seed);
		    };
		};
	    };
	};
    };
 /* save to file */
 /* replace name "fbm" by your favourite string */
    fd = creat ("fbm", 2);
    if (fd < 1) {
	fprintf (stderr, "Can't open file. \n");
	exit ();
    };
    write (fd, &max, sizeof (int));
    for (k = 0; k < max; k++) {
	for (i = 0; i < max; i++) {
	    for (j = 0; j < max; j++) {
	    /* somewhat arbitrary scaling, based on -2:+2 range */
		scaledvalue = 2048 + 1024 * fbm[i][j][k];
		write (fd, &scaledvalue, sizeof (int));
	    };
	};
    };
    close (fd);
}
float   expect (type, i, j, k, dist, max)
				/* computes the expected values, given the
				   type of point and the distances */
int     type,
        i,
        j,
        k,
        dist,
        max;
{
    float   exp;
    int     in,
            jn,
            kn,
            neighbour;
    float   w,
            sumw;
    exp = 0.0;
    sumw = 0.0;
    for (neighbour = 0; neighbour < neigh[type]; neighbour++) {

    /* if an index is out of range, take the mirror image with negative
       weight */
	w = weight[type][neighbour];
	in = i + dist * offset[type][neighbour][0];
	if (in > max || in < 0) {
	    w = -w;
	    in = i - dist * offset[type][neighbour][0];
	};
	jn = j + dist * offset[type][neighbour][1];
	if (jn > max || jn < 0) {
	    w = -w;
	    jn = j - dist * offset[type][neighbour][1];
	};
	kn = k + dist * offset[type][neighbour][2];
	if (kn > max || kn < 0) {
	    w = -w;
	    kn = k - dist * offset[type][neighbour][2];
	};
    /* above, only one of the three indices can be out of range  */
	exp = exp + fbm[in][jn][kn] * w;
	sumw = sumw + w;
    };
    return exp / sumw;
}
float   gauss (mean, std, seed)	/* note that std is standard dev., not
				   variance */
 /* also seed is pointer, since through ran it returns a new value */
 /* you should substitute a better normal distribution generator */
float   mean,
        std;
int    *seed;
{
    float   sum;
    float   ran ();
    int     i;
    sum = 0.0;
    for (i = 1; i <= 12; i++)
	sum = sum + ran (seed);
    return (mean + (sum - 6.0) * std);
}
float   ran (seed)
int    *seed;
 /* use your own function here (should return uniformly distributed */
 /* float between 0 and 1) */
{
    int     max = 017777777777;
    srand (*seed);
    *seed = rand ();
    return (*seed / (float) max);
}
int     scrseed (i, j, k, inseed, max)
int     i,
        j,
        k,
        inseed,
        max;
 /* compute seed as function of indices */
{
    return (i * fudge[0] + j * fudge[1] + (k % max) * fudge[2] + inseed);
}