Logo Search packages:      
Sourcecode: randim version File versions  Download package

draw.c

/* draw.c: randim functions related to actually drawing image. */

/* 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.  Please see the file "COPYING"
   for details.  If you have not received it along with this program,
   please write to the Free Software Foundation, Inc., 675 Mass. Ave.,
   Cambridge, MA 02139, USA. */

#include "randim.h"

/*************************************************************************/
/* image generation */

/* apply_trans() chooses a random transformation and applies it to the
   current point.  It relies heavily on global variables, partly to avoid
   passing overhead.  Uses pointer incrementing for speed.  Hence have
   arrangements
                        [1 2 3] [4]
  [1 2] [3]             [5 6 7] [8]
  [4 5] [6] for 2d,           [9 a b] [c] for 3d.     
 */
inline float *
apply_trans()
{
  float                 *p,
                  newx,newy,newz;
  float                 f;

  p = a[prob[qrand() >> RANDSHIFT]];

  if (dimension == 2) {
    newx = *p++ * gx;
    newx += *p++ * gy;
    newx += *p++;
    newy = *p++ * gx;
    newy += *p++ * gy;
    newy += *p++;
    vy = (gy = newy);
    vx = (gx = newx);
    p += 6;  /* hack to get trans return right */
    }
  else if (cmode & 2) {
    newx = *p++ * gx; newx += *p++ * gy; newx += *p++ * gz;
    newx += *p++;
    newy = *p++ * gx; newy += *p++ * gy; newy += *p++ * gz;
    newy += *p++;
    newz = *p++ * gx; newz += *p++ * gy; newz += *p++ * gz;
    newz += *p++;
    gx = newx; gy = newy; gz = newz;
    vx = gx; vy = gy;
    f = (gz * ozscale + ozoffset) * NCOLORS;
    if ((f < 0) || (f > NCOLORS)) f = 1.0;
    p = &f;
    }
  else {    /*dimension = 3*/
    newx = *p++ * gx; newx += *p++ * gy; newx += *p++ * gz;
    newx += *p++;
    newy = *p++ * gx; newy += *p++ * gy; newy += *p++ * gz;
    newy += *p++;
    newz = *p++ * gx; newz += *p++ * gy; newz += *p++ * gz;
    newz += *p++;
    gx = newx; gy = newy; gz = newz;
    /*convert to 2d*/
    vx = gx * _cx + gy * _cy + gz * _cz;
    vy = gx * _sx + gy * _sy + gz * _sz;
    }

  /*  if (isnan(vx) || isnan(vy)) {
    printf("apply_trans: isnan(%f,%f)\n",vx,vy);
    printf("%d / %d\n",p - &(a[0][0]), asize);
    } */

  /* either point to int color value or which trans was chosen */
  return(p);
}


/* apply_ftrans() similar to apply_trans but applies given transformation
   rather than random one (could add to apply_trans but would add an extra
   if statement into the most speed-determining part of the program) */
inline void
apply_ftrans(int tr)
{
  float                 *p,
                  newx,newy,newz;
  float                 f;

 /* if using qrand() make sure RANDSHIFT is incremented in init() */
  p = a[tr];
  if (dimension == 2) {
    newx = *p++ * gx;
    newx += *p++ * gy;
    newx += *p++;
    newy = *p++ * gx;
    newy += *p++ * gy;
    newy += *p++;
    vy = (gy = newy);
    vx = (gx = newx);
    }
  else if (cmode & 2) {
    newx = *p++ * gx; newx += *p++ * gy; newx += *p++ * 0;
    newx += *p++;
    newy = *p++ * gx; newy += *p++ * gy; newy += *p++ * 0;
    newy += *p++;
    newz = *p++ * gx; newz += *p++ * gy; newz += *p++ * gz;
    newz += *p++;
    gx = newx; gy = newy; gz = newz;
    vx = gx; vy = gy;
    /*f = log((2.71828-1)*(gz * ozscale + ozoffset)+1) * NCOLORS; */
    f = (gz * ozscale + ozoffset) * NCOLORS;
    if ((f < 0) || (f > NCOLORS)) f = 1.0;
    p = &f;
    }
  else {    /*dimension = 3, no cmode*/
    newx = *p++ * gx; newx += *p++ * gy; newx += *p++ * gz;
    newx += *p++;
    newy = *p++ * gx; newy += *p++ * gy; newy += *p++ * gz;
    newy += *p++;
    newz = *p++ * gx; newz += *p++ * gy; newz += *p++ * gz;
    newz += *p++;
    gx = newx; gy = newy; gz = newz;
    vx = gx * _cx + gy * _cy + gz * _cz;
    vy = gx * _sx + gy * _sy + gz * _sz;
    }  
}


/* draw() computes a fixed number of points from the transforms, scaling
  and plotting them if they're in range.  It is called periodically by Xt.
  In accessing transformation data, pointer reference and incrementing
  are used to reduce lookup time.*/
Boolean
draw(XtPointer cdata)
{
  XPoint          *plistptr;
  XPoint          *cplistptr[NCOLORS];
  float                 px,py;  /* should be shorts, but for an unknown
                                   reason this caused an artifact plotting a
                                   cross in the middle, so they're floats :( */
  short                 j,count,jtop=PN; /*j is rapid index, i slow index*/
  short                 i,
                  ccount[NCOLORS];
  unsigned long         diff;
  float                 *ap;

  gx = startx, gy = starty, gz = startz;  /*set to stored start values */

  /*Plot N points, where N = PN*n; these numbers are defined in .h and init()*/
  for (i=0;i<ploopn;i++) {
    count = 0;
    if (cmode & 1) {
      for (j=0;j<n_trans;j++)
      cplistptr[j] = cplotlist[j], ccount[j] = 0;
      }
    else if (cmode & 2) {
      for (j=0;j<NCOLORS;j++)
      cplistptr[j] = cplotlist[j], ccount[j] = 0;
      }
    else
      plistptr = plotlist;
    for (j=0;j<jtop;j++) {    /*Plot batch of points.*/
      ap = apply_trans();     /* set vx,vy */
      px = vx*xscale + xoffset, py = vy*yscale + yoffset; /**/
      if (inrange(px,py)) {
      px += (hsize2 - 1), py += (vsize2 - 1); /**/
      if (cmode & 1) {
        diff = (ap - &(a[0][0])) / asize - 1;
        cplistptr[diff]->x = px, cplistptr[diff]->y = py;
        cplistptr[diff]++, ccount[diff]++, count++;
        }
      else if (cmode & 2) {
        diff = (int) *ap;
        cplistptr[diff]->x = px, cplistptr[diff]->y = py;
        cplistptr[diff]++, ccount[diff]++, count++;
        }
      else {
        plistptr->x = px, plistptr->y = py;
        plistptr++;
        count++;
        }
      }
      else if (!zoomf)  /*Out of range - reset.*/
      gx = 1.0 / xscale, gy = 1.0 / yscale, gz = 1.0;
      else {            /*If zoomed, keep if in original range.*/
      px = vx*oxscale + oxoffset, py = vy*oyscale + oyoffset;
      if (!inrange(px,py))
        gx = 1.0 / xscale, gy = 1.0 / yscale, gz = 1.0;
      }     /*else*/
      } /*for*/
    /*this actually plots the points, in batch mode*/
    if (cmode & 1)
      for (j=0;j<n_trans;j++)
      XDrawPoints(display,canvas,cgc[j],cplotlist[j],ccount[j],
                CoordModeOrigin);
    else if (cmode & 2)
      for (j=0;j<NCOLORS;j++)
      XDrawPoints(display,canvas,cgc[j],cplotlist[j],ccount[j],
                CoordModeOrigin);
    else
      XDrawPoints(display,canvas,gc,plotlist,count,CoordModeOrigin);
    /*(other mode is CoordModePrevious)*/
    if (slidepoints) {
      plottedpoints += count;  /* keep track for slidepoints */
      if (plottedpoints > slidepoints)
      next_im();
      }
    }

  startx = gx, starty = gy, startz = gz;  /*reset stored values*/
  return(False);  /*so Xt will keep calling this procedure*/
}


/* animate() acts similarly to draw() except that it draws into a pixmap
   until animpoints is reached, then copies the pixmap into the visible
   window, clears the pixmap, and modifies the transformation set (starting
   the next frame of an animation.
   Before doing this, it calls check_astep() to make sure the stepsize is
   within a reasonable range. */
Boolean
animate(XtPointer cdata)
{
  XPoint          *plistptr;
  XPoint          *cplistptr[NCOLORS];
  float                 px,py;  /* should be shorts, but for an unknown
                                   reason this caused an artifact plotting a
                                   cross in the middle, so they're floats :( */
  short                 j,count,jtop=PN; /*j is rapid index, i slow index*/
  short                 i,
                  ccount[NCOLORS];
  unsigned long         diff;
  float                 *ap;

  gx = startx, gy = starty, gz = startz;

  /*Plot N points, where N = PN*n; these numbers are defined in .h and init()*/
  for (i=0;i<ploopn;i++) {
    count = 0;
    if (cmode & 1) {
      for (j=0;j<n_trans;j++)
      cplistptr[j] = cplotlist[j], ccount[j] = 0;
      }
    else if (cmode & 2) {
      for (j=0;j<NCOLORS;j++)
      cplistptr[j] = cplotlist[j], ccount[j] = 0;
      }
    else
      plistptr = plotlist;
    for (j=0;j<jtop;j++) {    /*Plot batch of points.*/
      ap = apply_trans();
      px = vx*xscale + xoffset, py = vy*yscale + yoffset;
      if (inrange(px,py)) {
      px += (hsize2 - 1), py += (vsize2 - 1);
      if (cmode & 1) {
        diff = (ap - &(a[0][0])) / asize - 1;
        cplistptr[diff]->x = px, cplistptr[diff]->y = py;
        cplistptr[diff]++, ccount[diff]++, count++;
        }
      else if (cmode & 2) {
        diff = (int) *ap;
        cplistptr[diff]->x = px, cplistptr[diff]->y = py;
        cplistptr[diff]++, ccount[diff]++, count++;
        }
      else {
        plistptr->x = px, plistptr->y = py;
        plistptr++;
        count++;
        }
      } /* in range */
      else if (!zoomf)        /*Out of range - reset.*/
      gx = 1.0 / xscale, gy = 1.0 / yscale, gz = 1.0;
      else {            /*If zoomed, keep if in original range.*/
      px = vx*oxscale + oxoffset, py = vy*oyscale + oyoffset;
      if (!inrange(px,py))
        gx = 1.0 / xscale, gy = 1.0 / yscale, gz = 1.0;
      } /*else*/
      } /* for j */
    /*this actually plots the points, in batch mode*/
    if (cmode & 1)
      for (j=0;j<n_trans;j++)
      XDrawPoints(display,hcanvas,cgc[j],cplotlist[j],ccount[j],
                CoordModeOrigin);
    else if (cmode & 2)
      for (j=0;j<NCOLORS;j++)
      XDrawPoints(display,hcanvas,cgc[j],cplotlist[j],ccount[j],
                CoordModeOrigin);
    else
      XDrawPoints(display,hcanvas,gc,plotlist,count,CoordModeOrigin);
    plottedpoints += count;  /* keep track for anim framelength */
    if (plottedpoints > animpoints) {  /* new frame */
      plottedpoints = 0;
      XCopyArea(display,hcanvas,canvas,gcCopy,0,0,hsize,vsize,0,0);
      XFillRectangle(display,hcanvas,gcClear,0,0,hsize,vsize);
      modify_trans();
      }

    }  /* for ploopn */

  startx = gx, starty = gy, startz = gz;  /*reset stored values*/
  return(False);  /*so Xt will keep calling this procedure*/

}

/*************************************************************************/
/*
The "Zen" of Computer Images

If you are reading this it means you have downloaded this program,
hopefully run it, and taken an interest in the code -- for whatever
reason.  Maybe the beauty of the images has caught you up, maybe the
nature of the mathematics or the algorithms.  In any case, I'll assume
that you share enough of my kind of fascination with life to take an
interest in what follows...

I first wrote this program on an 8086 IBM, with only the "fully random"
image mode, not very well implemented at that.  It could take many
minutes for an image to become reasonably visible on the screen.  Far
from being a source of frustration though, this slowness led me to
exploration of one of the deepest mysteries of art -- the role of the
perceiver.  Here there could be no question that there was nothing
"built into" the image with any semantic content.  Chance resemblance
was not ruled out of course -- just as a cloud may take on the aspect of
a ship or a face, the eye can see patterns in randim images.  But the
first time I put on a piece of music and started up an image, I did not
expect very much special -- a way of occupying my ear while I did
something different with my eyes perhaps, as when reading a newspaper or
a book.

Imagine my surprise, then, when I began to see things in the image that
related to the music, often in the most profound of ways.  Shapes
depicted actors or landscapes in the stories my mind built around the
music; the very space between the image points took on new meaning.
This often remained true even as the music continued to evolve while the
image remained constant.  What was happening was that, because of the
slow speed, the image in the beginning consisted of only a few dots,
which my mind could connect in many alternative ways -- like a Necker
cube but indefinitely more subtle.  Given this kind of Rorschach, the
forms given to my mind from the music influenced which configurations
came to the fore.  Slowly the image changed with the addition of more
dots, and some interpretations had to be discarded while new ones became
possible.  As the music progressed, new forms emerged in keeping with
it, and sometimes the effect went in the other direction, my attention
directed to parts of the music in keeping with the tableaus in the image
I saw.  The interpretive resources of the mind are powerful indeed.

Herein was framed a challenge -- to write a program to generate images
so that this process is facilitated as much as possible.  Inspiration
in this endeavor comes from systems like the Chinese _I Ching_, or the
Tarot, which operate on the same principle as the daily horoscopes
published in daily newspapers -- they are phrased in terms such as to
be applicable to a very wide range of potential situations.  This
property tends to be pooh-poohed by those seeking science in
astrology, but it is nontrivial to achieve and not without its uses.
C. G. Jung, for example, wrote of the benefits of using the _I Ching_
for enhancing self knowledge and balance in the personality.  As he
wrote, "Don't you see how useful the _I Ching_ is in making you
project your hitherto unrealized thoughts into its abstruse
symbolism?"  [the "reply" of a hypothetical Chinese sage defending the
_I Ching_]

Although I am not sure whether the present incarnation of randim could
be used as a means to psychological growth (!), the exercise of drawing
structured meaning from the abstract forms it produces is both
pleasurable and good for maintaining mental fluidity.  Like any
activity, if it is too difficult, it soon loses its interest, yet if it
is too easy, it loses its fascination.

There are a number of ways to practice this with randim, each optimized
by different parameter settings.  The simplest is simply to view the
completed images, looking for whatever you can find in them.  Music or
some other external source of material can often help with this.  Now
that PCs are much faster than the old 8086, the original method for
obtaining a dynamic image by watching the plotting process doesn't work
anymore -- but there are three other alternatives.  The first is simply
to call up new images at will at transitions in the music.  The second is
to zoom in on an image until this slows the plotting speed.  The third is
to use the animation mode.  In this case, care must be taken with the
frame rate settings as well as the initial seed transformations -- some
types work better than others.  With any luck, you will find yourself
pleasantly surprised at the large number of correspondences between the
music and dynamic aspects of the image you will be able to find...

(ABR 10/98)

*/


/* densityDraw() acts similarly to draw() except that it stores colors and
   numbers of hits into a temporary storage area until denspoints is reached,
   then it determines color at each point based on average color and intensity
   based on a logarithmic function of the number of hits, and plots this into
   an offscreen pixmap for copying onto the screen.
*/
Boolean
densityDraw(XtPointer cdata)
{
  XPoint          *plistptr;
  XPoint          *cplistptr[NCOLORS];
  float                 px,py;  /* should be shorts, but for an unknown
                                   reason this caused an artifact plotting a
                                   cross in the middle, so they're floats :( */
  short                 j,count,jtop=PN; /*j is rapid index, i slow index*/
  short                 i,k,ncols, col,
                  ccount[NCOLORS];
  unsigned long         diff;
  float                 *ap;

  static IfsPixel **phosphor = NULL;
  static int            xdim=0, ydim=0;
  static XGCValues      gcvals;           /*for setting graphics context*/
  static char           progFlag = 1;
  IfsPixel        *ifsPtr;
  int             maxC, progPt;
  float                 sc;
  short                 intens;

  /* Allocate phosphor array */
  if ((xdim != hsize) || (ydim != vsize)) {
    if (phosphor != NULL) {
      for (i=0; i<vsize; i++) free(phosphor[i]);
      free(phosphor);
    }
    phosphor = calloc(vsize, sizeof(IfsPixel *));
    if (!phosphor) memfail("phosphor matrix");
    for (i=0; i<vsize; i++)
      if (!(phosphor[i] = calloc(hsize, sizeof(IfsPixel)) ))
        memfail("phosphor row");
    xdim = hsize, ydim = vsize;
    dotmsg(1);
    progPt = denspoints / MAXDOTS;
  } else if (diNew) {
    /* clear it */
    for (i=0; i<vsize; i++) {
      ifsPtr = phosphor[i];
      for (j=0; j<hsize; j++) {
        ifsPtr->avgColor = 0, ifsPtr->hitFreq = 0;
        ifsPtr++;
      }
    }
    diNew = 0;
    progFlag = 1;
    dotmsg(1);
    progPt = denspoints / MAXDOTS;
  }

  gx = startx, gy = starty, gz = startz;

  /*"Plot" N points, where N = PN*n; these are defined in .h and init()*/
  for (i=0;i<ploopn;i++) {
    count = 0;
    if (cmode & 1) {
      for (j=0;j<n_trans;j++)
      cplistptr[j] = cplotlist[j], ccount[j] = 0;
      }
    else if (cmode & 2) {
      for (j=0;j<NCOLORS;j++)
      cplistptr[j] = cplotlist[j], ccount[j] = 0;
      }
    else
      plistptr = plotlist;
    for (j=0;j<jtop;j++) {    /*Plot batch of points.*/
      ap = apply_trans();
      px = vx*xscale + xoffset, py = vy*yscale + yoffset;
      if (inrange(px,py)) {
      px += (hsize2 - 1), py += (vsize2 - 1);
      if (cmode & 1) {
        diff = (ap - &(a[0][0])) / asize - 1;
        cplistptr[diff]->x = px, cplistptr[diff]->y = py;
        cplistptr[diff]++, ccount[diff]++, count++;
        }
      else if (cmode & 2) {
        diff = (int) *ap;
        cplistptr[diff]->x = px, cplistptr[diff]->y = py;
        cplistptr[diff]++, ccount[diff]++, count++;
        }
      else {
        plistptr->x = px, plistptr->y = py;
        plistptr++;
        count++;
        }
      } /* in range */
      else if (!zoomf)        /*Out of range - reset.*/
        /* gx = (nrand(hsize) - xoffset - hsize2 + 1) / xscale,
           gy = (nrand(vsize) - yoffset - vsize2 + 1) / yscale, gz = 1.0; */
      gx = 1.0 / xscale, gy = 1.0 / yscale, gz = 1.0;
      else {            /*If zoomed, keep if in original range.*/
      px = vx*oxscale + oxoffset, py = vy*oyscale + oyoffset;
      if (!inrange(px,py))
        gx = 1.0 / xscale, gy = 1.0 / yscale, gz = 1.0;
      } /*else*/
      } /* for j */
    /*count up the points... doesn't seem to be any point in batching here*/
    if (cmode > 0) {
      ncols = (cmode & 1) ? n_trans : NCOLORS;
      for (j=0; j<ncols; j++)
        for (k=0; k<ccount[j]; k++) {
          ifsPtr = &(phosphor[cplotlist[j][k].y][cplotlist[j][k].x]);
          ifsPtr->avgColor = j;  /*PENDING: actually compute average */
          ifsPtr->hitFreq++;
        }
    }
    else {
      for (j=0; j<count; j++) {
        ifsPtr = &(phosphor[plotlist[j].y][plotlist[j].x]);
        ifsPtr->avgColor = 256;  /* no color */
        ifsPtr->hitFreq++;
      }
    }

    plottedpoints += count;  /* keep track for anim framelength */
    if (progFlag && (plottedpoints > progPt)) {
      dotmsg(0);
      progPt += denspoints/MAXDOTS;
    }
    if (plottedpoints > denspoints) {  /* new frame */
      maxC = 0;
      /* set range */
      for (j=0; j<vsize; j++) {
        ifsPtr = phosphor[j];
        for (k=0; k<hsize; k++) {
          if (ifsPtr->hitFreq > maxC) maxC = ifsPtr->hitFreq;
          ifsPtr++;
        }
      }
      sc = (double) N_INTS / log(maxC+2.0);
      /* draw into the hcanvas based on the IfsPixels */
      for (j=0; j<vsize; j++) {
        ifsPtr = phosphor[j];
        for (k=0; k<hsize; k++) {
          col = ifsPtr->avgColor;
          intens = log(ifsPtr->hitFreq+1) * sc;
          ifsPtr->avgColor = 0/*, ifsPtr->hitFreq = 0*/;
          ifsPtr++;
          gcvals.foreground = colors[NCOLORS + col * N_INTS + intens];
          XChangeGC(display, cgc[col],GCForeground, &gcvals);
          XDrawPoint(display,hcanvas,cgc[col], k,j);
        }
      }
      plottedpoints = 0;
      progFlag = 0;
      XCopyArea(display,hcanvas,canvas,gcCopy,0,0,hsize,vsize,0,0);
      XFillRectangle(display,hcanvas,gcClear,0,0,hsize,vsize);
      dotmsg(1);
      progPt = denspoints / MAXDOTS;
      }

    }  /* for ploopn */

  startx = gx, starty = gy, startz = gz;  /*reset stored values*/
  return(False);  /*so Xt will keep calling this procedure*/

}

/************************************************************************/
/* image transitioning */

/* next_im() clears and plots a new image with new transformations */
void
next_im()
{
  int i;

  XClearWindow(display,canvas);     /*clear display*/
  if (animflag || diFlag)
    XFillRectangle(display,hcanvas,gcClear,0,0,hsize,vsize);
  diNew = diFlag;
  plottedpoints = 0;

  /*Set trans variables - not always needed, but doesn't take long.*/
  n_trans = (int) pow(2,(double) logtn);
  hfactor = (int) pow(2,(double) loghtn);

  define(ttype);
  if (diffEqFlag)       /*if in diffEq mode, call to alter matrices*/
    for(i=0;i<n_trans;i++)
      addI(i);
  scale(SCALE_MAX);
  if (animflag) init_mods();

  /*clear and replot transreps*/
  for (i=0;i<8;i++) XClearWindow(display,trepWin[i]);
  plot_transreps();

  startx = starty = startz = 1.0;

  /* reactivate draw function */
  resume_draw();

}


/* add_trans() clears and plots a new image after a transformation has been
   changed */
void
add_trans()
{
  /*If in diffEq mode call function to update matrices appropriately.*/
  if (diffEqFlag) addI(tmodn);

  XClearWindow(display,canvas);
  plottedpoints = 0;
  scale(SCALE_MAX);
  if (animflag) init_mods();
  plot_transrep(tmodn);
  startx = starty = startz = 1.0;
  diNew = diFlag;
  resume_draw();
}

/* short functions to halt and resume drawing */
void
halt_draw()
{
  if (drawflag) {       /*stop drawing while we do this*/
    XtRemoveWorkProc(DrawID);
    drawflag = 0;
      }
}

void
resume_draw()
{
  if (!drawflag) {
    if (animflag) DrawID = XtAppAddWorkProc(appcontext, animate, NULL);
    else if (diFlag)  DrawID = XtAppAddWorkProc(appcontext, densityDraw, NULL);
    else DrawID = XtAppAddWorkProc(appcontext, draw, NULL);
    drawflag = 1;
    }
  plot_transreps();
}

Generated by  Doxygen 1.6.0   Back to index