Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  sphere-designer.c   Sprache: C

 

/*
 * GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * 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 3 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, see <https://www.gnu.org/licenses/>.
 */


/*
 * SphereDesigner v0.4 - creates textured spheres
 * by Vidar Madsen <vidar@prosalg.no>
 *
 * Status: Last updated 1999-09-11
 *
 * Known issues:
 * - Might crash if you click OK or Cancel before first preview is rendered
 * - Phong might look weird with transparent textures
 *
 * Todo:
 * - Saving / Loading of presets needs an overhaul
 * - Antialiasing
 * - Global controls: Gamma, ++
 * - Beautification of GUI
 * - Clean up messy source (lots of Glade remnants)
 * - (Probably more. ;-)
 */


#include "config.h"

#include <string.h>
#include <errno.h>

#include <glib/gstdio.h>

#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

#include "libgimp/stdplugins-intl.h"


#define PLUG_IN_PROC   "plug-in-spheredesigner"
#define PLUG_IN_BINARY "sphere-designer"
#define PLUG_IN_ROLE   "gimp-sphere-designer"

#define RESPONSE_RESET 1

#define PREVIEWSIZE 150

/* These must be adjusted as more functionality is added */
#define MAXOBJECT 5
#define MAXLIGHT 5
#define MAXTEXTURE 20
#define MAXTEXTUREPEROBJ 20
#define MAXNORMAL 20
#define MAXNORMALPEROBJ 20
#define MAXATMOS 1
#define MAXCOLPERGRADIENT 5

enum
{
  TRIANGLE,
  DISC,
  PLANE,
  SPHERE,
  CYLINDER,
  LIGHT
};

enum
{
  SOLID,
  CHECKER,
  MARBLE,
  LIZARD,
  IMAGE,
  PHONG,
  REFLECTION,
  REFRACTION,
  PERLIN,
  WOOD,
  TRANSPARENT,
  SPIRAL,
  SPOTS,
  SMOKE
};

enum
{
  PERSPECTIVE,
  ORTHOGONAL,
  FISHEYE
};

enum
{
  FOG
};

enum
{
  TYPE,
  TEXTURE,
  NUM_COLUMNS
};


/* World-flags */
#define SMARTAMBIENT 0x00000001

/* Object-flags */
#define NOSHADOW   0x00000001

/* Texture-flags */
#define GRADIENT   0x00000001

typedef struct
{
  gshort  xsize, ysize;
  guchar *rgb;
} image;

typedef struct
{
  gshort        numcol;
  gdouble       pos[MAXCOLPERGRADIENT];
  GimpVector4   color[MAXCOLPERGRADIENT];
} gradient;

typedef struct
{
  gint          majtype;
  gint          type;
  gulong        flags;
  GimpVector4   color1, color2;
  gradient      gradient;
  GimpVector4   ambient, diffuse;
  gdouble       oscale;
  GimpVector4   scale, translate, rotate;
  image         image;
  GimpVector4   reflection;
  GimpVector4   refraction;
  GimpVector4   transparent;
  gdouble       ior;
  GimpVector4   phongcolor;
  gdouble       phongsize;
  gdouble       amount;
  gdouble       exp;
  GimpVector4   turbulence;
} texture;

typedef struct
{
  gshort  type;
  gdouble density;
  GimpVector4  color;
  gdouble turbulence;
} atmos;

typedef struct
{
  gshort  type;
  gulong  flags;
  gshort  numtexture;
  texture texture[MAXTEXTUREPEROBJ];
  gshort  numnormal;
  texture normal[MAXNORMALPEROBJ];
} common;

typedef struct
{
  common com;
  GimpVector4 a, b, c;
} triangle;

typedef struct
{
  common        com;
  GimpVector4   a;
  gdouble       b, r;
} disc;

typedef struct
{
  common        com;
  GimpVector4   a;
  gdouble       r;
} sphere;

typedef struct
{
  common        com;
  GimpVector4   a, b, c;
} cylinder;

typedef struct
{
  common        com;
  GimpVector4   a;
  gdouble       b;
} plane;

typedef struct
{
  common        com;
  GimpVector4   color;
  GimpVector4   a;
} light;

typedef struct
{
  GimpVector4   v1, v2;
  gshort        inside;
  gdouble       ior;
} ray;

typedef union
{
  common   com;
  triangle tri;
  disc     disc;
  plane    plane;
  sphere   sphere;
  cylinder cylinder;
} object;


struct world_t
{
  gint    numobj;
  object  obj[MAXOBJECT];
  gint    numlight;
  light   light[MAXLIGHT];
  gint    numtexture;
  texture texture[MAXTEXTURE];
  gulong  flags;
  gshort  quality;
  gdouble smartambient;
  gshort  numatmos;
  atmos   atmos[MAXATMOS];
};


typedef struct _Designer      Designer;
typedef struct _DesignerClass DesignerClass;

struct _Designer
{
  GimpPlugIn parent_instance;
};

struct _DesignerClass
{
  GimpPlugInClass parent_class;
};


#define DESIGNER_TYPE  (designer_get_type ())
#define DESIGNER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DESIGNER_TYPE, Designer))

GType                   designer_get_type         (void) G_GNUC_CONST;

static GList          * designer_query_procedures (GimpPlugIn           *plug_in);
static GimpProcedure  * designer_create_procedure (GimpPlugIn           *plug_in,
                                                   const gchar          *name);

static GimpValueArray * designer_run              (GimpProcedure        *procedure,
                                                   GimpRunMode           run_mode,
                                                   GimpImage            *image,
                                                   GimpDrawable        **drawables,
                                                   GimpProcedureConfig  *config,
                                                   gpointer              run_data);

static inline void vset          (GimpVector4          *v,
                                  gdouble               a,
                                  gdouble               b,
                                  gdouble               c);
static void        restartrender (void);
static void        drawcolor1    (GtkWidget            *widget);
static void        drawcolor2    (GtkWidget            *widget);
static gboolean    render        (void);
static void        realrender    (GimpDrawable         *drawable);
static void        fileselect    (GtkFileChooserAction  action,
                                  GtkWidget            *parent);
static gint        traceray      (ray                  *r,
                                  GimpVector4          *col,
                                  gint                  level,
                                  gdouble               imp);
static gdouble     turbulence    (gdouble              *point,
                                  gdouble               lofreq,
                                  gdouble               hifreq);


G_DEFINE_TYPE (Designer, designer, GIMP_TYPE_PLUG_IN)

GIMP_MAIN (DESIGNER_TYPE)
DEFINE_STD_SET_I18N


static GtkWidget *drawarea = NULL;

static guchar          *img;
static gint             img_stride;
static cairo_surface_t *buffer;

static guint  idle_id = 0;

static sphere s;

struct textures_t
{
  gint   index;
  gchar *s;
  glong  n;
};

static struct textures_t textures[] =
{
  { 0, N_("Solid"),   SOLID   },
  { 1, N_("Checker"), CHECKER },
  { 2, N_("Marble"),  MARBLE  },
  { 3, N_("Lizard"),  LIZARD  },
  { 4, N_("Phong"),   PHONG   },
  { 5, N_("Noise"),   PERLIN  },
  { 6, N_("Wood"),    WOOD    },
  { 7, N_("Spiral"),  SPIRAL  },
  { 8, N_("Spots"),   SPOTS   },
  { 0, NULL,          0       }
};

#define COLORBUTTONWIDTH  30
#define COLORBUTTONHEIGHT 20

static GtkTreeView *texturelist = NULL;

static GtkWidget *scalexscale, *scaleyscale, *scalezscale;
static GtkWidget *rotxscale, *rotyscale, *rotzscale;
static GtkWidget *posxscale, *posyscale, *poszscale;
static GtkWidget *scalescale;
static GtkWidget *turbulencescale;
static GtkWidget *amountscale;
static GtkWidget *expscale;
static GtkWidget *typemenu;
static GtkWidget *texturemenu;

#define DOT(a,b) (a[0] * b[0] + a[1] * b[1] + a[2] * b[2])

#define B 256

static gint      p[B + B + 2];
static gdouble   g[B + B + 2][3];
static gboolean  start = TRUE;
static GRand    *gr;


static void
designer_class_init (DesignerClass *klass)
{
  GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass);

  plug_in_class->query_procedures = designer_query_procedures;
  plug_in_class->create_procedure = designer_create_procedure;
  plug_in_class->set_i18n         = STD_SET_I18N;
}

static void
designer_init (Designer *designer)
{
}

static GList *
designer_query_procedures (GimpPlugIn *plug_in)
{
  return g_list_append (NULL, g_strdup (PLUG_IN_PROC));
}

static GimpProcedure *
designer_create_procedure (GimpPlugIn  *plug_in,
                           const gchar *name)
{
  GimpProcedure *procedure = NULL;

  if (! strcmp (name, PLUG_IN_PROC))
    {
      procedure = gimp_image_procedure_new (plug_in, name,
                                            GIMP_PDB_PROC_TYPE_PLUGIN,
                                            designer_run, NULL, NULL);

      gimp_procedure_set_image_types (procedure, "RGB*, GRAY*");
      gimp_procedure_set_sensitivity_mask (procedure,
                                           GIMP_PROCEDURE_SENSITIVE_DRAWABLE);

      gimp_procedure_set_menu_label (procedure, _("Sphere _Designer..."));
      gimp_procedure_add_menu_path (procedure, "<Image>/Filters/Render");

      gimp_procedure_set_documentation (procedure,
                                        _("Create an image of a textured "
                                          "sphere"),
                                        "This plug-in can be used to create "
                                        "textured and/or bumpmapped spheres, "
                                        "and uses a small lightweight "
                                        "raytracer to perform the task with "
                                        "good quality",
                                        name);
      gimp_procedure_set_attribution (procedure,
                                      "Vidar Madsen",
                                      "Vidar Madsen",
                                      "1999");

      gimp_procedure_add_bytes_aux_argument (procedure, "settings-data",
                                             "Settings data",
                                             "TODO: eventually we must implement proper args for every settings",
                                             GIMP_PARAM_READWRITE);
    }

  return procedure;
}

static void
init (void)
{
  gint i, j, k;
  gdouble v[3], s;

  /* Create an array of random gradient vectors uniformly on the unit sphere */

  gr = g_rand_new ();
  g_rand_set_seed (gr, 1);    /* Use static seed, to get reproducible results */

  for (i = 0; i < B; i++)
    {
      do
        {                     /* Choose uniformly in a cube */
          for (j = 0; j < 3; j++)
            v[j] = g_rand_double_range (gr, -1, 1);
          s = DOT (v, v);
        }
      while (s > 1.0);        /* If not in sphere try again */
      s = sqrt (s);
      for (j = 0; j < 3; j++) /* Else normalize */
        g[i][j] = v[j] / s;
    }

/* Create a pseudorandom permutation of [1..B] */

  for (i = 0; i < B; i++)
    p[i] = i;
  for (i = B; i > 0; i -= 2)
    {
      k = p[i];
      p[i] = p[j = g_rand_int_range (gr, 0, B)];
      p[j] = k;
    }

  /* Extend g and p arrays to allow for faster indexing */

  for (i = 0; i < B + 2; i++)
    {
      p[B + i] = p[i];
      for (j = 0; j < 3; j++)
        g[B + i][j] = g[i][j];
    }
  g_rand_free (gr);
}

#define setup(i,b0,b1,r0,r1) \
        t = vec[i] + 10000.; \
        b0 = ((int)t) & (B-1); \
        b1 = (b0+1) & (B-1); \
        r0 = t - (int)t; \
        r1 = r0 - 1.;


static gdouble
noise3 (gdouble * vec)
{
  gint    bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
  gdouble rx0, rx1, ry0, ry1, rz0, rz1, *q, sx, sy, sz, a, b, c, d, t, u, v;
  gint    i, j;

  if (start)
    {
      start = FALSE;
      init ();
    }

  setup (0, bx0, bx1, rx0, rx1);
  setup (1, by0, by1, ry0, ry1);
  setup (2, bz0, bz1, rz0, rz1);

  i = p[bx0];
  j = p[bx1];

  b00 = p[i + by0];
  b10 = p[j + by0];
  b01 = p[i + by1];
  b11 = p[j + by1];

#define at(rx,ry,rz) ( rx * q[0] + ry * q[1] + rz * q[2] )

#define surve(t) ( t * t * (3. - 2. * t) )

#define lerp(t, a, b) ( a + t * (b - a) )

  sx = surve (rx0);
  sy = surve (ry0);
  sz = surve (rz0);


  q = g[b00 + bz0];
  u = at (rx0, ry0, rz0);
  q = g[b10 + bz0];
  v = at (rx1, ry0, rz0);
  a = lerp (sx, u, v);

  q = g[b01 + bz0];
  u = at (rx0, ry1, rz0);
  q = g[b11 + bz0];
  v = at (rx1, ry1, rz0);
  b = lerp (sx, u, v);

  c = lerp (sy, a, b);          /* interpolate in y at lo x */

  q = g[b00 + bz1];
  u = at (rx0, ry0, rz1);
  q = g[b10 + bz1];
  v = at (rx1, ry0, rz1);
  a = lerp (sx, u, v);

  q = g[b01 + bz1];
  u = at (rx0, ry1, rz1);
  q = g[b11 + bz1];
  v = at (rx1, ry1, rz1);
  b = lerp (sx, u, v);

  d = lerp (sy, a, b);          /* interpolate in y at hi x */

  return 1.5 * lerp (sz, c, d); /* interpolate in z */
}

static double
turbulence (gdouble * point, gdouble lofreq, gdouble hifreq)
{
  gdouble freq, t, p[3];

  p[0] = point[0] + 123.456;
  p[1] = point[1] + 234.567;
  p[2] = point[2] + 345.678;

  t = 0;
  for (freq = lofreq; freq < hifreq; freq *= 2.)
    {
      t += noise3 (p) / freq;
      p[0] *= 2.;
      p[1] *= 2.;
      p[2] *= 2.;
    }
  return t - 0.3;               /* readjust to make mean value = 0.0 */
}

static struct world_t  world;

static inline void
vcopy (GimpVector4 *a, GimpVector4 *b)
{
  *a = *b;
}

static inline void
vcross (GimpVector4 *r, GimpVector4 *a, GimpVector4 *b)
{
  r->x = a->y * b->z - a->z * b->y;
  r->y = -(a->x * b->z - a->z * b->x);
  r->z = a->x * b->y - a->y * b->x;
}

static inline gdouble
vdot (GimpVector4 *a, GimpVector4 *b)
{
  return a->x * b->x + a->y * b->y + a->z * b->z;
}

static inline gdouble
vdist (GimpVector4 *a, GimpVector4 *b)
{
  gdouble x, y, z;

  x = a->x - b->x;
  y = a->y - b->y;
  z = a->z - b->z;

  return sqrt (x * x + y * y + z * z);
}

static inline gdouble
vdist2 (GimpVector4 *a, GimpVector4 *b)
{
  gdouble x, y, z;

  x = a->x - b->x;
  y = a->y - b->y;
  z = a->z - b->z;

  return x * x + y * y + z * z;
}

static inline gdouble
vlen (GimpVector4 *a)
{
  return sqrt (a->x * a->x + a->y * a->y + a->z * a->z);
}

static inline void
vnorm (GimpVector4 *a, gdouble v)
{
  gdouble d;

  d = vlen (a);
  a->x *= v / d;
  a->y *= v / d;
  a->z *= v / d;
}

static inline void
vrotate (GimpVector4 *axis, gdouble ang, GimpVector4 *vector)
{
  gdouble rad = ang / 180.0 * G_PI;
  gdouble ax  = vector->x;
  gdouble ay  = vector->y;
  gdouble az  = vector->z;
  gdouble x   = axis->x;
  gdouble y   = axis->y;
  gdouble z   = axis->z;
  gdouble c   = cos (rad);
  gdouble s   = sin (rad);
  gdouble c1  = 1.0 - c;
  gdouble xx  = c1 * x * x;
  gdouble yy  = c1 * y * y;
  gdouble zz  = c1 * z * z;
  gdouble xy  = c1 * x * y;
  gdouble xz  = c1 * x * z;
  gdouble yz  = c1 * y * z;
  gdouble sx  = s * x;
  gdouble sy  = s * y;
  gdouble sz  = s * z;

  vector->x = (xx + c) * ax + (xy + sz) * ay + (xz - sy) * az;
  vector->y = (xy - sz) * ax + (yy + c) * ay + (yz + sx) * az;
  vector->z = (xz + sy) * ax + (yz - sx) * ay + (zz + c) * az;
}

static inline void
vset (GimpVector4 *v, gdouble a, gdouble b, gdouble c)
{
  v->x = a;
  v->y = b;
  v->z = c;
  v->w = 1.0;
}

static inline void
vcset (GimpVector4 *v, gdouble a, gdouble b, gdouble c, gdouble d)
{
  v->x = a;
  v->y = b;
  v->z = c;
  v->w = d;
}

static inline void
vvrotate (GimpVector4 *p, GimpVector4 *rot)
{
  GimpVector4 axis;

  if (rot->x != 0.0)
    {
      vset (&axis, 1, 0, 0);
      vrotate (&axis, rot->x, p);
    }
  if (rot->y != 0.0)
    {
      vset (&axis, 0, 1, 0);
      vrotate (&axis, rot->y, p);
    }
  if (rot->z != 0.0)
    {
      vset (&axis, 0, 0, 1);
      vrotate (&axis, rot->z, p);
    }
}

static inline void
vsub (GimpVector4 *a, GimpVector4 *b)
{
  a->x -= b->x;
  a->y -= b->y;
  a->z -= b->z;
  a->w -= b->w;
}

static inline void
vadd (GimpVector4 *a, GimpVector4 *b)
{
  a->x += b->x;
  a->y += b->y;
  a->z += b->z;
  a->w += b->w;
}

static inline void
vneg (GimpVector4 *a)
{
  a->x = -a->x;
  a->y = -a->y;
  a->z = -a->z;
  a->w = -a->w;
}

static inline void
vmul (GimpVector4 *v, gdouble a)
{
  v->x *= a;
  v->y *= a;
  v->z *= a;
  v->w *= a;
}

static inline void
vvmul (GimpVector4 *a, GimpVector4 *b)
{
  a->x *= b->x;
  a->y *= b->y;
  a->z *= b->z;
  a->w *= b->w;
}

static inline void
vvdiv (GimpVector4 *a, GimpVector4 *b)
{
  a->x /= b->x;
  a->y /= b->y;
  a->z /= b->z;
}

static void
vmix (GimpVector4 *r, GimpVector4 *a, GimpVector4 *b, gdouble v)
{
  gdouble i = 1.0 - v;

  r->x = a->x * v + b->x * i;
  r->y = a->y * v + b->y * i;
  r->z = a->z * v + b->z * i;
  r->w = a->w * v + b->w * i;
}

static double
vmax (GimpVector4 *a)
{
  gdouble max = fabs (a->x);

  if (fabs (a->y) > max)
    max = fabs (a->y);
  if (fabs (a->z) > max)
    max = fabs (a->z);
  if (fabs (a->w) > max)
    max = fabs (a->w);

  return max;
}

#if 0
static void
vavg (GimpVector4 * a)
{
  gdouble s;

  s = (a->x + a->y + a->z) / 3.0;
  a->x = a->y = a->z = s;
}
#endif

static void
trianglenormal (GimpVector4 * n, gdouble *t, triangle * tri)
{
  triangle tmp;
  vcopy (&tmp.b, &tri->b);
  vcopy (&tmp.c, &tri->c);
  vsub (&tmp.b, &tri->a);
  vsub (&tmp.c, &tri->a);
  vset (&tmp.a, 0, 0, 0);
  vcross (n, &tmp.b, &tmp.c);
  if (t)
    *t = vdot (&tmp.b, &tmp.c);
}

static gdouble
checkdisc (ray * r, disc * disc)
{
  GimpVector4 p, *v = &disc->a;
  gdouble t, d2;
  gdouble i, j, k;

  i = r->v2.x - r->v1.x;
  j = r->v2.y - r->v1.y;
  k = r->v2.z - r->v1.z;

  t = -(v->x * r->v1.x + v->y * r->v1.y + v->z * r->v1.z - disc->b) /
    (v->x * i + v->y * j + v->z * k);

  p.x = r->v1.x + i * t;
  p.y = r->v1.y + j * t;
  p.z = r->v1.z + k * t;

  d2 = vdist2 (&p, v);

  if (d2 > disc->r * disc->r)
    t = 0.0;

  return t;
}

static gdouble
checksphere (ray * r, sphere * sphere)
{
  GimpVector4 cendir, rdir;
  gdouble dirproj, cdlensq;
  gdouble linear, constant, rsq, quadratic, discriminant;
  gdouble smallzero, solmin, solmax, tolerance = 0.001;

  vcopy (&rdir, &r->v2);
  vsub (&rdir, &r->v1);

  rsq = sphere->r * sphere->r;

  vcopy (&cendir, &r->v1);
  vsub (&cendir, &sphere->a);
  dirproj = vdot (&rdir, &cendir);
  cdlensq = vdot (&cendir, &cendir);

  if ((cdlensq >= rsq) && (dirproj > 0.0))
    return 0.0;

  linear = 2.0 * dirproj;
  constant = cdlensq - rsq;
  quadratic = vdot (&rdir, &rdir);

  smallzero = (constant / linear);
  if ((smallzero < tolerance) && (smallzero > -tolerance))
    {
      solmin = -linear / quadratic;

      if (solmin > tolerance)
        {
          return solmin;
          /*
           *hits = solmin;
           return 1;
           */

        }
      else
        return 0.0;
    }
  discriminant = linear * linear - 4.0 * quadratic * constant;
  if (discriminant < 0.0)
    return 0.0;
  quadratic *= 2.0;
  discriminant = sqrt (discriminant);
  solmax = (-linear + discriminant) / (quadratic);
  solmin = (-linear - discriminant) / (quadratic);

  if (solmax < tolerance)
    return 0.0;

  if (solmin < tolerance)
    {
      return solmax;
      /*
       * hits = solmax;
       * return 1;
       */

    }
  else
    {
      return solmin;
      /*
       * hits++ = solmin;
       * hits = solmax;
       * return 2;
       */

    }
}

static gdouble
checkcylinder (ray * r, cylinder * cylinder)
{
  /* FIXME */
  return 0.0;
}


static gdouble
checkplane (ray * r, plane * plane)
{
  GimpVector4 *v = &plane->a;
  gdouble t;
  gdouble i, j, k;

  i = r->v2.x - r->v1.x;
  j = r->v2.y - r->v1.y;
  k = r->v2.z - r->v1.z;

  t = -(v->x * r->v1.x + v->y * r->v1.y + v->z * r->v1.z - plane->b) /
    (v->x * i + v->y * j + v->z * k);

  return t;
}

static gdouble
checktri (ray * r, triangle * tri)
{
  GimpVector4  ed1, ed2;
  GimpVector4  tvec, pvec, qvec;
  gdouble det, idet, t, u, v;
  GimpVector4 *orig, dir;

  orig = &r->v1;
  dir = r->v2;
  vsub (&dir, orig);

  ed1.x = tri->c.x - tri->a.x;
  ed1.y = tri->c.y - tri->a.y;
  ed1.z = tri->c.z - tri->a.z;
  ed2.x = tri->b.x - tri->a.x;
  ed2.y = tri->b.y - tri->a.y;
  ed2.z = tri->b.z - tri->a.z;
  vcross (&pvec, &dir, &ed2);
  det = vdot (&ed1, &pvec);

  idet = 1.0 / det;

  tvec.x = orig->x;
  tvec.y = orig->y;
  tvec.z = orig->z;
  vsub (&tvec, &tri->a);
  u = vdot (&tvec, &pvec) * idet;

  if (u < 0.0)
    return 0;
  if (u > 1.0)
    return 0;

  vcross (&qvec, &tvec, &ed1);
  v = vdot (&dir, &qvec) * idet;

  if ((v < 0.0) || (u + v > 1.0))
    return 0;

  t = vdot (&ed2, &qvec) * idet;

  return t;
}

static void
transformpoint (GimpVector4 * p, texture * t)
{
  gdouble point[3], f;

  if ((t->rotate.x != 0.0) || (t->rotate.y != 0.0) || (t->rotate.z != 0.0))
    vvrotate (p, &t->rotate);
  vvdiv (p, &t->scale);

  vsub (p, &t->translate);

  if ((t->turbulence.x != 0.0) || (t->turbulence.y != 0.0) ||
      (t->turbulence.z != 0.0))
    {
      point[0] = p->x;
      point[1] = p->y;
      point[2] = p->z;
      f = turbulence (point, 1, 256);
      p->x += t->turbulence.x * f;
      p->y += t->turbulence.y * f;
      p->z += t->turbulence.z * f;
    }
}

static void
checker (GimpVector4 *q, GimpVector4 *col, texture *t)
{
  gint   c = 0;
  GimpVector4 p;

  p = *q;
  transformpoint (&p, t);

  vmul (&p, 0.25);

  p.x += 0.00001;
  p.y += 0.00001;
  p.z += 0.00001;

  if (p.x < 0.0)
    p.x = 0.5 - p.x;
  if (p.y < 0.0)
    p.y = 0.5 - p.y;
  if (p.z < 0.0)
    p.z = 0.5 - p.z;

  if ((p.x - (gint) p.x) < 0.5)
    c ^= 1;
  if ((p.y - (gint) p.y) < 0.5)
    c ^= 1;
  if ((p.z - (gint) p.z) < 0.5)
    c ^= 1;

  *col = (c) ? t->color1 : t->color2;
}

static void
gradcolor (GimpVector4 *col, gradient *t, gdouble val)
{
  gint    i;
  gdouble d;
  GimpVector4  tmpcol;

  val = CLAMP (val, 0.0, 1.0);

  for (i = 0; i < t->numcol; i++)
    {
      if (t->pos[i] == val)
        {
          *col = t->color[i];
          return;
        }
      if (t->pos[i] > val)
        {
          d = (val - t->pos[i - 1]) / (t->pos[i] - t->pos[i - 1]);
          vcopy (&tmpcol, &t->color[i]);
          vmul (&tmpcol, d);
          vcopy (col, &tmpcol);
          vcopy (&tmpcol, &t->color[i - 1]);
          vmul (&tmpcol, 1.0 - d);
          vadd (col, &tmpcol);
          return;
        }
    }
  g_printerr ("Error in gradient!\n");
  vset (col, 0, 1, 0);
}

static void
marble (GimpVector4 *q, GimpVector4 *col, texture *t)
{
  gdouble f;
  GimpVector4 p;

  p = *q;
  transformpoint (&p, t);

  f = sin (p.x * 4) / 2 + 0.5;
  f = pow (f, t->exp);

  if (t->flags & GRADIENT)
    gradcolor (col, &t->gradient, f);
  else
    vmix (col, &t->color1, &t->color2, f);
}

static void
lizard (GimpVector4 *q, GimpVector4 *col, texture *t)
{
  gdouble f;
  GimpVector4 p;

  p = *q;
  transformpoint (&p, t);

  f = fabs (sin (p.x * 4));
  f += fabs (sin (p.y * 4));
  f += fabs (sin (p.z * 4));
  f /= 3.0;
  f = pow (f, t->exp);

  if (t->flags & GRADIENT)
    gradcolor (col, &t->gradient, f);
  else
    vmix (col, &t->color1, &t->color2, f);
}

static void
wood (GimpVector4 *q, GimpVector4 *col, texture *t)
{
  gdouble f;
  GimpVector4 p;

  p = *q;
  transformpoint (&p, t);

  f = fabs (p.x);
  f = f - (int) f;

  f = pow (f, t->exp);

  if (t->flags & GRADIENT)
    gradcolor (col, &t->gradient, f);
  else
    vmix (col, &t->color1, &t->color2, f);
}

static void
spiral (GimpVector4 *q, GimpVector4 *col, texture *t)
{
  gdouble f;
  GimpVector4 p;

  p = *q;
  transformpoint (&p, t);

  f = fabs (atan2 (p.x, p.z) / G_PI / 2 + p.y + 99999);
  f = f - (int) f;

  f = pow (f, t->exp);

  if (t->flags & GRADIENT)
    gradcolor (col, &t->gradient, f);
  else
    vmix (col, &t->color1, &t->color2, f);
}

static void
spots (GimpVector4 *q, GimpVector4 *col, texture *t)
{
  gdouble f;
  GimpVector4 p, r;

  p = *q;
  transformpoint (&p, t);

  p.x += 10000.0;
  p.y += 10000.0;
  p.z += 10000.0;

  vset (&r, (gint) (p.x + 0.5), (gint) (p.y + 0.5), (gint) (p.z + 0.5));
  f = vdist (&p, &r);
  f = cos (f * G_PI);
  f = CLAMP (f, 0.0, 1.0);
  f = pow (f, t->exp);

  if (t->flags & GRADIENT)
    gradcolor (col, &t->gradient, f);
  else
    vmix (col, &t->color1, &t->color2, f);
}

static void
perlin (GimpVector4 * q, GimpVector4 * col, texture * t)
{
  gdouble f, point[3];
  GimpVector4  p;

  p = *q;
  transformpoint (&p, t);

  point[0] = p.x;
  point[1] = p.y;
  point[2] = p.z;

  f = turbulence (point, 1, 256) * 0.3 + 0.5;

  f = pow (f, t->exp);

  if (t->flags & GRADIENT)
    gradcolor (col, &t->gradient, f);
  else
    vmix (col, &t->color1, &t->color2, f);
}

static void
imagepixel (GimpVector4 * q, GimpVector4 * col, texture * t)
{
  GimpVector4 p;
  gint x, y;
  guchar *rgb;

  p = *q;
  transformpoint (&p, t);

  x = (p.x * t->image.xsize);
  y = (p.y * t->image.ysize);

  x = (x % t->image.xsize + t->image.xsize) % t->image.xsize;
  y = (y % t->image.ysize + t->image.ysize) % t->image.ysize;

  rgb = &t->image.rgb[x * 3 + (t->image.ysize - 1 - y) * t->image.xsize * 3];
  vset (col, rgb[0] / 255.0, rgb[1] / 255.0, rgb[2] / 255.0);
}

static void
objcolor (GimpVector4 *col, GimpVector4 *p, common *obj)
{
  gint     i;
  texture *t;
  GimpVector4   tmpcol;

  vcset (col, 0, 0, 0, 0);

  for (i = 0; i < obj->numtexture; i++)
    {
      t = &obj->texture[i];

      if (world.quality < 1)
        {
          vadd (col, &t->color1);
          continue;
        }

      vset (&tmpcol, 0, 0, 0);
      switch (t->type)
        {
        case SOLID:
          vcopy (&tmpcol, &t->color1);
          break;
        case CHECKER:
          checker (p, &tmpcol, t);
          break;
        case MARBLE:
          marble (p, &tmpcol, t);
          break;
        case LIZARD:
          lizard (p, &tmpcol, t);
          break;
        case PERLIN:
          perlin (p, &tmpcol, t);
          break;
        case WOOD:
          wood (p, &tmpcol, t);
          break;
        case SPIRAL:
          spiral (p, &tmpcol, t);
          break;
        case SPOTS:
          spots (p, &tmpcol, t);
          break;
        case IMAGE:
          imagepixel (p, &tmpcol, t);
          break;
        case PHONG:
        case REFRACTION:
        case REFLECTION:
        case TRANSPARENT:
        case SMOKE:
          /* Silently ignore non-color textures */
          continue;
          break;
        default:
          g_printerr ("Warning: unknown texture %d\n", t->type);
          break;
        }
      vmul (&tmpcol, t->amount);
      vadd (col, &tmpcol);
    }
  if (!i)
    {
      g_printerr ("Warning: object %p has no textures\n", obj);
    }
}

static void
objnormal (GimpVector4 *res, common *obj, GimpVector4 *p)
{
  gint i;

  switch (obj->type)
    {
    case TRIANGLE:
      trianglenormal (res, NULL, (triangle *) obj);
      break;
    case DISC:
      vcopy (res, &((disc *) obj)->a);
      break;
    case PLANE:
      vcopy (res, &((plane *) obj)->a);
      break;
    case SPHERE:
      vcopy (res, &((sphere *) obj)->a);
      vsub (res, p);
      break;
    case CYLINDER:
      vset (res, 1, 1, 1);      /* fixme */
      break;
    default:
      g_error ("objnormal(): Unsupported object type!?\n");
    }
  vnorm (res, 1.0);

  for (i = 0; i < obj->numnormal; i++)
    {
      gint     k;
      GimpVector4   tmpcol[6];
      GimpVector4   q[6], nres;
      texture *t = &obj->normal[i];
      gdouble  nstep = 0.1;

      vset (&nres, 0, 0, 0);
      for (k = 0; k < 6; k++)
        {
          vcopy (&q[k], p);
        }
      q[0].x += nstep;
      q[1].x -= nstep;
      q[2].y += nstep;
      q[3].y -= nstep;
      q[4].z += nstep;
      q[5].z -= nstep;

      switch (t->type)
        {
        case MARBLE:
          for (k = 0; k < 6; k++)
            marble (&q[k], &tmpcol[k], t);
          break;
        case LIZARD:
          for (k = 0; k < 6; k++)
            lizard (&q[k], &tmpcol[k], t);
          break;
        case PERLIN:
          for (k = 0; k < 6; k++)
            perlin (&q[k], &tmpcol[k], t);
          break;
        case WOOD:
          for (k = 0; k < 6; k++)
            wood (&q[k], &tmpcol[k], t);
          break;
        case SPIRAL:
          for (k = 0; k < 6; k++)
            spiral (&q[k], &tmpcol[k], t);
          break;
        case SPOTS:
          for (k = 0; k < 6; k++)
            spots (&q[k], &tmpcol[k], t);
          break;
        case IMAGE:
          for (k = 0; k < 6; k++)
            imagepixel (&q[k], &tmpcol[k], t);
          break;
        case CHECKER:
        case SOLID:
        case PHONG:
        case REFRACTION:
        case REFLECTION:
        case TRANSPARENT:
        case SMOKE:
          continue;
          break;
        default:
          g_printerr ("Warning: unknown texture %d\n", t->type);
          break;
        }

      nres.x = tmpcol[0].x - tmpcol[1].x;
      nres.y = tmpcol[2].x - tmpcol[3].x;
      nres.z = tmpcol[4].x - tmpcol[5].x;
      vadd (&nres, res);
      vnorm (&nres, 1.0);
      vmul (&nres, t->amount);
      vadd (res, &nres);
      vnorm (res, 1.0);
    }
}

/*
   Quality:
   0 = Color only
   1 = Textures
   2 = Light + Normals
   3 = Shadows
   4 = Phong
   5 = Reflection + Refraction
 */


static void
calclight (GimpVector4 * col, GimpVector4 * point, common * obj)
{
  gint i, j;
  ray r;
  gdouble b, a;
  GimpVector4 lcol;
  GimpVector4 norm;
  GimpVector4 pcol;

  vcset (col, 0, 0, 0, 0);

  objcolor (&pcol, point, obj);
  a = pcol.w;

  if (world.quality < 2)
    {
      vcopy (col, &pcol);
      return;
    }

  for (i = 0; i < obj->numtexture; i++)
    {
      if (obj->texture[i].type == PHONG)
        continue;
      if (obj->texture[i].type == REFLECTION)
        continue;
      if (obj->texture[i].type == REFRACTION)
        continue;
      if (obj->texture[i].type == TRANSPARENT)
        continue;
      if (obj->texture[i].type == SMOKE)
        continue;
      vcopy (&lcol, &pcol);
      vvmul (&lcol, &obj->texture[i].ambient);
      vadd (col, &lcol);
    }

  objnormal (&norm, obj, point);
  vnorm (&norm, 1.0);

  r.inside = -1;
  r.ior = 1.0;

  for (i = 0; i < world.numlight; i++)
    {
      vcopy (&r.v1, point);
      vcopy (&r.v2, &world.light[i].a);
      vmix (&r.v1, &r.v1, &r.v2, 0.9999);

      vsub (&r.v1, &r.v2);
      vnorm (&r.v1, 1.0);
      b = vdot (&r.v1, &norm);

      if (b < 0.0)
        continue;

      for (j = 0; j < obj->numtexture; j++)
        {
          if (obj->texture[j].type == PHONG)
            continue;
          if (obj->texture[j].type == REFLECTION)
            continue;
          if (obj->texture[j].type == REFRACTION)
            continue;
          if (obj->texture[j].type == TRANSPARENT)
            continue;
          if (obj->texture[j].type == SMOKE)
            continue;
          vcopy (&lcol, &pcol);
          vvmul (&lcol, &world.light[i].color);
          vvmul (&lcol, &obj->texture[j].diffuse);
          vmul (&lcol, b);
          vadd (col, &lcol);
        }
    }
  col->w = a;
}

static void
calcphong (common * obj, ray * r2, GimpVector4 * col)
{
  gint    i, j;
  ray     r;
  gdouble b;
  GimpVector4  lcol;
  GimpVector4  norm;
  GimpVector4  pcol;
  gdouble ps;

  vcopy (&pcol, col);

  vcopy (&norm, &r2->v2);
  vsub (&norm, &r2->v1);
  vnorm (&norm, 1.0);

  r.inside = -1;
  r.ior = 1.0;

  for (i = 0; i < world.numlight; i++)
    {
      vcopy (&r.v1, &r2->v1);
      vcopy (&r.v2, &world.light[i].a);
      vmix (&r.v1, &r.v1, &r.v2, 0.9999);

      if (traceray (&r, NULL, -1, 1.0))
        continue;

      /* OK, light is visible */

      vsub (&r.v1, &r.v2);
      vnorm (&r.v1, 1.0);
      b = -vdot (&r.v1, &norm);

      for (j = 0; j < obj->numtexture; j++)
        {
          if (obj->texture[j].type != PHONG)
            continue;

          ps = obj->texture[j].phongsize;

          if (b < (1 - ps))
            continue;
          ps = (b - (1 - ps)) / ps;

          vcopy (&lcol, &obj->texture[j].phongcolor);
          vvmul (&lcol, &world.light[i].color);
          vmul (&lcol, ps);
          vadd (col, &lcol);
        }
    }
}

static int
traceray (ray * r, GimpVector4 * col, gint level, gdouble imp)
{
  gint         i, b = -1;
  gdouble      t = -1.0, min = 0.0;
  common      *obj, *bobj = NULL;
  gint         hits = 0;
  GimpVector4  p = { 0, 0, 0, 0 };

  if ((level == 0) || (imp < 0.005))
    {
      vset (col, 0, 1, 0);
      return 0;
    }

  for (i = 0; i < world.numobj; i++)
    {
      obj = (common *) & world.obj[i];
      switch (obj->type)
        {
        case TRIANGLE:
          t = checktri (r, (triangle *) & world.obj[i]);
          break;
        case DISC:
          t = checkdisc (r, (disc *) & world.obj[i]);
          break;
        case PLANE:
          t = checkplane (r, (plane *) & world.obj[i]);
          break;
        case SPHERE:
          t = checksphere (r, (sphere *) & world.obj[i]);
          break;
        case CYLINDER:
          t = checkcylinder (r, (cylinder *) & world.obj[i]);
          break;
        default:
          g_error ("Illegal object!!\n");
        }
      if (t <= 0.0)
        continue;

      if (!(obj->flags & NOSHADOW) && (level == -1))
        {
          return i + 1;
        }

      hits++;
      if ((!bobj) || (t < min))
        {

          min = t;
          b = i;
          bobj = obj;
        }
    }
  if (level == -1)
    return 0;

  if (bobj)
    {
      p.x = r->v1.x + (r->v2.x - r->v1.x) * min;
      p.y = r->v1.y + (r->v2.y - r->v1.y) * min;
      p.z = r->v1.z + (r->v2.z - r->v1.z) * min;

      calclight (col, &p, bobj);

      if (world.flags & SMARTAMBIENT)
        {
          gdouble ambient = 0.3 * exp (-min / world.smartambient);
          GimpVector4 lcol;
          objcolor (&lcol, &p, bobj);
          vmul (&lcol, ambient);
          vadd (col, &lcol);
        }

      for (i = 0; i < bobj->numtexture; i++)
        {

          if ((world.quality >= 4)
              && ((bobj->texture[i].type == REFLECTION)
                  || (bobj->texture[i].type == PHONG)))
            {

              GimpVector4 refcol, norm, ocol;
              ray ref;

              objcolor (&ocol, &p, bobj);

              vcopy (&ref.v1, &p);
              vcopy (&ref.v2, &r->v1);
              ref.inside = r->inside;
              ref.ior = r->ior;

              vmix (&ref.v1, &ref.v1, &ref.v2, 0.9999); /* push it a tad */

              vsub (&ref.v2, &p);
              objnormal (&norm, bobj, &p);
              vnorm (&norm, 1.0);
              vrotate (&norm, 180.0, &ref.v2);

              vmul (&norm, -0.0001);    /* push it a tad */
              vadd (&ref.v1, &norm);

              vnorm (&ref.v2, 1.0);
              vadd (&ref.v2, &p);

              if ((world.quality >= 5)
                  && (bobj->texture[i].type == REFLECTION))
                {
                  traceray (&ref, &refcol, level - 1,
                            imp * vmax (&bobj->texture[i].reflection));
                  vvmul (&refcol, &bobj->texture[i].reflection);
                  refcol.w = ocol.w;
                  vadd (col, &refcol);
                }
              if (bobj->texture[i].type == PHONG)
                {
                  vcset (&refcol, 0, 0, 0, 0);
                  calcphong (bobj, &ref, &refcol);
                  refcol.w = ocol.w;
                  vadd (col, &refcol);
                }

            }

          if ((world.quality >= 5) && (col->w < 1.0))
            {
              GimpVector4 refcol;
              ray ref;

              vcopy (&ref.v1, &p);
              vcopy (&ref.v2, &p);
              vsub (&ref.v2, &r->v1);
              vnorm (&ref.v2, 1.0);
              vadd (&ref.v2, &p);

              vmix (&ref.v1, &ref.v1, &ref.v2, 0.999);  /* push it a tad */
              traceray (&ref, &refcol, level - 1, imp * (1.0 - col->w));
              vmul (&refcol, (1.0 - col->w));
              vadd (col, &refcol);
            }

          if ((world.quality >= 5) && (bobj->texture[i].type == TRANSPARENT))
            {
              GimpVector4 refcol;
              ray ref;

              vcopy (&ref.v1, &p);
              vcopy (&ref.v2, &p);
              vsub (&ref.v2, &r->v1);
              vnorm (&ref.v2, 1.0);
              vadd (&ref.v2, &p);

              vmix (&ref.v1, &ref.v1, &ref.v2, 0.999);  /* push it a tad */

              traceray (&ref, &refcol, level - 1,
                        imp * vmax (&bobj->texture[i].transparent));
              vvmul (&refcol, &bobj->texture[i].transparent);

              vadd (col, &refcol);
            }

          if ((world.quality >= 5) && (bobj->texture[i].type == SMOKE))
            {
              GimpVector4 smcol, raydir, norm;
              double tran;
              ray ref;

              vcopy (&ref.v1, &p);
              vcopy (&ref.v2, &p);
              vsub (&ref.v2, &r->v1);
              vnorm (&ref.v2, 1.0);
              vadd (&ref.v2, &p);

              objnormal (&norm, bobj, &p);
              vcopy (&raydir, &r->v2);
              vsub (&raydir, &r->v1);
              vnorm (&raydir, 1.0);
              tran = vdot (&norm, &raydir);
              if (tran < 0.0)
                continue;
              tran *= tran;
              vcopy (&smcol, &bobj->texture[i].color1);
              vmul (&smcol, tran);
              vadd (col, &smcol);
            }

          if ((world.quality >= 5) && (bobj->texture[i].type == REFRACTION))
            {
              GimpVector4 refcol, norm, tmpv;
              ray ref;
              double c1, c2, n1, n2, n;

              vcopy (&ref.v1, &p);
              vcopy (&ref.v2, &p);
              vsub (&ref.v2, &r->v1);
              vadd (&ref.v2, &r->v2);

              vmix (&ref.v1, &ref.v1, &ref.v2, 0.999);  /* push it a tad */

              vsub (&ref.v2, &p);
              objnormal (&norm, bobj, &p);

              if (r->inside == b)
                {
                  ref.inside = -1;
                  ref.ior = 1.0;
                }
              else
                {
                  ref.inside = b;
                  ref.ior = bobj->texture[i].ior;
                }

              c1 = vdot (&norm, &ref.v2);

              if (ref.inside < 0)
                c1 = -c1;

              n1 = r->ior;      /* IOR of current media  */
              n2 = ref.ior;     /* IOR of new media  */
              n = n1 / n2;
              c2 = 1.0 - n * n * (1.0 - c1 * c1);

              if (c2 < 0.0)
                {
                  /* FIXME: Internal reflection should occur */
                  c2 = sqrt (-c2);

                }
              else
                {
                  c2 = sqrt (c2);
                }

              vmul (&ref.v2, n);
              vcopy (&tmpv, &norm);
              vmul (&tmpv, n * c1 - c2);
              vadd (&ref.v2, &tmpv);

              vnorm (&ref.v2, 1.0);
              vadd (&ref.v2, &p);

              traceray (&ref, &refcol, level - 1,
                        imp * vmax (&bobj->texture[i].refraction));

              vvmul (&refcol, &bobj->texture[i].refraction);
              vadd (col, &refcol);
            }
        }
    }
  else
    {
      vcset (col, 0, 0, 0, 0);
      min = 10000.0;
      vcset (&p, 0, 0, 0, 0);
    }

  for (i = 0; i < world.numatmos; i++)
    {
      GimpVector4 tmpcol;
      if (world.atmos[i].type == FOG)
        {
          gdouble v, pt[3];
          pt[0] = p.x;
          pt[1] = p.y;
          pt[2] = p.z;
          if ((v = world.atmos[i].turbulence) > 0.0)
            v = turbulence (pt, 1, 256) * world.atmos[i].turbulence;
          v = exp (-(min + v) / world.atmos[i].density);
          vmul (col, v);
          vcopy (&tmpcol, &world.atmos[i].color);
          vmul (&tmpcol, 1.0 - v);
          vadd (col, &tmpcol);
        }
    }

  return hits;
}

static void
setdefaults (texture * t)
{
  memset (t, 0, sizeof (texture));
  t->type = SOLID;
  vcset (&t->color1, 1, 1, 1, 1);
  vcset (&t->color2, 0, 0, 0, 1);
  vcset (&t->diffuse, 1, 1, 1, 1);
  vcset (&t->ambient, 0, 0, 0, 1);
  vset (&t->scale, 1, 1, 1);
  vset (&t->rotate, 0, 0, 0);
  vset (&t->translate, 0, 0, 0);
  t->oscale = 1.0;
  t->amount = 1.0;
  t->exp = 1.0;
}

static gchar *
mklabel (texture * t)
{
  struct textures_t *l;
  static gchar tmps[100];

  if (t->majtype == 0)
    g_strlcpy (tmps, _("Texture"), sizeof(tmps));
  else if (t->majtype == 1)
    g_strlcpy (tmps, _("Bumpmap"), sizeof(tmps));
  else if (t->majtype == 2)
    g_strlcpy (tmps, _("Light"), sizeof(tmps));
  else
    g_strlcpy (tmps, "<unknown>", sizeof(tmps));
  if ((t->majtype == 0) || (t->majtype == 1))
    {
      g_strlcat (tmps, " / ", sizeof(tmps));
      l = textures;
      while (l->s)
        {
          if (t->type == l->n)
            {
              g_strlcat (tmps, gettext (l->s), sizeof(tmps));
              break;
            }
          l++;
        }
    }
  return tmps;
}

static texture *
currenttexture (void)
{
  GtkTreeSelection *sel;
  GtkTreeIter       iter;
  texture          *t = NULL;

  sel = gtk_tree_view_get_selection (texturelist);

  if (gtk_tree_selection_get_selected (sel, NULL, &iter))
    {
      gtk_tree_model_get (gtk_tree_view_get_model (texturelist), &iter,
                          TEXTURE, &t,
                          -1);
    }

  return t;
}

static void
relabel (void)
{
  GtkTreeModel     *model;
  GtkTreeSelection *sel;
  GtkTreeIter       iter;
  texture          *t = NULL;

  sel = gtk_tree_view_get_selection (texturelist);

  if (gtk_tree_selection_get_selected (sel, NULL, &iter))
    {
      model = gtk_tree_view_get_model (texturelist);

      gtk_tree_model_get (model, &iter,
                          TEXTURE, &t,
                          -1);
      gtk_list_store_set (GTK_LIST_STORE (model), &iter,
                          TYPE, mklabel (t),
                          -1);
    }
}

static gboolean noupdate = FALSE;

static void
setvals (texture *t)
{
  struct textures_t *l;

  if (!t)
    return;

  noupdate = TRUE;
  gtk_spin_button_set_value (GTK_SPIN_BUTTON (amountscale), t->amount);

  gtk_spin_button_set_value (GTK_SPIN_BUTTON (scalescale), t->oscale);

  gtk_spin_button_set_value (GTK_SPIN_BUTTON (scalexscale), t->scale.x);
  gtk_spin_button_set_value (GTK_SPIN_BUTTON (scaleyscale), t->scale.y);
  gtk_spin_button_set_value (GTK_SPIN_BUTTON (scalezscale), t->scale.z);

  gtk_spin_button_set_value (GTK_SPIN_BUTTON (rotxscale), t->rotate.x);
  gtk_spin_button_set_value (GTK_SPIN_BUTTON (rotyscale), t->rotate.y);
  gtk_spin_button_set_value (GTK_SPIN_BUTTON (rotzscale), t->rotate.z);

  gtk_spin_button_set_value (GTK_SPIN_BUTTON (posxscale), t->translate.x);
  gtk_spin_button_set_value (GTK_SPIN_BUTTON (posyscale), t->translate.y);
  gtk_spin_button_set_value (GTK_SPIN_BUTTON (poszscale), t->translate.z);

  gtk_spin_button_set_value (GTK_SPIN_BUTTON (turbulencescale), t->turbulence.x);
  gtk_spin_button_set_value (GTK_SPIN_BUTTON (expscale), t->exp);

  drawcolor1 (NULL);
  drawcolor2 (NULL);

  l = textures;
  while (l->s)
    {
      if (l->n == t->type)
        {
          gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (texturemenu),
                                         l->index);
          break;
        }
      l++;
    }

  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (typemenu), t->majtype);

  noupdate = FALSE;
}

static void
selectitem (GtkTreeSelection *treeselection,
            gpointer          data)
{
  setvals (currenttexture ());
}

static void
addtexture (void)
{
  GtkListStore *list_store;
  GtkTreeIter   iter;
  gint          n = s.com.numtexture;

  if (n == MAXTEXTUREPEROBJ - 1)
    return;

  setdefaults (&s.com.texture[n]);

  list_store = GTK_LIST_STORE (gtk_tree_view_get_model (texturelist));

  gtk_list_store_append (list_store, &iter);
  gtk_list_store_set (list_store, &iter,
                      TYPE,    mklabel (&s.com.texture[n]),
                      TEXTURE, &s.com.texture[n],
                      -1);
  gtk_tree_selection_select_iter (gtk_tree_view_get_selection (texturelist),
                                  &iter);

  s.com.numtexture++;

  restartrender ();
}

static void
duptexture (void)
{
  GtkListStore *list_store;
  GtkTreeIter   iter;
  texture      *t = currenttexture ();
  gint          n = s.com.numtexture;

  if (n == MAXTEXTUREPEROBJ - 1)
    return;
  if (!t)
    return;

  s.com.texture[n] = *t;

  list_store = GTK_LIST_STORE (gtk_tree_view_get_model (texturelist));

  gtk_list_store_append (list_store, &iter);
  gtk_list_store_set (list_store, &iter,
                      TYPE,    mklabel (&s.com.texture[n]),
                      TEXTURE, &s.com.texture[n],
                      -1);
  gtk_tree_selection_select_iter (gtk_tree_view_get_selection (texturelist),
                                  &iter);

  s.com.numtexture++;

  restartrender ();
}

static void
rebuildlist (void)
{
  GtkListStore     *list_store;
  GtkTreeSelection *sel;
  GtkTreeIter       iter;
  gint              n;

  sel = gtk_tree_view_get_selection (texturelist);

  for (n = 0; n < s.com.numtexture; n++)
    {
      if (s.com.numtexture && (s.com.texture[n].majtype < 0))
        {
          gint i;

          for (i = n; i < s.com.numtexture - 1; i++)
            s.com.texture[i] = s.com.texture[i + 1];

          s.com.numtexture--;
          n--;
        }
    }

  list_store = GTK_LIST_STORE (gtk_tree_view_get_model (texturelist));

  for (n = 0; n < s.com.numtexture; n++)
    {
      gtk_list_store_append (list_store, &iter);
      gtk_list_store_set (list_store, &iter,
                          TYPE,    mklabel (&s.com.texture[n]),
                          TEXTURE, &s.com.texture[n],
                          -1);
    }

  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
    gtk_tree_selection_select_iter (sel, &iter);

  restartrender ();
}

static void
deltexture (void)
{
  GtkTreeSelection *sel;
  GtkTreeModel     *model;
  GtkTreeIter       iter;
  texture          *t = NULL;

  sel = gtk_tree_view_get_selection (texturelist);

  if (gtk_tree_selection_get_selected (sel, NULL, &iter))
    {
      model = gtk_tree_view_get_model (texturelist);

      gtk_tree_model_get (model, &iter,
                          TEXTURE, &t,
                          -1);
      t->majtype = -1;
      gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
    }

  restartrender ();
}

static void
loadit (const gchar * fn)
{
  FILE    *f;
  gchar    endbuf[21 * (G_ASCII_DTOSTR_BUF_SIZE + 1)];
  gchar   *end = endbuf;
  gchar    line[1024];
  gchar    fmt_str[16];
  gint     i;
  texture *t;
  gint     majtype, type;

  f = g_fopen (fn, "rt");
  if (! f)
    {
      g_message (_("Could not open '%s' for reading: %s"),
                 gimp_filename_to_utf8 (fn), g_strerror (errno));
      return;
    }

  if (2 != fscanf (f, "%d %d", &majtype, &type) || majtype < 0 || majtype > 2)
    {
      g_message (_("File '%s' is not a valid save file."),
                 gimp_filename_to_utf8 (fn));
      fclose (f);
      return;
    }

  rewind (f);

  s.com.numtexture = 0;

  snprintf (fmt_str, sizeof (fmt_str), "%%d %%d %%%" G_GSIZE_FORMAT "s", sizeof (endbuf) - 1);

  while (!feof (f))
    {

      if (!fgets (line, 1023, f))
        break;

      i = s.com.numtexture;
      t = &s.com.texture[i];
      setdefaults (t);

      if (sscanf (line, fmt_str, &t->majtype, &t->type, end) != 3)
        t->color1.x = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->color1.y = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->color1.z = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->color1.w = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->color2.x = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->color2.y = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->color2.z = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->color2.w = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->oscale = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->turbulence.x = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->amount = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->exp = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->scale.x = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->scale.y = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->scale.z = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->rotate.x = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->rotate.y = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->rotate.z = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->translate.x = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->translate.y = g_ascii_strtod (end, &end);
      if (end && errno != ERANGE)
        t->translate.z = g_ascii_strtod (end, &end);

      s.com.numtexture++;
    }

  fclose (f);
}

static void
loadpreset_response (GtkWidget *dialog,
                     gint       response_id,
                     gpointer   data)
{
  if (response_id == GTK_RESPONSE_OK)
    {
      GtkTreeModel *model = gtk_tree_view_get_model (texturelist);
      gchar        *name;

      name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));

      gtk_list_store_clear (GTK_LIST_STORE (model));

      loadit (name);
      g_free (name);

      rebuildlist ();
    }

  gtk_widget_hide (dialog);
}

static void
saveit (const gchar *fn)
{
  gint   i;
  FILE  *f;
  gchar  buf[G_ASCII_DTOSTR_BUF_SIZE];

  f = g_fopen (fn, "wt");
  if (!f)
    {
      g_message (_("Could not open '%s' for writing: %s"),
                 gimp_filename_to_utf8 (fn), g_strerror (errno));
      return;
    }

  for (i = 0; i < s.com.numtexture; i++)
    {
      texture *t = &s.com.texture[i];

      if (t->majtype < 0)
        continue;

      fprintf (f, "%d %d", t->majtype, t->type);
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.x));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.y));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.z));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color1.w));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.x));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.y));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.z));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->color2.w));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->oscale));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->turbulence.x));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->amount));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->exp));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->scale.x));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->scale.y));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->scale.z));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->rotate.x));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->rotate.y));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->rotate.z));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->translate.x));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->translate.y));
      fprintf (f, " %s", g_ascii_dtostr (buf, sizeof (buf), t->translate.z));
      fprintf (f, "\n");
    }

  fclose (f);
}

static void
savepreset_response (GtkWidget *dialog,
                     gint       response_id,
                     gpointer   data)
{
  if (response_id == GTK_RESPONSE_OK)
    {
      gchar *name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));

      saveit (name);
      g_free (name);
    }

  gtk_widget_hide (dialog);
}

static void
loadpreset (GtkWidget *widget,
            GtkWidget *parent)
{
  fileselect (GTK_FILE_CHOOSER_ACTION_OPEN, parent);
}

static void
savepreset (GtkWidget *widget,
            GtkWidget *parent)
{
  fileselect (GTK_FILE_CHOOSER_ACTION_SAVE, parent);
}

static void
fileselect (GtkFileChooserAction  action,
            GtkWidget            *parent)
{
  static GtkWidget *windows[2] = { NULL, NULL };

  gchar *titles[]   = { N_("Open File"), N_("Save File") };
  void  *handlers[] = { loadpreset_response,   savepreset_response };

  if (! windows[action])
    {
      GtkWidget *dialog = windows[action] =
        gtk_file_chooser_dialog_new (gettext (titles[action]),
                                     GTK_WINDOW (parent),
                                     action,

                                     _("_Cancel"), GTK_RESPONSE_CANCEL,

                                     action == GTK_FILE_CHOOSER_ACTION_OPEN ?
                                     _("_Open") : _("_Save"),
                                     GTK_RESPONSE_OK,

                                     NULL);

      gimp_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
                                               GTK_RESPONSE_OK,
                                               GTK_RESPONSE_CANCEL,
                                               -1);

      gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);

      if (action == GTK_FILE_CHOOSER_ACTION_SAVE)
        gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
                                                        TRUE);

      g_signal_connect (dialog, "destroy",
                        G_CALLBACK (gtk_widget_destroyed),
                        &windows[action]);
      g_signal_connect (dialog, "response",
                        G_CALLBACK (handlers[action]),
                        NULL);
    }

  gtk_window_present (GTK_WINDOW (windows[action]));
}

static void
initworld (void)
{
  gint i;

  memset (&world, 0, sizeof (world));

  s.com.type = SPHERE;
  s.a.x = s.a.y = s.a.z = 0.0;
  s.r = 4.0;

  /* not: world.obj[0] = s;
   * s is a sphere so error C2115: '=' : incompatible types
   */

  memcpy (&world.obj[0], &s, sizeof (s));
  world.numobj = 1;

  world.obj[0].com.numtexture = 0;
  world.obj[0].com.numnormal = 0;

  for (i = 0; i < s.com.numtexture; i++)
    {
      common *c = &s.com;
      common *d = &world.obj[0].com;
      texture *t = &c->texture[i];
      if ((t->amount <= 0.0) || (t->majtype < 0))
        continue;
      if (t->majtype == 0)
        {                       /* Normal texture */
          if (t->type == PHONG)
            {
              t->phongcolor = t->color1;
              t->phongsize = t->oscale / 25.0;
            }
          d->texture[d->numtexture] = *t;
          vmul (&d->texture[d->numtexture].scale,
                d->texture[d->numtexture].oscale);
          d->numtexture++;
        }
      else if (t->majtype == 1)
        {                       /* Bumpmap */
          d->normal[d->numnormal] = *t;
          vmul (&d->normal[d->numnormal].scale,
                d->texture[d->numnormal].oscale);
          d->numnormal++;
        }
      else if (t->majtype == 2)
        {                       /* Lightsource */
          light l;
          vcopy (&l.a, &t->translate);
          vcopy (&l.color, &t->color1);
          vmul (&l.color, t->amount);
          world.light[world.numlight] = l;
          world.numlight++;
        }
    }

  world.quality = 5;

  world.flags |= SMARTAMBIENT;
  world.smartambient = 40.0;
}

static gboolean
draw (GtkWidget *widget,
      cairo_t   *cr)
{
  cairo_set_source_surface (cr, buffer, 0.0, 0.0);

  cairo_paint (cr);

  return TRUE;
}

static void
restartrender (void)
{
  if (idle_id)
    g_source_remove (idle_id);

  idle_id = g_idle_add ((GSourceFunc) render, NULL);
}

static void
selecttexture (GtkWidget *widget,
               gpointer   data)
{
  texture *t;

  if (noupdate)
    return;

  t = currenttexture ();
  if (!t)
    return;

  gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &t->type);

  relabel ();
  restartrender ();
}

static void
selecttype (GtkWidget *widget,
            gpointer   data)
{
  texture *t;

  if (noupdate)
    return;

  t = currenttexture ();
  if (!t)
    return;

  gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), &t->majtype);

  relabel ();
  restartrender ();
}

static void
getscales (GtkWidget *widget,
           gpointer   data)
{
  gdouble f;
  texture *t;

  if (noupdate)
    return;

  t = currenttexture ();
  if (!t)
    return;

  t->amount = gtk_spin_button_get_value (GTK_SPIN_BUTTON (amountscale));
  t->exp = gtk_spin_button_get_value (GTK_SPIN_BUTTON (expscale));

  f = gtk_spin_button_get_value (GTK_SPIN_BUTTON (turbulencescale));
  vset (&t->turbulence, f, f, f);

  t->oscale = gtk_spin_button_get_value (GTK_SPIN_BUTTON (scalescale));

  t->scale.x = gtk_spin_button_get_value (GTK_SPIN_BUTTON (scalexscale));
  t->scale.y = gtk_spin_button_get_value (GTK_SPIN_BUTTON (scaleyscale));
  t->scale.z = gtk_spin_button_get_value (GTK_SPIN_BUTTON (scalezscale));

  t->rotate.x = gtk_spin_button_get_value (GTK_SPIN_BUTTON (rotxscale));
  t->rotate.y = gtk_spin_button_get_value (GTK_SPIN_BUTTON (rotyscale));
  t->rotate.z = gtk_spin_button_get_value (GTK_SPIN_BUTTON (rotzscale));

  t->translate.x = gtk_spin_button_get_value (GTK_SPIN_BUTTON (posxscale));
  t->translate.y = gtk_spin_button_get_value (GTK_SPIN_BUTTON (posyscale));
  t->translate.z = gtk_spin_button_get_value (GTK_SPIN_BUTTON (poszscale));

  restartrender ();
}


static void
color1_changed (GimpColorButton *button)
{
  texture *t = currenttexture ();

  if (t)
    {
      GeglColor *color;
      gdouble    rgba[4];

      color = gimp_color_button_get_color (button);
      gegl_color_get_pixel (color, babl_format ("R'G'B'A double"), rgba);

      t->color1.x = rgba[0];
      t->color1.y = rgba[1];
      t->color1.z = rgba[2];
      t->color1.w = rgba[3];

      restartrender ();

      g_object_unref (color);
    }
}

static void
color2_changed (GimpColorButton *button)
{
  texture *t = currenttexture ();

  if (t)
    {
      GeglColor *color;
      gdouble    rgba[4];

      color = gimp_color_button_get_color (button);
      gegl_color_get_pixel (color, babl_format ("R'G'B'A double"), rgba);

      t->color2.x = rgba[0];
      t->color2.y = rgba[1];
      t->color2.z = rgba[2];
      t->color2.w = rgba[3];

      restartrender ();

      g_object_unref (color);
    }
}

static void
drawcolor1 (GtkWidget *w)
{
  static GtkWidget *lastw = NULL;
  GeglColor        *color;
  gdouble           rgba[4];
  texture          *t = currenttexture ();

  if (w)
    lastw = w;
  else
    w = lastw;

  if (!w)
    return;
  if (!t)
    return;

  color = gegl_color_new (NULL);
  rgba[0] = t->color1.x;
  rgba[1] = t->color1.y;
  rgba[2] = t->color1.z;
  rgba[3] = t->color1.w;
  gegl_color_set_pixel (color, babl_format ("R'G'B'A double"), rgba);

  gimp_color_button_set_color (GIMP_COLOR_BUTTON (w), color);
  g_object_unref (color);
}

static void
drawcolor2 (GtkWidget *w)
{
  static GtkWidget *lastw = NULL;
  GeglColor        *color;
  gdouble           rgba[4];
  texture          *t = currenttexture ();

  if (w)
    lastw = w;
  else
    w = lastw;

  if (!w)
    return;
  if (!t)
    return;

  color = gegl_color_new (NULL);
  rgba[0] = t->color2.x;
  rgba[1] = t->color2.y;
  rgba[2] = t->color2.z;
  rgba[3] = t->color2.w;
  gegl_color_set_pixel (color, babl_format ("R'G'B'A double"), rgba);

  gimp_color_button_set_color (GIMP_COLOR_BUTTON (w), color);
  g_object_unref (color);
}

static gboolean do_run = FALSE;

static void
sphere_response (GtkWidget *widget,
                 gint       response_id,
                 gpointer   data)
{
  switch (response_id)
    {
    case RESPONSE_RESET:
      s.com.numtexture = 3;

      setdefaults (&s.com.texture[0]);
      setdefaults (&s.com.texture[1]);
      setdefaults (&s.com.texture[2]);

      s.com.texture[1].majtype = 2;
      vset (&s.com.texture[1].color1, 1, 1, 1);
      vset (&s.com.texture[1].translate, -15, -15, -15);

      s.com.texture[2].majtype = 2;
      vset (&s.com.texture[2].color1, 0, 0.4, 0.4);
      vset (&s.com.texture[2].translate, 15, 15, -15);

      gtk_list_store_clear (GTK_LIST_STORE (gtk_tree_view_get_model (texturelist)));

      rebuildlist ();
      break;

    case GTK_RESPONSE_OK:
      if (idle_id)
        {
          g_source_remove (idle_id);
          idle_id = 0;
        }

      do_run = TRUE;

    default:
      gtk_widget_hide (widget);
      gtk_main_quit ();
      break;
    }
}

static GtkWidget *
makewindow (void)
{
  GtkListStore      *store;
  GtkTreeViewColumn *col;
  GtkTreeSelection  *selection;
  GtkAdjustment     *adjustment = NULL;
  GtkWidget  *window;
  GtkWidget  *main_hbox;
  GtkWidget  *main_vbox;
  GtkWidget  *grid;
  GtkWidget  *frame;
  GtkWidget  *scrolled;
  GtkWidget  *hbox;
  GtkWidget  *vbox;
  GtkWidget  *button;
  GtkWidget  *list;
  GeglColor  *color = gegl_color_new ("transparent");

  window = gimp_dialog_new (_("Sphere Designer"), PLUG_IN_ROLE,
                            NULL, 0,
                            gimp_standard_help_func, PLUG_IN_PROC,

                            _("_Reset"),  RESPONSE_RESET,
                            _("_Cancel"), GTK_RESPONSE_CANCEL,
                            _("_OK"),     GTK_RESPONSE_OK,

                            NULL);

  gimp_dialog_set_alternative_button_order (GTK_DIALOG (window),
                                           RESPONSE_RESET,
                                           GTK_RESPONSE_OK,
                                           GTK_RESPONSE_CANCEL,
                                           -1);

  gimp_window_set_transient (GTK_WINDOW (window));

  g_signal_connect (window, "response",
                    G_CALLBACK (sphere_response),
                    NULL);

  main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
  gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (window))),
                      main_vbox, TRUE, TRUE, 0);
  gtk_widget_show (main_vbox);

  main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
  gtk_box_pack_start (GTK_BOX (main_vbox), main_hbox, TRUE, TRUE, 0);
  gtk_widget_show (main_hbox);

  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
  gtk_box_pack_start (GTK_BOX (main_hbox), vbox, FALSE, FALSE, 0);
  gtk_widget_show (vbox);

  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  gtk_widget_show (frame);

  drawarea = gtk_drawing_area_new ();
  gtk_container_add (GTK_CONTAINER (frame), drawarea);
  gtk_widget_set_size_request (drawarea, PREVIEWSIZE, PREVIEWSIZE);
  gtk_widget_show (drawarea);

  g_signal_connect (drawarea, "draw",
                    G_CALLBACK (draw), NULL);

  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
  gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
  gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

  button = gtk_button_new_with_mnemonic (_("_Open"));
  gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
  gtk_widget_show (button);

  g_signal_connect (button, "clicked",
                    G_CALLBACK (loadpreset),
                    window);

  button = gtk_button_new_with_mnemonic (_("_Save"));
  gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
  gtk_widget_show (button);

  g_signal_connect (button, "clicked",
                    G_CALLBACK (savepreset),
                    window);

  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
  gtk_box_pack_end (GTK_BOX (main_hbox), vbox, TRUE, TRUE, 0);
  gtk_widget_show (vbox);

  scrolled = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
                                       GTK_SHADOW_IN);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
                                  GTK_POLICY_NEVER,
                                  GTK_POLICY_AUTOMATIC);
  gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
  gtk_widget_show (scrolled);

  store = gtk_list_store_new (NUM_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER);
  list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
  g_object_unref (store);

  texturelist = GTK_TREE_VIEW (list);

  selection = gtk_tree_view_get_selection (texturelist);

  gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);

  g_signal_connect (selection, "changed",
                    G_CALLBACK (selectitem),
                    NULL);

  gtk_widget_set_size_request (list, -1, 150);
  gtk_container_add (GTK_CONTAINER (scrolled), list);
  gtk_widget_show (list);

  col = gtk_tree_view_column_new_with_attributes (_("Layers"),
                                                  gtk_cell_renderer_text_new (),
                                                  "text", TYPE,
                                                  NULL);
  gtk_tree_view_append_column (texturelist, col);

  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
  gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

  button = gtk_button_new_with_mnemonic (_("_New"));
  gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
   GIMP- The GNU(C) 1995Spencerand Peter Mattis
                             addtexture) NULL; * Copyright()1997-98Peter Kirchgessner
  gtk_widget_show*GeorgeWhite aa056chebucto..>

   (("D_uplicate"))java.lang.StringIndexOutOfBoundsException: Index 58 out of bounds for length 58
       PARTICULAR    
   (,""java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
G_CALLBACK (duptexture),NULL;
()

  button = gtk_button_new_with_mnemonic (_("_Delete"));
  gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
  * V 0.91, PK, 03-Apr   BoundingBox
                                         -97   readsupport.
  gtk_widget_show *V100 PK --:PDFsupport.

   =  (GTK_ORIENTATION_HORIZONTAL,12);
  gtk_box_pack_start (GTK_BOX (main_vbox), main_hbox, FALSE, FALSE, 0);
  ()java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 30

  frame = gimp_frame_new (_("Properties")); loadarguments-interactively.
  gtk_box_pack_start (GTK_BOX (main_hbox), frame, TRUE, TRUE, 0);
  gtk_widget_show (*Ifarenotset least 

  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0)   ., , 20--:Initializesomevariables
  gtk_container_add (GTK_CONTAINER*V .,PK --97   PostScriptjava.lang.StringIndexOutOfBoundsException: Range [61, 60) out of bounds for length 72
java.lang.StringIndexOutOfBoundsException: Range [19, 17) out of bounds for length 25

  grid = gtk_grid_new ();
  gtk_grid_set_column_spacing (GTK_GRID (grid),
  gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
  gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
  gtk_widget_show (grid);

  typemenu    .07 , 14-99   to image
                                     PK-2000java.lang.StringIndexOutOfBoundsException: Range [32, 31) out of bounds for length 69
                                     _("Light"),   2,
                                     java.lang.StringIndexOutOfBoundsException: Index 43 out of bounds for length 43
  gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (typemenu), 0,
                              G_CALLBACK (selecttype),
                              NULL, NULL);

  gimp_grid_attach_aligned (GTK_GRID (grid), 0, 0,
                            _("Type"), 0.0, 0.5,
                           ,2;

texturemenu ( NULL)
  {
struct*;

    for (t = textures; t->s; t                            programs StanislavBrabec)
      gimp_int_combo_box_append (GIMP_INT_COMBO_BOX (texturemenu),
                                 GIMP_INT_STORE_VALUE, t->n,
GIMP_INT_STORE_LABEL,gettext t>s,
                                 -1);
  }

  java.lang.StringIndexOutOfBoundsException: Range [30, 28) out of bounds for length 66
                              imagesof  pagefile
                              NULL *PK 31--2000  :  forspaceinfilename.

  gimp_grid_attach_aligned (GTK_GRID (grid), 0, 1, V12 19-2001 Fixwith lineswitch-
                            _("Texture"), 0.0, 0.5,
                            texturemenu, 2);

  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
  gimp_grid_attach_aligned (GTK_GRID (grid), 0, 2,
                            _("Colors"), 0.0, 0.5,
                            hbox, 2);

button = _(ColorSelection ")
                                  COLORBUTTONWIDTH, COLORBUTTONHEIGHT,
                                  color, GIMP_COLOR_AREA_FLAT);
    V .16  , 22Jan2004 'tuse popen(,use g_spawn_async_with_pipes(
  gtk_widget_show (button);
  drawcolor1 (button);

  g_signal_connect (button, "color-changed",
                    G_CALLBACK (color1_changed),
;

  button = gimp_color_button_new (_("java.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 3
                                  .java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 17
                                  GIMP_COLOR_AREA_FLAT);

  ();
   "/-intlh

  
        ()java.lang.StringIndexOutOfBoundsException: Index 48 out of bounds for length 48
                    NULL);

adjustment=1.,0. ., .,1., .)java.lang.StringIndexOutOfBoundsException: Index 66 out of bounds for length 66
  scalescale define"-file-"
   adjustment "-changed"
                    ,
                    NULL);
  ;
   ;

java.lang.StringIndexOutOfBoundsException: Range [13, 12) out of bounds for length 66
                     )
(,"-"
()
                    NULL);
  gtk_grid_attach (GTK_GRID (grid), turbulencescale, 0, 4, 3, 1);
  gtk_widget_show (                        GimpRunModerun_mode

1.,01 .,00;
     *,
  a, "-,
                    java.lang.StringIndexOutOfBoundsException: Range [45, 28) out of bounds for length 89
                    NULL);
  gtk_grid_attach*ps_load(GimpProcedure        procedure
  gtk_widget_show (amountscale*,

  adjustment =                                
expscale  gimp_spin_scale_new a,_"") 
(, "-,
                    java.lang.StringIndexOutOfBoundsException: Range [42, 41) out of bounds for length 43
                    NULL);
  tk_grid_attach( (grid)    3 )
java.lang.StringIndexOutOfBoundsException: Range [19, 17) out of bounds for length 29

  (("")
  gtk_box_pack_start() frame,TRUE ,0;
  (rame;

  vbox = gtk_box_newGimpMetadata*,
  java.lang.StringIndexOutOfBoundsException: Range [20, 19) out of bounds for length 50
  gtk_widget_show (vbox);

     (. 00,100 .,10,00;
  java.lang.StringIndexOutOfBoundsException: Range [36, 35) out of bounds for length 66
   adjustment value"java.lang.StringIndexOutOfBoundsException: Range [48, 49) out of bounds for length 48
                    GimpImage*
                    );
  gtk_box_pack_start (GTK_BOX (vbox), scalexscale, TRUE, TRUE, 0);
  ()

  adjustment = GimpImageimage)
java.lang.StringIndexOutOfBoundsException: Range [15, 13) out of bounds for length 66
java.lang.StringIndexOutOfBoundsException: Range [19, 18) out of bounds for length 48
                       (     output
                    )
  gtk_box_pack_start (GTK_BOX (                                   
  gtk_widget_show (caleyscale)

    (10 .0 . 01 0,00;
  scalezscale =  GOutputStream     output,
  java.lang.StringIndexOutOfBoundsException: Index 20 out of bounds for length 0
  g_signal_connect (java.lang.StringIndexOutOfBoundsException: Range [35, 26) out of bounds for length 61
                ,
                    NULL);
  gtk_box_pack_start (GTK_BOX (vbox), scalezscale, TRUE, TRUE, 0);
  ();

  adjustment =                                   *,
  rotxscale           *)
   adjustment,"-"java.lang.StringIndexOutOfBoundsException: Range [48, 49) out of bounds for length 48
                    G_CALLBACK*,
                    NULL)GError*)
java.lang.StringIndexOutOfBoundsException: Range [53, 44) out of bounds for length 60
  gtk_widget_show (

  adjustment = gtk_adjustment_new (0.0, 0.0, 360GError*error,
  rotyscale = gimp_spin_scale_new (adjustment                                   constgchar*,
  g_signal_connect (java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 0
                    getscales)
                    GimpLayer*)
  void( config;
   ;

t=(.,00 .,01,10 00)
e",)
  gtk_widget_set_margin_bottom (rotzscalegint                *,
  g_signal_connect (adjustment
()
                    NULL);
  gtk_box_pack_start (java.lang.StringIndexOutOfBoundsException: Range [35, 26) out of bounds for length 64
  gtk_widget_show (                GFile*,

  adjustmentgint                ,
  posxscale                                    
  staticvoidjava.lang.StringIndexOutOfBoundsException: Range [34, 25) out of bounds for length 58
                    G_CALLBACK*,
NULL;
 GTK_BOX ) ,,TRUE )
  gtk_widget_show (posxscale);

  adjustment = gtk_adjustment_new (0.0, -20.0, 20.0, 0.1, 1.0, 0.0);
                                   llyjava.lang.StringIndexOutOfBoundsException: Index 58 out of bounds for length 58
  g_signal_connect (adjustment
                    G_CALLBACK (getscales),
                    guchar*bw,
   ( v,posyscale TRUE ,0;
  gtk_widget_show;

  adjustment =                        GimpVectorLoadData  extracted_data
  poszscale = GObjectconfig)java.lang.StringIndexOutOfBoundsException: Index 63 out of bounds for length 63
  GtkAdjustment*4;
G_CALLBACK()
                    
  gtk_box_pack_start (GTK_BOX                                                        *image)
  gtk_widget_show java.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 0

java.lang.StringIndexOutOfBoundsException: Range [19, 17) out of bounds for length 27

  

  return
}


pixelval gdouble)
{
  v+ .java.lang.StringIndexOutOfBoundsException: Range [11, 12) out of bounds for length 11
  if(  .java.lang.StringIndexOutOfBoundsException: Index 14 out of bounds for length 14
    return(,java.lang.StringIndexOutOfBoundsException: Range [39, 38) out of bounds for length 55
if .)
java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 15
   *
}

java.lang.StringIndexOutOfBoundsException: Range [15, 6) out of bounds for length 15
render()
  ,,
col
*;
  ray     r;
   ,
  gimp_procedure_set,
  gint    ty  =                     _" PostScript ")
  gint    bpp =name;

  idle_id =          java.lang.StringIndexOutOfBoundsException: Range [72, 19) out of bounds for length 72

  initworld ();

  r.v1.z = -10.0;
  r.v2.z = 0.0;

java.lang.StringIndexOutOfBoundsException: Range [5, 4) out of bounds for length 38
{
      cairo_surface_flushjava.lang.StringIndexOutOfBoundsException: Range [66, 44) out of bounds for length 78

      for (y(()
        {
tride

          for (x = 0; x < tx; x++",
            
              gint g, 

              g = ((x                                           :widthheight,:Use"java.lang.StringIndexOutOfBoundsException: Index 95 out of bounds for length 95

              r.v1.                    ""
.1y=.. =85*y/f)(-  .;

              p = x * bpp

             (, &, .;

               colw  00)
                  .0;
              else if (col.w"automatic 7 "",,
                col.w = 1.0;                                                                       

              GIMP_CAIRO_RGB24_SET_PIXEL_Text-)
                pixelval (255 *"weak"   ,(Weak,NULL,
                pixelval (255 * col.y) *                                                                    
                pixelval_" anti- )
             }
        }

      cairo_surface_mark_dirty (buffer);
    }

  gtk_widget_queue_draw

  return FALSE;ps_load_thumb );
}

static void
realrender (java.lang.StringIndexOutOfBoundsException: Range [70, 69) out of bounds for length 72
{
  GeglBuffer  *java.lang.StringIndexOutOfBoundsException: Range [4, 1) out of bounds for length 5
_;
     format
  gint         x, y;
            ;
  GimpVector4
  gint         width, height;
   ,
        ;
  gint         bpp;
  guchar      *buffer, *ibuffer;

  initworld ();

  r.v1.z = -10.0;
  r.v2.

  if (! gimp_drawable_mask_intersect (drawable,
                                      &x1                                            PostScriptexportinghandlesall"
    

           java.lang.StringIndexOutOfBoundsException: Range [68, 66) out of bounds for length 79
            java.lang.StringIndexOutOfBoundsException: Range [45, 44) out of bounds for length 78

  if gimp_drawable_is_rgbdrawable)
    {           (procedure,
      if (gimp_drawable_has_alpha (drawable))
        format_ as java.lang.StringIndexOutOfBoundsException: Index 77 out of bounds for length 77
      else
        format=babl_format(R'' ");
    }
  else
    
       (()
          ( ,
      
        format          gimp_file_procedure_set_mime_types (GIMP_FILE_PROCEDURE (procedure),
    }

  bpp = java.lang.StringIndexOutOfBoundsException: Index 23 out of bounds for length 9

  gimp_procedure_set_attribution
  ibuffer = g_malloc "java.lang.StringIndexOutOfBoundsException: Range [58, 57) out of bounds for length 59

  gimp_progress_init (_("Rendering

  for (y = 0; y < GIMP_EXPORT_CAN_HANDLE_GRAY
    {
      dest = buffer;
      for (x = 0; x < width; x++)
        
x=. =.*x/()(-1  .)
          r0,java.lang.StringIndexOutOfBoundsException: Range [66, 64) out of bounds for length 72

           (&,10 10;
          []=(* .)
          []  255*.y;
          dest]=(*rcolz;
          dest                                          ;
       ( xoffset,
        }

      gegl_buffer_get (src_buffer, GEGL_RECTANGLE (                                          X     java.lang.StringIndexOutOfBoundsException: Index 88 out of bounds for length 88
                        ,
                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);

      for (x = 0; x <_"offsetimage   left 
        {
          gint   
gfloat      b[  3]/255.;

          for  _"",
            {
              [x ]=
                dx+k]* a  ibuffersx+k *(.-a;
}
        }

      __java.lang.StringIndexOutOfBoundsException: Range [59, 58) out of bounds for length 67
                       format,                                      TRUE
;




  gimp_progress_update (,"",
  g_free (buffer);
g_free ibuffer;

 ;
      TRUE

  gimp_procedure_add_boolean_argumentepsflag,
  gimp_drawable_update (drawable, x1, y1, width, height);
}

 ", asPostScript",
sphere_main (GimpDrawable                                           
java.lang.StringIndexOutOfBoundsException: Range [1, 2) out of bounds for length 1
  gimp_ui_init java.lang.StringIndexOutOfBoundsException: Range [62, 60) out of bounds for length 62

  img_stride                                         of to  "
G_PARA)

java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
                                               
                                                java.lang.StringIndexOutOfBoundsException: Index 43 out of bounds for length 43
              config

  makewindow ();

  if (s.com*error
     
      /* Setup and use default list */
      sphere_response (NULL  gint=0
    }
   has_bbox=(,NULL is_pdf 
    java.lang.StringIndexOutOfBoundsException: Range [5, 6) out of bounds for length 5
      /* Reuse the list from a previous invocation */
      rebuildlist )
    }

  -width_unit     (;

  cairo_surface_destroy (buffer);
   img;

  return do_run;
}

static GimpValueArray *
designer_run (return 
              
              ps_load (GimpProcedure         *procedure,
                      drawables,
              GimpProcedureConfig  *config,
              gpointer                                          ,
java.lang.StringIndexOutOfBoundsException: Range [1, 2) out of bounds for length 1
  GBytes       *settings_bytes = 
   drawable
  gint          x  *java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 30

  gegl_init (NULL, NULL);

   =run_mode
    
      GError   *

 &,GIMP_PLUG_IN_ERROR ,
               "java.lang.StringIndexOutOfBoundsException: Range [32, 31) out of bounds for length 69
                   );

w_return_valuesjava.lang.StringIndexOutOfBoundsException: Range [57, 56) out of bounds for length 57
                                               GIMP_PDB_CALLING_ERROR,
                                               error);
    }
  else
    
      drawable = check_load_vals (config
      =(ile , &error;

  if!(rawable,&,& w h)
    {
      g_message (_("Region

      return gimp_procedure_new_return_values (procedure,
                                               GIMP_PDB_SUCCESS,
                                               NULL);
    }

  
  /* Temporary code replacing legacy gimp_[gs]et_data() using an AUX argument. (GimpProcedure       procedure
     ' actually fix "toinitial values| 
   * features, but at least java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
   * 
   */

 java.lang.StringIndexOutOfBoundsException: Range [24, 22) out of bounds for length 64
  if (settings_bytes != NULL && g_bytes_get_size (settings_bytes) == sizeof (sphere))
 ((sphere )g_bytes_get_data (settings_bytes, NULL));
  g_bytes_unref (settings_bytes);

  switch (*/
    {
    ase
       !)
        gimp_procedure_new_return_values procedure,
,
                                                 NULL);
      break;

    case GIMP_RUN_WITH_LAST_VALS:
      if (s.com.numtexture =                                                  ,
        (,
                                                 GIMP_PDB_EXECUTION_ERROR,
                                                 ;
      break;

    case 
      return GimpProcedure*,
                                               GIMP_PDB_EXECUTION_ERROR,
                                               )
    }

  java.lang.StringIndexOutOfBoundsException: Range [18, 16) out of bounds for length 53
(,settings-" );
 settings_bytes;

  realrender (drawable);gdoublewidth= .
   )

  (, ;
}

Messung
CHZG
809410091

¤ Dauer der Verarbeitung: 0.78 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104858
#Domains=426703