// radio array, 5x5x5, random spacing
//
// compile with    cc -o a3d05 a3d05.c -lgd -lpng -lm
//
// Uses the libgd library.  For documentation see the file:
//      /usr/share/doc/gd-*/index.html  
//   or the website:  http://www.libgd.org/Reference
//  
// You will need truetype fonts.  If you don't have them, you can
// copy ../fonts/truetype/.. from openoffice.org to /usr/share/
//
// Uses swftools to build a swf movie from individual png images
//        for more information, see http://www.swftools.org/
//
// This constructs half the slides, then mirrors them

#include "gd.h"
#include "math.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define  RMMKDIR     "rm -rf a3d05dir ; mkdir a3d05dir"
#define  PNG2SWF     "png2swf -o a3d05.swf -r 10 a3d05dir/*.png" 
#define  PNGFMT      "a3d05dir/a%04d.png"

#define  YCENT       130
#define  WAVELENGTH  10.0         // radio wavelength in pixel units
#define  SPACE       40.0         // array spacing in pixel units
#define  NPICS       50           // ( half the ) number of frames
#define  RSPACE      30.0         // random variation

#define  TANGX       0.0          // target X angle in degrees
#define  TANGY       0.0          // target Y angle in degrees
#define  DANGX       0.0          // display center X angle in degrees
#define  DANGY       0.0          // display center Y angle in degrees

#define  DANGW       150.0        // display angle width in degrees
#define  DANGS       10.0         // display angle step

#define  ZSCALE      400.0        // depth scaling
// #define  TILT        0.04         // array tilt around X axis, radians
#define  TILT        0.00         // array tilt around X axis, radians
#define  SPOT        10           // size of drawn spot
#define  ASIZE       5            // number of elements per side
#define  XLEFT       40           // left side pixel count
#define  XSIZE       1000         // display window in pixels
#define  YSIZE       750          // display window in pixels
#define  FNT         "DejaVuMonoSans"
#define  FSZ         10

// ==========================================================================

int main() {

   gdImagePtr im                    ; // pixel image map used by gd
   double   space  = (double) SPACE ; // nominal spacing between server-sats
   double   xleft  = (double) XLEFT ; // left of pixel array
   double   yacent = (double) YCENT ; // center of upper drawing
   double   xsize  = (double) XSIZE ; // width of pixels
   double   ysize  = (double) YSIZE ; // height of pixels
   double   dangw  = (double) DANGW ; // degree width of output pixel array
   double   dangx  = (double) DANGX ; // degree center of output pixel array
   double   dangy  = (double) DANGY ; // degree center of output pixel array
   double   dangw2 = 0.5*dangw      ; // half width of output pixel array
   double   xwide  = xsize-xleft    ; // pixel width of output pixel array
   int      ybot   = 2*YCENT        ; // top of output pixel array
   double   dybot  = (double) ybot  ; // real ""
   double   ytall  = ysize-dybot    ; // height of output pixel array
   double   pi2    = 8.0*atan(1.0)  ; // two times PI
   double   d2r    = pi2/360.0      ; // degrees to radians conversion factor
   int      oxc    =(XSIZE+XLEFT)/2 ; // center of output pixel array
   int      oyc    =(YSIZE+ybot)/2  ; // center of output pixel array
   double   xacent = (double) oxc   ; // real ""
   double   aspect =ytall/xwide     ; // aspect ration of output pixel array
   double   angH   =dangw*aspect    ; // angular height of output pixel array
   double   angH2 = 0.5*angH        ; // half height of output pixel array

   double   angx                    ; // pixel X angle radians
   double   angx0=d2r*(dangx-dangw2); // left pixel value, radians
   double   angxs=d2r*dangw/xwide   ; // pixel X slope radians per pixel
   double   angxf=angx0-angxs*xleft ; // pixel X offset radians

   double   angy                    ; // pixel Y angle
   double   angy0=d2r*(dangy+angH2) ; // top pixel value, radians
   double   angys=d2r*angH/ytall    ; // pixel Y slope radians
   double   angyf=angy0-angys*ysize ; // pixel Y offset radians

   double   k     = pi2/WAVELENGTH  ; // wavenumber
   double   rspace= RSPACE          ; // random spacing amount
   double   tilt  = TILT            ; // tilt of array (around Y) in radians
   FILE     *pngout                 ; // file handle for PNG output frame
   char     dirname[80]             ; // frame output directory
   char     framename[80]           ; // directory/name of frame
   char     labstring[80]           ; // used for labellin
   char     link0[80]               ; // directory/name of source 
   char     link1[80]               ; // directory/name of target hardlink
   int      black, sun1, white, red ; // color map numbers
   int      green, blue, gray       ; // color map numbers
   int      dgray, trans            ; // color map numbers
   int      gcolor[256]             ; // color map numbers
   double   ax[ASIZE][ASIZE][ASIZE] ; // x position of element
   double   ay[ASIZE][ASIZE][ASIZE] ; // y position of element
   double   az[ASIZE][ASIZE][ASIZE] ; // z position of element
   double   rx[ASIZE][ASIZE][ASIZE] ; // x variation of element
   double   ry[ASIZE][ASIZE][ASIZE] ; // y variation of element
   double   rz[ASIZE][ASIZE][ASIZE] ; // z variation of element
   double   ap[ASIZE][ASIZE][ASIZE] ; // phase of element
   double   xx, yy, zz              ; //
   double   xr, yr, zr              ; //
   double   bx, by, bz              ; // coordinates rotated on y axis
   double   cx, cy, cz              ; // coordinates rotated on x axis
   int      ix, iy, iz              ; // integer counters
   int      ox, oy                  ; // output pixel position
   int      i                       ; // general counter
   double   zscale                  ; // scaling for perspective depth
   int      icolor                  ; //
   int      cir                     ; //
   double   stilt = sin(tilt)       ; // sin of rotation (around y) of array
   double   ctilt = cos(tilt)       ; // cos of rotation (around y) of array
   int      mag                     ; // pixel magnitude, 0-255
   double   ss, cc                  ; // summed sin and cos components
   double   stx = sin(TANGX )       ; // sin of X target angle
   double   ctx = cos(TANGX )       ; // cos of X target angle
   double   sty = sin(TANGY )       ; // sin of Y target angle
   double   cty = cos(TANGY )       ; // cos of Y target angle
   double   spx                     ; // sin of pixel X angle
   double   cpx                     ; // cos of pixel X angle
   double   spy                     ; // sin of pixel Y angle
   double   cpy                     ; // cos of pixel Y angle
   double   phase                   ; // phase
   int      xm                      ; // tickmark max
   int      xs                      ; // tickmark scale
   int      frame                   ; // animation frame count
   int      pct=0                   ; // percentage of complete frame
   int      pct0=0                  ; // previous percentage
   double   rfactor=0.0             ; // random amount added to position
   int      asize = ASIZE           ; // nominal spacing of server-sats
   double   offset=0.5*(1.0-asize)  ; // offsets counter integer to center
   double   asize3=asize*asize*asize; //
   double   norm = 255.0 /asize3    ; // normalizes summed amplitude
#ifdef DEBUG
   printf( "%12.6f%12.6f%15.9f\n",  angx0, angxf, angxs );
   printf( "%12.6f%12.6f%15.9f\n",  angy0, angyf, angys );
#endif

//  set up target directory

   system( RMMKDIR );

//  random value generation ----------------------------------------------

   for( iz=0; iz<asize ; iz++ ) {
      for( iy=0; iy<asize ; iy++ ) {
         for( ix=0; ix<asize ; ix++ ) {
            rx[ix][iy][iz]= rspace* ( drand48()-0.5 ) ;
            ry[ix][iy][iz]= rspace* ( drand48()-0.5 ) ;
            rz[ix][iy][iz]= rspace* ( drand48()-0.5 ) ;
   }  }  }

//  outer loop, sinusodial random factor change --------------------------
//  from 0 to 1 to 0
  
   for( frame=0 ; frame<NPICS ; frame++ ) {
      rfactor =0.5*(1.0-cos( pi2*( ((double)frame)/((double)(NPICS-1)) )));

#ifndef SINGLE
      printf( "%04d/%04d\r", frame, NPICS ) ;
      fflush(stdout);
#endif

//  define array with even spacing ---------------------------------------

   for( iz=0; iz<asize ; iz++ ) {
      zz = space*(((double)iz)+offset );
      for( iy=0; iy<asize ; iy++ ) {
         yy = space*(((double)iy)+offset) ;
         zr = zz*ctilt - yy*stilt;
         yr = yy*ctilt + zz*stilt;
         xr = -2.0*yr ;
         for( ix=0; ix<asize ; ix++ ) {
            // compute array positions 
            xx = xr + space*(((double)ix)+offset) ;
            ax[ix][iy][iz] = xx + rfactor*rx[ix][iy][iz] ;
            ay[ix][iy][iz] = yr + rfactor*ry[ix][iy][iz] ;
	    az[ix][iy][iz] = zr + rfactor*rz[ix][iy][iz] ;

            // compute target angle

            // first rotation, Y axis, by TANGX
            // bx = xx*ctx - zr*stx ;
            // by = yr ;
            // bz = zr*ctx + xx*stx ;

            // second rotation, X axis, by TANGY 
            // cx = bx ;
            // cy = by*cty - bz*sty ;
            // cz = bz*cty + by*sty ;

            // ap[ix][iy][iz] = -k*cz    ;

            // combine the math to find element phase
            ap[ix][iy][iz] = -k*( ( az[ix][iy][iz]*ctx
                                   +ax[ix][iy][iz]*stx)*cty 
                                 +  ay[ix][iy][iz]*sty )  ;
         }
   }  }
#ifdef SINGLE
   printf( "array setup done\n");
#endif

// set up array and define colors -----------------------------------------

   im = gdImageCreateTrueColor(XSIZE, YSIZE );

   // Allocate standard colors
   black = gdImageColorAllocate(im,   0,   0,   0);
   white = gdImageColorAllocate(im, 255, 255, 255);
   sun1  = gdImageColorAllocate(im,  51,  51, 102);
   red   = gdImageColorAllocate(im, 255,   0,   0);
   green = gdImageColorAllocate(im,   0, 255,   0);
   blue  = gdImageColorAllocate(im,   0,   0, 255);
   gray  = gdImageColorAllocate(im, 128, 128, 128);
   dgray = gdImageColorAllocate(im,  48,  48,  48);
   trans = gdImageColorAllocate(im, 1, 1, 1);

   // allocate gray scale array 
   for( icolor=0 ; icolor<256 ; icolor++ ) {
     gcolor[icolor] = gdImageColorAllocate(im, icolor, icolor, icolor);
   }

#ifdef SINGLE
   printf( "color setup done\n");
#endif

//  draw array with perspective ---------------------------------------------

   for( iz=0; iz<asize ; iz++ ) {
      for( iy=0; iy<asize ; iy++ ) {
         for( ix=0; ix<asize ; ix++ ) {
            zscale = 1.0 / ( 1.0- az[ix][iy][iz]/ZSCALE ) ;
            cir    = (int) ( zscale * SPOT );
            ox     = (int) ( xacent + zscale*ax[ix][iy][iz] ) ;
            oy     = (int) ( yacent + zscale*ay[ix][iy][iz] ) ;
            icolor = 180 + (int)(30.0*az[ix][iy][iz]/space ) ;
            if( icolor > 255 ) { icolor = 255 ; }
            gdImageFilledEllipse(im, ox, oy, cir, cir, gcolor[icolor] );
   }  }  }

#ifdef SINGLE
   printf( "array drawn\n");
#endif

//   now compute phases

   for( ox=XLEFT ; ox<XSIZE ; ox++ ) {
      angx  =  angxf + angxs*((double)ox );
      spx   = sin( angx );
      cpx   = cos( angx );
 
      pct0  = pct ;
      pct   = (100*(ox-XLEFT))/(XSIZE-XLEFT) ;

#ifndef SINGLE
      if( pct != pct0 ) {
         printf( "%04d/%04d%4d\r", frame, NPICS, pct ) ;
         fflush(stdout);
      }
#endif

      for( oy=ybot ; oy<YSIZE ; oy++ ) {
         angy  =  angyf + angys*((double)oy );
         spy   = sin( angy );
         cpy   = cos( angy );

         // sum the components
         ss = 0.0 ;
         cc = 0.0 ;

#ifdef DEBUG
         if( ( ox==oxc ) && ( oy==oyc ) ) {
              printf( "%4d%5d%12.6f%12.6f%12.6f%12.6f\n\n", ox, oy,
                              cpx, spx, cpy, spy );
         }
#endif

         // compute z distance and phase of each source to plane
         for( iz=0; iz<asize ; iz++ ) {
            for( iy=0; iy<asize ; iy++ ) {
               for( ix=0; ix<asize ; ix++ ) {

                  // xr = ax[ix][iy][iz] ;
                  // yr = ax[ix][iy][iz] ;
                  // zr = az[ix][iy][iz] ;

                  // compute pixel angle
                  // first rotation, Y axis, by TANGX
                  // bx = xr*cpx - zr*spx ;
                  // by = yr ;
	          // bz = zr*cpx + xr*spx ;
   
                  // second rotation, X axis, by TANGY 
   
                  // cx = bx ;
                  // cy = by*cpy - bz*spy ;
                  // cz = bz*cpy + by*spy ;
   
                  // ap[ix][iy][iz] = -k*cz    ;
                  // ap[ix][iy][iz] = -k*( (zr*cpx+xr*spx)*cpy + yr*spy);
   
                  phase = k*( ay[ix][iy][iz]*spy +
                          ( az[ix][iy][iz]*cpx + ax[ix][iy][iz]*spx)*cpy ) ;
                  ss += sin( phase + ap[ix][iy][iz] );
                  cc += cos( phase + ap[ix][iy][iz] );

#ifdef DEBUG
                  if( ( ox==oxc ) && ( oy==oyc ) ) {
                     printf( "%3d%3d%3d%12.6f%12.6f%12.6f%12.6f\n", ix,iy,iz,
                              phase, ap[ix][iy][iz], ss, cc );
                  }
#endif
               }
            }
         } 

         mag = (int) (norm*sqrt( ss*ss + cc*cc ));
         if( abs(mag-181) < 10 ) {
            gdImageSetPixel( im, ox, oy, red         ) ;
         }
         else {
            gdImageSetPixel( im, ox, oy, gcolor[mag] ) ;
         }
   }  }

#ifdef SINGLE
   printf( "intensity points drawn\n");
#endif

   // draw tickmarks on edges ----------------------------------------------

   // draw x tickmarks 
   xm = (int) (0.001+0.5*(DANGW/DANGS)) ; 
   xs = (XSIZE-XLEFT)*DANGS/DANGW;
   for( ix = -xm ; ix <= xm ; ix++ ) {
      ox = oxc + (int) ( xs*ix ) ;
      gdImageLine( im, ox, ybot, ox, ybot-5, white );

      sprintf( labstring, "%3d", ( (int)( ix*DANGS ) ) );
      gdImageStringFT( im, NULL,                   // imagespace, bounding
                       white, FNT, FSZ,            // color, font, fontsize
                       0.0, ox+FSZ/2, ybot-FSZ/2,  // angle, x, y
                       labstring );                // the text

   }
   ox = (int) ( xacent ) ;
   gdImageLine( im, xacent, ybot, ox, ybot-10, white );

   // draw y tickmarks
   xm = (int) (0.001+0.5*(angH/DANGS)) ; 
   for( ix = -xm ; ix <= xm ; ix++ ) {
      oy = oyc + (int) ( xs*ix ) ;
      gdImageLine( im, xleft, oy,  xleft-5, oy, white );

      sprintf( labstring, "%3d", ( (int)( ix*DANGS ) ) );
      gdImageStringFT( im, NULL,                   // imagespace, bounding
                       white, FNT, FSZ,            // color, font, fontsize
                       0.0, xleft-4*FSZ, oy+FSZ/2, // angle, x, y
                       labstring );                // the text

   }
   ox = (int) ( xacent ) ;
   gdImageLine( im, xleft, oyc, xleft-10, oyc, white );

   // output the frame ------------------------------------------------------

   sprintf( framename, PNGFMT , frame );
   pngout = fopen( framename, "wb");
   gdImagePngEx( im, pngout, 1 );
   gdImageDestroy(im);
   fclose(pngout);

}  // end of single frame

   // link to the other half of the sequence
   for( i=0 ; i < NPICS ; i++ ) {
      sprintf( link0 , PNGFMT, i );
      sprintf( link1 , PNGFMT, 2*NPICS-i );
      link( link0, link1 ) ;
   }

   system( PNG2SWF );
}


