/*
** psdraw.c
*/

#include <stdio.h>
#include <stdlib.h>
#include "peek.h"

/*
** psdraw_surface()
** Assumes that the object has been unedged and oriented, and 
** processed so that verts know their screen coords and faces
** know of they're visible.
** 
** given a surface and a unitary light vector, draws the surface
** according to the parameters passed.
*/
int
psdraw_surface(FILE *file, Part *surf, Enviro *env, int page, char *text) {
  static int flip = 0;  /* equals 0 or 1 depending if drawing on
			   lower or upper half of the page */
  int i, edges;
  Part *face = NULL, *vert, *vert2;
  Image *face_im, *vert_im;
  float *norm = NULL, to_eye[THREE], gray, dot;
  char err2[ERRSTRLEN];
  
#ifdef DEBUG
  printf("psdraw_surface(%d): hi, surf->d = %d\n", surf->ID, surf->d);
#endif
  if (3 < surf->d) {
    sprintf(err2, "psdraw_surface: can't draw %d-D surface (only <= 3)\n",
	    surf->d);
    strcat(err, err2); return(-1);
  }
  if (3 == surf->d) {
    /* first shade in the visible faces */
    edges = (env->frontwidth) || (env->backwidth);
    face_im = NULL;
    if (edges)
      fprintf(file, "gsave\n");
    do {
      face_im = (!face_im ? surf->thought : face_im->next);
      face = face_im->sense;
#ifdef DEBUG
      printf("psdraw_surface(%d): testing face %d for shading\n",
	     surf->ID, face->ID);
#endif
      if (face->spare) {
#ifdef DEBUG
	fprintf(file, "%%%% shaded face %d\n", face->ID);
	printf("psdraw_surface(%d): will shade face %d\n",
	       surf->ID, face->ID);
#endif
	fprintf(file, "newpath\n");
	vert_im = NULL;
	do {
	  vert_im = (!vert_im ? face->thought : vert_im->next);
	  vert = vert_im->sense;
#ifdef DEBUG
	  printf("psdraw_surface(%d): vert %d of shaded face %d\n",
		 surf->ID, vert->ID, face->ID);
#endif
	  fprintf(file, "%g %g %s\n", 
		  flip*612 +(1 -2*flip)*(env->scale*72
					 * (vert->coord_screen[1]) +180),
		  flip*792 +(1 -2*flip)*(env->scale*72
					 * (-vert->coord_screen[0])+180),
		  (vert == face->thought->sense ? "moveto" : "lineto"));
	} while (vert_im->next);
	fprintf(file, "closepath\n");
	norm = face->norm;
#ifdef DEBUG
	printf("psdraw_surface: on face %d:\n", face->ID);
	printf("norm = %g %g %g\nlight = %g %g %g\n",
	       norm[0], norm[1], norm[2], 
	       (env->light)[0], (env->light)[1], (env->light)[2]);
#endif
	gray = norm[0]*(env->light)[0] +norm[1]*(env->light)[1] 
	  +norm[2]*(env->light)[2];
	gray = env->amb + (1 - env->amb)*(gray + 1)*(gray + 1)*0.25;
	fprintf(file, "%g setgray fill\n", gray);
      }
    } while (face_im->next);
    if (edges)
      fprintf(file, "grestore\n");
#ifdef DEBUG
    printf("psdraw_surface(): done with shaded faces\n");
#endif
    /* now draw the wire frame on top of the shaded faces */
    face_im = NULL;
    do {
      face_im = (!face_im ? surf->thought : face_im->next);
      face = face_im->sense;
      if ((env->frontwidth && face->spare) 
	  || (env->backwidth && !(face->spare))) {
	/* if we are supposed to draw edges on this kind of face */
#ifdef DEBUG
	fprintf(file, "%%%% edges of face %d\n", face->ID);
#endif
	fprintf(file, "newpath\n");
	vert_im = NULL;
	do {
	  vert_im = (!vert_im ? face->thought : vert_im->next);
	  vert = vert_im->sense;
	  fprintf(file, "%g %g %s\n",
		  flip*612 + (1 - 2*flip)*(env->scale*72
					   * (vert->coord_screen[1]) +180),
		  flip*792 + (1 - 2*flip)*(env->scale*72
					   * (-vert->coord_screen[0])+180),
		  (vert == face->thought->sense ? "moveto" : "lineto"));
	} while (vert_im->next);
	fprintf(file, "closepath\n");
	fprintf(file, "1 setlinejoin\n");
	fprintf(file, "%g setlinewidth\n",
		((face->spare) ? env->frontwidth : env->backwidth));
	fprintf(file, "stroke\n");
      }
    } while (face_im->next);
  }    
  else if (2 == surf->d) {
#ifdef DEBUG
    printf("psdraw_surface(%d): drawing single face...\n", surf->ID);
#endif
    /* single face is visible, so draw it */
    fprintf(file, "newpath\n");
    vert_im = NULL;
    do {
      vert_im = (!vert_im ? surf->thought : vert_im->next);
      vert = vert_im->sense;
#ifdef DEBUG
      printf("psdraw_surface(%d): vert %d of shaded face/surface %d\n",
	     surf->ID, vert->ID, surf->ID);
#endif
      fprintf(file, "%g %g %s\n", 
	      flip*612 +(1 -2*flip)*(env->scale*72
				     * (vert->coord_screen[1]) +180),
	      flip*792 +(1 -2*flip)*(env->scale*72
				     * (-vert->coord_screen[0])+180),
	      (vert == surf->thought->sense ? "moveto" : "lineto"));
    } while (vert_im->next);
    fprintf(file, "closepath\n");
    /* to determine how to shade face, check to see if we are on the
       lit side or the shadow side of the face */
    vert = surf->thought->sense;
    for (i=0; i<=THREE; i++ )
      to_eye[i] = env->eye[i] - vert->coord[i];
    dot = env->light[0]*to_eye[0] + env->light[1]*to_eye[1] 
      + env->light[2]*to_eye[2];
    /* dot > 0 : we're on the lit side; 
       dot < 0 : we're on shadow side */
    gray = norm[0]*(env->light)[0] +norm[1]*(env->light)[1] 
      +norm[2]*(env->light)[2];
    /* if we're on the shadow side and gray > -gray, then gray represents
       gray for lit side, so set it to -gray, the gray for the shadow side */
    if (dot < 0 && gray > -gray)
      gray = -gray;
    gray = env->amb + (1 - env->amb)*(gray + 1)*(gray + 1)*0.25;
    fprintf(file, "%g setgray fill\n", gray);
    /* now draw edges for this face */
    if (env->frontwidth) {
      fprintf(file, "newpath\n");
      vert_im = NULL;
      do {
	vert_im = (!vert_im ? face->thought : vert_im->next);
	vert = vert_im->sense;
	fprintf(file, "%g %g %s\n",
		flip*612 + (1 - 2*flip)*(env->scale*72
					 * (vert->coord_screen[1]) +180),
		flip*792 + (1 - 2*flip)*(env->scale*72
					 * (-vert->coord_screen[0])+180),
		(vert == face->thought->sense ? "moveto" : "lineto"));
      } while (vert_im->next);
      fprintf(file, "closepath\n");
      fprintf(file, "1 setlinejoin\n");
      fprintf(file, "%g setlinewidth\n", env->frontwidth);
      fprintf(file, "stroke\n");
    }
  }
  else if (1 == surf->d) {
#ifdef DEBUG
    printf("psdraw_surface(%d): drawing single edge\n", surf->ID);
#endif
    vert = surf->thought->sense;
    vert2 = surf->thought->next->sense;
    fprintf(file, "newpath\n");
    fprintf(file, "%g %g moveto\n",
	    flip*612 + (1 - 2*flip)*(env->scale*72 * 
				      (vert->coord_screen[1]) +180),
	    flip*792 + (1 - 2*flip)*(env->scale*72 * 
				     (-vert->coord_screen[0])+180));
    fprintf(file, "%g %g lineto\n",
	    flip*612 + (1 - 2*flip)*(env->scale*72 * 
				      (vert2->coord_screen[1]) +180),
	    flip*792 + (1 - 2*flip)*(env->scale*72 * 
				     (-vert2->coord_screen[0])+180));
    fprintf(file, "1 setlinejoin\n");
    fprintf(file, "%g setlinewidth\n", env->frontwidth);
    fprintf(file, "stroke\n");
  }
  else /* 0 == surf->d */ {
#ifdef DEBUG
    printf("psdraw_surface(%d): drawing single point\n", surf->ID);
#endif
    fprintf(file, "newpath\n");
    if (env->frontwidth) {
      fprintf(file, "%g %g moveto\n",
	      flip*612 + (1 - 2*flip)*(env->scale*72 * 
				       (surf->coord_screen[1]) +180),
	      flip*792 + (1 - 2*flip)*(env->scale*72 * 
				       (-surf->coord_screen[0])+180));
      fprintf(file, "%g %g lineto\n",
	      flip*612 + (1 - 2*flip)*(env->scale*72 * 
				       (surf->coord_screen[1]) +180),
	      flip*792 + (1 - 2*flip)*(env->scale*72 * 
				       (-surf->coord_screen[0])+180));
      fprintf(file, "1 setlinejoin\n");
      fprintf(file, "%g setlinewidth\n", env->frontwidth);
      fprintf(file, "stroke\n");
    }
  }
  if (text) {
    fprintf(file, "gsave\n");
    fprintf(file, "%d %d translate\n",
	    flip*612 + (1 - 2*flip)*530, flip*792 + (1 - 2*flip)*198);
    fprintf(file, "%d rotate\n", (2*flip - 1)*90);
    fprintf(file, "/Times-Roman findfont\n30 scalefont\nsetfont\n0 0 moveto");
    fprintf(file, "(%s) show\n", text);
    fprintf(file, "grestore\n");
  }
  if (flip || page) {
    fprintf(file, "showpage\n\n");
  }
  if (!page)
    flip = 1 - flip;
  return(0);
}

/* psslice_4d()
** given an object, an initial position matrix, an interframe matrix,
** and the number of frames, writes to given filename the postscript
** file which shows the series of cross-sections of the object as
** manipulated by the given matrices.
**
** NOTE: for time being, only draws first surface of first piece
** (calls psdraw_surface() instead of non-existant psdraw_object() )
*/
int
psslice_4d(char *filename, Piece *object, Matx *init, Matx *delta, 
	   Enviro *env, int frames) {
  FILE *file, *joe;
  Piece *cross_object;
  int i, three;
  char err2[ERRSTRLEN], text[3];

  if (4 != object->space_d) {
    sprintf(err, "psslice_4d: object must be 4-dimensional (space_d = 4)\n");
    return(-1);
  }

  file = fopen(filename, "w");
  fprintf(file, "%%!\n");
  xform(object, init);
  for (i=1; i<=frames; i++) {
    cross_object = NULL;
    if (slice_object(object, &cross_object, ZERO, 0)) {
      sprintf(err2, "psslice_4d: on frame %d of %d\n", i, frames);
      strcat(err, err2);  return(-1);
    }
    if (cross_object) { 
      if (orient_object(cross_object)) {
	sprintf(err2, "psslice_4d: on frame %d of %d\n", i, frames);
	strcat(err, err2);  return(-1);
      }
      if (WtoS_sysI(cross_object, env)) {
	sprintf(err2, "psslice_4d: on frame %d of %d\n", i, frames);
	strcat(err, err2); return(-1);
      }
      sprintf(text, "%d", i);
      /* this should really be psdraw_object, but it doesn't exist yet */
      if (psdraw_surface(file, cross_object->thought->sense, env, 
		     (i == frames ? 1 : 0), text)) {
	sprintf(err2, "psslice_4d: on frame %d of %d\n", i, frames);
	strcat(err, err2); return(-1);
      }
      dispose_object(cross_object);
    } 
    xform(object, delta);
  }
  fclose (file);
  return(0);
}
