// echrome04.c
// compile:  gcc -o echrome04 echrome04.c -lgd -lpng -lm
//
//  Plating and unplating an electrochromic thruster

#define  NAME  "echrome04"
#define  RATE   10  

#define FNT    "DejaVuMonoSans"

#define FSZ     20               // this scales the font and the whole drawing
#define NPICS   50               // 

#define LINETHICK 1
#define XSIZE   (60*FSZ)
#define YSIZE   (32*FSZ)
#define WIDE    ( 2*FSZ)
#define TALL    (16*FSZ)
#define XCENT   (30*FSZ)
#define YCENT   (16*FSZ)
#define ITILT   0.18

#define  ASIZE  2 

#include <gd.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>

#define PI 3.14159265359
#define DEG2RAD(x) ((x)*PI/180.)

// ----------------------------------------------------
char     rmmkdir[200] ;
char     pngfmt[ 200] ;
double   ffrac                ; // frame fraction
int      sign                 ; // sign of battery

gdImagePtr im;

int      ytop= YCENT - TALL/2 ;
int      ybot= YCENT + TALL/2 ;
int      deltay = (int)( ((double)ITILT)*((double)XCENT) ) ;
int      black, white, gray   ;
int      red,   lred          ;
int      green, lgreen        ;
int      sun1, dgray, trans   ;
int      light[256]           ; // Yellow color range
int      light1               ;
double   pi2                  ; //  
double   atom[1000]           ;

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

//----------------------------------------
void atom_init() {
   int  y  ;
   for( y=ytop ; y<= ybot ; y += ASIZE ) {
      atom[y]=drand48() ;
}  }

//----------------------------------------
void frame_init() {
   im     = gdImageCreateTrueColor(XSIZE, YSIZE);

   white  = gdImageColorAllocate (im, 255, 255, 255);
   black  = gdImageColorAllocate (im,   0,   0,   0);
   gray   = gdImageColorAllocate (im, 127, 127, 127);
   dgray  = gdImageColorAllocate (im,  64,  64,  64);
   red    = gdImageColorAllocate (im, 255,   0,   0);
   green  = gdImageColorAllocate (im,   0, 255,   0);
   lred   = gdImageColorAllocate (im, 255, 224, 224);
   lgreen = gdImageColorAllocate (im, 224, 255, 224);
   sun1   = gdImageColorAllocate (im,   0,  51, 102);
   trans  = gdImageColorAllocate (im,   1,   1,   1);
}

//----------------------------------------
void light_init() {
   int i          ;
   int rx, gx, bx ;
   double di      ;

   for( i=0 ; i < 256 ; i++ ) {
      di        = (double) i                             ;
      int rx    = (double)(                         di ) ;
      int gx    = (double)(  51.0 + (204.0/256.0) * di ) ;
      int bx    = (double)( 102.0 - (102.0/256.0) * di ) ;

      // printf( "%3d%4d%4d%4d\n", i, rx, gx, bx );

      light[i] = gdImageColorAllocate( im, rx, gx, bx ) ;
   }
   sun1   = light[0] ;
   light1 = light[255] ;
}  

//----------------------------------------
void draw_background() {
   gdImageFilledRectangle( im, 0, 0, XSIZE-1, YSIZE-1, sun1 );
}

//----------------------------------------
void draw_light() {

   int    light01  = light[ 127 ] ;

   // gamma is funny, so this corrects additive light
   int    light02  = light[ ((int)(127.0 * (1.0+0.3*sqrt(ffrac)))) ] ;

   int    light03  = light[ ((int)(127.0 *      ffrac ))     ] ;
   int    light04  = light[ ((int)(127.0 * (1.0-ffrac)))     ] ;

   gdPoint  alight[8] ;

   // incoming triangle
   alight[0].x = 0             ;
   alight[0].y = ytop - deltay ;
   alight[1].x = 0             ;
   alight[1].y = ytop + deltay ;
   alight[2].x = XCENT         ;
   alight[2].y = ytop          ;
   gdImageFilledPolygon( im , alight, 3 , light01 ) ;

   // reflected addition 
   alight[0].x = 0             ;
   alight[0].y = ytop + deltay ;
   alight[1].x = 0             ;
   alight[1].y = ybot - deltay ;
   alight[2].x = XCENT         ;
   alight[2].y = ybot          ;
   alight[3].x = XCENT         ;
   alight[3].y = ytop          ;
   gdImageFilledPolygon( im , alight, 4 , light02 ) ;

   // reflected triangle
   alight[0].x = 0             ;
   alight[0].y = ybot + deltay ;
   alight[1].x = 0             ;
   alight[1].y = ybot - deltay ;
   alight[2].x = XCENT         ;
   alight[2].y = ybot          ;
   gdImageFilledPolygon( im , alight, 3 , light03 ) ;

   // passthrough 
   alight[0].x = XSIZE-1       ;
   alight[0].y = ytop + deltay ;
   alight[1].x = XSIZE-1       ;
   alight[1].y = ybot + deltay ;
   alight[2].x = XCENT         ;
   alight[2].y = ybot          ;
   alight[3].x = XCENT         ;
   alight[3].y = ytop          ;
   gdImageFilledPolygon( im , alight, 4 , light04 ) ;
}

//----------------------------------------
void draw_sun() {
   gdImageFilledRectangle( im, 0, ytop-deltay, 10, ybot-deltay , light1    );
}

//----------------------------------------
void draw_atoms() {
   int  y  ;
   double a = 0.5+(double) XCENT      ;
   double f = ((double)(WIDE-ASIZE))*(1.0-ffrac) ;
   for( y=ytop ; y< ybot ; y += ASIZE ) {
      int x = (int)(a+f*atom[y])   ;
      gdImageFilledRectangle( im, x, y, x+ASIZE, y+ASIZE, white );
}  }


//----------------------------------------
void draw_frame() {
 
   int ffx, fx1, fx2, ffy, fww, fw2, xcc ;
   int yb1, yb2, yb3, yb4, yb5      ;
   int xb0, xb1, xb2, xb3, xb4, xb5 ;
   int xc0, xc1, xc2, xc3, xc4, xc5 ;
   int yc1, yc2           ;

   ffx = (int)      FSZ   ;
   fx1 = (int) (0.7*FSZ)  ; 
   fx2 = (int) (0.5*FSZ)  ;
   ffy = (int) (0.7*FSZ)  ;
   fww = (int)    (WIDE)  ; 
   fw2 = (int)  (WIDE/2)  ; 
   xcc = (int)   (XCENT)  ;

   // printf( "%5d %5d %5d %5d %5d %5d\n", ybot, xcc, ffx, ffy, fx2, fw2 );

   yb1 = ybot +     ffy   ;
   yb2 = ybot + 2 * ffy   ;
   yb3 = ybot + 3 * ffy   ;  // center
   yb4 = ybot + 4 * ffy   ;
   yb5 = ybot + 5 * ffy   ;
 
   xb0 = xcc  - fw2       ;
   xb1 = xcc  + fw2 - fx1 ;
   xb2 = xb1  +       fx2 ;
   xb3 = xb2  +       fx2 ;
   xb4 = xb3  +       fx2 ;
   xb5 = xcc  + fw2 * 3   ;

   xc0 = xcc  - 2*fww     ; 
   xc1 = xc0  + ffx       ;
   xc2 = xc1  + ffx       ;
   xc3 = xcc  + 3*fww     ;
   xc4 = xc3  - ffx       ;
   xc5 = xc4  - ffx       ;
   yc1 = yb3  - ffx       ;
   yc2 = yb3  + ffx       ;

   // printf( "%5d %5d %5d %5d %5d %5d  %5d %5d %5d %5d %5d\n", 
   //          xb0,xb1,xb2,xb3,xb4,xb5, yb1,yb2,yb3,yb4,yb5     );

   gdImageSetThickness( im, 3 );
   gdImageRectangle( im, xcc-fww, ybot, xcc     ,  ytop, white );
   gdImageRectangle( im, xcc+fww, ybot, xcc+2*fww, ytop, white );
 
   // battery
   gdImageSetThickness( im, 5 );

   gdImageLine(      im,     xb0, ybot,       xb0,  yb3, white );
   gdImageLine(      im,     xb0,  yb3,       xb1,  yb3, white );
   gdImageLine(      im,     xb1,  yb1,       xb1,  yb5, white );
   gdImageLine(      im,     xb2,  yb2,       xb2,  yb4, white );
   gdImageLine(      im,     xb3,  yb1,       xb3,  yb5, white );
   gdImageLine(      im,     xb4,  yb2,       xb4,  yb4, white );
   gdImageLine(      im,     xb5,  yb3,       xb4,  yb3, white );
   gdImageLine(      im,     xb5, ybot,       xb5,  yb3, white );
 
   gdImageSetThickness( im, 7 );

   if(      sign == 0 ) {
      gdImageLine(   im,     xc0, yb3,        xc2,  yb3, green );
      gdImageLine(   im,     xc1, yc1,        xc1,  yc2, green );
      gdImageLine(   im,     xc3, yb3,        xc5,  yb3, red   );
   }
   else if( sign == 1 ) {
      gdImageLine(   im,     xc0, yb3,        xc2,  yb3, red   );
      gdImageLine(   im,     xc4, yc1,        xc4,  yc2, green );
      gdImageLine(   im,     xc3, yb3,        xc5,  yb3, green );
   }
   gdImageSetThickness( im, 1 );
}

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

int main () {
   FILE   *pngout              ;
   char   comment[80]          ;
   char   dirname[80]          ;
   char   framename[80]        ;
   int    frame                ; // frame count
   int    npics                ; // last frame count
   int    i                    ; 
   pi2    = 8.0*atan(1.0)      ; // pi

   npics  = (double)NPICS      ;

   sprintf( rmmkdir, "rm -rf %sdir ; mkdir %sdir", NAME, NAME );

   system( rmmkdir )           ; // make directory
   atom_init()                 ; // set up atom points
 
   for( frame = 0 ; frame <= npics ; frame++ ) {

      printf( "%04d/%04d\r", frame, npics );   fflush( stdout );

      frame_init()             ; // set up colors
      light_init()             ;

      // sinusoidal fraction variation
      ffrac = 0.6*(0.8333-cos( pi2*((double)frame)/npics ));

      if(        ffrac < 0.0 )   { sign=-1 ; ffrac=0.0 ; }
      else if(   ffrac > 1.0 )   { sign=-1 ; ffrac=1.0 ; }
      else if( frame < NPICS/2 ) { sign= 1 ; }
      else                       { sign= 0 ; }

      draw_background()        ; // draw sunw background
      draw_light()             ; // draw light
      draw_sun()               ; // draw sun
      draw_atoms()             ; // draw atoms
      draw_frame()             ; // draw frame and battery

      /*
      gdImageStringFT( im, NULL,  white, FNT, 1.5*FSZ, 0.0,       // TITLE
                           (int)(2.0*FSZ), (int)(2.0*FSZ),
                          "Electrochromic Light Shutter" );
      
      gdImageStringFT( im, NULL, black, FNT, 0.7*FSZ, 0.0,    // TITLE
                           (int)(XCENT+3*WIDE),
                           YSIZE-(int)( 0.5*FSZ),
                          "Polarity(?)" );
      */
      
      sprintf( framename, "%sdir/a%04d.png", NAME , frame );
      pngout = fopen ( framename , "wb");
      gdImagePngEx( im, pngout, 1 );
      gdImageDestroy (im);
      fclose (pngout);
   } // end of frame loop
}  
