/* * This is a plug-in version of the ppmforge program. * * Copyright (C) 1999 Maurits Rijk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Maurits Rijk * * http://home-2.consunet.nl/~cb007736 * * Modified for 1.2 and the Graphics Muse Tools CD * by Michael J. Hammel * 01/2002 */ /* * Version Information: * * 0.9 16-Oct-1999 initial release * */ #include #include #include #include #include "libgimp/gimp.h" #include "gtk/gtk.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef M_E #define M_E 2.7182818284590452354 #endif typedef struct { guchar red; guchar green; guchar blue; } pixel; typedef guchar pixval; #define PPM_ASSIGN(pixel, r, g, b) \ (pixel).red = (r); \ (pixel).green = (g); \ (pixel).blue = (b) #define pm_error printf #define pm_message printf #define max(x, y) (((x) > (y)) ? (x) : (y)) #define min(x, y) (((x) < (y)) ? (x) : (y)) /* Definitions used to address real and imaginary parts in a two-dimensional array of complex numbers as stored by fourn(). */ #define Real(v, x, y) v[1 + (((x) * meshsize) + (y)) * 2] #define Imag(v, x, y) v[2 + (((x) * meshsize) + (y)) * 2] /* Co-ordinate indices within arrays. */ #define X 0 #define Y 1 #define Z 2 /* Definition for obtaining random numbers. */ #define nrand 4 /* Gauss() sample count */ #define Cast(low, high) ((low)+(((high)-(low)) * ((random() & 0x7FFF) / arand))) /* Utility definition to get an array's element count (at compile time). For example: int arr[] = {1,2,3,4,5}; ... printf("%d", ELEMENTS(arr)); would print a five. ELEMENTS("abc") can also be used to tell how many bytes are in a string constant INCLUDING THE TRAILING NULL. */ #define ELEMENTS(array) (sizeof(array)/sizeof((array)[0])) static GimpDrawable *_drawable; static char *_image_name; static gint _image_width; static gint _image_height; static GtkWidget *_dlg; /* Declare local functions. */ static void query (void); static void run (char *name, int nparams, GimpParam * param, int *nreturn_vals, GimpParam ** return_vals); static gint dialog(GimpDrawable *drawable); GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; static int run_flag = 0; /* Display parameters */ #define SCRSAT 255 /* Screen maximum intensity */ /* Function prototypes */ static void fourn(float data[], int nn[], int ndim, int isign); static void initgauss(unsigned int seed); static double gauss(void); static void spectralsynth(float **x, unsigned int n, double h); static void initseed(void); static void temprgb(double temp, double *r, double *g, double *b); static void etoile(pixel *pix); static void genplanet(float *a, unsigned int n); static gboolean planet(void); /* Local variables */ static gboolean dimspec = FALSE; static gboolean powerspec = FALSE; static gboolean icespec = FALSE; static gboolean glacspec = FALSE; static gboolean starspec = FALSE; static gboolean starcspec = FALSE; static double arand, gaussadd, gaussfac; /* Gaussian random parameters */ static double fracdim; /* Fractal dimension */ static double powscale; /* Power law scaling exponent */ static int meshsize = 256; /* FFT mesh size */ static unsigned int rseed; /* Current random seed */ static gboolean seedspec = FALSE; /* Did the user specify a seed ? */ static gboolean clouds = FALSE; /* Just generate clouds */ static gboolean stars = FALSE; /* Just generate stars */ static int screenxsize = 256; /* Screen X size */ static int screenysize = 256; /* Screen Y size */ static double inclangle, hourangle; /* Star position relative to planet */ static gboolean inclspec = FALSE; /* No inclination specified yet */ static gboolean hourspec = FALSE; /* No hour specified yet */ static double icelevel; /* Ice cap theshold */ static double glaciers; /* Glacier level */ static int starfraction; /* Star fraction */ static int starcolour; /* Star colour saturation */ /* FOURN -- Multi-dimensional fast Fourier transform Called with arguments: data A one-dimensional array of floats (NOTE!!! NOT DOUBLES!!), indexed from one (NOTE!!! NOT ZERO!!), containing pairs of numbers representing the complex valued samples. The Fourier transformed results are returned in the same array. nn An array specifying the edge size in each dimension. THIS ARRAY IS INDEXED FROM ONE, AND ALL THE EDGE SIZES MUST BE POWERS OF TWO!!! ndim Number of dimensions of FFT to perform. Set to 2 for two dimensional FFT. isign If 1, a Fourier transform is done; if -1 the inverse transformation is performed. This function is essentially as given in Press et al., "Numerical Recipes In C", Section 12.11, pp. 467-470. */ static void fourn(float data[], int nn[], int ndim, int isign) { int i1, i2, i3; int i2rev, i3rev, ip1, ip2, ip3, ifp1, ifp2; int ibit, idim, k1, k2, n, nprev, nrem, ntot; float tempi, tempr; double theta, wi, wpi, wpr, wr, wtemp; #define SWAP(a,b) tempr=(a); (a) = (b); (b) = tempr ntot = 1; for (idim = 1; idim <= ndim; idim++) ntot *= nn[idim]; nprev = 1; for (idim = ndim; idim >= 1; idim--) { n = nn[idim]; nrem = ntot / (n * nprev); ip1 = nprev << 1; ip2 = ip1 * n; ip3 = ip2 * nrem; i2rev = 1; for (i2 = 1; i2 <= ip2; i2 += ip1) { if (i2 < i2rev) { for (i1 = i2; i1 <= i2 + ip1 - 2; i1 += 2) { for (i3 = i1; i3 <= ip3; i3 += ip2) { i3rev = i2rev + i3 - i2; SWAP(data[i3], data[i3rev]); SWAP(data[i3 + 1], data[i3rev + 1]); } } } ibit = ip2 >> 1; while (ibit >= ip1 && i2rev > ibit) { i2rev -= ibit; ibit >>= 1; } i2rev += ibit; } ifp1 = ip1; while (ifp1 < ip2) { ifp2 = ifp1 << 1; theta = isign * (M_PI * 2) / (ifp2 / ip1); wtemp = sin(0.5 * theta); wpr = -2.0 * wtemp * wtemp; wpi = sin(theta); wr = 1.0; wi = 0.0; for (i3 = 1; i3 <= ifp1; i3 += ip1) { for (i1 = i3; i1 <= i3 + ip1 - 2; i1 += 2) { for (i2 = i1; i2 <= ip3; i2 += ifp2) { k1 = i2; k2 = k1 + ifp1; tempr = wr * data[k2] - wi * data[k2 + 1]; tempi = wr * data[k2 + 1] + wi * data[k2]; data[k2] = data[k1] - tempr; data[k2 + 1] = data[k1 + 1] - tempi; data[k1] += tempr; data[k1 + 1] += tempi; } } wr = (wtemp = wr) * wpr - wi * wpi + wr; wi = wi * wpr + wtemp * wpi + wi; } ifp1 = ifp2; } nprev *= n; } } #undef SWAP /* INITGAUSS -- Initialise random number generators. As given in Peitgen & Saupe, page 77. */ static void initgauss(unsigned int seed) { /* Range of random generator */ arand = pow(2.0, 15.0) - 1.0; gaussadd = sqrt(3.0 * nrand); gaussfac = 2 * gaussadd / (nrand * arand); srandom(seed); } /* GAUSS -- Return a Gaussian random number. As given in Peitgen & Saupe, page 77. */ static double gauss(void) { int i; double sum = 0.0; for (i = 1; i <= nrand; i++) { sum += (random() & 0x7FFF); } return gaussfac * sum - gaussadd; } /* SPECTRALSYNTH -- Spectrally synthesised fractal motion in two dimensions. This algorithm is given under the name SpectralSynthesisFM2D on page 108 of Peitgen & Saupe. */ static void spectralsynth(float **x, unsigned int n, double h) { unsigned bl; int i, j, i0, j0, nsize[3]; double rad, phase, rcos, rsin; float *a; bl = ((((unsigned long) n) * n) + 1) * 2 * sizeof(float); a = (float *) g_malloc0(bl); *x = a; for (i = 0; i <= n / 2; i++) { for (j = 0; j <= n / 2; j++) { phase = 2 * M_PI * ((random() & 0x7FFF) / arand); if (i != 0 || j != 0) { rad = pow((double) (i * i + j * j), -(h + 1) / 2) * gauss(); } else { rad = 0; } rcos = rad * cos(phase); rsin = rad * sin(phase); Real(a, i, j) = rcos; Imag(a, i, j) = rsin; i0 = (i == 0) ? 0 : n - i; j0 = (j == 0) ? 0 : n - j; Real(a, i0, j0) = rcos; Imag(a, i0, j0) = - rsin; } } Imag(a, n / 2, 0) = 0; Imag(a, 0, n / 2) = 0; Imag(a, n / 2, n / 2) = 0; for (i = 1; i <= n / 2 - 1; i++) { for (j = 1; j <= n / 2 - 1; j++) { phase = 2 * M_PI * ((random() & 0x7FFF) / arand); rad = pow((double) (i * i + j * j), -(h + 1) / 2) * gauss(); rcos = rad * cos(phase); rsin = rad * sin(phase); Real(a, i, n - j) = rcos; Imag(a, i, n - j) = rsin; Real(a, n - i, j) = rcos; Imag(a, n - i, j) = - rsin; } } nsize[0] = 0; nsize[1] = nsize[2] = n; /* Dimension of frequency domain array */ fourn(a, nsize, 2, -1); /* Take inverse 2D Fourier transform */ } /* INITSEED -- Generate initial random seed, if needed. */ static void initseed(void) { int i = time(0) ^ 0xF37C; srandom(i); for (i = 0; i < 7; i++) random(); rseed = random(); } /* TEMPRGB -- Calculate the relative R, G, and B components for a black body emitting light at a given temperature. The Planck radiation equation is solved directly for the R, G, and B wavelengths defined for the CIE 1931 Standard Colorimetric Observer. The colour temperature is specified in degrees Kelvin. */ static void temprgb(double temp, double *r, double *g, double *b) { double c1 = 3.7403e10, c2 = 14384.0, er, eg, eb, es; /* Lambda is the wavelength in microns: 5500 angstroms is 0.55 microns. */ #define Planck(lambda) ((c1 * pow((double) lambda, -5.0)) / \ (pow(M_E, c2 / (lambda * temp)) - 1)) er = Planck(0.7000); eg = Planck(0.5461); eb = Planck(0.4358); #undef Planck es = 1.0 / max(er, max(eg, eb)); *r = er * es; *g = eg * es; *b = eb * es; } /* ETOILE -- Set a pixel in the starry sky. */ static void etoile(pixel *pix) { if ((random() % 1000) < starfraction) { #define StarQuality 0.5 /* Brightness distribution exponent */ #define StarIntensity 8 /* Brightness scale factor */ #define StarTintExp 0.5 /* Tint distribution exponent */ double v = StarIntensity * pow(1 / (1 - Cast(0, 0.9999)), (double) StarQuality), temp, r, g, b; if (v > 255) { v = 255; } /* We make a special case for star colour of zero in order to prevent floating point roundoff which would otherwise result in more than 256 star colours. We can guarantee that if you specify no star colour, you never get more than 256 shades in the image. */ if (starcolour == 0) { int vi = v; PPM_ASSIGN(*pix, vi, vi, vi); } else { temp = 5500 + starcolour * pow(1 / (1 - Cast(0, 0.9999)), StarTintExp) * ((random() & 7) ? -1 : 1); /* Constrain temperature to a reasonable value: >= 2600K (S Cephei/R Andromedae), <= 28,000 (Spica). */ temp = max(2600, min(28000, temp)); temprgb(temp, &r, &g, &b); PPM_ASSIGN(*pix, (int) (r * v + 0.499), (int) (g * v + 0.499), (int) (b * v + 0.499)); } } else { PPM_ASSIGN(*pix, 0, 0, 0); } } /* GENPLANET -- Generate planet from elevation array. */ static void genplanet(float *a, unsigned int n) { int i, j; unsigned char *cp, *ap; double *u, *u1; unsigned int *bxf, *bxc; #define RGBQuant 255 pixel *pixels; /* Pixel vector */ pixel *rpix; /* Current pixel pointer */ #define Atthick 1.03 /* Atmosphere thickness as a percentage of planet's diameter */ double athfac = sqrt(Atthick * Atthick - 1.0); double sunvec[3]; gboolean flipped = FALSE; double shang, siang; GimpPixelRgn pixel_rgn; gimp_pixel_rgn_init(&pixel_rgn, _drawable, 0, 0, _image_width, _image_height, FALSE, FALSE); if (!stars) { u = g_new(double, screenxsize); u1 = g_new(double, screenxsize); bxf = g_new(unsigned int, screenxsize); bxc = g_new(unsigned int, screenxsize); /* Compute incident light direction vector. */ #ifdef NoExtremeCrescents shang = hourspec ? hourangle : Cast(-(M_PI * 5) / 8, (M_PI * 5) / 8); #else shang = hourspec ? hourangle : Cast(0, 2 * M_PI); #endif siang = inclspec ? inclangle : Cast(-M_PI * 0.12, M_PI * 0.12); sunvec[X] = sin(shang) * cos(siang); sunvec[Y] = sin(siang); sunvec[Z] = cos(shang) * cos(siang); /* Allow only 25% of random pictures to be crescents */ if (!hourspec && ((random() % 100) < 75)) { flipped = sunvec[Z] < 0 ? TRUE : FALSE; sunvec[Z] = fabs(sunvec[Z]); } /* Prescale the grid points into intensities. */ cp = g_new(unsigned char, n * n); ap = cp; for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { *ap++ = (255.0 * (Real(a, i, j) + 1.0)) / 2.0; } } /* Fill the screen from the computed intensity grid by mapping screen points onto the grid, then calculating each pixel value by bilinear interpolation from the surrounding grid points. (N.b. the pictures would undoubtedly look better when generated with small grids if a more well-behaved interpolation were used.) Before we get started, precompute the line-level interpolation parameters and store them in an array so we don't have to do this every time around the inner loop. */ #define UPRJ(a,size) ((a)/((size)-1.0)) for (j = 0; j < screenxsize; j++) { double bx = (n - 1) * UPRJ(j, screenxsize); bxf[j] = floor(bx); bxc[j] = bxf[j] + 1; u[j] = bx - bxf[j]; u1[j] = 1 - u[j]; } } pixels = g_new(pixel, screenxsize); for (i = 0; i < screenysize; i++) { double t, t1, by, dy, dysq, sqomdysq, icet, svx, svy, svz, azimuth; int byf, byc, lcos; if (!stars) { /* Skip all this setup if just stars */ by = (n - 1) * UPRJ(i, screenysize); dy = 2 * (((screenysize / 2) - i) / ((double) screenysize)); dysq = dy * dy; sqomdysq = sqrt(1.0 - dysq); svx = sunvec[X]; svy = sunvec[Y] * dy; svz = sunvec[Z] * sqomdysq; byf = floor(by) * n; byc = byf + n; t = by - floor(by); t1 = 1 - t; } if (clouds) { /* Render the FFT output as clouds. */ for (j = 0; j < screenxsize; j++) { double r = t1 * u1[j] * cp[byf + bxf[j]] + t * u1[j] * cp[byc + bxf[j]] + t * u[j] * cp[byc + bxc[j]] + t1 * u[j] * cp[byf + bxc[j]]; pixval w = (r > 127.0) ? (RGBQuant * ((r - 127.0) / 128.0)) : 0; PPM_ASSIGN(*(pixels + j), w, w, RGBQuant); } } else if (stars) { /* Generate a starry sky. Note that no FFT is performed; the output is generated directly from a power law mapping of a pseudorandom sequence into intensities. */ for (j = 0; j < screenxsize; j++) { etoile(pixels + j); } } else { for (j = 0; j < screenxsize; j++) { PPM_ASSIGN(*(pixels + j), 0, 0, 0); } azimuth = asin(((((double) i) / (screenysize - 1)) * 2) - 1); icet = pow(fabs(sin(azimuth)), 1.0 / icelevel) - 0.5; lcos = (screenysize / 2) * fabs(cos(azimuth)); rpix = pixels + (screenxsize / 2) - lcos; for (j = (screenxsize / 2) - lcos; j <= (screenxsize / 2) + lcos; j++) { double r = t1 * u1[j] * cp[byf + bxf[j]] + t * u1[j] * cp[byc + bxf[j]] + t * u[j] * cp[byc + bxc[j]] + t1 * u[j] * cp[byf + bxc[j]], ice; int ir, ig, ib; static unsigned char pgnd[][3] = { {206, 205, 0}, {208, 207, 0}, {211, 208, 0}, {214, 208, 0}, {217, 208, 0}, {220, 208, 0}, {222, 207, 0}, {225, 205, 0}, {227, 204, 0}, {229, 202, 0}, {231, 199, 0}, {232, 197, 0}, {233, 194, 0}, {234, 191, 0}, {234, 188, 0}, {233, 185, 0}, {232, 183, 0}, {231, 180, 0}, {229, 178, 0}, {227, 176, 0}, {225, 174, 0}, {223, 172, 0}, {221, 170, 0}, {219, 168, 0}, {216, 166, 0}, {214, 164, 0}, {212, 162, 0}, {210, 161, 0}, {207, 159, 0}, {205, 157, 0}, {203, 156, 0}, {200, 154, 0}, {198, 152, 0}, {195, 151, 0}, {193, 149, 0}, {190, 148, 0}, {188, 147, 0}, {185, 145, 0}, {183, 144, 0}, {180, 143, 0}, {177, 141, 0}, {175, 140, 0}, {172, 139, 0}, {169, 138, 0}, {167, 137, 0}, {164, 136, 0}, {161, 135, 0}, {158, 134, 0}, {156, 133, 0}, {153, 132, 0}, {150, 132, 0}, {147, 131, 0}, {145, 130, 0}, {142, 130, 0}, {139, 129, 0}, {136, 128, 0}, {133, 128, 0}, {130, 127, 0}, {127, 127, 0}, {125, 127, 0}, {122, 127, 0}, {119, 127, 0}, {116, 127, 0}, {113, 127, 0}, {110, 128, 0}, {107, 128, 0}, {104, 128, 0}, {102, 127, 0}, { 99, 126, 0}, { 97, 124, 0}, { 95, 122, 0}, { 93, 120, 0}, { 92, 117, 0}, { 92, 114, 0}, { 92, 111, 0}, { 93, 108, 0}, { 94, 106, 0}, { 96, 104, 0}, { 98, 102, 0}, {100, 100, 0}, {103, 99, 0}, {106, 99, 0}, {109, 99, 0}, {111, 100, 0}, {114, 101, 0}, {117, 102, 0}, {120, 103, 0}, {123, 102, 0}, {125, 102, 0}, {128, 100, 0}, {130, 98, 0}, {132, 96, 0}, {133, 94, 0}, {134, 91, 0}, {134, 88, 0}, {134, 85, 0}, {133, 82, 0}, {131, 80, 0}, {129, 78, 0} }; if (r >= 128) { int ix = ((r - 128) * (ELEMENTS(pgnd) - 1)) / 127; /* Land area. Look up colour based on elevation from precomputed colour map table. */ ir = pgnd[ix][0]; ig = pgnd[ix][1]; ib = pgnd[ix][2]; } else { /* Water. Generate clouds above water based on elevation. */ ir = ig = r > 64 ? (r - 64) * 4 : 0; ib = 255; } /* Generate polar ice caps. */ ice = max(0.0, (icet + glaciers * max(-0.5, (r - 128) / 128.0))); if (ice > 0.125) { ir = ig = ib = 255; } /* Apply limb darkening by cosine rule. */ { double dx = 2 * (((screenxsize / 2) - j) / ((double) screenysize)), dxsq = dx * dx, ds, di, inx; double dsq, dsat; di = svx * dx + svy + svz * sqrt(1.0 - dxsq); #define PlanetAmbient 0.05 if (di < 0) di = 0; di = min(1.0, di); ds = sqrt(dxsq + dysq); ds = min(1.0, ds); /* Calculate atmospheric absorption based on the thickness of atmosphere traversed by light on its way to the surface. */ #define AtSatFac 1.0 dsq = ds * ds; dsat = AtSatFac * ((sqrt(Atthick * Atthick - dsq) - sqrt(1.0 * 1.0 - dsq)) / athfac); #define AtSat(x,y) x = ((x)*(1.0-dsat))+(y)*dsat AtSat(ir, 127); AtSat(ig, 127); AtSat(ib, 255); inx = PlanetAmbient + (1.0 - PlanetAmbient) * di; ir *= inx; ig *= inx; ib *= inx; } PPM_ASSIGN(*rpix, ir, ig, ib); rpix++; } /* Left stars */ #define StarClose 2 for (j = 0; j < (screenxsize / 2) - (lcos + StarClose); j++) { etoile(pixels + j); } /* Right stars */ for (j = (screenxsize / 2) + (lcos + StarClose); j < screenxsize; j++) { etoile(pixels + j); } } gimp_pixel_rgn_set_row(&pixel_rgn, (guchar*) pixels, 0, i, _image_width); } g_free(pixels); gimp_drawable_update(_drawable->id, 0, 0, screenxsize, screenysize); gimp_drawable_flush(_drawable); gimp_displays_flush(); if (!stars) { g_free(cp); g_free(u); g_free(u1); g_free(bxf); g_free(bxc); } } /* PLANET -- Make a planet. */ static gboolean planet(void) { float *a = (float *) 0; int i, j; #ifdef VMS double rmin = HUGE_VAL, rmax = -HUGE_VAL, rmean, rrange; #else double rmin = 1e50, rmax = -1e50, rmean, rrange; #endif if (!seedspec) { initseed(); } initgauss(rseed); if (!stars) { spectralsynth(&a, meshsize, 3.0 - fracdim); if (a == (float *) 0) { return FALSE; } /* Apply power law scaling if non-unity scale is requested. */ if (powscale != 1.0) { for (i = 0; i < meshsize; i++) { for (j = 0; j < meshsize; j++) { double r = Real(a, i, j); if (r > 0) { Real(a, i, j) = pow(r, powscale); } } } } /* Compute extrema for autoscaling. */ for (i = 0; i < meshsize; i++) { for (j = 0; j < meshsize; j++) { double r = Real(a, i, j); rmin = min(rmin, r); rmax = max(rmax, r); } } rmean = (rmin + rmax) / 2; rrange = (rmax - rmin) / 2; for (i = 0; i < meshsize; i++) { for (j = 0; j < meshsize; j++) { Real(a, i, j) = (Real(a, i, j) - rmean) / rrange; } } } genplanet(a, meshsize); if (a != (float *) 0) { free((char *) a); } return TRUE; } MAIN () static void query() { static GimpParamDef args[] = { {GIMP_PDB_INT32, "run_mode", "Interactive"}, {GIMP_PDB_IMAGE, "image", "Input image (unused)"}, {GIMP_PDB_DRAWABLE, "drawable", "Input drawable"}, }; static GimpParamDef *return_vals = NULL; static int nargs = sizeof(args) / sizeof(args[0]); static int nreturn_vals = 0; gimp_install_procedure("forge", "Creates an artificial world.", "", "Maurits Rijk", "Maurits Rijk", "1999", "/Filters/Render/Forge", "RGB*", GIMP_PLUGIN, nargs, nreturn_vals, args, return_vals); } static void close_callback(GtkWidget *widget, gpointer data) { gtk_main_quit(); } static void run(char *name, int n_params, GimpParam *param, int *nreturn_vals, GimpParam **return_vals) { static GimpParam values[1]; GimpRunModeType run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; *nreturn_vals = 1; *return_vals = values; /* Get the specified drawable */ _drawable = gimp_drawable_get(param[2].data.d_drawable); _image_name = gimp_image_get_filename(param[1].data.d_image); _image_width = gimp_image_width(param[1].data.d_image); _image_height = gimp_image_height(param[1].data.d_image); run_mode = (GimpRunModeType) param[0].data.d_int32; if (run_mode == GIMP_RUN_INTERACTIVE) { if (!dialog(_drawable)) { /* The dialog was closed, or something similarly evil happened. */ status = GIMP_PDB_EXECUTION_ERROR; } } if (status == GIMP_PDB_SUCCESS) { gimp_drawable_detach(_drawable); } values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = status; } static void init_parameters(void) { /* Set defaults when explicit specifications were not given. The default fractal dimension and power scale depend upon whether we're generating a planet or clouds. */ #ifdef RandomParameters arand = pow(2.0, 15.0) - 1.0; V srandom((int) (time((long *) 0) ^ 0xF37C)); for (i = 0; i < 7; i++) { V random(); } #endif if (!dimspec) { #ifdef RandomParameters /* fracdim = clouds ? Cast(1.9, 2.3) : Cast(2.1, 2.7); */ fracdim = clouds ? Cast(1.9, 2.3) : Cast(2.0, 2.7); #else fracdim = clouds ? 2.15 : 2.4; #endif } if (!powerspec) { #ifdef RandomParameters /* powscale = clouds ? Cast(0.6, 0.8) : Cast(1.0, 1.5); */ powscale = clouds ? Cast(0.6, 0.8) : Cast(1.0, 1.5); #else powscale = clouds ? 0.75 : 1.2; #endif } if (!icespec) { #ifdef RandomParameters icelevel = Cast(0.2, 0.6); #else icelevel = 0.4; #endif } if (!glacspec) { #ifdef RandomParameters glaciers = Cast(0.6, 0.85); #else glaciers = 0.75; #endif } if (!starspec) { #ifdef RandomParameters starfraction = Cast(75, 125); #else starfraction = 100; #endif } if (!starcspec) { #ifdef RandomParameters starcolour = Cast(100, 150); #else starcolour = 125; #endif } screenxsize = _image_width; screenysize = _image_height; } typedef struct { GtkWidget *planet; GtkWidget *clouds; GtkWidget *night; GtkWidget *dimension; GtkWidget *power; GtkWidget *glaciers; GtkWidget *ice; GtkWidget *hour; GtkWidget *inclination; GtkWidget *stars; GtkWidget *saturation; GtkWidget *seed; gint forced; } DialogData_t; static void planet_cb(GtkWidget *widget, DialogData_t *data) { if (GTK_WIDGET_STATE(widget) & GTK_STATE_SELECTED) { if (!dimspec) { data->forced++; gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->dimension), 2.4); } if (!powerspec) { data->forced++; gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->power), 1.2); } } } static void clouds_cb(GtkWidget *widget, DialogData_t *data) { clouds = (GTK_WIDGET_STATE(widget) & GTK_STATE_SELECTED); if (clouds) { if (!dimspec) { data->forced++; gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->dimension), 2.15); } if (!powerspec) { data->forced++; gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->power), 0.75); } } } static void night_cb(GtkWidget *widget, DialogData_t *data) { stars = (GTK_WIDGET_STATE(widget) & GTK_STATE_SELECTED); if (stars) { if (!dimspec) { data->forced++; gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->dimension), 2.4); } if (!powerspec) { data->forced++; gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->power), 1.2); } } } static void dimension_cb(GtkWidget *widget, DialogData_t *data) { if (data->forced) data->forced--; else dimspec = TRUE; fracdim = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(widget)); } static void power_cb(GtkWidget *widget, DialogData_t *data) { if (data->forced) data->forced--; else powerspec = TRUE; powscale = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(widget)); } static void glaciers_cb(GtkWidget *widget, gpointer data) { glacspec = TRUE; glaciers = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(widget)); } static void ice_cb(GtkWidget *widget, gpointer data) { icespec = TRUE; icelevel = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(widget)); } static void stars_cb(GtkWidget *widget, gpointer data) { starspec = TRUE; starfraction = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(widget)); } static void hour_cb(GtkWidget *widget, gpointer data) { hourspec = TRUE; hourangle = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(widget)); } static void inclination_cb(GtkWidget *widget, gpointer data) { inclspec = TRUE; inclangle = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(widget)); } static void saturation_cb(GtkWidget *widget, gpointer data) { starcspec = TRUE; starcolour = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(widget)); } static void seed_cb(GtkWidget *widget, gpointer data) { seedspec = TRUE; rseed = gtk_spin_button_get_value_as_float(GTK_SPIN_BUTTON(widget)); } static void cancel_cb(GtkWidget *widget, DialogData_t *data) { g_free(data); gtk_main_quit(); } static void ok_cb(GtkWidget *widget, DialogData_t *data) { run_flag = 1; gtk_widget_destroy(GTK_WIDGET(_dlg)); g_free(data); init_parameters(); planet(); } static void set_default_values(DialogData_t *data) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->planet), TRUE); gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->glaciers), 0.75); gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->ice), 0.4); gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->hour), 0); gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->inclination), 0); gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->stars), 100.0); gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->saturation), 125.0); gtk_spin_button_set_value(GTK_SPIN_BUTTON(data->seed), 0); } static void defaults_cb(GtkWidget *widget, DialogData_t *data) { set_default_values(data); } static void help_cb(GtkWidget *widget, gpointer data) { printf("Fix me! (show help)\n"); } static GtkWidget* create_label_in_table(GtkWidget *table, int row, int col, const char *text) { GtkWidget *label = gtk_label_new(text); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); gtk_table_attach(GTK_TABLE(table), label, col, col + 1, row, row + 1, GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show(label); return label; } GtkWidget* create_int_spin_button_in_table(GtkWidget *table, int row, int col, int value, int min, int max) { GtkObject *adj = gtk_adjustment_new(value, min, max, 1, 1, 1); GtkWidget *button = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1, 0); gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(button), TRUE); gtk_table_attach_defaults(GTK_TABLE(table), button, col, col + 1, row, row + 1); gtk_widget_show(button); return button; } GtkWidget* create_float_spin_button_in_table(GtkWidget *table, int row, int col, gfloat value, gfloat min, gfloat max) { GtkObject *adj = gtk_adjustment_new(value, min, max, 0.1, 1, 1); GtkWidget *button = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 0.1, 1); gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(button), TRUE); gtk_table_attach_defaults(GTK_TABLE(table), button, col, col + 1, row, row + 1); gtk_widget_show(button); return button; } static gint dialog(GimpDrawable *drawable) { DialogData_t *data = g_new(DialogData_t, 1); GtkWidget *table, *frame, *hbox, *button; GtkWidget *ok, *cancel, *defaults, *help; GSList *group; guchar *color_cube; gchar **argv; gint argc = 1; data->forced = 0; argv = g_new(gchar *, 1); argv[0] = g_strdup("forge"); gtk_init(&argc, &argv); gtk_rc_parse(gimp_gtkrc()); gdk_set_use_xshm(gimp_use_xshm()); gtk_preview_set_gamma(gimp_gamma()); gtk_preview_set_install_cmap(gimp_install_cmap()); color_cube = gimp_color_cube(); gtk_preview_set_color_cube(color_cube[0], color_cube[1], color_cube[2], color_cube[3]); gtk_widget_set_default_visual(gtk_preview_get_visual()); gtk_widget_set_default_colormap(gtk_preview_get_cmap()); _dlg = gtk_dialog_new(); gtk_window_set_title(GTK_WINDOW(_dlg), "Forge 0.9"); gtk_window_position(GTK_WINDOW(_dlg), GTK_WIN_POS_MOUSE); gtk_signal_connect(GTK_OBJECT(_dlg), "destroy", (GtkSignalFunc) close_callback, NULL); /* Add buttons */ ok = gtk_button_new_with_label("OK"); GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT); gtk_signal_connect(GTK_OBJECT(ok), "clicked", GTK_SIGNAL_FUNC(ok_cb), data); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(_dlg)->action_area), ok, TRUE, TRUE, 0); gtk_widget_grab_default(ok); gtk_widget_show(ok); cancel = gtk_button_new_with_label("Cancel"); gtk_signal_connect(GTK_OBJECT(cancel), "clicked", GTK_SIGNAL_FUNC(cancel_cb), data); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(_dlg)->action_area), cancel, TRUE, TRUE, 0); gtk_widget_show(cancel); defaults = gtk_button_new_with_label("Defaults"); gtk_signal_connect(GTK_OBJECT(defaults), "clicked", GTK_SIGNAL_FUNC(defaults_cb), data); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(_dlg)->action_area), defaults, TRUE, TRUE, 0); gtk_widget_show(defaults); help = gtk_button_new_with_label("Help"); gtk_signal_connect(GTK_OBJECT(help), "clicked", GTK_SIGNAL_FUNC(help_cb), NULL); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(_dlg)->action_area), help, TRUE, TRUE, 0); gtk_widget_show(help); table = gtk_table_new(11, 3, FALSE); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(_dlg)->vbox), table); gtk_table_set_row_spacings(GTK_TABLE(table), 10); gtk_table_set_col_spacings(GTK_TABLE(table), 10); gtk_container_set_border_width(GTK_CONTAINER(table), 10); gtk_widget_show(table); frame = gtk_frame_new("Type"); gtk_table_attach_defaults(GTK_TABLE(table), frame, 0, 2, 0, 1); gtk_widget_show(frame); hbox = gtk_hbox_new(FALSE, 1); gtk_container_add(GTK_CONTAINER(frame), hbox); gtk_widget_show(hbox); button = gtk_radio_button_new_with_label(NULL, "Planet"); data->planet = button; gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) planet_cb, data); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 10); gtk_widget_show(button); group = gtk_radio_button_group(GTK_RADIO_BUTTON(button)); button = gtk_radio_button_new_with_label(group, "Clouds"); data->clouds = button; gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) clouds_cb, data); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 10); gtk_widget_show(button); group = gtk_radio_button_group(GTK_RADIO_BUTTON(button)); button = gtk_radio_button_new_with_label(group, "Night"); data->night = button; gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) night_cb, data); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 10); gtk_widget_show(button); (void) create_label_in_table(table, 2, 0, "Dimension (0.0 - 3.0):"); button = create_float_spin_button_in_table(table, 2, 1, 2.4, 0, 3); data->dimension = button; gtk_signal_connect(GTK_OBJECT(button), "changed", (GtkSignalFunc) dimension_cb, data); (void) create_label_in_table(table, 3, 0, "Power:"); button = create_float_spin_button_in_table(table, 3, 1, 1.2, 0, G_MAXFLOAT); data->power = button; gtk_signal_connect(GTK_OBJECT(button), "changed", (GtkSignalFunc) power_cb, data); (void) create_label_in_table(table, 4, 0, "Glaciers:"); button = create_float_spin_button_in_table(table, 4, 1, 0.75, 0, G_MAXFLOAT); data->glaciers = button; gtk_signal_connect(GTK_OBJECT(button), "changed", (GtkSignalFunc) glaciers_cb, NULL); (void) create_label_in_table(table, 5, 0, "Ice:"); button = create_float_spin_button_in_table(table, 5, 1, 0.4, 0, G_MAXFLOAT); data->ice = button; gtk_signal_connect(GTK_OBJECT(button), "changed", (GtkSignalFunc) ice_cb, NULL); (void) create_label_in_table(table, 6, 0, "Hour (0 - 24):"); button = create_float_spin_button_in_table(table, 6, 1, 0, 0, 24); data->hour = button; gtk_signal_connect(GTK_OBJECT(button), "changed", (GtkSignalFunc) hour_cb, NULL); (void) create_label_in_table(table, 7, 0, "Inclination (-90 - 90):"); button = create_float_spin_button_in_table(table, 7, 1, 0, -90, 90); data->inclination = button; gtk_signal_connect(GTK_OBJECT(button), "changed", (GtkSignalFunc) inclination_cb, NULL); (void) create_label_in_table(table, 8, 0, "Stars (0 - 100):"); button = create_int_spin_button_in_table(table, 8, 1, 100, 0, 100); data->stars = button; gtk_signal_connect(GTK_OBJECT(button), "changed", (GtkSignalFunc) stars_cb, NULL); (void) create_label_in_table(table, 9, 0, "Saturation:"); button = create_int_spin_button_in_table(table, 9, 1, 125, 0, G_MAXINT); data->saturation = button; gtk_signal_connect(GTK_OBJECT(button), "changed", (GtkSignalFunc) saturation_cb, NULL); (void) create_label_in_table(table, 10, 0, "Seed:"); button = create_int_spin_button_in_table(table, 10, 1, 0, 0, G_MAXINT); data->seed = button; gtk_signal_connect(GTK_OBJECT(button), "changed", (GtkSignalFunc) seed_cb, NULL); set_default_values(data); gtk_widget_show(_dlg); gtk_main(); gdk_flush(); return run_flag; }